Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 1 | # 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 Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 7 | import collections |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 8 | import logging |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 9 | import re |
| 10 | |
| 11 | import common |
| 12 | |
| 13 | from autotest_lib.client.bin import utils |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 14 | from autotest_lib.client.common_lib import global_config |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 15 | from autotest_lib.client.cros.audio import cras_utils |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 16 | from autotest_lib.server.cros.dynamic_suite import constants as ds_constants |
| 17 | from autotest_lib.server.hosts import base_label |
| 18 | from autotest_lib.server.hosts import common_label |
Garry Wang | 11b5e87 | 2020-03-11 15:14:08 -0700 | [diff] [blame] | 19 | from autotest_lib.server.hosts import servo_constants |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 20 | from autotest_lib.site_utils import hwid_lib |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 21 | |
| 22 | # pylint: disable=missing-docstring |
C Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 23 | LsbOutput = collections.namedtuple('LsbOutput', ['unibuild', 'board']) |
| 24 | |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 25 | # fallback values if we can't contact the HWID server |
| 26 | HWID_LABELS_FALLBACK = ['sku', 'phase', 'touchscreen', 'touchpad', 'variant', 'stylus'] |
| 27 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 28 | # Repair and Deploy taskName |
| 29 | REPAIR_TASK_NAME = 'repair' |
| 30 | DEPLOY_TASK_NAME = 'deploy' |
| 31 | |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 32 | |
C Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 33 | def _parse_lsb_output(host): |
Allen Li | a0c7afc | 2019-02-26 15:50:06 -0800 | [diff] [blame] | 34 | """Parses the LSB output and returns key data points for labeling. |
C Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 35 | |
Allen Li | a0c7afc | 2019-02-26 15:50:06 -0800 | [diff] [blame] | 36 | @param host: Host that the command will be executed against |
| 37 | @returns: LsbOutput with the result of parsing the /etc/lsb-release output |
| 38 | """ |
| 39 | release_info = utils.parse_cmd_output('cat /etc/lsb-release', |
| 40 | run_method=host.run) |
C Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 41 | |
Allen Li | a0c7afc | 2019-02-26 15:50:06 -0800 | [diff] [blame] | 42 | unibuild = release_info.get('CHROMEOS_RELEASE_UNIBUILD') == '1' |
| 43 | return LsbOutput(unibuild, release_info['CHROMEOS_RELEASE_BOARD']) |
C Shapiro | 05dd322 | 2017-09-22 10:42:33 -0600 | [diff] [blame] | 44 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 45 | |
C Shapiro | be0ff8d | 2019-06-14 10:41:43 -0600 | [diff] [blame] | 46 | class DeviceSkuLabel(base_label.StringPrefixLabel): |
| 47 | """Determine the correct device_sku label for the device.""" |
| 48 | |
| 49 | _NAME = ds_constants.DEVICE_SKU_LABEL |
| 50 | |
| 51 | def generate_labels(self, host): |
| 52 | device_sku = host.host_info_store.get().device_sku |
| 53 | if device_sku: |
| 54 | return [device_sku] |
| 55 | |
| 56 | mosys_cmd = 'mosys platform sku' |
| 57 | result = host.run(command=mosys_cmd, ignore_status=True) |
| 58 | if result.exit_status == 0: |
| 59 | return [result.stdout.strip()] |
| 60 | |
| 61 | return [] |
| 62 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 63 | def update_for_task(self, task_name): |
| 64 | # This label is stored in the lab config, so only deploy tasks update it |
| 65 | # or when no task name is mentioned. |
| 66 | return task_name in (DEPLOY_TASK_NAME, '') |
| 67 | |
C Shapiro | be0ff8d | 2019-06-14 10:41:43 -0600 | [diff] [blame] | 68 | |
Ned Nguyen | e0a619d | 2019-07-01 15:50:23 -0600 | [diff] [blame] | 69 | class BrandCodeLabel(base_label.StringPrefixLabel): |
| 70 | """Determine the correct brand_code (aka RLZ-code) for the device.""" |
| 71 | |
| 72 | _NAME = ds_constants.BRAND_CODE_LABEL |
| 73 | |
| 74 | def generate_labels(self, host): |
| 75 | brand_code = host.host_info_store.get().brand_code |
| 76 | if brand_code: |
| 77 | return [brand_code] |
| 78 | |
Greg Edelston | 7cea0c4 | 2019-11-26 15:17:22 -0700 | [diff] [blame] | 79 | cros_config_cmd = 'cros_config / brand-code' |
| 80 | result = host.run(command=cros_config_cmd, ignore_status=True) |
Ned Nguyen | e0a619d | 2019-07-01 15:50:23 -0600 | [diff] [blame] | 81 | if result.exit_status == 0: |
| 82 | return [result.stdout.strip()] |
| 83 | |
| 84 | return [] |
| 85 | |
| 86 | |
Shijin Abraham | c09587d | 2020-02-14 20:46:55 -0800 | [diff] [blame] | 87 | class BluetoothPeerLabel(base_label.StringPrefixLabel): |
| 88 | """Return the Bluetooth peer labels. |
| 89 | |
| 90 | working_bluetooth_btpeer label is applied if a Raspberry Pi Bluetooth peer |
| 91 | is detected.There can be up to 4 Bluetooth peers. Labels |
| 92 | working_bluetooth_btpeer:[1-4] will be assigned depending on the number of |
| 93 | peers present. |
| 94 | |
| 95 | """ |
| 96 | |
| 97 | _NAME = 'working_bluetooth_btpeer' |
| 98 | |
| 99 | def exists(self, host): |
| 100 | return len(host._btpeer_host_list) > 0 |
| 101 | |
| 102 | def generate_labels(self, host): |
| 103 | labels_list = [] |
| 104 | count = 1 |
| 105 | |
| 106 | for (btpeer, btpeer_host) in \ |
| 107 | zip(host.btpeer_list, host._btpeer_host_list): |
| 108 | try: |
| 109 | # Initialize one device type to make sure the peer is working |
| 110 | bt_hid_device = btpeer.get_bluetooth_hid_mouse() |
| 111 | if bt_hid_device.CheckSerialConnection(): |
| 112 | labels_list.append(str(count)) |
| 113 | count += 1 |
| 114 | except Exception as e: |
| 115 | logging.error('Error with initializing bt_hid_mouse on ' |
| 116 | 'btpeer %s %s', btpeer_host.hostname, e) |
| 117 | |
| 118 | logging.info('Bluetooth Peer labels are %s', labels_list) |
| 119 | return labels_list |
| 120 | |
| 121 | def update_for_task(self, task_name): |
Shijin Abraham | 76bc1db | 2020-03-06 10:52:10 -0800 | [diff] [blame] | 122 | # This label is stored in the state config, so only repair tasks update |
| 123 | # it or when no task name is mentioned. |
| 124 | return task_name in (REPAIR_TASK_NAME, '') |
Shijin Abraham | c09587d | 2020-02-14 20:46:55 -0800 | [diff] [blame] | 125 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 126 | |
Mary Ruthven | 935ebad | 2018-06-13 16:13:20 -0700 | [diff] [blame] | 127 | class Cr50Label(base_label.StringPrefixLabel): |
Mary Ruthven | 6c46264 | 2019-09-17 19:13:36 -0700 | [diff] [blame] | 128 | """Label indicating the cr50 image type.""" |
Mary Ruthven | 935ebad | 2018-06-13 16:13:20 -0700 | [diff] [blame] | 129 | |
| 130 | _NAME = 'cr50' |
| 131 | |
| 132 | def __init__(self): |
| 133 | self.ver = None |
| 134 | |
Mary Ruthven | 935ebad | 2018-06-13 16:13:20 -0700 | [diff] [blame] | 135 | def exists(self, host): |
| 136 | # Make sure the gsctool version command runs ok |
| 137 | self.ver = host.run('gsctool -a -f', ignore_status=True) |
| 138 | return self.ver.exit_status == 0 |
| 139 | |
Mary Ruthven | 6c46264 | 2019-09-17 19:13:36 -0700 | [diff] [blame] | 140 | def _get_version(self, region): |
| 141 | """Get the version number of the given region""" |
| 142 | return re.search(region + ' (\d+\.\d+\.\d+)', self.ver.stdout).group(1) |
Mary Ruthven | 935ebad | 2018-06-13 16:13:20 -0700 | [diff] [blame] | 143 | |
| 144 | def generate_labels(self, host): |
| 145 | # Check the major version to determine prePVT vs PVT |
Mary Ruthven | 6c46264 | 2019-09-17 19:13:36 -0700 | [diff] [blame] | 146 | version = self._get_version('RW') |
| 147 | major_version = int(version.split('.')[1]) |
Mary Ruthven | 935ebad | 2018-06-13 16:13:20 -0700 | [diff] [blame] | 148 | # PVT images have a odd major version prePVT have even |
Mary Ruthven | 6c46264 | 2019-09-17 19:13:36 -0700 | [diff] [blame] | 149 | return ['pvt' if (major_version % 2) else 'prepvt'] |
| 150 | |
Xixuan Wu | 61b2b26 | 2020-03-06 10:09:55 -0800 | [diff] [blame] | 151 | def update_for_task(self, task_name): |
| 152 | # This label is stored in the state config, so only repair tasks update |
| 153 | # it or when no task name is mentioned. |
| 154 | return task_name in (REPAIR_TASK_NAME, '') |
| 155 | |
Mary Ruthven | 6c46264 | 2019-09-17 19:13:36 -0700 | [diff] [blame] | 156 | |
| 157 | class Cr50RWKeyidLabel(Cr50Label): |
| 158 | """Label indicating the cr50 RW version.""" |
| 159 | _REGION = 'RW' |
| 160 | _NAME = 'cr50-rw-keyid' |
| 161 | |
| 162 | def _get_keyid_info(self, region): |
| 163 | """Get the keyid of the given region.""" |
| 164 | match = re.search('keyids:.*%s (\S+)' % region, self.ver.stdout) |
| 165 | keyid = match.group(1).rstrip(',') |
| 166 | is_prod = int(keyid, 16) & (1 << 2) |
| 167 | return [keyid, 'prod' if is_prod else 'dev'] |
| 168 | |
| 169 | def generate_labels(self, host): |
| 170 | """Get the key type.""" |
| 171 | return self._get_keyid_info(self._REGION) |
| 172 | |
| 173 | |
| 174 | class Cr50ROKeyidLabel(Cr50RWKeyidLabel): |
| 175 | """Label indicating the RO key type.""" |
| 176 | _REGION = 'RO' |
| 177 | _NAME = 'cr50-ro-keyid' |
| 178 | |
| 179 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 180 | class ChameleonLabel(base_label.BaseLabel): |
| 181 | """Determine if a Chameleon is connected to this host.""" |
| 182 | |
| 183 | _NAME = 'chameleon' |
| 184 | |
| 185 | def exists(self, host): |
Xixuan Wu | 7afb54f | 2019-09-17 11:45:20 -0700 | [diff] [blame] | 186 | # See crbug.com/1004500#2 for details. |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 187 | has_chameleon = host._chameleon_host is not None |
Gregory Nisbet | b2f6d79 | 2019-09-11 14:30:47 -0700 | [diff] [blame] | 188 | # TODO(crbug.com/995900) -- debug why chameleon label is flipping |
| 189 | try: |
| 190 | logging.info("has_chameleon %s", has_chameleon) |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 191 | logging.info("_chameleon_host %s", |
| 192 | getattr(host, "_chameleon_host", "NO_ATTRIBUTE")) |
| 193 | logging.info("chameleon %s", |
| 194 | getattr(host, "chameleon", "NO_ATTRIBUTE")) |
Gregory Nisbet | b2f6d79 | 2019-09-11 14:30:47 -0700 | [diff] [blame] | 195 | except: |
| 196 | pass |
| 197 | return has_chameleon |
| 198 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 199 | def update_for_task(self, task_name): |
| 200 | # This label is stored in the state config, so only repair tasks update |
| 201 | # it or when no task name is mentioned. |
| 202 | return task_name in (REPAIR_TASK_NAME, '') |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 203 | |
| 204 | |
| 205 | class ChameleonConnectionLabel(base_label.StringPrefixLabel): |
| 206 | """Return the Chameleon connection label.""" |
| 207 | |
| 208 | _NAME = 'chameleon' |
| 209 | |
| 210 | def exists(self, host): |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 211 | return host._chameleon_host is not None |
Joseph Hwang | eac4431 | 2016-08-31 12:08:38 +0800 | [diff] [blame] | 212 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 213 | def generate_labels(self, host): |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 214 | return [host.chameleon.get_label()] |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 215 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 216 | def update_for_task(self, task_name): |
| 217 | # This label is stored in the lab config, so only deploy tasks update it |
| 218 | # or when no task name is mentioned. |
| 219 | return task_name in (DEPLOY_TASK_NAME, '') |
| 220 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 221 | |
Joseph Hwang | eac4431 | 2016-08-31 12:08:38 +0800 | [diff] [blame] | 222 | class ChameleonPeripheralsLabel(base_label.StringPrefixLabel): |
| 223 | """Return the Chameleon peripherals labels. |
| 224 | |
| 225 | The 'chameleon:bt_hid' label is applied if the bluetooth |
| 226 | classic hid device, i.e, RN-42 emulation kit, is detected. |
| 227 | |
| 228 | Any peripherals plugged into the chameleon board would be |
| 229 | detected and applied proper labels in this class. |
| 230 | """ |
| 231 | |
| 232 | _NAME = 'chameleon' |
| 233 | |
| 234 | def exists(self, host): |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 235 | return host._chameleon_host is not None |
Joseph Hwang | eac4431 | 2016-08-31 12:08:38 +0800 | [diff] [blame] | 236 | |
| 237 | def generate_labels(self, host): |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 238 | labels = [] |
| 239 | try: |
| 240 | bt_hid_device = host.chameleon.get_bluetooth_hid_mouse() |
| 241 | if bt_hid_device.CheckSerialConnection(): |
| 242 | labels.append('bt_hid') |
| 243 | except: |
| 244 | logging.error('Error with initializing bt_hid_mouse') |
howardchung | 83e5527 | 2019-08-08 14:08:05 +0800 | [diff] [blame] | 245 | |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 246 | try: |
| 247 | ble_hid_device = host.chameleon.get_ble_mouse() |
| 248 | if ble_hid_device.CheckSerialConnection(): |
| 249 | labels.append('bt_ble_hid') |
| 250 | except: |
| 251 | logging.error('Error with initializing ble_hid_mouse') |
howardchung | 83e5527 | 2019-08-08 14:08:05 +0800 | [diff] [blame] | 252 | |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 253 | try: |
| 254 | bt_a2dp_sink = host.chameleon.get_bluetooth_a2dp_sink() |
| 255 | if bt_a2dp_sink.CheckSerialConnection(): |
| 256 | labels.append('bt_a2dp_sink') |
| 257 | except: |
| 258 | logging.error('Error with initializing bt_a2dp_sink') |
howardchung | 83e5527 | 2019-08-08 14:08:05 +0800 | [diff] [blame] | 259 | |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 260 | try: |
Joseph Hwang | 94044ea | 2020-01-03 14:47:43 +0800 | [diff] [blame] | 261 | bt_audio_device = host.chameleon.get_bluetooth_audio() |
| 262 | if bt_audio_device.IsDetected(): |
| 263 | labels.append('bt_audio') |
| 264 | except: |
| 265 | logging.error('Error in detecting bt_audio') |
| 266 | |
| 267 | try: |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 268 | bt_base_device = host.chameleon.get_bluetooth_base() |
| 269 | if bt_base_device.IsDetected(): |
| 270 | labels.append('bt_base') |
| 271 | except: |
| 272 | logging.error('Error in detecting bt_base') |
howardchung | 83e5527 | 2019-08-08 14:08:05 +0800 | [diff] [blame] | 273 | |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 274 | if labels != []: |
| 275 | labels.append('bt_peer') |
Joseph Hwang | 89e779c | 2019-12-24 16:05:56 +0800 | [diff] [blame] | 276 | |
Shijin Abraham | 783a7dd | 2020-02-14 15:36:11 -0800 | [diff] [blame] | 277 | logging.info('Chameleon Bluetooth labels are %s', labels) |
| 278 | return labels |
Shijin Abraham | ff61ac3 | 2019-05-20 12:35:44 -0700 | [diff] [blame] | 279 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 280 | def update_for_task(self, task_name): |
| 281 | # This label is stored in the lab config, so only deploy tasks update it |
| 282 | # or when no task name is mentioned. |
| 283 | return task_name in (DEPLOY_TASK_NAME, '') |
Joseph Hwang | eac4431 | 2016-08-31 12:08:38 +0800 | [diff] [blame] | 284 | |
| 285 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 286 | class AudioLoopbackDongleLabel(base_label.BaseLabel): |
| 287 | """Return the label if an audio loopback dongle is plugged in.""" |
| 288 | |
| 289 | _NAME = 'audio_loopback_dongle' |
| 290 | |
| 291 | def exists(self, host): |
Gregory Nisbet | e280ea2 | 2019-08-16 17:50:03 -0700 | [diff] [blame] | 292 | # Based on crbug.com/991285, AudioLoopbackDongle sometimes flips. |
| 293 | # Ensure that AudioLoopbackDongle.exists returns True |
| 294 | # forever, after it returns True *once*. |
| 295 | if self._cached_exists(host): |
| 296 | # If the current state is True, return it, don't run the command on |
| 297 | # the DUT and potentially flip the state. |
| 298 | return True |
| 299 | # If the current state is not True, run the command on |
| 300 | # the DUT. The new state will be set to whatever the command |
| 301 | # produces. |
| 302 | return self._host_run_exists(host) |
| 303 | |
| 304 | def _cached_exists(self, host): |
| 305 | """Get the state of AudioLoopbackDongle in the data store""" |
| 306 | info = host.host_info_store.get() |
| 307 | for label in info.labels: |
| 308 | if label.startswith(self._NAME): |
| 309 | return True |
| 310 | return False |
| 311 | |
| 312 | def _host_run_exists(self, host): |
| 313 | """Detect presence of audio_loopback_dongle by physically |
| 314 | running a command on the DUT.""" |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 315 | nodes_info = host.run(command=cras_utils.get_cras_nodes_cmd(), |
| 316 | ignore_status=True).stdout |
| 317 | if (cras_utils.node_type_is_plugged('HEADPHONE', nodes_info) and |
| 318 | cras_utils.node_type_is_plugged('MIC', nodes_info)): |
Otabek Kasimov | cefc0d1 | 2020-02-07 17:13:52 -0800 | [diff] [blame] | 319 | return True |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 320 | return False |
| 321 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 322 | def update_for_task(self, task_name): |
| 323 | # This label is stored in the state config, so only repair tasks update |
| 324 | # it or when no task name is mentioned. |
| 325 | return task_name in (REPAIR_TASK_NAME, '') |
| 326 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 327 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 328 | class ServoLabel(base_label.BaseLabel): |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 329 | # class will be removed as part of decommission the old servo label |
| 330 | # "not_connected" and "wrong_config" will be part of ServoHost |
Otabek Kasimov | cefc0d1 | 2020-02-07 17:13:52 -0800 | [diff] [blame] | 331 | """ |
| 332 | Label servo is applying if a servo is present. |
| 333 | Label servo_state present always. |
| 334 | """ |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 335 | |
Otabek Kasimov | cefc0d1 | 2020-02-07 17:13:52 -0800 | [diff] [blame] | 336 | _NAME_OLD = 'servo' |
Garry Wang | 11b5e87 | 2020-03-11 15:14:08 -0700 | [diff] [blame] | 337 | _NAME = servo_constants.SERVO_STATE_LABEL_PREFIX |
Otabek Kasimov | cefc0d1 | 2020-02-07 17:13:52 -0800 | [diff] [blame] | 338 | |
| 339 | def get(self, host): |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 340 | state = self._get_state(host) |
| 341 | servo_state = self._NAME + ':' + state |
Garry Wang | 11b5e87 | 2020-03-11 15:14:08 -0700 | [diff] [blame] | 342 | if state == servo_constants.SERVO_STATE_NOT_CONNECTED: |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 343 | return [servo_state] |
| 344 | return [self._NAME_OLD, servo_state] |
Otabek Kasimov | cefc0d1 | 2020-02-07 17:13:52 -0800 | [diff] [blame] | 345 | |
| 346 | def get_all_labels(self): |
| 347 | return set([self._NAME]), set([self._NAME_OLD]) |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 348 | |
| 349 | def exists(self, host): |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 350 | pass |
Gregory Nisbet | 8b008f8 | 2019-08-20 13:06:19 -0700 | [diff] [blame] | 351 | |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 352 | def _get_state(self, host): |
| 353 | # Based on crbug.com/995900, Servo sometimes flips. |
| 354 | # Ensure that labels has servo or servo_state label |
| 355 | # forever, after it returns state *once*. |
| 356 | state = self._cached_servo_state_status(host) |
| 357 | if state: |
| 358 | # If status exist we do not need to run anything |
| 359 | return state |
| 360 | |
| 361 | # by last changes this point should not reached now |
| 362 | # only till not all DUTs have servo_state label |
| 363 | servo_label = self._cached_servo_label(host) |
| 364 | if servo_label: |
Garry Wang | 11b5e87 | 2020-03-11 15:14:08 -0700 | [diff] [blame] | 365 | return servo_constants.SERVO_STATE_WORKING |
| 366 | return servo_constants.SERVO_STATE_NOT_CONNECTED |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 367 | |
| 368 | def _cached_servo_state_status(self, host): |
Gregory Nisbet | 8b008f8 | 2019-08-20 13:06:19 -0700 | [diff] [blame] | 369 | """Get the state of Servo in the data store""" |
| 370 | info = host.host_info_store.get() |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 371 | # First try to find targeted label |
Gregory Nisbet | 8b008f8 | 2019-08-20 13:06:19 -0700 | [diff] [blame] | 372 | for label in info.labels: |
Otabek Kasimov | 7267a7a | 2020-03-04 11:18:45 -0800 | [diff] [blame] | 373 | if label.startswith(self._NAME): |
| 374 | suffix_length = len(self._NAME) + 1 |
| 375 | return label[suffix_length:] |
| 376 | return '' |
| 377 | |
| 378 | def _cached_servo_label(self, host): |
| 379 | """Get the state of Servo in the data store""" |
| 380 | info = host.host_info_store.get() |
| 381 | # First try to find targeted label |
| 382 | for label in info.labels: |
| 383 | if label is not None and label.strip() == self._NAME_OLD: |
Gregory Nisbet | 8b008f8 | 2019-08-20 13:06:19 -0700 | [diff] [blame] | 384 | return True |
| 385 | return False |
| 386 | |
Eshwar Narayan | 871a2c0 | 2020-02-06 11:15:24 -0800 | [diff] [blame] | 387 | def update_for_task(self, task_name): |
| 388 | # This label is stored in the state config, so only repair tasks update |
| 389 | # it or when no task name is mentioned. |
| 390 | return task_name in (REPAIR_TASK_NAME, '') |
| 391 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 392 | |
Otabek Kasimov | 4318591 | 2020-03-11 16:01:52 -0700 | [diff] [blame] | 393 | class ServoTypeLabel(base_label.StringPrefixLabel): |
Otabek Kasimov | e756528 | 2020-04-14 13:26:12 -0700 | [diff] [blame] | 394 | _NAME = servo_constants.SERVO_TYPE_LABEL_PREFIX |
Otabek Kasimov | 4318591 | 2020-03-11 16:01:52 -0700 | [diff] [blame] | 395 | |
| 396 | def generate_labels(self, host): |
| 397 | info = host.host_info_store.get() |
| 398 | |
| 399 | servo_type = self._get_from_labels(info) |
| 400 | if servo_type != '': |
| 401 | return [servo_type] |
| 402 | |
| 403 | if host.servo is not None: |
| 404 | try: |
| 405 | servo_type = host.servo.get_servo_version() |
| 406 | if servo_type != '': |
| 407 | return [servo_type] |
| 408 | except Exception as e: |
| 409 | # We don't want fail the label and break DUTs here just |
| 410 | # because of servo issue. |
| 411 | logging.error("Failed to update servo_type, %s", str(e)) |
| 412 | return [] |
| 413 | |
| 414 | def _get_from_labels(self, info): |
| 415 | prefix = self._NAME + ':' |
| 416 | for label in info.labels: |
| 417 | if label.startswith(prefix): |
| 418 | suffix_length = len(prefix) |
| 419 | return label[suffix_length:] |
| 420 | return '' |
| 421 | |
| 422 | def update_for_task(self, task_name): |
| 423 | # This label is stored in the lab config, |
| 424 | # only deploy and repair tasks update it |
| 425 | # or when no task name is mentioned. |
| 426 | # use REPAIR_TASK_NAME till restore value for all DUTs |
| 427 | return task_name in (REPAIR_TASK_NAME, DEPLOY_TASK_NAME, '') |
| 428 | |
| 429 | |
Xixuan Wu | 78569d0 | 2019-09-15 16:08:25 -0700 | [diff] [blame] | 430 | def _parse_hwid_labels(hwid_info_list): |
| 431 | if len(hwid_info_list) == 0: |
| 432 | return hwid_info_list |
| 433 | |
| 434 | res = [] |
| 435 | # See crbug.com/997816#c7 for details of two potential formats of returns |
| 436 | # from HWID server. |
| 437 | if isinstance(hwid_info_list[0], dict): |
| 438 | # Format of hwid_info: |
| 439 | # [{u'name': u'sku', u'value': u'xxx'}, ..., ] |
| 440 | for hwid_info in hwid_info_list: |
| 441 | value = hwid_info.get('value', '') |
| 442 | name = hwid_info.get('name', '') |
| 443 | # There should always be a name but just in case there is not. |
| 444 | if name: |
| 445 | new_label = name if not value else '%s:%s' % (name, value) |
| 446 | res.append(new_label) |
| 447 | else: |
| 448 | # Format of hwid_info: |
| 449 | # [<DUTLabel name: 'sku' value: u'xxx'>, ..., ] |
| 450 | for hwid_info in hwid_info_list: |
| 451 | new_label = str(hwid_info) |
| 452 | logging.info('processing hwid label: %s', new_label) |
| 453 | res.append(new_label) |
| 454 | |
| 455 | return res |
| 456 | |
| 457 | |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 458 | class HWIDLabel(base_label.StringLabel): |
| 459 | """Return all the labels generated from the hwid.""" |
| 460 | |
| 461 | # We leave out _NAME because hwid_lib will generate everything for us. |
| 462 | |
| 463 | def __init__(self): |
| 464 | # Grab the key file needed to access the hwid service. |
| 465 | self.key_file = global_config.global_config.get_config_value( |
| 466 | 'CROS', 'HWID_KEY', type=str) |
| 467 | |
| 468 | |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 469 | @staticmethod |
| 470 | def _merge_hwid_label_lists(new, old): |
| 471 | """merge a list of old and new values for hwid_labels. |
| 472 | preferring new values if available |
| 473 | |
| 474 | @returns: list of labels""" |
| 475 | # TODO(gregorynisbet): what is the appropriate way to merge |
| 476 | # old and new information? |
| 477 | retained = set(x for x in old) |
| 478 | for label in new: |
| 479 | key, sep, value = label.partition(':') |
| 480 | # If we have a key-value key such as variant:aaa, |
| 481 | # then we remove all the old labels with the same key. |
| 482 | if sep: |
| 483 | retained = set(x for x in retained if (not x.startswith(key + ':'))) |
| 484 | return list(sorted(retained.union(new))) |
| 485 | |
| 486 | |
| 487 | def _hwid_label_names(self): |
| 488 | """get the labels that hwid_lib controls. |
| 489 | |
| 490 | @returns: hwid_labels |
| 491 | """ |
| 492 | all_hwid_labels, _ = self.get_all_labels() |
| 493 | # If and only if get_all_labels was unsuccessful, |
| 494 | # it will return a falsey value. |
Gregory Nisbet | bfd120f | 2019-09-04 14:49:46 -0700 | [diff] [blame] | 495 | out = all_hwid_labels or HWID_LABELS_FALLBACK |
| 496 | |
| 497 | # TODO(gregorynisbet): remove this |
| 498 | # TODO(crbug.com/999785) |
| 499 | if "sku" not in out: |
| 500 | logging.info("sku-less label names %s", out) |
| 501 | |
| 502 | return out |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 503 | |
| 504 | |
| 505 | def _old_label_values(self, host): |
| 506 | """get the hwid_lib labels on previous run |
| 507 | |
| 508 | @returns: hwid_labels""" |
| 509 | out = [] |
| 510 | info = host.host_info_store.get() |
| 511 | for hwid_label in self._hwid_label_names(): |
| 512 | for label in info.labels: |
| 513 | # NOTE: we want *all* the labels starting |
| 514 | # with this prefix. |
| 515 | if label.startswith(hwid_label): |
| 516 | out.append(label) |
| 517 | return out |
| 518 | |
| 519 | |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 520 | def generate_labels(self, host): |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 521 | # use previous values as default |
| 522 | old_hwid_labels = self._old_label_values(host) |
Xixuan Wu | e63f835 | 2019-09-13 15:18:03 -0700 | [diff] [blame] | 523 | logging.info("old_hwid_labels: %r", old_hwid_labels) |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 524 | hwid = host.run_output('crossystem hwid').strip() |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 525 | hwid_info_list = [] |
| 526 | try: |
| 527 | hwid_info_response = hwid_lib.get_hwid_info( |
| 528 | hwid=hwid, |
| 529 | info_type=hwid_lib.HWID_INFO_LABEL, |
| 530 | key_file=self.key_file, |
| 531 | ) |
Xixuan Wu | e63f835 | 2019-09-13 15:18:03 -0700 | [diff] [blame] | 532 | logging.info("hwid_info_response: %r", hwid_info_response) |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 533 | hwid_info_list = hwid_info_response.get('labels', []) |
| 534 | except hwid_lib.HwIdException as e: |
| 535 | logging.info("HwIdException: %s", e) |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 536 | |
Xixuan Wu | 78569d0 | 2019-09-15 16:08:25 -0700 | [diff] [blame] | 537 | new_hwid_labels = _parse_hwid_labels(hwid_info_list) |
| 538 | logging.info("new HWID labels: %r", new_hwid_labels) |
Gregory Nisbet | bfd120f | 2019-09-04 14:49:46 -0700 | [diff] [blame] | 539 | |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 540 | return HWIDLabel._merge_hwid_label_lists( |
| 541 | old=old_hwid_labels, |
Xixuan Wu | 78569d0 | 2019-09-15 16:08:25 -0700 | [diff] [blame] | 542 | new=new_hwid_labels, |
Gregory Nisbet | fb68a1f | 2019-08-22 10:27:33 -0700 | [diff] [blame] | 543 | ) |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 544 | |
| 545 | |
| 546 | def get_all_labels(self): |
| 547 | """We need to try all labels as a prefix and as standalone. |
| 548 | |
| 549 | We don't know for sure which labels are prefix labels and which are |
| 550 | standalone so we try all of them as both. |
| 551 | """ |
| 552 | all_hwid_labels = [] |
| 553 | try: |
| 554 | all_hwid_labels = hwid_lib.get_all_possible_dut_labels( |
| 555 | self.key_file) |
| 556 | except IOError: |
| 557 | logging.error('Can not open key file: %s', self.key_file) |
| 558 | except hwid_lib.HwIdException as e: |
| 559 | logging.error('hwid service: %s', e) |
| 560 | return all_hwid_labels, all_hwid_labels |
| 561 | |
| 562 | |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 563 | CROS_LABELS = [ |
Eshwar Narayan | f46904c | 2020-02-11 17:57:31 -0800 | [diff] [blame] | 564 | AudioLoopbackDongleLabel(), #STATECONFIG |
Shijin Abraham | 76bc1db | 2020-03-06 10:52:10 -0800 | [diff] [blame] | 565 | BluetoothPeerLabel(), #STATECONFIG |
Eshwar Narayan | f46904c | 2020-02-11 17:57:31 -0800 | [diff] [blame] | 566 | ChameleonConnectionLabel(), #LABCONFIG |
| 567 | ChameleonLabel(), #STATECONFIG |
| 568 | ChameleonPeripheralsLabel(), #LABCONFIG |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 569 | common_label.OSLabel(), |
Eshwar Narayan | f46904c | 2020-02-11 17:57:31 -0800 | [diff] [blame] | 570 | DeviceSkuLabel(), #LABCONFIG |
Kevin Cheng | 80ad573 | 2016-03-31 16:01:56 -0700 | [diff] [blame] | 571 | HWIDLabel(), |
Eshwar Narayan | f46904c | 2020-02-11 17:57:31 -0800 | [diff] [blame] | 572 | ServoLabel(), #STATECONFIG |
Otabek Kasimov | 4318591 | 2020-03-11 16:01:52 -0700 | [diff] [blame] | 573 | ServoTypeLabel(), #LABCONFIG |
Xixuan Wu | 457b4ac | 2020-03-02 14:39:08 -0800 | [diff] [blame] | 574 | # Temporarily add back as there's no way to reference cr50 configs. |
| 575 | # See crbug.com/1057145 for the root cause. |
| 576 | # See crbug.com/1057719 for future tracking. |
| 577 | Cr50Label(), |
| 578 | Cr50ROKeyidLabel(), |
Kevin Cheng | a2619dc | 2016-03-28 11:42:08 -0700 | [diff] [blame] | 579 | ] |
Garry Wang | e4b6d6e | 2019-06-17 17:08:46 -0700 | [diff] [blame] | 580 | |
| 581 | LABSTATION_LABELS = [ |
Garry Wang | e4b6d6e | 2019-06-17 17:08:46 -0700 | [diff] [blame] | 582 | common_label.OSLabel(), |
| 583 | ] |