blob: 02617be230c129d85a8ba7606cbe82e54ffe29f6 [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)
Xixuan Wu6833caf2019-09-17 10:37:17 -0700254 logging.info("chameleon_host_list %s",
255 getattr(host, "_chameleon_host_list", "NO_ATTRIBUTE"))
256 logging.info("chameleon_list %s",
257 getattr(host, "chameleon_list", "NO_ATTRIBUTE"))
258 logging.info("multi_chameleon %s",
259 getattr(host, "multi_chameleon", "NO_ATTRIBUTE"))
Gregory Nisbetb2f6d792019-09-11 14:30:47 -0700260 except:
261 pass
262 return has_chameleon
263
Kevin Chenga2619dc2016-03-28 11:42:08 -0700264
265
266class ChameleonConnectionLabel(base_label.StringPrefixLabel):
267 """Return the Chameleon connection label."""
268
269 _NAME = 'chameleon'
270
271 def exists(self, host):
howardchung83e55272019-08-08 14:08:05 +0800272 return len(host._chameleon_host_list) > 0
Kevin Chenga2619dc2016-03-28 11:42:08 -0700273
Joseph Hwangeac44312016-08-31 12:08:38 +0800274
Kevin Chenga2619dc2016-03-28 11:42:08 -0700275 def generate_labels(self, host):
howardchung83e55272019-08-08 14:08:05 +0800276 return [[chameleon.get_label()] for chameleon in host.chameleon_list]
Kevin Chenga2619dc2016-03-28 11:42:08 -0700277
278
Joseph Hwangeac44312016-08-31 12:08:38 +0800279class ChameleonPeripheralsLabel(base_label.StringPrefixLabel):
280 """Return the Chameleon peripherals labels.
281
282 The 'chameleon:bt_hid' label is applied if the bluetooth
283 classic hid device, i.e, RN-42 emulation kit, is detected.
284
285 Any peripherals plugged into the chameleon board would be
286 detected and applied proper labels in this class.
287 """
288
289 _NAME = 'chameleon'
290
291 def exists(self, host):
howardchung83e55272019-08-08 14:08:05 +0800292 return len(host._chameleon_host_list) > 0
Joseph Hwangeac44312016-08-31 12:08:38 +0800293
294
295 def generate_labels(self, host):
howardchung83e55272019-08-08 14:08:05 +0800296 labels_list = []
297
298 for chameleon, chameleon_host in \
299 zip(host.chameleon_list, host._chameleon_host_list):
300 labels = []
301 try:
302 bt_hid_device = chameleon.get_bluetooth_hid_mouse()
303 if bt_hid_device.CheckSerialConnection():
304 labels.append('bt_hid')
305 except:
306 logging.error('Error with initializing bt_hid_mouse on '
307 'chameleon %s', chameleon_host.hostname)
308
309 try:
310 ble_hid_device = chameleon.get_ble_mouse()
311 if ble_hid_device.CheckSerialConnection():
312 labels.append('bt_ble_hid')
313 except:
314 logging.error('Error with initializing ble_hid_mouse on '
315 'chameleon %s', chameleon_host.hostname)
316
317 try:
318 bt_a2dp_sink = chameleon.get_bluetooth_a2dp_sink()
319 if bt_a2dp_sink.CheckSerialConnection():
320 labels.append('bt_a2dp_sink')
321 except:
322 logging.error('Error with initializing bt_a2dp_sink on '
323 'chameleon %s', chameleon_host.hostname)
324
325 if labels != []:
326 labels.append('bt_peer')
327
328 if host.multi_chameleon:
329 labels_list.append(labels)
330 else:
331 labels_list.extend(labels)
332
333
334 logging.info('Bluetooth labels are %s', labels_list)
335 return labels_list
Shijin Abrahamff61ac32019-05-20 12:35:44 -0700336
337
Joseph Hwangeac44312016-08-31 12:08:38 +0800338
339
Kevin Chenga2619dc2016-03-28 11:42:08 -0700340class AudioLoopbackDongleLabel(base_label.BaseLabel):
341 """Return the label if an audio loopback dongle is plugged in."""
342
343 _NAME = 'audio_loopback_dongle'
344
345 def exists(self, host):
Gregory Nisbete280ea22019-08-16 17:50:03 -0700346 # Based on crbug.com/991285, AudioLoopbackDongle sometimes flips.
347 # Ensure that AudioLoopbackDongle.exists returns True
348 # forever, after it returns True *once*.
349 if self._cached_exists(host):
350 # If the current state is True, return it, don't run the command on
351 # the DUT and potentially flip the state.
352 return True
353 # If the current state is not True, run the command on
354 # the DUT. The new state will be set to whatever the command
355 # produces.
356 return self._host_run_exists(host)
357
358 def _cached_exists(self, host):
359 """Get the state of AudioLoopbackDongle in the data store"""
360 info = host.host_info_store.get()
361 for label in info.labels:
362 if label.startswith(self._NAME):
363 return True
364 return False
365
366 def _host_run_exists(self, host):
367 """Detect presence of audio_loopback_dongle by physically
368 running a command on the DUT."""
Kevin Chenga2619dc2016-03-28 11:42:08 -0700369 nodes_info = host.run(command=cras_utils.get_cras_nodes_cmd(),
370 ignore_status=True).stdout
371 if (cras_utils.node_type_is_plugged('HEADPHONE', nodes_info) and
372 cras_utils.node_type_is_plugged('MIC', nodes_info)):
373 return True
374 return False
375
376
377class PowerSupplyLabel(base_label.StringPrefixLabel):
378 """
379 Return the label describing the power supply type.
380
381 Labels representing this host's power supply.
382 * `power:battery` when the device has a battery intended for
383 extended use
384 * `power:AC_primary` when the device has a battery not intended
385 for extended use (for moving the machine, etc)
386 * `power:AC_only` when the device has no battery at all.
387 """
388
389 _NAME = 'power'
390
391 def __init__(self):
392 self.psu_cmd_result = None
393
394
395 def exists(self, host):
396 self.psu_cmd_result = host.run(command='mosys psu type',
397 ignore_status=True)
398 return self.psu_cmd_result.stdout.strip() != 'unknown'
399
400
401 def generate_labels(self, host):
402 if self.psu_cmd_result.exit_status:
403 # The psu command for mosys is not included for all platforms. The
404 # assumption is that the device will have a battery if the command
405 # is not found.
406 return ['battery']
407 return [self.psu_cmd_result.stdout.strip()]
408
409
410class StorageLabel(base_label.StringPrefixLabel):
411 """
412 Return the label describing the storage type.
413
414 Determine if the internal device is SCSI or dw_mmc device.
415 Then check that it is SSD or HDD or eMMC or something else.
416
417 Labels representing this host's internal device type:
418 * `storage:ssd` when internal device is solid state drive
419 * `storage:hdd` when internal device is hard disk drive
420 * `storage:mmc` when internal device is mmc drive
Gwendal Grignou327fec62017-07-26 15:25:43 -0700421 * `storage:nvme` when internal device is NVMe drive
Alexis Savery570e7fb2018-06-26 10:48:15 -0700422 * `storage:ufs` when internal device is ufs drive
Kevin Chenga2619dc2016-03-28 11:42:08 -0700423 * None When internal device is something else or
424 when we are unable to determine the type
425 """
426
427 _NAME = 'storage'
428
429 def __init__(self):
430 self.type_str = ''
431
432
433 def exists(self, host):
434 # The output should be /dev/mmcblk* for SD/eMMC or /dev/sd* for scsi
435 rootdev_cmd = ' '.join(['. /usr/sbin/write_gpt.sh;',
436 '. /usr/share/misc/chromeos-common.sh;',
437 'load_base_vars;',
438 'get_fixed_dst_drive'])
439 rootdev = host.run(command=rootdev_cmd, ignore_status=True)
440 if rootdev.exit_status:
441 logging.info("Fail to run %s", rootdev_cmd)
442 return False
443 rootdev_str = rootdev.stdout.strip()
444
445 if not rootdev_str:
446 return False
447
448 rootdev_base = os.path.basename(rootdev_str)
449
450 mmc_pattern = '/dev/mmcblk[0-9]'
451 if re.match(mmc_pattern, rootdev_str):
452 # Use type to determine if the internal device is eMMC or somthing
453 # else. We can assume that MMC is always an internal device.
454 type_cmd = 'cat /sys/block/%s/device/type' % rootdev_base
455 type = host.run(command=type_cmd, ignore_status=True)
456 if type.exit_status:
457 logging.info("Fail to run %s", type_cmd)
458 return False
459 type_str = type.stdout.strip()
460
461 if type_str == 'MMC':
462 self.type_str = 'mmc'
463 return True
464
465 scsi_pattern = '/dev/sd[a-z]+'
466 if re.match(scsi_pattern, rootdev.stdout):
467 # Read symlink for /sys/block/sd* to determine if the internal
468 # device is connected via ata or usb.
469 link_cmd = 'readlink /sys/block/%s' % rootdev_base
470 link = host.run(command=link_cmd, ignore_status=True)
471 if link.exit_status:
472 logging.info("Fail to run %s", link_cmd)
473 return False
474 link_str = link.stdout.strip()
475 if 'usb' in link_str:
476 return False
Alexis Savery570e7fb2018-06-26 10:48:15 -0700477 elif 'ufs' in link_str:
478 self.type_str = 'ufs'
479 return True
Kevin Chenga2619dc2016-03-28 11:42:08 -0700480
481 # Read rotation to determine if the internal device is ssd or hdd.
482 rotate_cmd = str('cat /sys/block/%s/queue/rotational'
483 % rootdev_base)
484 rotate = host.run(command=rotate_cmd, ignore_status=True)
485 if rotate.exit_status:
486 logging.info("Fail to run %s", rotate_cmd)
487 return False
488 rotate_str = rotate.stdout.strip()
489
490 rotate_dict = {'0':'ssd', '1':'hdd'}
491 self.type_str = rotate_dict.get(rotate_str)
492 return True
493
Gwendal Grignou327fec62017-07-26 15:25:43 -0700494 nvme_pattern = '/dev/nvme[0-9]+n[0-9]+'
495 if re.match(nvme_pattern, rootdev_str):
Gwendal Grignou3660c192017-12-06 10:11:23 -0800496 self.type_str = 'nvme'
Gwendal Grignou327fec62017-07-26 15:25:43 -0700497 return True
498
Kevin Chenga2619dc2016-03-28 11:42:08 -0700499 # All other internal device / error case will always fall here
500 return False
501
502
503 def generate_labels(self, host):
504 return [self.type_str]
505
506
507class ServoLabel(base_label.BaseLabel):
508 """Label to apply if a servo is present."""
509
510 _NAME = 'servo'
511
512 def exists(self, host):
Gregory Nisbet8b008f82019-08-20 13:06:19 -0700513 # Based on crbug.com/995900, Servo sometimes flips.
514 # Ensure that ServoLabel.exists returns True
515 # forever, after it returns True *once*.
516 if self._cached_exists(host):
517 # If the current state is True, return it, don't run the command on
518 # the DUT and potentially flip the state.
519 return True
520 # If the current state is not True, run the command on
521 # the DUT. The new state will be set to whatever the command
522 # produces.
523 return self._host_run_exists(host)
524
525 def _cached_exists(self, host):
526 """Get the state of Servo in the data store"""
527 info = host.host_info_store.get()
528 for label in info.labels:
529 if label.startswith(self._NAME):
530 return True
531 return False
532
533 def _host_run_exists(self, host):
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700534 """
535 Check if the servo label should apply to the host or not.
536
537 @returns True if a servo host is detected, False otherwise.
538 """
Kevin Cheng745b8162017-05-26 09:48:36 -0700539 servo_host_hostname = None
Prathmesh Prabhu37ae79b2018-09-12 10:37:44 -0700540 servo_args = servo_host.get_servo_args_for_host(host)
Kevin Cheng745b8162017-05-26 09:48:36 -0700541 if servo_args:
542 servo_host_hostname = servo_args.get(servo_host.SERVO_HOST_ATTR)
Kevin Chengbbe5cf12016-06-08 21:50:01 -0700543 return (servo_host_hostname is not None
544 and servo_host.servo_host_is_up(servo_host_hostname))
Kevin Chenga2619dc2016-03-28 11:42:08 -0700545
546
Ilja H. Friedel50290642017-12-01 19:39:53 -0800547class ArcLabel(base_label.BaseLabel):
548 """Label indicates if host has ARC support."""
549
550 _NAME = 'arc'
551
552 @base_label.forever_exists_decorate
553 def exists(self, host):
554 return 0 == host.run(
555 'grep CHROMEOS_ARC_VERSION /etc/lsb-release',
556 ignore_status=True).exit_status
557
558
559class CtsArchLabel(base_label.StringLabel):
560 """Labels to determine the abi of the CTS bundle (arm or x86 only)."""
Rohit Makasana5a153502016-06-13 15:50:09 -0700561
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700562 _NAME = ['cts_abi_arm', 'cts_abi_x86', 'cts_cpu_arm', 'cts_cpu_x86']
Rohit Makasana5a153502016-06-13 15:50:09 -0700563
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700564 def _get_cts_abis(self, arch):
Rohit Makasana5a153502016-06-13 15:50:09 -0700565 """Return supported CTS ABIs.
566
567 @return List of supported CTS bundle ABIs.
568 """
569 cts_abis = {'x86_64': ['arm', 'x86'], 'arm': ['arm']}
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700570 return cts_abis.get(arch, [])
571
572 def _get_cts_cpus(self, arch):
573 """Return supported CTS native CPUs.
574
575 This is needed for CTS_Instant scheduling.
576 @return List of supported CTS native CPUs.
577 """
578 cts_cpus = {'x86_64': ['x86'], 'arm': ['arm']}
579 return cts_cpus.get(arch, [])
Rohit Makasana5a153502016-06-13 15:50:09 -0700580
Rohit Makasana5a153502016-06-13 15:50:09 -0700581 def generate_labels(self, host):
Ilja H. Friedela7502f52018-10-16 23:33:25 -0700582 cpu_arch = host.get_cpu_arch()
583 abi_labels = ['cts_abi_' + abi for abi in self._get_cts_abis(cpu_arch)]
584 cpu_labels = ['cts_cpu_' + cpu for cpu in self._get_cts_cpus(cpu_arch)]
585 return abi_labels + cpu_labels
Rohit Makasana5a153502016-06-13 15:50:09 -0700586
587
Kevin Chenga2619dc2016-03-28 11:42:08 -0700588class VideoGlitchLabel(base_label.BaseLabel):
589 """Label indicates if host supports video glitch detection tests."""
590
591 _NAME = 'video_glitch_detection'
592
593 def exists(self, host):
594 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
595
596 return board in video_test_constants.SUPPORTED_BOARDS
597
598
Kevin Chenga2619dc2016-03-28 11:42:08 -0700599class InternalDisplayLabel(base_label.StringLabel):
600 """Label that determines if the device has an internal display."""
601
602 _NAME = 'internal_display'
603
604 def generate_labels(self, host):
605 from autotest_lib.client.cros.graphics import graphics_utils
606 from autotest_lib.client.common_lib import utils as common_utils
607
608 def __system_output(cmd):
609 return host.run(cmd).stdout
610
611 def __read_file(remote_path):
612 return host.run('cat %s' % remote_path).stdout
613
614 # Hijack the necessary client functions so that we can take advantage
615 # of the client lib here.
616 # FIXME: find a less hacky way than this
617 original_system_output = utils.system_output
618 original_read_file = common_utils.read_file
619 utils.system_output = __system_output
620 common_utils.read_file = __read_file
621 try:
622 return ([self._NAME]
623 if graphics_utils.has_internal_display()
624 else [])
625 finally:
626 utils.system_output = original_system_output
627 common_utils.read_file = original_read_file
628
629
630class LucidSleepLabel(base_label.BaseLabel):
631 """Label that determines if device has support for lucid sleep."""
632
633 # TODO(kevcheng): See if we can determine if this label is applicable a
634 # better way (crbug.com/592146).
635 _NAME = 'lucidsleep'
RaviChandra Sadinenic06b00e2018-11-03 09:56:11 -0700636 LUCID_SLEEP_BOARDS = ['nocturne', 'poppy']
Kevin Chenga2619dc2016-03-28 11:42:08 -0700637
638 def exists(self, host):
639 board = host.get_board().replace(ds_constants.BOARD_PREFIX, '')
640 return board in self.LUCID_SLEEP_BOARDS
641
642
Xixuan Wu78569d02019-09-15 16:08:25 -0700643def _parse_hwid_labels(hwid_info_list):
644 if len(hwid_info_list) == 0:
645 return hwid_info_list
646
647 res = []
648 # See crbug.com/997816#c7 for details of two potential formats of returns
649 # from HWID server.
650 if isinstance(hwid_info_list[0], dict):
651 # Format of hwid_info:
652 # [{u'name': u'sku', u'value': u'xxx'}, ..., ]
653 for hwid_info in hwid_info_list:
654 value = hwid_info.get('value', '')
655 name = hwid_info.get('name', '')
656 # There should always be a name but just in case there is not.
657 if name:
658 new_label = name if not value else '%s:%s' % (name, value)
659 res.append(new_label)
660 else:
661 # Format of hwid_info:
662 # [<DUTLabel name: 'sku' value: u'xxx'>, ..., ]
663 for hwid_info in hwid_info_list:
664 new_label = str(hwid_info)
665 logging.info('processing hwid label: %s', new_label)
666 res.append(new_label)
667
668 return res
669
670
Kevin Cheng80ad5732016-03-31 16:01:56 -0700671class HWIDLabel(base_label.StringLabel):
672 """Return all the labels generated from the hwid."""
673
674 # We leave out _NAME because hwid_lib will generate everything for us.
675
676 def __init__(self):
677 # Grab the key file needed to access the hwid service.
678 self.key_file = global_config.global_config.get_config_value(
679 'CROS', 'HWID_KEY', type=str)
680
681
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700682 @staticmethod
683 def _merge_hwid_label_lists(new, old):
684 """merge a list of old and new values for hwid_labels.
685 preferring new values if available
686
687 @returns: list of labels"""
688 # TODO(gregorynisbet): what is the appropriate way to merge
689 # old and new information?
690 retained = set(x for x in old)
691 for label in new:
692 key, sep, value = label.partition(':')
693 # If we have a key-value key such as variant:aaa,
694 # then we remove all the old labels with the same key.
695 if sep:
696 retained = set(x for x in retained if (not x.startswith(key + ':')))
697 return list(sorted(retained.union(new)))
698
699
700 def _hwid_label_names(self):
701 """get the labels that hwid_lib controls.
702
703 @returns: hwid_labels
704 """
705 all_hwid_labels, _ = self.get_all_labels()
706 # If and only if get_all_labels was unsuccessful,
707 # it will return a falsey value.
Gregory Nisbetbfd120f2019-09-04 14:49:46 -0700708 out = all_hwid_labels or HWID_LABELS_FALLBACK
709
710 # TODO(gregorynisbet): remove this
711 # TODO(crbug.com/999785)
712 if "sku" not in out:
713 logging.info("sku-less label names %s", out)
714
715 return out
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700716
717
718 def _old_label_values(self, host):
719 """get the hwid_lib labels on previous run
720
721 @returns: hwid_labels"""
722 out = []
723 info = host.host_info_store.get()
724 for hwid_label in self._hwid_label_names():
725 for label in info.labels:
726 # NOTE: we want *all* the labels starting
727 # with this prefix.
728 if label.startswith(hwid_label):
729 out.append(label)
730 return out
731
732
Kevin Cheng80ad5732016-03-31 16:01:56 -0700733 def generate_labels(self, host):
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700734 # use previous values as default
735 old_hwid_labels = self._old_label_values(host)
Xixuan Wue63f8352019-09-13 15:18:03 -0700736 logging.info("old_hwid_labels: %r", old_hwid_labels)
Kevin Cheng80ad5732016-03-31 16:01:56 -0700737 hwid = host.run_output('crossystem hwid').strip()
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700738 hwid_info_list = []
739 try:
740 hwid_info_response = hwid_lib.get_hwid_info(
741 hwid=hwid,
742 info_type=hwid_lib.HWID_INFO_LABEL,
743 key_file=self.key_file,
744 )
Xixuan Wue63f8352019-09-13 15:18:03 -0700745 logging.info("hwid_info_response: %r", hwid_info_response)
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700746 hwid_info_list = hwid_info_response.get('labels', [])
747 except hwid_lib.HwIdException as e:
748 logging.info("HwIdException: %s", e)
Kevin Cheng80ad5732016-03-31 16:01:56 -0700749
Xixuan Wu78569d02019-09-15 16:08:25 -0700750 new_hwid_labels = _parse_hwid_labels(hwid_info_list)
751 logging.info("new HWID labels: %r", new_hwid_labels)
Gregory Nisbetbfd120f2019-09-04 14:49:46 -0700752
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700753 return HWIDLabel._merge_hwid_label_lists(
754 old=old_hwid_labels,
Xixuan Wu78569d02019-09-15 16:08:25 -0700755 new=new_hwid_labels,
Gregory Nisbetfb68a1f2019-08-22 10:27:33 -0700756 )
Kevin Cheng80ad5732016-03-31 16:01:56 -0700757
758
759 def get_all_labels(self):
760 """We need to try all labels as a prefix and as standalone.
761
762 We don't know for sure which labels are prefix labels and which are
763 standalone so we try all of them as both.
764 """
765 all_hwid_labels = []
766 try:
767 all_hwid_labels = hwid_lib.get_all_possible_dut_labels(
768 self.key_file)
769 except IOError:
770 logging.error('Can not open key file: %s', self.key_file)
771 except hwid_lib.HwIdException as e:
772 logging.error('hwid service: %s', e)
773 return all_hwid_labels, all_hwid_labels
774
775
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800776class DetachableBaseLabel(base_label.BaseLabel):
777 """Label indicating if device has detachable keyboard."""
778
779 _NAME = 'detachablebase'
780
781 def exists(self, host):
782 return host.run('which hammerd', ignore_status=True).exit_status == 0
783
784
Tom Hughese9552342018-12-18 14:29:25 -0800785class FingerprintLabel(base_label.BaseLabel):
786 """Label indicating whether device has fingerprint sensor."""
787
788 _NAME = 'fingerprint'
789
790 def exists(self, host):
791 return host.run('test -c /dev/cros_fp',
792 ignore_status=True).exit_status == 0
793
794
Garry Wang17a829e2019-03-20 12:03:18 -0700795class ReferenceDesignLabel(base_label.StringPrefixLabel):
796 """Determine the correct reference design label for the device. """
797
798 _NAME = 'reference_design'
799
800 def __init__(self):
801 self.response = None
802
803 def exists(self, host):
804 self.response = host.run('mosys platform family', ignore_status=True)
805 return self.response.exit_status == 0
806
807 def generate_labels(self, host):
808 if self.exists(host):
809 return [self.response.stdout.strip()]
810
811
Kevin Chenga2619dc2016-03-28 11:42:08 -0700812CROS_LABELS = [
813 AccelsLabel(),
Rohit Makasanac6e5d622016-06-16 17:13:39 -0700814 ArcLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700815 AudioLoopbackDongleLabel(),
816 BluetoothLabel(),
Kevin Chenga8455302016-08-31 20:54:41 +0000817 BoardLabel(),
C Shapiro05dd3222017-09-22 10:42:33 -0600818 ModelLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700819 ChameleonConnectionLabel(),
820 ChameleonLabel(),
Joseph Hwangeac44312016-08-31 12:08:38 +0800821 ChameleonPeripheralsLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700822 common_label.OSLabel(),
Mary Ruthven935ebad2018-06-13 16:13:20 -0700823 Cr50Label(),
Ilja H. Friedel50290642017-12-01 19:39:53 -0800824 CtsArchLabel(),
Chih-Yu Huangcaca4882018-01-30 11:21:48 +0800825 DetachableBaseLabel(),
C Shapiro8315f5f2019-06-19 15:53:29 -0600826 DeviceSkuLabel(),
Ned Nguyene0a619d2019-07-01 15:50:23 -0600827 BrandCodeLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700828 ECLabel(),
Tom Hughese9552342018-12-18 14:29:25 -0800829 FingerprintLabel(),
Kevin Cheng80ad5732016-03-31 16:01:56 -0700830 HWIDLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700831 InternalDisplayLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700832 LucidSleepLabel(),
833 PowerSupplyLabel(),
Garry Wang17a829e2019-03-20 12:03:18 -0700834 ReferenceDesignLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700835 ServoLabel(),
836 StorageLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700837 VideoGlitchLabel(),
Kevin Chenga2619dc2016-03-28 11:42:08 -0700838]
Garry Wange4b6d6e2019-06-17 17:08:46 -0700839
840LABSTATION_LABELS = [
841 BoardLabel(),
842 ModelLabel(),
843 common_label.OSLabel(),
Garry Wang1d0598a2019-09-06 16:31:56 -0700844 ECLabel(),
845 PowerSupplyLabel(),
Garry Wange4b6d6e2019-06-17 17:08:46 -0700846]