blob: 5b91bf2b96018c3adfffb96f4cafc0a577a69bb3 [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
27def _parse_lsb_output(host):
Allen Lia0c7afc2019-02-26 15:50:06 -080028 """Parses the LSB output and returns key data points for labeling.
C Shapiro05dd3222017-09-22 10:42:33 -060029
Allen Lia0c7afc2019-02-26 15:50:06 -080030 @param host: Host that the command will be executed against
31 @returns: LsbOutput with the result of parsing the /etc/lsb-release output
32 """
33 release_info = utils.parse_cmd_output('cat /etc/lsb-release',
34 run_method=host.run)
C Shapiro05dd3222017-09-22 10:42:33 -060035
Allen Lia0c7afc2019-02-26 15:50:06 -080036 unibuild = release_info.get('CHROMEOS_RELEASE_UNIBUILD') == '1'
37 return LsbOutput(unibuild, release_info['CHROMEOS_RELEASE_BOARD'])
C Shapiro05dd3222017-09-22 10:42:33 -060038
Kevin Chenga2619dc2016-03-28 11:42:08 -070039
Kevin Chenga8455302016-08-31 20:54:41 +000040class BoardLabel(base_label.StringPrefixLabel):
41 """Determine the correct board label for the device."""
42
43 _NAME = ds_constants.BOARD_PREFIX.rstrip(':')
44
45 def generate_labels(self, host):
46 # We only want to apply the board labels once, which is when they get
47 # added to the AFE. That way we don't have to worry about the board
48 # label switching on us if the wrong builds get put on the devices.
49 # crbug.com/624207 records one event of the board label switching
50 # unexpectedly on us.
Allen Lia0c7afc2019-02-26 15:50:06 -080051 board = host.host_info_store.get().board
52 if board:
53 return [board]
Kevin Chenga8455302016-08-31 20:54:41 +000054 for label in host._afe_host.labels:
55 if label.startswith(self._NAME + ':'):
56 return [label.split(':')[-1]]
57
C Shapiro10970222017-10-24 08:55:55 -060058 return [_parse_lsb_output(host).board]
Kevin Chenga8455302016-08-31 20:54:41 +000059
60
C Shapirob05c00b2017-07-18 15:06:49 -060061class ModelLabel(base_label.StringPrefixLabel):
62 """Determine the correct model label for the device."""
63
64 _NAME = ds_constants.MODEL_LABEL
65
66 def generate_labels(self, host):
C Shapirod7ba4a72018-01-16 17:04:35 -070067 # Based on the issue explained in BoardLabel, return the existing
68 # label if it has already been set once.
Allen Lia0c7afc2019-02-26 15:50:06 -080069 model = host.host_info_store.get().model
70 if model:
71 return [model]
C Shapirod7ba4a72018-01-16 17:04:35 -070072 for label in host._afe_host.labels:
73 if label.startswith(self._NAME + ':'):
74 return [label.split(':')[-1]]
C Shapirob05c00b2017-07-18 15:06:49 -060075
C Shapiro32700032017-11-03 12:46:55 -060076 lsb_output = _parse_lsb_output(host)
77 model = None
78
79 if lsb_output.unibuild:
C Shapiro26fb1012017-12-14 16:38:03 -070080 test_label_cmd = 'cros_config / test-label'
81 result = host.run(command=test_label_cmd, ignore_status=True)
C Shapiro32700032017-11-03 12:46:55 -060082 if result.exit_status == 0:
83 model = result.stdout.strip()
C Shapiro26fb1012017-12-14 16:38:03 -070084 if not model:
85 mosys_cmd = 'mosys platform model'
86 result = host.run(command=mosys_cmd, ignore_status=True)
87 if result.exit_status == 0:
88 model = result.stdout.strip()
C Shapiro32700032017-11-03 12:46:55 -060089
90 # We need some sort of backwards compatibility for boards that
91 # are not yet supported with mosys and unified builds.
92 # This is necessary so that we can begin changing cbuildbot to take
93 # advantage of the model/board label differentiations for
94 # scheduling, while still retaining backwards compatibility.
95 return [model or lsb_output.board]
C Shapirob05c00b2017-07-18 15:06:49 -060096
97
C Shapirobe0ff8d2019-06-14 10:41:43 -060098class DeviceSkuLabel(base_label.StringPrefixLabel):
99 """Determine the correct device_sku label for the device."""
100
101 _NAME = ds_constants.DEVICE_SKU_LABEL
102
103 def generate_labels(self, host):
104 device_sku = host.host_info_store.get().device_sku
105 if device_sku:
106 return [device_sku]
107
108 mosys_cmd = 'mosys platform sku'
109 result = host.run(command=mosys_cmd, ignore_status=True)
110 if result.exit_status == 0:
111 return [result.stdout.strip()]
112
113 return []
114
115
Ned Nguyene0a619d2019-07-01 15:50:23 -0600116class BrandCodeLabel(base_label.StringPrefixLabel):
117 """Determine the correct brand_code (aka RLZ-code) for the device."""
118
119 _NAME = ds_constants.BRAND_CODE_LABEL
120
121 def generate_labels(self, host):
122 brand_code = host.host_info_store.get().brand_code
123 if brand_code:
124 return [brand_code]
125
126 mosys_cmd = 'mosys platform brand'
127 result = host.run(command=mosys_cmd, ignore_status=True)
128 if result.exit_status == 0:
129 return [result.stdout.strip()]
130
131 return []
132
133
Kevin Chenga2619dc2016-03-28 11:42:08 -0700134class BluetoothLabel(base_label.BaseLabel):
135 """Label indicating if bluetooth is detected."""
136
137 _NAME = 'bluetooth'
138
139 def exists(self, host):
C Shapirobcd9c862019-05-22 17:42:08 -0600140 # Based on crbug.com/966219, the label is flipping sometimes.
141 # Potentially this is caused by testing itself.
142 # Making this label permanently sticky.
143 info = host.host_info_store.get()
144 for label in info.labels:
145 if label.startswith(self._NAME):
146 return True
147
Kevin Chenga2619dc2016-03-28 11:42:08 -0700148 result = host.run('test -d /sys/class/bluetooth/hci0',
149 ignore_status=True)
150
151 return result.exit_status == 0
152
153
154class ECLabel(base_label.BaseLabel):
155 """Label to determine the type of EC on this host."""
156
157 _NAME = 'ec:cros'
158
159 def exists(self, host):
160 cmd = 'mosys ec info'
161 # The output should look like these, so that the last field should
162 # match our EC version scheme:
163 #
164 # stm | stm32f100 | snow_v1.3.139-375eb9f
165 # ti | Unknown-10de | peppy_v1.5.114-5d52788
166 #
167 # Non-Chrome OS ECs will look like these:
168 #
169 # ENE | KB932 | 00BE107A00
170 # ite | it8518 | 3.08
171 #
172 # And some systems don't have ECs at all (Lumpy, for example).
173 regexp = r'^.*\|\s*(\S+_v\d+\.\d+\.\d+-[0-9a-f]+)\s*$'
174
175 ecinfo = host.run(command=cmd, ignore_status=True)
176 if ecinfo.exit_status == 0:
177 res = re.search(regexp, ecinfo.stdout)
178 if res:
179 logging.info("EC version is %s", res.groups()[0])
180 return True
181 logging.info("%s got: %s", cmd, ecinfo.stdout)
182 # Has an EC, but it's not a Chrome OS EC
183 logging.info("%s exited with status %d", cmd, ecinfo.exit_status)
184 return False
185
186
Mary Ruthven935ebad2018-06-13 16:13:20 -0700187class Cr50Label(base_label.StringPrefixLabel):
188 """Label indicating the cr50 version."""
189
190 _NAME = 'cr50'
191
192 def __init__(self):
193 self.ver = None
194
195
196 def exists(self, host):
197 # Make sure the gsctool version command runs ok
198 self.ver = host.run('gsctool -a -f', ignore_status=True)
199 return self.ver.exit_status == 0
200
201
202 def generate_labels(self, host):
203 # Check the major version to determine prePVT vs PVT
Mary Ruthven35207022019-04-15 11:57:29 -0700204 version_info = re.search('RW (\d+\.(\d+)\.\d+)$', self.ver.stdout)
205 full_version = version_info.group(1)
206 major_version = int(version_info.group(2))
Mary Ruthven935ebad2018-06-13 16:13:20 -0700207 # PVT images have a odd major version prePVT have even
Mary Ruthven35207022019-04-15 11:57:29 -0700208 return [full_version, 'pvt' if (major_version % 2) else 'prepvt']
Mary Ruthven935ebad2018-06-13 16:13:20 -0700209
210
Kevin Chenga2619dc2016-03-28 11:42:08 -0700211class AccelsLabel(base_label.BaseLabel):
212 """Determine the type of accelerometers on this host."""
213
214 _NAME = 'accel:cros-ec'
215
216 def exists(self, host):
217 # Check to make sure we have ectool
218 rv = host.run('which ectool', ignore_status=True)
219 if rv.exit_status:
220 logging.info("No ectool cmd found; assuming no EC accelerometers")
221 return False
222
223 # Check that the EC supports the motionsense command
Kevin Cheng856f8a32016-03-31 16:08:08 -0700224 rv = host.run('ectool motionsense', ignore_status=True)
Kevin Chenga2619dc2016-03-28 11:42:08 -0700225 if rv.exit_status:
226 logging.info("EC does not support motionsense command; "
227 "assuming no EC accelerometers")
228 return False
229
230 # Check that EC motion sensors are active
Kevin Cheng17e2f002016-05-04 08:48:03 -0700231 active = host.run('ectool motionsense active').stdout.split('\n')
Kevin Chenga2619dc2016-03-28 11:42:08 -0700232 if active[0] == "0":
233 logging.info("Motion sense inactive; assuming no EC accelerometers")
234 return False
235
236 logging.info("EC accelerometers found")
237 return True
238
239
240class ChameleonLabel(base_label.BaseLabel):
241 """Determine if a Chameleon is connected to this host."""
242
243 _NAME = 'chameleon'
244
245 def exists(self, host):
246 return host._chameleon_host is not None
247
248
249class ChameleonConnectionLabel(base_label.StringPrefixLabel):
250 """Return the Chameleon connection label."""
251
252 _NAME = 'chameleon'
253
254 def exists(self, host):
255 return host._chameleon_host is not None
256
Joseph Hwangeac44312016-08-31 12:08:38 +0800257
Kevin Chenga2619dc2016-03-28 11:42:08 -0700258 def generate_labels(self, host):
259 return [host.chameleon.get_label()]
260
261
Joseph Hwangeac44312016-08-31 12:08:38 +0800262class ChameleonPeripheralsLabel(base_label.StringPrefixLabel):
263 """Return the Chameleon peripherals labels.
264
265 The 'chameleon:bt_hid' label is applied if the bluetooth
266 classic hid device, i.e, RN-42 emulation kit, is detected.
267
268 Any peripherals plugged into the chameleon board would be
269 detected and applied proper labels in this class.
270 """
271
272 _NAME = 'chameleon'
273
274 def exists(self, host):
275 return host._chameleon_host is not None
276
277
278 def generate_labels(self, host):
Shijin Abrahamff61ac32019-05-20 12:35:44 -0700279 labels = []
Shijin Abraham77c74882019-06-11 09:41:15 -0700280 try:
281 bt_hid_device = host.chameleon.get_bluetooth_hid_mouse()
282 if bt_hid_device.CheckSerialConnection():
283 labels.append('bt_hid')
284 except:
285 logging.error('Error with initializing bt_hid_mouse')
286 try:
287 ble_hid_device = host.chameleon.get_ble_mouse()
288 if ble_hid_device.CheckSerialConnection():
289 labels.append('bt_ble_hid')
290 except:
291 logging.error('Error with initializing bt_ble_hid')
292 try:
293 bt_a2dp_sink = host.chameleon.get_bluetooth_a2dp_sink()
294 if bt_a2dp_sink.CheckSerialConnection():
295 labels.append('bt_a2dp_sink')
296 except:
297 logging.error('Error with initializing bt_a2dp_sink')
298 if labels != []:
299 labels.append('bt_peer')
300 logging.info('Bluetooth labels are %s', labels)
Shijin Abrahamff61ac32019-05-20 12:35:44 -0700301 return labels
302
303
Joseph Hwangeac44312016-08-31 12:08:38 +0800304
305
Kevin Chenga2619dc2016-03-28 11:42:08 -0700306class AudioLoopbackDongleLabel(base_label.BaseLabel):
307 """Return the label if an audio loopback dongle is plugged in."""
308
309 _NAME = 'audio_loopback_dongle'
310
311 def exists(self, host):
Gregory Nisbete280ea22019-08-16 17:50:03 -0700312 # Based on crbug.com/991285, AudioLoopbackDongle sometimes flips.
313 # Ensure that AudioLoopbackDongle.exists returns True
314 # forever, after it returns True *once*.
315 if self._cached_exists(host):
316 # If the current state is True, return it, don't run the command on
317 # the DUT and potentially flip the state.
318 return True
319 # If the current state is not True, run the command on
320 # the DUT. The new state will be set to whatever the command
321 # produces.
322 return self._host_run_exists(host)
323
324 def _cached_exists(self, host):
325 """Get the state of AudioLoopbackDongle in the data store"""
326 info = host.host_info_store.get()
327 for label in info.labels:
328 if label.startswith(self._NAME):
329 return True
330 return False
331
332 def _host_run_exists(self, host):
333 """Detect presence of audio_loopback_dongle by physically
334 running a command on the DUT."""
Kevin Chenga2619dc2016-03-28 11:42:08 -0700335 nodes_info = host.run(command=cras_utils.get_cras_nodes_cmd(),
336 ignore_status=True).stdout
337 if (cras_utils.node_type_is_plugged('HEADPHONE', nodes_info) and
338 cras_utils.node_type_is_plugged('MIC', nodes_info)):
339 return True
340 return False
341
342
343class PowerSupplyLabel(base_label.StringPrefixLabel):
344 """
345 Return the label describing the power supply type.
346
347 Labels representing this host's power supply.
348 * `power:battery` when the device has a battery intended for
349 extended use
350 * `power:AC_primary` when the device has a battery not intended
351 for extended use (for moving the machine, etc)
352 * `power:AC_only` when the device has no battery at all.
353 """
354
355 _NAME = 'power'
356
357 def __init__(self):
358 self.psu_cmd_result = None
359
360
361 def exists(self, host):
362 self.psu_cmd_result = host.run(command='mosys psu type',
363 ignore_status=True)
364 return self.psu_cmd_result.stdout.strip() != 'unknown'
365
366
367 def generate_labels(self, host):
368 if self.psu_cmd_result.exit_status:
369 # The psu command for mosys is not included for all platforms. The
370 # assumption is that the device will have a battery if the command
371 # is not found.
372 return ['battery']
373 return [self.psu_cmd_result.stdout.strip()]
374
375
376class StorageLabel(base_label.StringPrefixLabel):
377 """
378 Return the label describing the storage type.
379
380 Determine if the internal device is SCSI or dw_mmc device.
381 Then check that it is SSD or HDD or eMMC or something else.
382
383 Labels representing this host's internal device type:
384 * `storage:ssd` when internal device is solid state drive
385 * `storage:hdd` when internal device is hard disk drive
386 * `storage:mmc` when internal device is mmc drive
Gwendal Grignou327fec62017-07-26 15:25:43 -0700387 * `storage:nvme` when internal device is NVMe drive
Alexis Savery570e7fb2018-06-26 10:48:15 -0700388 * `storage:ufs` when internal device is ufs drive
Kevin Chenga2619dc2016-03-28 11:42:08 -0700389 * None When internal device is something else or
390 when we are unable to determine the type
391 """
392
393 _NAME = 'storage'
394
395 def __init__(self):
396 self.type_str = ''
397
398
399 def exists(self, host):
400 # The output should be /dev/mmcblk* for SD/eMMC or /dev/sd* for scsi
401 rootdev_cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
402 '. /usr/share/misc/chromeos-common.sh;',
403 'load_base_vars;',
404 'get_fixed_dst_drive'])
405 rootdev = host.run(command=rootdev_cmd, ignore_status=True)
406 if rootdev.exit_status:
407 logging.info("Fail to run %s", rootdev_cmd)
408 return False
409 rootdev_str = rootdev.stdout.strip()
410
411 if not rootdev_str:
412 return False
413
414 rootdev_base = os.path.basename(rootdev_str)
415
416 mmc_pattern = '/dev/mmcblk[0-9]'
417 if re.match(mmc_pattern, rootdev_str):
418 # Use type to determine if the internal device is eMMC or somthing
419 # else. We can assume that MMC is always an internal device.
420 type_cmd = 'cat /sys/block/%s/device/type' % rootdev_base
421 type = host.run(command=type_cmd, ignore_status=True)
422 if type.exit_status:
423 logging.info("Fail to run %s", type_cmd)
424 return False
425 type_str = type.stdout.strip()
426
427 if type_str == 'MMC':
428 self.type_str = 'mmc'
429 return True
430
431 scsi_pattern = '/dev/sd[a-z]+'
432 if re.match(scsi_pattern, rootdev.stdout):
433 # Read symlink for /sys/block/sd* to determine if the internal
434 # device is connected via ata or usb.
435 link_cmd = 'readlink /sys/block/%s' % rootdev_base
436 link = host.run(command=link_cmd, ignore_status=True)
437 if link.exit_status:
438 logging.info("Fail to run %s", link_cmd)
439 return False
440 link_str = link.stdout.strip()
441 if 'usb' in link_str:
442 return False
Alexis Savery570e7fb2018-06-26 10:48:15 -0700443 elif 'ufs' in link_str:
444 self.type_str = 'ufs'
445 return True
Kevin Chenga2619dc2016-03-28 11:42:08 -0700446
447 # Read rotation to determine if the internal device is ssd or hdd.
448 rotate_cmd = str('cat /sys/block/%s/queue/rotational'
449 % rootdev_base)
450 rotate = host.run(command=rotate_cmd, ignore_status=True)
451 if rotate.exit_status:
452 logging.info("Fail to run %s", rotate_cmd)
453 return False
454 rotate_str = rotate.stdout.strip()
455
456 rotate_dict = {'0':'ssd', '1':'hdd'}
457 self.type_str = rotate_dict.get(rotate_str)
458 return True
459
Gwendal Grignou327fec62017-07-26 15:25:43 -0700460 nvme_pattern = '/dev/nvme[0-9]+n[0-9]+'
461 if re.match(nvme_pattern, rootdev_str):
Gwendal Grignou3660c192017-12-06 10:11:23 -0800462 self.type_str = 'nvme'
Gwendal Grignou327fec62017-07-26 15:25:43 -0700463 return True
464
Kevin Chenga2619dc2016-03-28 11:42:08 -0700465 # All other internal device / error case will always fall here
466 return False
467
468
469 def generate_labels(self, host):
470 return [self.type_str]
471
472
473class ServoLabel(base_label.BaseLabel):
474 """Label to apply if a servo is present."""
475
476 _NAME = 'servo'
477
478 def exists(self, host):
Gregory Nisbet8b008f82019-08-20 13:06:19 -0700479 # Based on crbug.com/995900, Servo sometimes flips.
480 # Ensure that ServoLabel.exists returns True
481 # forever, after it returns True *once*.
482 if self._cached_exists(host):
483 # If the current state is True, return it, don't run the command on
484 # the DUT and potentially flip the state.
485 return True
486 # If the current state is not True, run the command on
487 # the DUT. The new state will be set to whatever the command
488 # produces.
489 return self._host_run_exists(host)
490
491 def _cached_exists(self, host):
492 """Get the state of Servo in the data store"""
493 info = host.host_info_store.get()
494 for label in info.labels:
495 if label.startswith(self._NAME):
496 return True
497 return False
498
499 def _host_run_exists(self, host):
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700500 """
501 Check if the servo label should apply to the host or not.
502
503 @returns True if a servo host is detected, False otherwise.
504 """
Kevin Cheng745b8162017-05-26 09:48:36 -0700505 servo_host_hostname = None
Prathmesh Prabhu37ae79b2018-09-12 10:37:44 -0700506 servo_args = servo_host.get_servo_args_for_host(host)
Kevin Cheng745b8162017-05-26 09:48:36 -0700507 if servo_args:
508 servo_host_hostname = servo_args.get(servo_host.SERVO_HOST_ATTR)
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700509 return (servo_host_hostname is not None
510 and servo_host.servo_host_is_up(servo_host_hostname))
Kevin Chenga2619dc2016-03-28 11:42:08 -0700511
512
Ilja H. Friedel50290642017-12-01 19:39:53 -0800513class ArcLabel(base_label.BaseLabel):
514 """Label indicates if host has ARC support."""
515
516 _NAME = 'arc'
517
518 @base_label.forever_exists_decorate
519 def exists(self, host):
520 return 0 == host.run(
521 'grep CHROMEOS_ARC_VERSION /etc/lsb-release',
522 ignore_status=True).exit_status
523
524
525class CtsArchLabel(base_label.StringLabel):
526 """Labels to determine the abi of the CTS bundle (arm or x86 only)."""
Rohit Makasana5a153502016-06-13 15:50:09 -0700527
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700528 _NAME = ['cts_abi_arm', 'cts_abi_x86', 'cts_cpu_arm', 'cts_cpu_x86']
Rohit Makasana5a153502016-06-13 15:50:09 -0700529
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700530 def _get_cts_abis(self, arch):
Rohit Makasana5a153502016-06-13 15:50:09 -0700531 """Return supported CTS ABIs.
532
533 @return List of supported CTS bundle ABIs.
534 """
535 cts_abis = {'x86_64': ['arm', 'x86'], 'arm': ['arm']}
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700536 return cts_abis.get(arch, [])
537
538 def _get_cts_cpus(self, arch):
539 """Return supported CTS native CPUs.
540
541 This is needed for CTS_Instant scheduling.
542 @return List of supported CTS native CPUs.
543 """
544 cts_cpus = {'x86_64': ['x86'], 'arm': ['arm']}
545 return cts_cpus.get(arch, [])
Rohit Makasana5a153502016-06-13 15:50:09 -0700546
Rohit Makasana5a153502016-06-13 15:50:09 -0700547 def generate_labels(self, host):
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700548 cpu_arch = host.get_cpu_arch()
549 abi_labels = ['cts_abi_' + abi for abi in self._get_cts_abis(cpu_arch)]
550 cpu_labels = ['cts_cpu_' + cpu for cpu in self._get_cts_cpus(cpu_arch)]
551 return abi_labels + cpu_labels
Rohit Makasana5a153502016-06-13 15:50:09 -0700552
553
Kevin Chenga2619dc2016-03-28 11:42:08 -0700554class VideoGlitchLabel(base_label.BaseLabel):
555 """Label indicates if host supports video glitch detection tests."""
556
557 _NAME = 'video_glitch_detection'
558
559 def exists(self, host):
560 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
561
562 return board in video_test_constants.SUPPORTED_BOARDS
563
564
Kevin Chenga2619dc2016-03-28 11:42:08 -0700565class InternalDisplayLabel(base_label.StringLabel):
566 """Label that determines if the device has an internal display."""
567
568 _NAME = 'internal_display'
569
570 def generate_labels(self, host):
571 from autotest_lib.client.cros.graphics import graphics_utils
572 from autotest_lib.client.common_lib import utils as common_utils
573
574 def __system_output(cmd):
575 return host.run(cmd).stdout
576
577 def __read_file(remote_path):
578 return host.run('cat %s' % remote_path).stdout
579
580 # Hijack the necessary client functions so that we can take advantage
581 # of the client lib here.
582 # FIXME: find a less hacky way than this
583 original_system_output = utils.system_output
584 original_read_file = common_utils.read_file
585 utils.system_output = __system_output
586 common_utils.read_file = __read_file
587 try:
588 return ([self._NAME]
589 if graphics_utils.has_internal_display()
590 else [])
591 finally:
592 utils.system_output = original_system_output
593 common_utils.read_file = original_read_file
594
595
596class LucidSleepLabel(base_label.BaseLabel):
597 """Label that determines if device has support for lucid sleep."""
598
599 # TODO(kevcheng): See if we can determine if this label is applicable a
600 # better way (crbug.com/592146).
601 _NAME = 'lucidsleep'
RaviChandra Sadinenic06b00e2018-11-03 09:56:11 -0700602 LUCID_SLEEP_BOARDS = ['nocturne', 'poppy']
Kevin Chenga2619dc2016-03-28 11:42:08 -0700603
604 def exists(self, host):
605 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
606 return board in self.LUCID_SLEEP_BOARDS
607
608
Kevin Cheng80ad5732016-03-31 16:01:56 -0700609class HWIDLabel(base_label.StringLabel):
610 """Return all the labels generated from the hwid."""
611
612 # We leave out _NAME because hwid_lib will generate everything for us.
613
614 def __init__(self):
615 # Grab the key file needed to access the hwid service.
616 self.key_file = global_config.global_config.get_config_value(
617 'CROS', 'HWID_KEY', type=str)
618
619
620 def generate_labels(self, host):
621 hwid_labels = []
622 hwid = host.run_output('crossystem hwid').strip()
623 hwid_info_list = hwid_lib.get_hwid_info(hwid, hwid_lib.HWID_INFO_LABEL,
624 self.key_file).get('labels', [])
625
626 for hwid_info in hwid_info_list:
627 # If it's a prefix, we'll have:
628 # {'name': prefix_label, 'value': postfix_label} and create
629 # 'prefix_label:postfix_label'; otherwise it'll just be
630 # {'name': label} which should just be 'label'.
631 value = hwid_info.get('value', '')
632 name = hwid_info.get('name', '')
633 # There should always be a name but just in case there is not.
634 if name:
635 hwid_labels.append(name if not value else
636 '%s:%s' % (name, value))
637 return hwid_labels
638
639
640 def get_all_labels(self):
641 """We need to try all labels as a prefix and as standalone.
642
643 We don't know for sure which labels are prefix labels and which are
644 standalone so we try all of them as both.
645 """
646 all_hwid_labels = []
647 try:
648 all_hwid_labels = hwid_lib.get_all_possible_dut_labels(
649 self.key_file)
650 except IOError:
651 logging.error('Can not open key file: %s', self.key_file)
652 except hwid_lib.HwIdException as e:
653 logging.error('hwid service: %s', e)
654 return all_hwid_labels, all_hwid_labels
655
656
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800657class DetachableBaseLabel(base_label.BaseLabel):
658 """Label indicating if device has detachable keyboard."""
659
660 _NAME = 'detachablebase'
661
662 def exists(self, host):
663 return host.run('which hammerd', ignore_status=True).exit_status == 0
664
665
Tom Hughese9552342018-12-18 14:29:25 -0800666class FingerprintLabel(base_label.BaseLabel):
667 """Label indicating whether device has fingerprint sensor."""
668
669 _NAME = 'fingerprint'
670
671 def exists(self, host):
672 return host.run('test -c /dev/cros_fp',
673 ignore_status=True).exit_status == 0
674
675
Garry Wang17a829e2019-03-20 12:03:18 -0700676class ReferenceDesignLabel(base_label.StringPrefixLabel):
677 """Determine the correct reference design label for the device. """
678
679 _NAME = 'reference_design'
680
681 def __init__(self):
682 self.response = None
683
684 def exists(self, host):
685 self.response = host.run('mosys platform family', ignore_status=True)
686 return self.response.exit_status == 0
687
688 def generate_labels(self, host):
689 if self.exists(host):
690 return [self.response.stdout.strip()]
691
692
Kevin Chenga2619dc2016-03-28 11:42:08 -0700693CROS_LABELS = [
694 AccelsLabel(),
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700695 ArcLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700696 AudioLoopbackDongleLabel(),
697 BluetoothLabel(),
Kevin Chenga8455302016-08-31 20:54:41 +0000698 BoardLabel(),
C Shapiro05dd3222017-09-22 10:42:33 -0600699 ModelLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700700 ChameleonConnectionLabel(),
701 ChameleonLabel(),
Joseph Hwangeac44312016-08-31 12:08:38 +0800702 ChameleonPeripheralsLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700703 common_label.OSLabel(),
Mary Ruthven935ebad2018-06-13 16:13:20 -0700704 Cr50Label(),
Ilja H. Friedel50290642017-12-01 19:39:53 -0800705 CtsArchLabel(),
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800706 DetachableBaseLabel(),
C Shapiro8315f5f2019-06-19 15:53:29 -0600707 DeviceSkuLabel(),
Ned Nguyene0a619d2019-07-01 15:50:23 -0600708 BrandCodeLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700709 ECLabel(),
Tom Hughese9552342018-12-18 14:29:25 -0800710 FingerprintLabel(),
Kevin Cheng80ad5732016-03-31 16:01:56 -0700711 HWIDLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700712 InternalDisplayLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700713 LucidSleepLabel(),
714 PowerSupplyLabel(),
Garry Wang17a829e2019-03-20 12:03:18 -0700715 ReferenceDesignLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700716 ServoLabel(),
717 StorageLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700718 VideoGlitchLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700719]
Garry Wange4b6d6e2019-06-17 17:08:46 -0700720
721LABSTATION_LABELS = [
722 BoardLabel(),
723 ModelLabel(),
724 common_label.OSLabel(),
725]