blob: 793dd30ef4340b31c44807e253c029e68161b7b3 [file] [log] [blame]
Kevin Chenga2619dc2016-03-28 11:42:08 -07001# Copyright 2016 The Chromium OS Authors. All rights reserved.
2# Use of this source code is governed by a BSD-style license that can be
3# found in the LICENSE file.
4
5"""This class defines the CrosHost Label class."""
6
C Shapiro05dd3222017-09-22 10:42:33 -06007import collections
Kevin Chenga2619dc2016-03-28 11:42:08 -07008import logging
9import os
10import re
11
12import common
13
14from autotest_lib.client.bin import utils
Kevin Cheng80ad5732016-03-31 16:01:56 -070015from autotest_lib.client.common_lib import global_config
Kevin Chenga2619dc2016-03-28 11:42:08 -070016from autotest_lib.client.cros.audio import cras_utils
Kevin Chenga2619dc2016-03-28 11:42:08 -070017from autotest_lib.client.cros.video import constants as video_test_constants
18from autotest_lib.server.cros.dynamic_suite import constants as ds_constants
19from autotest_lib.server.hosts import base_label
20from autotest_lib.server.hosts import common_label
Kevin Chengd9dfa582016-05-04 09:37:34 -070021from autotest_lib.server.hosts import servo_host
Kevin Cheng80ad5732016-03-31 16:01:56 -070022from autotest_lib.site_utils import hwid_lib
Kevin Chenga2619dc2016-03-28 11:42:08 -070023
24# pylint: disable=missing-docstring
C Shapiro05dd3222017-09-22 10:42:33 -060025LsbOutput = collections.namedtuple('LsbOutput', ['unibuild', 'board'])
26
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -070027# fallback values if we can't contact the HWID server
28HWID_LABELS_FALLBACK = ['sku', 'phase', 'touchscreen', 'touchpad', 'variant', 'stylus']
29
30
C Shapiro05dd3222017-09-22 10:42:33 -060031def _parse_lsb_output(host):
Allen Lia0c7afc2019-02-26 15:50:06 -080032 """Parses the LSB output and returns key data points for labeling.
C Shapiro05dd3222017-09-22 10:42:33 -060033
Allen Lia0c7afc2019-02-26 15:50:06 -080034 @param host: Host that the command will be executed against
35 @returns: LsbOutput with the result of parsing the /etc/lsb-release output
36 """
37 release_info = utils.parse_cmd_output('cat /etc/lsb-release',
38 run_method=host.run)
C Shapiro05dd3222017-09-22 10:42:33 -060039
Allen Lia0c7afc2019-02-26 15:50:06 -080040 unibuild = release_info.get('CHROMEOS_RELEASE_UNIBUILD') == '1'
41 return LsbOutput(unibuild, release_info['CHROMEOS_RELEASE_BOARD'])
C Shapiro05dd3222017-09-22 10:42:33 -060042
Kevin Chenga2619dc2016-03-28 11:42:08 -070043
Kevin Chenga8455302016-08-31 20:54:41 +000044class BoardLabel(base_label.StringPrefixLabel):
45 """Determine the correct board label for the device."""
46
47 _NAME = ds_constants.BOARD_PREFIX.rstrip(':')
48
49 def generate_labels(self, host):
50 # We only want to apply the board labels once, which is when they get
51 # added to the AFE. That way we don't have to worry about the board
52 # label switching on us if the wrong builds get put on the devices.
53 # crbug.com/624207 records one event of the board label switching
54 # unexpectedly on us.
Allen Lia0c7afc2019-02-26 15:50:06 -080055 board = host.host_info_store.get().board
56 if board:
57 return [board]
Kevin Chenga8455302016-08-31 20:54:41 +000058 for label in host._afe_host.labels:
59 if label.startswith(self._NAME + ':'):
60 return [label.split(':')[-1]]
61
C Shapiro10970222017-10-24 08:55:55 -060062 return [_parse_lsb_output(host).board]
Kevin Chenga8455302016-08-31 20:54:41 +000063
64
C Shapirob05c00b2017-07-18 15:06:49 -060065class ModelLabel(base_label.StringPrefixLabel):
66 """Determine the correct model label for the device."""
67
68 _NAME = ds_constants.MODEL_LABEL
69
70 def generate_labels(self, host):
C Shapirod7ba4a72018-01-16 17:04:35 -070071 # Based on the issue explained in BoardLabel, return the existing
72 # label if it has already been set once.
Allen Lia0c7afc2019-02-26 15:50:06 -080073 model = host.host_info_store.get().model
74 if model:
75 return [model]
C Shapirod7ba4a72018-01-16 17:04:35 -070076 for label in host._afe_host.labels:
77 if label.startswith(self._NAME + ':'):
78 return [label.split(':')[-1]]
C Shapirob05c00b2017-07-18 15:06:49 -060079
C Shapiro32700032017-11-03 12:46:55 -060080 lsb_output = _parse_lsb_output(host)
81 model = None
82
83 if lsb_output.unibuild:
C Shapiro26fb1012017-12-14 16:38:03 -070084 test_label_cmd = 'cros_config / test-label'
85 result = host.run(command=test_label_cmd, ignore_status=True)
C Shapiro32700032017-11-03 12:46:55 -060086 if result.exit_status == 0:
87 model = result.stdout.strip()
C Shapiro26fb1012017-12-14 16:38:03 -070088 if not model:
89 mosys_cmd = 'mosys platform model'
90 result = host.run(command=mosys_cmd, ignore_status=True)
91 if result.exit_status == 0:
92 model = result.stdout.strip()
C Shapiro32700032017-11-03 12:46:55 -060093
94 # We need some sort of backwards compatibility for boards that
95 # are not yet supported with mosys and unified builds.
96 # This is necessary so that we can begin changing cbuildbot to take
97 # advantage of the model/board label differentiations for
98 # scheduling, while still retaining backwards compatibility.
99 return [model or lsb_output.board]
C Shapirob05c00b2017-07-18 15:06:49 -0600100
101
C Shapirobe0ff8d2019-06-14 10:41:43 -0600102class DeviceSkuLabel(base_label.StringPrefixLabel):
103 """Determine the correct device_sku label for the device."""
104
105 _NAME = ds_constants.DEVICE_SKU_LABEL
106
107 def generate_labels(self, host):
108 device_sku = host.host_info_store.get().device_sku
109 if device_sku:
110 return [device_sku]
111
112 mosys_cmd = 'mosys platform sku'
113 result = host.run(command=mosys_cmd, ignore_status=True)
114 if result.exit_status == 0:
115 return [result.stdout.strip()]
116
117 return []
118
119
Ned Nguyene0a619d2019-07-01 15:50:23 -0600120class BrandCodeLabel(base_label.StringPrefixLabel):
121 """Determine the correct brand_code (aka RLZ-code) for the device."""
122
123 _NAME = ds_constants.BRAND_CODE_LABEL
124
125 def generate_labels(self, host):
126 brand_code = host.host_info_store.get().brand_code
127 if brand_code:
128 return [brand_code]
129
130 mosys_cmd = 'mosys platform brand'
131 result = host.run(command=mosys_cmd, ignore_status=True)
132 if result.exit_status == 0:
133 return [result.stdout.strip()]
134
135 return []
136
137
Kevin Chenga2619dc2016-03-28 11:42:08 -0700138class BluetoothLabel(base_label.BaseLabel):
139 """Label indicating if bluetooth is detected."""
140
141 _NAME = 'bluetooth'
142
143 def exists(self, host):
C Shapirobcd9c862019-05-22 17:42:08 -0600144 # Based on crbug.com/966219, the label is flipping sometimes.
145 # Potentially this is caused by testing itself.
146 # Making this label permanently sticky.
147 info = host.host_info_store.get()
148 for label in info.labels:
149 if label.startswith(self._NAME):
150 return True
151
Kevin Chenga2619dc2016-03-28 11:42:08 -0700152 result = host.run('test -d /sys/class/bluetooth/hci0',
153 ignore_status=True)
154
155 return result.exit_status == 0
156
157
158class ECLabel(base_label.BaseLabel):
159 """Label to determine the type of EC on this host."""
160
161 _NAME = 'ec:cros'
162
163 def exists(self, host):
164 cmd = 'mosys ec info'
165 # The output should look like these, so that the last field should
166 # match our EC version scheme:
167 #
168 # stm | stm32f100 | snow_v1.3.139-375eb9f
169 # ti | Unknown-10de | peppy_v1.5.114-5d52788
170 #
171 # Non-Chrome OS ECs will look like these:
172 #
173 # ENE | KB932 | 00BE107A00
174 # ite | it8518 | 3.08
175 #
176 # And some systems don't have ECs at all (Lumpy, for example).
177 regexp = r'^.*\|\s*(\S+_v\d+\.\d+\.\d+-[0-9a-f]+)\s*$'
178
179 ecinfo = host.run(command=cmd, ignore_status=True)
180 if ecinfo.exit_status == 0:
181 res = re.search(regexp, ecinfo.stdout)
182 if res:
183 logging.info("EC version is %s", res.groups()[0])
184 return True
185 logging.info("%s got: %s", cmd, ecinfo.stdout)
186 # Has an EC, but it's not a Chrome OS EC
187 logging.info("%s exited with status %d", cmd, ecinfo.exit_status)
188 return False
189
190
Mary Ruthven935ebad2018-06-13 16:13:20 -0700191class Cr50Label(base_label.StringPrefixLabel):
192 """Label indicating the cr50 version."""
193
194 _NAME = 'cr50'
195
196 def __init__(self):
197 self.ver = None
198
199
200 def exists(self, host):
201 # Make sure the gsctool version command runs ok
202 self.ver = host.run('gsctool -a -f', ignore_status=True)
203 return self.ver.exit_status == 0
204
205
206 def generate_labels(self, host):
207 # Check the major version to determine prePVT vs PVT
Mary Ruthven35207022019-04-15 11:57:29 -0700208 version_info = re.search('RW (\d+\.(\d+)\.\d+)$', self.ver.stdout)
209 full_version = version_info.group(1)
210 major_version = int(version_info.group(2))
Mary Ruthven935ebad2018-06-13 16:13:20 -0700211 # PVT images have a odd major version prePVT have even
Mary Ruthven35207022019-04-15 11:57:29 -0700212 return [full_version, 'pvt' if (major_version % 2) else 'prepvt']
Mary Ruthven935ebad2018-06-13 16:13:20 -0700213
214
Kevin Chenga2619dc2016-03-28 11:42:08 -0700215class AccelsLabel(base_label.BaseLabel):
216 """Determine the type of accelerometers on this host."""
217
218 _NAME = 'accel:cros-ec'
219
220 def exists(self, host):
221 # Check to make sure we have ectool
222 rv = host.run('which ectool', ignore_status=True)
223 if rv.exit_status:
224 logging.info("No ectool cmd found; assuming no EC accelerometers")
225 return False
226
227 # Check that the EC supports the motionsense command
Kevin Cheng856f8a32016-03-31 16:08:08 -0700228 rv = host.run('ectool motionsense', ignore_status=True)
Kevin Chenga2619dc2016-03-28 11:42:08 -0700229 if rv.exit_status:
230 logging.info("EC does not support motionsense command; "
231 "assuming no EC accelerometers")
232 return False
233
234 # Check that EC motion sensors are active
Kevin Cheng17e2f002016-05-04 08:48:03 -0700235 active = host.run('ectool motionsense active').stdout.split('\n')
Kevin Chenga2619dc2016-03-28 11:42:08 -0700236 if active[0] == "0":
237 logging.info("Motion sense inactive; assuming no EC accelerometers")
238 return False
239
240 logging.info("EC accelerometers found")
241 return True
242
243
244class ChameleonLabel(base_label.BaseLabel):
245 """Determine if a Chameleon is connected to this host."""
246
247 _NAME = 'chameleon'
248
249 def exists(self, host):
Gregory Nisbetb2f6d792019-09-11 14:30:47 -0700250 has_chameleon = len(host._chameleon_host_list) > 0
251 # TODO(crbug.com/995900) -- debug why chameleon label is flipping
252 try:
253 logging.info("has_chameleon %s", has_chameleon)
254 logging.info("chameleon_host_list %s", getattr(host, "_chameleon_host_list", "NO_ATTRIBUTE"))
255 logging.info("host: %s chameleon_list %s", getattr(host, "chameleon_list", "NO_ATTRIBUTE"))
256 logging.info("host: %s multi_chameleon", getattr(host, "multi_chameleon", "NO_ATTRIBUTE"))
257 except:
258 pass
259 return has_chameleon
260
Kevin Chenga2619dc2016-03-28 11:42:08 -0700261
262
263class ChameleonConnectionLabel(base_label.StringPrefixLabel):
264 """Return the Chameleon connection label."""
265
266 _NAME = 'chameleon'
267
268 def exists(self, host):
howardchung83e55272019-08-08 14:08:05 +0800269 return len(host._chameleon_host_list) > 0
Kevin Chenga2619dc2016-03-28 11:42:08 -0700270
Joseph Hwangeac44312016-08-31 12:08:38 +0800271
Kevin Chenga2619dc2016-03-28 11:42:08 -0700272 def generate_labels(self, host):
howardchung83e55272019-08-08 14:08:05 +0800273 return [[chameleon.get_label()] for chameleon in host.chameleon_list]
Kevin Chenga2619dc2016-03-28 11:42:08 -0700274
275
Joseph Hwangeac44312016-08-31 12:08:38 +0800276class ChameleonPeripheralsLabel(base_label.StringPrefixLabel):
277 """Return the Chameleon peripherals labels.
278
279 The 'chameleon:bt_hid' label is applied if the bluetooth
280 classic hid device, i.e, RN-42 emulation kit, is detected.
281
282 Any peripherals plugged into the chameleon board would be
283 detected and applied proper labels in this class.
284 """
285
286 _NAME = 'chameleon'
287
288 def exists(self, host):
howardchung83e55272019-08-08 14:08:05 +0800289 return len(host._chameleon_host_list) > 0
Joseph Hwangeac44312016-08-31 12:08:38 +0800290
291
292 def generate_labels(self, host):
howardchung83e55272019-08-08 14:08:05 +0800293 labels_list = []
294
295 for chameleon, chameleon_host in \
296 zip(host.chameleon_list, host._chameleon_host_list):
297 labels = []
298 try:
299 bt_hid_device = chameleon.get_bluetooth_hid_mouse()
300 if bt_hid_device.CheckSerialConnection():
301 labels.append('bt_hid')
302 except:
303 logging.error('Error with initializing bt_hid_mouse on '
304 'chameleon %s', chameleon_host.hostname)
305
306 try:
307 ble_hid_device = chameleon.get_ble_mouse()
308 if ble_hid_device.CheckSerialConnection():
309 labels.append('bt_ble_hid')
310 except:
311 logging.error('Error with initializing ble_hid_mouse on '
312 'chameleon %s', chameleon_host.hostname)
313
314 try:
315 bt_a2dp_sink = chameleon.get_bluetooth_a2dp_sink()
316 if bt_a2dp_sink.CheckSerialConnection():
317 labels.append('bt_a2dp_sink')
318 except:
319 logging.error('Error with initializing bt_a2dp_sink on '
320 'chameleon %s', chameleon_host.hostname)
321
322 if labels != []:
323 labels.append('bt_peer')
324
325 if host.multi_chameleon:
326 labels_list.append(labels)
327 else:
328 labels_list.extend(labels)
329
330
331 logging.info('Bluetooth labels are %s', labels_list)
332 return labels_list
Shijin Abrahamff61ac32019-05-20 12:35:44 -0700333
334
Joseph Hwangeac44312016-08-31 12:08:38 +0800335
336
Kevin Chenga2619dc2016-03-28 11:42:08 -0700337class AudioLoopbackDongleLabel(base_label.BaseLabel):
338 """Return the label if an audio loopback dongle is plugged in."""
339
340 _NAME = 'audio_loopback_dongle'
341
342 def exists(self, host):
Gregory Nisbete280ea22019-08-16 17:50:03 -0700343 # Based on crbug.com/991285, AudioLoopbackDongle sometimes flips.
344 # Ensure that AudioLoopbackDongle.exists returns True
345 # forever, after it returns True *once*.
346 if self._cached_exists(host):
347 # If the current state is True, return it, don't run the command on
348 # the DUT and potentially flip the state.
349 return True
350 # If the current state is not True, run the command on
351 # the DUT. The new state will be set to whatever the command
352 # produces.
353 return self._host_run_exists(host)
354
355 def _cached_exists(self, host):
356 """Get the state of AudioLoopbackDongle in the data store"""
357 info = host.host_info_store.get()
358 for label in info.labels:
359 if label.startswith(self._NAME):
360 return True
361 return False
362
363 def _host_run_exists(self, host):
364 """Detect presence of audio_loopback_dongle by physically
365 running a command on the DUT."""
Kevin Chenga2619dc2016-03-28 11:42:08 -0700366 nodes_info = host.run(command=cras_utils.get_cras_nodes_cmd(),
367 ignore_status=True).stdout
368 if (cras_utils.node_type_is_plugged('HEADPHONE', nodes_info) and
369 cras_utils.node_type_is_plugged('MIC', nodes_info)):
370 return True
371 return False
372
373
374class PowerSupplyLabel(base_label.StringPrefixLabel):
375 """
376 Return the label describing the power supply type.
377
378 Labels representing this host's power supply.
379 * `power:battery` when the device has a battery intended for
380 extended use
381 * `power:AC_primary` when the device has a battery not intended
382 for extended use (for moving the machine, etc)
383 * `power:AC_only` when the device has no battery at all.
384 """
385
386 _NAME = 'power'
387
388 def __init__(self):
389 self.psu_cmd_result = None
390
391
392 def exists(self, host):
393 self.psu_cmd_result = host.run(command='mosys psu type',
394 ignore_status=True)
395 return self.psu_cmd_result.stdout.strip() != 'unknown'
396
397
398 def generate_labels(self, host):
399 if self.psu_cmd_result.exit_status:
400 # The psu command for mosys is not included for all platforms. The
401 # assumption is that the device will have a battery if the command
402 # is not found.
403 return ['battery']
404 return [self.psu_cmd_result.stdout.strip()]
405
406
407class StorageLabel(base_label.StringPrefixLabel):
408 """
409 Return the label describing the storage type.
410
411 Determine if the internal device is SCSI or dw_mmc device.
412 Then check that it is SSD or HDD or eMMC or something else.
413
414 Labels representing this host's internal device type:
415 * `storage:ssd` when internal device is solid state drive
416 * `storage:hdd` when internal device is hard disk drive
417 * `storage:mmc` when internal device is mmc drive
Gwendal Grignou327fec62017-07-26 15:25:43 -0700418 * `storage:nvme` when internal device is NVMe drive
Alexis Savery570e7fb2018-06-26 10:48:15 -0700419 * `storage:ufs` when internal device is ufs drive
Kevin Chenga2619dc2016-03-28 11:42:08 -0700420 * None When internal device is something else or
421 when we are unable to determine the type
422 """
423
424 _NAME = 'storage'
425
426 def __init__(self):
427 self.type_str = ''
428
429
430 def exists(self, host):
431 # The output should be /dev/mmcblk* for SD/eMMC or /dev/sd* for scsi
432 rootdev_cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
433 '. /usr/share/misc/chromeos-common.sh;',
434 'load_base_vars;',
435 'get_fixed_dst_drive'])
436 rootdev = host.run(command=rootdev_cmd, ignore_status=True)
437 if rootdev.exit_status:
438 logging.info("Fail to run %s", rootdev_cmd)
439 return False
440 rootdev_str = rootdev.stdout.strip()
441
442 if not rootdev_str:
443 return False
444
445 rootdev_base = os.path.basename(rootdev_str)
446
447 mmc_pattern = '/dev/mmcblk[0-9]'
448 if re.match(mmc_pattern, rootdev_str):
449 # Use type to determine if the internal device is eMMC or somthing
450 # else. We can assume that MMC is always an internal device.
451 type_cmd = 'cat /sys/block/%s/device/type' % rootdev_base
452 type = host.run(command=type_cmd, ignore_status=True)
453 if type.exit_status:
454 logging.info("Fail to run %s", type_cmd)
455 return False
456 type_str = type.stdout.strip()
457
458 if type_str == 'MMC':
459 self.type_str = 'mmc'
460 return True
461
462 scsi_pattern = '/dev/sd[a-z]+'
463 if re.match(scsi_pattern, rootdev.stdout):
464 # Read symlink for /sys/block/sd* to determine if the internal
465 # device is connected via ata or usb.
466 link_cmd = 'readlink /sys/block/%s' % rootdev_base
467 link = host.run(command=link_cmd, ignore_status=True)
468 if link.exit_status:
469 logging.info("Fail to run %s", link_cmd)
470 return False
471 link_str = link.stdout.strip()
472 if 'usb' in link_str:
473 return False
Alexis Savery570e7fb2018-06-26 10:48:15 -0700474 elif 'ufs' in link_str:
475 self.type_str = 'ufs'
476 return True
Kevin Chenga2619dc2016-03-28 11:42:08 -0700477
478 # Read rotation to determine if the internal device is ssd or hdd.
479 rotate_cmd = str('cat /sys/block/%s/queue/rotational'
480 % rootdev_base)
481 rotate = host.run(command=rotate_cmd, ignore_status=True)
482 if rotate.exit_status:
483 logging.info("Fail to run %s", rotate_cmd)
484 return False
485 rotate_str = rotate.stdout.strip()
486
487 rotate_dict = {'0':'ssd', '1':'hdd'}
488 self.type_str = rotate_dict.get(rotate_str)
489 return True
490
Gwendal Grignou327fec62017-07-26 15:25:43 -0700491 nvme_pattern = '/dev/nvme[0-9]+n[0-9]+'
492 if re.match(nvme_pattern, rootdev_str):
Gwendal Grignou3660c192017-12-06 10:11:23 -0800493 self.type_str = 'nvme'
Gwendal Grignou327fec62017-07-26 15:25:43 -0700494 return True
495
Kevin Chenga2619dc2016-03-28 11:42:08 -0700496 # All other internal device / error case will always fall here
497 return False
498
499
500 def generate_labels(self, host):
501 return [self.type_str]
502
503
504class ServoLabel(base_label.BaseLabel):
505 """Label to apply if a servo is present."""
506
507 _NAME = 'servo'
508
509 def exists(self, host):
Gregory Nisbet8b008f82019-08-20 13:06:19 -0700510 # Based on crbug.com/995900, Servo sometimes flips.
511 # Ensure that ServoLabel.exists returns True
512 # forever, after it returns True *once*.
513 if self._cached_exists(host):
514 # If the current state is True, return it, don't run the command on
515 # the DUT and potentially flip the state.
516 return True
517 # If the current state is not True, run the command on
518 # the DUT. The new state will be set to whatever the command
519 # produces.
520 return self._host_run_exists(host)
521
522 def _cached_exists(self, host):
523 """Get the state of Servo in the data store"""
524 info = host.host_info_store.get()
525 for label in info.labels:
526 if label.startswith(self._NAME):
527 return True
528 return False
529
530 def _host_run_exists(self, host):
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700531 """
532 Check if the servo label should apply to the host or not.
533
534 @returns True if a servo host is detected, False otherwise.
535 """
Kevin Cheng745b8162017-05-26 09:48:36 -0700536 servo_host_hostname = None
Prathmesh Prabhu37ae79b2018-09-12 10:37:44 -0700537 servo_args = servo_host.get_servo_args_for_host(host)
Kevin Cheng745b8162017-05-26 09:48:36 -0700538 if servo_args:
539 servo_host_hostname = servo_args.get(servo_host.SERVO_HOST_ATTR)
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700540 return (servo_host_hostname is not None
541 and servo_host.servo_host_is_up(servo_host_hostname))
Kevin Chenga2619dc2016-03-28 11:42:08 -0700542
543
Ilja H. Friedel50290642017-12-01 19:39:53 -0800544class ArcLabel(base_label.BaseLabel):
545 """Label indicates if host has ARC support."""
546
547 _NAME = 'arc'
548
549 @base_label.forever_exists_decorate
550 def exists(self, host):
551 return 0 == host.run(
552 'grep CHROMEOS_ARC_VERSION /etc/lsb-release',
553 ignore_status=True).exit_status
554
555
556class CtsArchLabel(base_label.StringLabel):
557 """Labels to determine the abi of the CTS bundle (arm or x86 only)."""
Rohit Makasana5a153502016-06-13 15:50:09 -0700558
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700559 _NAME = ['cts_abi_arm', 'cts_abi_x86', 'cts_cpu_arm', 'cts_cpu_x86']
Rohit Makasana5a153502016-06-13 15:50:09 -0700560
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700561 def _get_cts_abis(self, arch):
Rohit Makasana5a153502016-06-13 15:50:09 -0700562 """Return supported CTS ABIs.
563
564 @return List of supported CTS bundle ABIs.
565 """
566 cts_abis = {'x86_64': ['arm', 'x86'], 'arm': ['arm']}
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700567 return cts_abis.get(arch, [])
568
569 def _get_cts_cpus(self, arch):
570 """Return supported CTS native CPUs.
571
572 This is needed for CTS_Instant scheduling.
573 @return List of supported CTS native CPUs.
574 """
575 cts_cpus = {'x86_64': ['x86'], 'arm': ['arm']}
576 return cts_cpus.get(arch, [])
Rohit Makasana5a153502016-06-13 15:50:09 -0700577
Rohit Makasana5a153502016-06-13 15:50:09 -0700578 def generate_labels(self, host):
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700579 cpu_arch = host.get_cpu_arch()
580 abi_labels = ['cts_abi_' + abi for abi in self._get_cts_abis(cpu_arch)]
581 cpu_labels = ['cts_cpu_' + cpu for cpu in self._get_cts_cpus(cpu_arch)]
582 return abi_labels + cpu_labels
Rohit Makasana5a153502016-06-13 15:50:09 -0700583
584
Kevin Chenga2619dc2016-03-28 11:42:08 -0700585class VideoGlitchLabel(base_label.BaseLabel):
586 """Label indicates if host supports video glitch detection tests."""
587
588 _NAME = 'video_glitch_detection'
589
590 def exists(self, host):
591 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
592
593 return board in video_test_constants.SUPPORTED_BOARDS
594
595
Kevin Chenga2619dc2016-03-28 11:42:08 -0700596class InternalDisplayLabel(base_label.StringLabel):
597 """Label that determines if the device has an internal display."""
598
599 _NAME = 'internal_display'
600
601 def generate_labels(self, host):
602 from autotest_lib.client.cros.graphics import graphics_utils
603 from autotest_lib.client.common_lib import utils as common_utils
604
605 def __system_output(cmd):
606 return host.run(cmd).stdout
607
608 def __read_file(remote_path):
609 return host.run('cat %s' % remote_path).stdout
610
611 # Hijack the necessary client functions so that we can take advantage
612 # of the client lib here.
613 # FIXME: find a less hacky way than this
614 original_system_output = utils.system_output
615 original_read_file = common_utils.read_file
616 utils.system_output = __system_output
617 common_utils.read_file = __read_file
618 try:
619 return ([self._NAME]
620 if graphics_utils.has_internal_display()
621 else [])
622 finally:
623 utils.system_output = original_system_output
624 common_utils.read_file = original_read_file
625
626
627class LucidSleepLabel(base_label.BaseLabel):
628 """Label that determines if device has support for lucid sleep."""
629
630 # TODO(kevcheng): See if we can determine if this label is applicable a
631 # better way (crbug.com/592146).
632 _NAME = 'lucidsleep'
RaviChandra Sadinenic06b00e2018-11-03 09:56:11 -0700633 LUCID_SLEEP_BOARDS = ['nocturne', 'poppy']
Kevin Chenga2619dc2016-03-28 11:42:08 -0700634
635 def exists(self, host):
636 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
637 return board in self.LUCID_SLEEP_BOARDS
638
639
Kevin Cheng80ad5732016-03-31 16:01:56 -0700640class HWIDLabel(base_label.StringLabel):
641 """Return all the labels generated from the hwid."""
642
643 # We leave out _NAME because hwid_lib will generate everything for us.
644
645 def __init__(self):
646 # Grab the key file needed to access the hwid service.
647 self.key_file = global_config.global_config.get_config_value(
648 'CROS', 'HWID_KEY', type=str)
649
650
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700651 @staticmethod
652 def _merge_hwid_label_lists(new, old):
653 """merge a list of old and new values for hwid_labels.
654 preferring new values if available
655
656 @returns: list of labels"""
657 # TODO(gregorynisbet): what is the appropriate way to merge
658 # old and new information?
659 retained = set(x for x in old)
660 for label in new:
661 key, sep, value = label.partition(':')
662 # If we have a key-value key such as variant:aaa,
663 # then we remove all the old labels with the same key.
664 if sep:
665 retained = set(x for x in retained if (not x.startswith(key + ':')))
666 return list(sorted(retained.union(new)))
667
668
669 def _hwid_label_names(self):
670 """get the labels that hwid_lib controls.
671
672 @returns: hwid_labels
673 """
674 all_hwid_labels, _ = self.get_all_labels()
675 # If and only if get_all_labels was unsuccessful,
676 # it will return a falsey value.
Gregory Nisbetbfd120f2019-09-04 14:49:46 -0700677 out = all_hwid_labels or HWID_LABELS_FALLBACK
678
679 # TODO(gregorynisbet): remove this
680 # TODO(crbug.com/999785)
681 if "sku" not in out:
682 logging.info("sku-less label names %s", out)
683
684 return out
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700685
686
687 def _old_label_values(self, host):
688 """get the hwid_lib labels on previous run
689
690 @returns: hwid_labels"""
691 out = []
692 info = host.host_info_store.get()
693 for hwid_label in self._hwid_label_names():
694 for label in info.labels:
695 # NOTE: we want *all* the labels starting
696 # with this prefix.
697 if label.startswith(hwid_label):
698 out.append(label)
699 return out
700
701
Kevin Cheng80ad5732016-03-31 16:01:56 -0700702 def generate_labels(self, host):
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700703 # use previous values as default
704 old_hwid_labels = self._old_label_values(host)
Xixuan Wue63f8352019-09-13 15:18:03 -0700705 logging.info("old_hwid_labels: %r", old_hwid_labels)
Kevin Cheng80ad5732016-03-31 16:01:56 -0700706 hwid = host.run_output('crossystem hwid').strip()
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700707 hwid_info_list = []
708 try:
709 hwid_info_response = hwid_lib.get_hwid_info(
710 hwid=hwid,
711 info_type=hwid_lib.HWID_INFO_LABEL,
712 key_file=self.key_file,
713 )
Xixuan Wue63f8352019-09-13 15:18:03 -0700714 logging.info("hwid_info_response: %r", hwid_info_response)
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700715 hwid_info_list = hwid_info_response.get('labels', [])
716 except hwid_lib.HwIdException as e:
717 logging.info("HwIdException: %s", e)
Kevin Cheng80ad5732016-03-31 16:01:56 -0700718
719 for hwid_info in hwid_info_list:
Gregory Nisbet4d71e1e2019-08-30 16:02:03 -0700720 # TODO(gregorynisbet): if hwid_info is not a list, something has
721 # gone horribly wrong. Here we log the offending hwid_info object
722 # and just continue with the old values.
723 if not isinstance(hwid_info, dict):
724 logging.info("hwid_info: %s", hwid_info)
725 return old_hwid_labels
Kevin Cheng80ad5732016-03-31 16:01:56 -0700726 # If it's a prefix, we'll have:
727 # {'name': prefix_label, 'value': postfix_label} and create
728 # 'prefix_label:postfix_label'; otherwise it'll just be
729 # {'name': label} which should just be 'label'.
730 value = hwid_info.get('value', '')
731 name = hwid_info.get('name', '')
732 # There should always be a name but just in case there is not.
733 if name:
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700734 new_label = name if not value else '%s:%s' % (name, value)
735 hwid_info_list.append(new_label)
736
Gregory Nisbetbfd120f2019-09-04 14:49:46 -0700737 # TODO(gregorynisbet): remove this
738 # TODO(crbug.com/999785)
739 logging.info("old_hwid_labels %s", old_hwid_labels)
740 logging.info("hwid_info_list %s", hwid_info_list)
741
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700742 return HWIDLabel._merge_hwid_label_lists(
743 old=old_hwid_labels,
744 new=hwid_info_list,
745 )
Kevin Cheng80ad5732016-03-31 16:01:56 -0700746
747
748 def get_all_labels(self):
749 """We need to try all labels as a prefix and as standalone.
750
751 We don't know for sure which labels are prefix labels and which are
752 standalone so we try all of them as both.
753 """
754 all_hwid_labels = []
755 try:
756 all_hwid_labels = hwid_lib.get_all_possible_dut_labels(
757 self.key_file)
758 except IOError:
759 logging.error('Can not open key file: %s', self.key_file)
760 except hwid_lib.HwIdException as e:
761 logging.error('hwid service: %s', e)
762 return all_hwid_labels, all_hwid_labels
763
764
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800765class DetachableBaseLabel(base_label.BaseLabel):
766 """Label indicating if device has detachable keyboard."""
767
768 _NAME = 'detachablebase'
769
770 def exists(self, host):
771 return host.run('which hammerd', ignore_status=True).exit_status == 0
772
773
Tom Hughese9552342018-12-18 14:29:25 -0800774class FingerprintLabel(base_label.BaseLabel):
775 """Label indicating whether device has fingerprint sensor."""
776
777 _NAME = 'fingerprint'
778
779 def exists(self, host):
780 return host.run('test -c /dev/cros_fp',
781 ignore_status=True).exit_status == 0
782
783
Garry Wang17a829e2019-03-20 12:03:18 -0700784class ReferenceDesignLabel(base_label.StringPrefixLabel):
785 """Determine the correct reference design label for the device. """
786
787 _NAME = 'reference_design'
788
789 def __init__(self):
790 self.response = None
791
792 def exists(self, host):
793 self.response = host.run('mosys platform family', ignore_status=True)
794 return self.response.exit_status == 0
795
796 def generate_labels(self, host):
797 if self.exists(host):
798 return [self.response.stdout.strip()]
799
800
Kevin Chenga2619dc2016-03-28 11:42:08 -0700801CROS_LABELS = [
802 AccelsLabel(),
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700803 ArcLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700804 AudioLoopbackDongleLabel(),
805 BluetoothLabel(),
Kevin Chenga8455302016-08-31 20:54:41 +0000806 BoardLabel(),
C Shapiro05dd3222017-09-22 10:42:33 -0600807 ModelLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700808 ChameleonConnectionLabel(),
809 ChameleonLabel(),
Joseph Hwangeac44312016-08-31 12:08:38 +0800810 ChameleonPeripheralsLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700811 common_label.OSLabel(),
Mary Ruthven935ebad2018-06-13 16:13:20 -0700812 Cr50Label(),
Ilja H. Friedel50290642017-12-01 19:39:53 -0800813 CtsArchLabel(),
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800814 DetachableBaseLabel(),
C Shapiro8315f5f2019-06-19 15:53:29 -0600815 DeviceSkuLabel(),
Ned Nguyene0a619d2019-07-01 15:50:23 -0600816 BrandCodeLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700817 ECLabel(),
Tom Hughese9552342018-12-18 14:29:25 -0800818 FingerprintLabel(),
Kevin Cheng80ad5732016-03-31 16:01:56 -0700819 HWIDLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700820 InternalDisplayLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700821 LucidSleepLabel(),
822 PowerSupplyLabel(),
Garry Wang17a829e2019-03-20 12:03:18 -0700823 ReferenceDesignLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700824 ServoLabel(),
825 StorageLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700826 VideoGlitchLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700827]
Garry Wange4b6d6e2019-06-17 17:08:46 -0700828
829LABSTATION_LABELS = [
830 BoardLabel(),
831 ModelLabel(),
832 common_label.OSLabel(),
Garry Wang1d0598a2019-09-06 16:31:56 -0700833 ECLabel(),
834 PowerSupplyLabel(),
Garry Wange4b6d6e2019-06-17 17:08:46 -0700835]