blob: 1d1c181de7978dad9a7cb180d6899d43da409d3c [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):
28 """Parses the LSB output and returns key data points for labeling.
29
30 @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)
35
36 unibuild = release_info.get('CHROMEOS_RELEASE_UNIBUILD') == '1'
37 return LsbOutput(unibuild, release_info['CHROMEOS_RELEASE_BOARD'])
38
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.
51 for label in host._afe_host.labels:
52 if label.startswith(self._NAME + ':'):
53 return [label.split(':')[-1]]
54
C Shapiro10970222017-10-24 08:55:55 -060055 return [_parse_lsb_output(host).board]
Kevin Chenga8455302016-08-31 20:54:41 +000056
57
C Shapirob05c00b2017-07-18 15:06:49 -060058class ModelLabel(base_label.StringPrefixLabel):
59 """Determine the correct model label for the device."""
60
61 _NAME = ds_constants.MODEL_LABEL
62
63 def generate_labels(self, host):
C Shapirod7ba4a72018-01-16 17:04:35 -070064 # Based on the issue explained in BoardLabel, return the existing
65 # label if it has already been set once.
66 for label in host._afe_host.labels:
67 if label.startswith(self._NAME + ':'):
68 return [label.split(':')[-1]]
C Shapirob05c00b2017-07-18 15:06:49 -060069
C Shapiro32700032017-11-03 12:46:55 -060070 lsb_output = _parse_lsb_output(host)
71 model = None
72
73 if lsb_output.unibuild:
C Shapiro26fb1012017-12-14 16:38:03 -070074 test_label_cmd = 'cros_config / test-label'
75 result = host.run(command=test_label_cmd, ignore_status=True)
C Shapiro32700032017-11-03 12:46:55 -060076 if result.exit_status == 0:
77 model = result.stdout.strip()
C Shapiro26fb1012017-12-14 16:38:03 -070078 if not model:
79 mosys_cmd = 'mosys platform model'
80 result = host.run(command=mosys_cmd, ignore_status=True)
81 if result.exit_status == 0:
82 model = result.stdout.strip()
C Shapiro32700032017-11-03 12:46:55 -060083
84 # We need some sort of backwards compatibility for boards that
85 # are not yet supported with mosys and unified builds.
86 # This is necessary so that we can begin changing cbuildbot to take
87 # advantage of the model/board label differentiations for
88 # scheduling, while still retaining backwards compatibility.
89 return [model or lsb_output.board]
C Shapirob05c00b2017-07-18 15:06:49 -060090
91
Kevin Chenga2619dc2016-03-28 11:42:08 -070092class LightSensorLabel(base_label.BaseLabel):
93 """Label indicating if a light sensor is detected."""
94
95 _NAME = 'lightsensor'
96 _LIGHTSENSOR_SEARCH_DIR = '/sys/bus/iio/devices'
97 _LIGHTSENSOR_FILES = [
98 "in_illuminance0_input",
99 "in_illuminance_input",
100 "in_illuminance0_raw",
101 "in_illuminance_raw",
102 "illuminance0_input",
103 ]
104
105 def exists(self, host):
106 search_cmd = "find -L %s -maxdepth 4 | egrep '%s'" % (
107 self._LIGHTSENSOR_SEARCH_DIR, '|'.join(self._LIGHTSENSOR_FILES))
108 # Run the search cmd following the symlinks. Stderr_tee is set to
109 # None as there can be a symlink loop, but the command will still
110 # execute correctly with a few messages printed to stderr.
111 result = host.run(search_cmd, stdout_tee=None, stderr_tee=None,
112 ignore_status=True)
113
114 return result.exit_status == 0
115
116
117class BluetoothLabel(base_label.BaseLabel):
118 """Label indicating if bluetooth is detected."""
119
120 _NAME = 'bluetooth'
121
122 def exists(self, host):
123 result = host.run('test -d /sys/class/bluetooth/hci0',
124 ignore_status=True)
125
126 return result.exit_status == 0
127
128
129class ECLabel(base_label.BaseLabel):
130 """Label to determine the type of EC on this host."""
131
132 _NAME = 'ec:cros'
133
134 def exists(self, host):
135 cmd = 'mosys ec info'
136 # The output should look like these, so that the last field should
137 # match our EC version scheme:
138 #
139 # stm | stm32f100 | snow_v1.3.139-375eb9f
140 # ti | Unknown-10de | peppy_v1.5.114-5d52788
141 #
142 # Non-Chrome OS ECs will look like these:
143 #
144 # ENE | KB932 | 00BE107A00
145 # ite | it8518 | 3.08
146 #
147 # And some systems don't have ECs at all (Lumpy, for example).
148 regexp = r'^.*\|\s*(\S+_v\d+\.\d+\.\d+-[0-9a-f]+)\s*$'
149
150 ecinfo = host.run(command=cmd, ignore_status=True)
151 if ecinfo.exit_status == 0:
152 res = re.search(regexp, ecinfo.stdout)
153 if res:
154 logging.info("EC version is %s", res.groups()[0])
155 return True
156 logging.info("%s got: %s", cmd, ecinfo.stdout)
157 # Has an EC, but it's not a Chrome OS EC
158 logging.info("%s exited with status %d", cmd, ecinfo.exit_status)
159 return False
160
161
Mary Ruthven935ebad2018-06-13 16:13:20 -0700162class Cr50Label(base_label.StringPrefixLabel):
163 """Label indicating the cr50 version."""
164
165 _NAME = 'cr50'
166
167 def __init__(self):
168 self.ver = None
169
170
171 def exists(self, host):
172 # Make sure the gsctool version command runs ok
173 self.ver = host.run('gsctool -a -f', ignore_status=True)
174 return self.ver.exit_status == 0
175
176
177 def generate_labels(self, host):
178 # Check the major version to determine prePVT vs PVT
179 major_ver = int(re.search('RW \d+\.(\d+)\.\d+[\r\n]',
180 self.ver.stdout).group(1))
181 # PVT images have a odd major version prePVT have even
182 return ['pvt' if (major_ver % 2) else 'prepvt']
183
184
Kevin Chenga2619dc2016-03-28 11:42:08 -0700185class AccelsLabel(base_label.BaseLabel):
186 """Determine the type of accelerometers on this host."""
187
188 _NAME = 'accel:cros-ec'
189
190 def exists(self, host):
191 # Check to make sure we have ectool
192 rv = host.run('which ectool', ignore_status=True)
193 if rv.exit_status:
194 logging.info("No ectool cmd found; assuming no EC accelerometers")
195 return False
196
197 # Check that the EC supports the motionsense command
Kevin Cheng856f8a32016-03-31 16:08:08 -0700198 rv = host.run('ectool motionsense', ignore_status=True)
Kevin Chenga2619dc2016-03-28 11:42:08 -0700199 if rv.exit_status:
200 logging.info("EC does not support motionsense command; "
201 "assuming no EC accelerometers")
202 return False
203
204 # Check that EC motion sensors are active
Kevin Cheng17e2f002016-05-04 08:48:03 -0700205 active = host.run('ectool motionsense active').stdout.split('\n')
Kevin Chenga2619dc2016-03-28 11:42:08 -0700206 if active[0] == "0":
207 logging.info("Motion sense inactive; assuming no EC accelerometers")
208 return False
209
210 logging.info("EC accelerometers found")
211 return True
212
213
214class ChameleonLabel(base_label.BaseLabel):
215 """Determine if a Chameleon is connected to this host."""
216
217 _NAME = 'chameleon'
218
219 def exists(self, host):
220 return host._chameleon_host is not None
221
222
223class ChameleonConnectionLabel(base_label.StringPrefixLabel):
224 """Return the Chameleon connection label."""
225
226 _NAME = 'chameleon'
227
228 def exists(self, host):
229 return host._chameleon_host is not None
230
Joseph Hwangeac44312016-08-31 12:08:38 +0800231
Kevin Chenga2619dc2016-03-28 11:42:08 -0700232 def generate_labels(self, host):
233 return [host.chameleon.get_label()]
234
235
Joseph Hwangeac44312016-08-31 12:08:38 +0800236class ChameleonPeripheralsLabel(base_label.StringPrefixLabel):
237 """Return the Chameleon peripherals labels.
238
239 The 'chameleon:bt_hid' label is applied if the bluetooth
240 classic hid device, i.e, RN-42 emulation kit, is detected.
241
242 Any peripherals plugged into the chameleon board would be
243 detected and applied proper labels in this class.
244 """
245
246 _NAME = 'chameleon'
247
248 def exists(self, host):
249 return host._chameleon_host is not None
250
251
252 def generate_labels(self, host):
Joseph Hwangbf6b0752018-08-10 08:18:10 +0800253 bt_hid_device = host.chameleon.get_bluetooth_hid_mouse()
Joseph Hwangeac44312016-08-31 12:08:38 +0800254 return ['bt_hid'] if bt_hid_device.CheckSerialConnection() else []
255
256
Kevin Chenga2619dc2016-03-28 11:42:08 -0700257class AudioLoopbackDongleLabel(base_label.BaseLabel):
258 """Return the label if an audio loopback dongle is plugged in."""
259
260 _NAME = 'audio_loopback_dongle'
261
262 def exists(self, host):
263 nodes_info = host.run(command=cras_utils.get_cras_nodes_cmd(),
264 ignore_status=True).stdout
265 if (cras_utils.node_type_is_plugged('HEADPHONE', nodes_info) and
266 cras_utils.node_type_is_plugged('MIC', nodes_info)):
267 return True
268 return False
269
270
271class PowerSupplyLabel(base_label.StringPrefixLabel):
272 """
273 Return the label describing the power supply type.
274
275 Labels representing this host's power supply.
276 * `power:battery` when the device has a battery intended for
277 extended use
278 * `power:AC_primary` when the device has a battery not intended
279 for extended use (for moving the machine, etc)
280 * `power:AC_only` when the device has no battery at all.
281 """
282
283 _NAME = 'power'
284
285 def __init__(self):
286 self.psu_cmd_result = None
287
288
289 def exists(self, host):
290 self.psu_cmd_result = host.run(command='mosys psu type',
291 ignore_status=True)
292 return self.psu_cmd_result.stdout.strip() != 'unknown'
293
294
295 def generate_labels(self, host):
296 if self.psu_cmd_result.exit_status:
297 # The psu command for mosys is not included for all platforms. The
298 # assumption is that the device will have a battery if the command
299 # is not found.
300 return ['battery']
301 return [self.psu_cmd_result.stdout.strip()]
302
303
304class StorageLabel(base_label.StringPrefixLabel):
305 """
306 Return the label describing the storage type.
307
308 Determine if the internal device is SCSI or dw_mmc device.
309 Then check that it is SSD or HDD or eMMC or something else.
310
311 Labels representing this host's internal device type:
312 * `storage:ssd` when internal device is solid state drive
313 * `storage:hdd` when internal device is hard disk drive
314 * `storage:mmc` when internal device is mmc drive
Gwendal Grignou327fec62017-07-26 15:25:43 -0700315 * `storage:nvme` when internal device is NVMe drive
Alexis Savery570e7fb2018-06-26 10:48:15 -0700316 * `storage:ufs` when internal device is ufs drive
Kevin Chenga2619dc2016-03-28 11:42:08 -0700317 * None When internal device is something else or
318 when we are unable to determine the type
319 """
320
321 _NAME = 'storage'
322
323 def __init__(self):
324 self.type_str = ''
325
326
327 def exists(self, host):
328 # The output should be /dev/mmcblk* for SD/eMMC or /dev/sd* for scsi
329 rootdev_cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
330 '. /usr/share/misc/chromeos-common.sh;',
331 'load_base_vars;',
332 'get_fixed_dst_drive'])
333 rootdev = host.run(command=rootdev_cmd, ignore_status=True)
334 if rootdev.exit_status:
335 logging.info("Fail to run %s", rootdev_cmd)
336 return False
337 rootdev_str = rootdev.stdout.strip()
338
339 if not rootdev_str:
340 return False
341
342 rootdev_base = os.path.basename(rootdev_str)
343
344 mmc_pattern = '/dev/mmcblk[0-9]'
345 if re.match(mmc_pattern, rootdev_str):
346 # Use type to determine if the internal device is eMMC or somthing
347 # else. We can assume that MMC is always an internal device.
348 type_cmd = 'cat /sys/block/%s/device/type' % rootdev_base
349 type = host.run(command=type_cmd, ignore_status=True)
350 if type.exit_status:
351 logging.info("Fail to run %s", type_cmd)
352 return False
353 type_str = type.stdout.strip()
354
355 if type_str == 'MMC':
356 self.type_str = 'mmc'
357 return True
358
359 scsi_pattern = '/dev/sd[a-z]+'
360 if re.match(scsi_pattern, rootdev.stdout):
361 # Read symlink for /sys/block/sd* to determine if the internal
362 # device is connected via ata or usb.
363 link_cmd = 'readlink /sys/block/%s' % rootdev_base
364 link = host.run(command=link_cmd, ignore_status=True)
365 if link.exit_status:
366 logging.info("Fail to run %s", link_cmd)
367 return False
368 link_str = link.stdout.strip()
369 if 'usb' in link_str:
370 return False
Alexis Savery570e7fb2018-06-26 10:48:15 -0700371 elif 'ufs' in link_str:
372 self.type_str = 'ufs'
373 return True
Kevin Chenga2619dc2016-03-28 11:42:08 -0700374
375 # Read rotation to determine if the internal device is ssd or hdd.
376 rotate_cmd = str('cat /sys/block/%s/queue/rotational'
377 % rootdev_base)
378 rotate = host.run(command=rotate_cmd, ignore_status=True)
379 if rotate.exit_status:
380 logging.info("Fail to run %s", rotate_cmd)
381 return False
382 rotate_str = rotate.stdout.strip()
383
384 rotate_dict = {'0':'ssd', '1':'hdd'}
385 self.type_str = rotate_dict.get(rotate_str)
386 return True
387
Gwendal Grignou327fec62017-07-26 15:25:43 -0700388 nvme_pattern = '/dev/nvme[0-9]+n[0-9]+'
389 if re.match(nvme_pattern, rootdev_str):
Gwendal Grignou3660c192017-12-06 10:11:23 -0800390 self.type_str = 'nvme'
Gwendal Grignou327fec62017-07-26 15:25:43 -0700391 return True
392
Kevin Chenga2619dc2016-03-28 11:42:08 -0700393 # All other internal device / error case will always fall here
394 return False
395
396
397 def generate_labels(self, host):
398 return [self.type_str]
399
400
401class ServoLabel(base_label.BaseLabel):
402 """Label to apply if a servo is present."""
403
404 _NAME = 'servo'
405
406 def exists(self, host):
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700407 """
408 Check if the servo label should apply to the host or not.
409
410 @returns True if a servo host is detected, False otherwise.
411 """
Kevin Cheng745b8162017-05-26 09:48:36 -0700412 servo_host_hostname = None
Prathmesh Prabhu37ae79b2018-09-12 10:37:44 -0700413 servo_args = servo_host.get_servo_args_for_host(host)
Kevin Cheng745b8162017-05-26 09:48:36 -0700414 if servo_args:
415 servo_host_hostname = servo_args.get(servo_host.SERVO_HOST_ATTR)
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700416 return (servo_host_hostname is not None
417 and servo_host.servo_host_is_up(servo_host_hostname))
Kevin Chenga2619dc2016-03-28 11:42:08 -0700418
419
420class VideoLabel(base_label.StringLabel):
421 """Labels detailing video capabilities."""
422
423 # List gathered from
424 # https://chromium.googlesource.com/chromiumos/
425 # platform2/+/master/avtest_label_detect/main.c#19
Hirokazu Hondad6cfe922017-10-03 13:07:37 +0900426 # TODO(hiroh): '4k_video' won't be used. It will be removed in the future.
Kevin Chenga2619dc2016-03-28 11:42:08 -0700427 _NAME = [
428 'hw_jpeg_acc_dec',
429 'hw_video_acc_h264',
430 'hw_video_acc_vp8',
431 'hw_video_acc_vp9',
432 'hw_video_acc_enc_h264',
433 'hw_video_acc_enc_vp8',
434 'webcam',
Hirokazu Honda57dbf002017-09-21 16:05:06 +0900435 '4k_video',
Hirokazu Hondad6cfe922017-10-03 13:07:37 +0900436 '4k_video_h264',
437 '4k_video_vp8',
438 '4k_video_vp9',
Kevin Chenga2619dc2016-03-28 11:42:08 -0700439 ]
440
441 def generate_labels(self, host):
442 result = host.run('/usr/local/bin/avtest_label_detect',
443 ignore_status=True).stdout
444 return re.findall('^Detected label: (\w+)$', result, re.M)
445
446
Ilja H. Friedel50290642017-12-01 19:39:53 -0800447class ArcLabel(base_label.BaseLabel):
448 """Label indicates if host has ARC support."""
449
450 _NAME = 'arc'
451
452 @base_label.forever_exists_decorate
453 def exists(self, host):
454 return 0 == host.run(
455 'grep CHROMEOS_ARC_VERSION /etc/lsb-release',
456 ignore_status=True).exit_status
457
458
459class CtsArchLabel(base_label.StringLabel):
460 """Labels to determine the abi of the CTS bundle (arm or x86 only)."""
Rohit Makasana5a153502016-06-13 15:50:09 -0700461
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700462 _NAME = ['cts_abi_arm', 'cts_abi_x86', 'cts_cpu_arm', 'cts_cpu_x86']
Rohit Makasana5a153502016-06-13 15:50:09 -0700463
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700464 def _get_cts_abis(self, arch):
Rohit Makasana5a153502016-06-13 15:50:09 -0700465 """Return supported CTS ABIs.
466
467 @return List of supported CTS bundle ABIs.
468 """
469 cts_abis = {'x86_64': ['arm', 'x86'], 'arm': ['arm']}
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700470 return cts_abis.get(arch, [])
471
472 def _get_cts_cpus(self, arch):
473 """Return supported CTS native CPUs.
474
475 This is needed for CTS_Instant scheduling.
476 @return List of supported CTS native CPUs.
477 """
478 cts_cpus = {'x86_64': ['x86'], 'arm': ['arm']}
479 return cts_cpus.get(arch, [])
Rohit Makasana5a153502016-06-13 15:50:09 -0700480
Rohit Makasana5a153502016-06-13 15:50:09 -0700481 def generate_labels(self, host):
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700482 cpu_arch = host.get_cpu_arch()
483 abi_labels = ['cts_abi_' + abi for abi in self._get_cts_abis(cpu_arch)]
484 cpu_labels = ['cts_cpu_' + cpu for cpu in self._get_cts_cpus(cpu_arch)]
485 return abi_labels + cpu_labels
Rohit Makasana5a153502016-06-13 15:50:09 -0700486
487
Ilja H. Friedel50290642017-12-01 19:39:53 -0800488class SparseCoverageLabel(base_label.StringLabel):
489 """Label indicates if it is desirable to cover a test for this build."""
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700490
Ilja H. Friedel50290642017-12-01 19:39:53 -0800491 # Prime numbers. We can easily construct 6, 10, 15 and 30 from these.
492 _NAME = ['sparse_coverage_2', 'sparse_coverage_3', 'sparse_coverage_5']
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700493
Ilja H. Friedel50290642017-12-01 19:39:53 -0800494 def _should_cover(self, host, nth_build):
495 release_info = utils.parse_cmd_output(
496 'cat /etc/lsb-release', run_method=host.run)
497 build = release_info.get('CHROMEOS_RELEASE_BUILD_NUMBER')
498 branch = release_info.get('CHROMEOS_RELEASE_BRANCH_NUMBER')
499 patch = release_info.get('CHROMEOS_RELEASE_PATCH_NUMBER')
500 builder = release_info.get('CHROMEOS_RELEASE_BUILDER_PATH')
501 if not 'release' in builder:
502 # Sparse coverage only makes sense on release/canary builds.
503 return True
504 if patch != '0':
505 # We are on a paladin or pfq build. These are never sparse.
506 # Redundant with release check above but just in case.
507 return True
508 if branch != '0':
509 # We are on a branch. For now these are not sparse.
510 # TODO(ihf): Consider sparse coverage on beta.
511 return True
512 # Now we can be sure we are on master.
513 if int(build) % nth_build == 0:
514 # We only want to cover one in n builds on master. This is the
515 # lucky one.
516 return True
517 # We skip all other builds on master.
518 return False
519
520 def generate_labels(self, host):
521 labels = []
522 for n in [2, 3, 5]:
523 if self._should_cover(host, n):
524 labels.append('sparse_coverage_%d' % n)
525 return labels
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700526
527
Kevin Chenga2619dc2016-03-28 11:42:08 -0700528class VideoGlitchLabel(base_label.BaseLabel):
529 """Label indicates if host supports video glitch detection tests."""
530
531 _NAME = 'video_glitch_detection'
532
533 def exists(self, host):
534 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
535
536 return board in video_test_constants.SUPPORTED_BOARDS
537
538
Kevin Chenga2619dc2016-03-28 11:42:08 -0700539class InternalDisplayLabel(base_label.StringLabel):
540 """Label that determines if the device has an internal display."""
541
542 _NAME = 'internal_display'
543
544 def generate_labels(self, host):
545 from autotest_lib.client.cros.graphics import graphics_utils
546 from autotest_lib.client.common_lib import utils as common_utils
547
548 def __system_output(cmd):
549 return host.run(cmd).stdout
550
551 def __read_file(remote_path):
552 return host.run('cat %s' % remote_path).stdout
553
554 # Hijack the necessary client functions so that we can take advantage
555 # of the client lib here.
556 # FIXME: find a less hacky way than this
557 original_system_output = utils.system_output
558 original_read_file = common_utils.read_file
559 utils.system_output = __system_output
560 common_utils.read_file = __read_file
561 try:
562 return ([self._NAME]
563 if graphics_utils.has_internal_display()
564 else [])
565 finally:
566 utils.system_output = original_system_output
567 common_utils.read_file = original_read_file
568
569
570class LucidSleepLabel(base_label.BaseLabel):
571 """Label that determines if device has support for lucid sleep."""
572
573 # TODO(kevcheng): See if we can determine if this label is applicable a
574 # better way (crbug.com/592146).
575 _NAME = 'lucidsleep'
RaviChandra Sadinenic06b00e2018-11-03 09:56:11 -0700576 LUCID_SLEEP_BOARDS = ['nocturne', 'poppy']
Kevin Chenga2619dc2016-03-28 11:42:08 -0700577
578 def exists(self, host):
579 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
580 return board in self.LUCID_SLEEP_BOARDS
581
582
Kevin Cheng80ad5732016-03-31 16:01:56 -0700583class HWIDLabel(base_label.StringLabel):
584 """Return all the labels generated from the hwid."""
585
586 # We leave out _NAME because hwid_lib will generate everything for us.
587
588 def __init__(self):
589 # Grab the key file needed to access the hwid service.
590 self.key_file = global_config.global_config.get_config_value(
591 'CROS', 'HWID_KEY', type=str)
592
593
594 def generate_labels(self, host):
595 hwid_labels = []
596 hwid = host.run_output('crossystem hwid').strip()
597 hwid_info_list = hwid_lib.get_hwid_info(hwid, hwid_lib.HWID_INFO_LABEL,
598 self.key_file).get('labels', [])
599
600 for hwid_info in hwid_info_list:
601 # If it's a prefix, we'll have:
602 # {'name': prefix_label, 'value': postfix_label} and create
603 # 'prefix_label:postfix_label'; otherwise it'll just be
604 # {'name': label} which should just be 'label'.
605 value = hwid_info.get('value', '')
606 name = hwid_info.get('name', '')
607 # There should always be a name but just in case there is not.
608 if name:
609 hwid_labels.append(name if not value else
610 '%s:%s' % (name, value))
611 return hwid_labels
612
613
614 def get_all_labels(self):
615 """We need to try all labels as a prefix and as standalone.
616
617 We don't know for sure which labels are prefix labels and which are
618 standalone so we try all of them as both.
619 """
620 all_hwid_labels = []
621 try:
622 all_hwid_labels = hwid_lib.get_all_possible_dut_labels(
623 self.key_file)
624 except IOError:
625 logging.error('Can not open key file: %s', self.key_file)
626 except hwid_lib.HwIdException as e:
627 logging.error('hwid service: %s', e)
628 return all_hwid_labels, all_hwid_labels
629
630
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800631class DetachableBaseLabel(base_label.BaseLabel):
632 """Label indicating if device has detachable keyboard."""
633
634 _NAME = 'detachablebase'
635
636 def exists(self, host):
637 return host.run('which hammerd', ignore_status=True).exit_status == 0
638
639
Kevin Chenga2619dc2016-03-28 11:42:08 -0700640CROS_LABELS = [
641 AccelsLabel(),
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700642 ArcLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700643 AudioLoopbackDongleLabel(),
644 BluetoothLabel(),
Kevin Chenga8455302016-08-31 20:54:41 +0000645 BoardLabel(),
C Shapiro05dd3222017-09-22 10:42:33 -0600646 ModelLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700647 ChameleonConnectionLabel(),
648 ChameleonLabel(),
Joseph Hwangeac44312016-08-31 12:08:38 +0800649 ChameleonPeripheralsLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700650 common_label.OSLabel(),
Mary Ruthven935ebad2018-06-13 16:13:20 -0700651 Cr50Label(),
Ilja H. Friedel50290642017-12-01 19:39:53 -0800652 CtsArchLabel(),
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800653 DetachableBaseLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700654 ECLabel(),
Kevin Cheng80ad5732016-03-31 16:01:56 -0700655 HWIDLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700656 InternalDisplayLabel(),
657 LightSensorLabel(),
658 LucidSleepLabel(),
659 PowerSupplyLabel(),
660 ServoLabel(),
Ilja H. Friedel50290642017-12-01 19:39:53 -0800661 SparseCoverageLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700662 StorageLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700663 VideoGlitchLabel(),
664 VideoLabel(),
665]