Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 1 | # Copyright 2016 The Chromium 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 | |
Simran Basi | aa467ad | 2016-02-03 16:56:22 -0800 | [diff] [blame] | 5 | """Utility functions for AFE-based interactions. |
| 6 | |
| 7 | NOTE: This module should only be used in the context of a running test. Any |
| 8 | utilities that require accessing the AFE, should do so by creating |
| 9 | their own instance of the AFE client and interact with it directly. |
| 10 | """ |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 11 | |
| 12 | import common |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 13 | from autotest_lib.client.common_lib import error |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 14 | from autotest_lib.server.cros import provision |
| 15 | from autotest_lib.server.cros.dynamic_suite import constants |
xixuan | 5dc64ea | 2016-05-20 17:27:51 -0700 | [diff] [blame] | 16 | from autotest_lib.client.common_lib import global_config |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 17 | from autotest_lib.server.cros.dynamic_suite import frontend_wrappers |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 18 | |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 19 | |
| 20 | AFE = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10) |
| 21 | |
xixuan | 5dc64ea | 2016-05-20 17:27:51 -0700 | [diff] [blame] | 22 | _CONFIG = global_config.global_config |
| 23 | ENABLE_DEVSERVER_TRIGGER_AUTO_UPDATE = _CONFIG.get_config_value( |
| 24 | 'CROS', 'enable_devserver_trigger_auto_update', type=bool, |
| 25 | default=False) |
| 26 | |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 27 | |
| 28 | def host_in_lab(host): |
| 29 | """Check if the host is in the lab and an object the AFE knows. |
| 30 | |
| 31 | This check ensures that autoserv and the host's current job is running |
| 32 | inside a fully Autotest instance, aka a lab environment. If this is the |
| 33 | case it then verifies the host is registed with the configured AFE |
| 34 | instance. |
| 35 | |
| 36 | @param host: Host object to verify. |
| 37 | |
| 38 | @returns The host model object. |
| 39 | """ |
Simran Basi | aa467ad | 2016-02-03 16:56:22 -0800 | [diff] [blame] | 40 | if not host.job or not host.job.in_lab: |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 41 | return False |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 42 | return host._afe_host |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 43 | |
| 44 | |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 45 | def get_labels(host, prefix=None): |
| 46 | """Get labels of a host with name started with given prefix. |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 47 | |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 48 | @param prefix: Prefix of label names, if None, return all labels. |
| 49 | |
| 50 | @returns List of labels that match the prefix or if prefix is None, all |
| 51 | labels. |
| 52 | """ |
| 53 | if not prefix: |
| 54 | return host._afe_host.labels |
| 55 | |
| 56 | return [label for label in host._afe_host.labels |
| 57 | if label.startswith(prefix)] |
| 58 | |
| 59 | |
| 60 | def get_build(host): |
| 61 | """Retrieve the current build for a given host stored inside host._afe_host. |
| 62 | |
| 63 | Looks through a host's labels in host._afe_host.labels to determine |
| 64 | its build. |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 65 | |
Dan Shi | b3b6db3 | 2016-02-03 14:54:05 -0800 | [diff] [blame] | 66 | @param host: Host object to get build. |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 67 | |
| 68 | @returns The current build or None if it could not find it or if there |
| 69 | were multiple build labels assigned to the host. |
| 70 | """ |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 71 | for label_prefix in [provision.CROS_VERSION_PREFIX, |
| 72 | provision.ANDROID_BUILD_VERSION_PREFIX, |
| 73 | provision.TESTBED_BUILD_VERSION_PREFIX]: |
| 74 | full_label_prefix = label_prefix + ':' |
| 75 | build_labels = get_labels(host, full_label_prefix) |
| 76 | if build_labels: |
| 77 | return build_labels[0][len(full_label_prefix):] |
| 78 | return None |
| 79 | |
| 80 | |
| 81 | def get_boards(host): |
| 82 | """Retrieve all boards for a given host stored inside host._afe_host. |
| 83 | |
| 84 | @param host: Host object to get board. |
| 85 | |
| 86 | @returns List of all boards. |
| 87 | """ |
| 88 | return [board[len(constants.BOARD_PREFIX):] |
| 89 | for board in get_labels(host, constants.BOARD_PREFIX)] |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 90 | |
| 91 | |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 92 | def get_board(host): |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 93 | """Retrieve the board for a given host stored inside host._afe_host. |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 94 | |
| 95 | @param host: Host object to get board. |
| 96 | |
| 97 | @returns The current board or None if it could not find it. |
| 98 | """ |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 99 | boards = get_boards(host) |
| 100 | if not boards: |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 101 | return None |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 102 | return boards[0] |
| 103 | |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 104 | |
| 105 | |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 106 | def clear_version_labels(host): |
| 107 | """Clear version labels for a given host. |
| 108 | |
| 109 | @param host: Host whose version labels to clear. |
| 110 | """ |
Kevin Cheng | 4ce6761 | 2016-07-29 13:34:17 -0700 | [diff] [blame] | 111 | host._afe_host.labels = [label for label in host._afe_host.labels |
| 112 | if not label.startswith(host.VERSION_PREFIX)] |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 113 | if not host_in_lab(host): |
| 114 | return |
| 115 | |
| 116 | host_list = [host.hostname] |
| 117 | labels = AFE.get_labels( |
| 118 | name__startswith=host.VERSION_PREFIX, |
| 119 | host__hostname=host.hostname) |
| 120 | |
| 121 | for label in labels: |
| 122 | label.remove_hosts(hosts=host_list) |
| 123 | |
| 124 | |
| 125 | def add_version_label(host, image_name): |
| 126 | """Add version labels to a host. |
| 127 | |
| 128 | @param host: Host to add the version label for. |
| 129 | @param image_name: Name of the build version to add to the host. |
| 130 | """ |
Kevin Cheng | 4ce6761 | 2016-07-29 13:34:17 -0700 | [diff] [blame] | 131 | label = '%s:%s' % (host.VERSION_PREFIX, image_name) |
| 132 | host._afe_host.labels.append(label) |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 133 | if not host_in_lab(host): |
| 134 | return |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 135 | AFE.run('label_add_hosts', id=label, hosts=[host.hostname]) |
| 136 | |
| 137 | |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 138 | def get_stable_version(board, android=False): |
| 139 | """Retrieves a board's stable version from the AFE. |
| 140 | |
| 141 | @param board: Board to lookup. |
| 142 | @param android: If True, indicates we are looking up a Android/Brillo-based |
| 143 | board. There is no default version that works for all |
| 144 | Android/Brillo boards. If False, we are looking up a Chrome |
| 145 | OS based board. |
| 146 | |
| 147 | @returns Stable version of the given board. |
| 148 | """ |
| 149 | return AFE.run('get_stable_version', board=board, android=android) |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 150 | |
| 151 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 152 | def lookup_job_repo_url(host): |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 153 | """Looks up the job_repo_url for the host. |
| 154 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 155 | The method is kept for backwards compatibility with test |
| 156 | autoupdate_EndToEndTest in existing builds. It should not be used for new |
| 157 | code. |
| 158 | TODO(dshi): Update autoupdate_EndToEndTest to use get_host_attribute after |
| 159 | lab is updated. After R50 is in stable channel, this method can be removed. |
| 160 | |
| 161 | @param host: A Host object to lookup for job_repo_url. |
| 162 | |
| 163 | @returns Host attribute `job_repo_url` of the given host. |
| 164 | """ |
| 165 | return get_host_attribute(host, host.job_repo_url_attribute) |
| 166 | |
| 167 | |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 168 | def get_host_attribute(host, attribute, use_local_value=True): |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 169 | """Looks up the value of host attribute for the host. |
| 170 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 171 | @param host: A Host object to lookup for attribute value. |
| 172 | @param attribute: Name of the host attribute. |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 173 | @param use_local_value: Boolean to indicate if the local value or AFE value |
| 174 | should be retrieved. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 175 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 176 | @returns value for the given attribute or None if not found. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 177 | """ |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 178 | local_value = host._afe_host.attributes.get(attribute) |
| 179 | if not host_in_lab(host) or use_local_value: |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 180 | return local_value |
| 181 | |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 182 | hosts = AFE.get_hosts(hostname=host.hostname) |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 183 | if hosts and attribute in hosts[0].attributes: |
| 184 | return hosts[0].attributes[attribute] |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 185 | else: |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 186 | return local_value |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 187 | |
| 188 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 189 | def clear_host_attributes_before_provision(host): |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 190 | """Clear host attributes before provision, e.g., job_repo_url. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 191 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 192 | @param host: A Host object to clear attributes before provision. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 193 | """ |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 194 | attributes = host.get_attributes_to_clear_before_provision() |
| 195 | for attribute in attributes: |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 196 | host._afe_host.attributes.pop(attribute, None) |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 197 | if not host_in_lab(host): |
| 198 | return |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 199 | |
| 200 | for attribute in attributes: |
| 201 | update_host_attribute(host, attribute, None) |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 202 | |
| 203 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 204 | def update_host_attribute(host, attribute, value): |
| 205 | """Updates the host attribute with given value. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 206 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 207 | @param host: A Host object to update attribute value. |
| 208 | @param attribute: Name of the host attribute. |
| 209 | @param value: Value for the host attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 210 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 211 | @raises AutoservError: If we failed to update the attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 212 | """ |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 213 | host._afe_host.attributes[attribute] = value |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 214 | if not host_in_lab(host): |
| 215 | return |
| 216 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 217 | AFE.set_host_attribute(attribute, value, hostname=host.hostname) |
Kevin Cheng | 05ae2a4 | 2016-06-06 10:12:48 -0700 | [diff] [blame] | 218 | if get_host_attribute(host, attribute, use_local_value=False) != value: |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 219 | raise error.AutoservError( |
| 220 | 'Failed to update host attribute `%s` with %s, host %s' % |
| 221 | (attribute, value, host.hostname)) |
| 222 | |
| 223 | |
| 224 | def machine_install_and_update_labels(host, *args, **dargs): |
| 225 | """Calls machine_install and updates the version labels on a host. |
| 226 | |
| 227 | @param host: Host object to run machine_install on. |
| 228 | @param *args: Args list to pass to machine_install. |
| 229 | @param **dargs: dargs dict to pass to machine_install. |
| 230 | """ |
| 231 | clear_version_labels(host) |
| 232 | clear_host_attributes_before_provision(host) |
xixuan | 5dc64ea | 2016-05-20 17:27:51 -0700 | [diff] [blame] | 233 | if not ENABLE_DEVSERVER_TRIGGER_AUTO_UPDATE: |
| 234 | image_name, host_attributes = host.machine_install(*args, **dargs) |
| 235 | else: |
| 236 | image_name, host_attributes = host.machine_install_by_devserver( |
| 237 | *args, **dargs) |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 238 | for attribute, value in host_attributes.items(): |
| 239 | update_host_attribute(host, attribute, value) |
Kevin Cheng | e12e71d | 2016-08-15 11:37:18 -0700 | [diff] [blame^] | 240 | add_version_label(host, image_name) |
Dan Shi | 6450e14 | 2016-03-11 11:52:20 -0800 | [diff] [blame] | 241 | |
| 242 | |
Kevin Cheng | 212880e | 2016-06-28 12:09:01 -0700 | [diff] [blame] | 243 | def get_os(host): |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 244 | """Retrieve the os for a given host stored inside host._afe_host. |
Kevin Cheng | 212880e | 2016-06-28 12:09:01 -0700 | [diff] [blame] | 245 | |
| 246 | @param host: Host object to get board. |
| 247 | |
| 248 | @returns The os or None if it could not find it. |
| 249 | """ |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 250 | full_os_prefix = constants.OS_PREFIX + ':' |
| 251 | os_labels = get_labels(host, full_os_prefix) |
| 252 | if not os_labels: |
Kevin Cheng | 212880e | 2016-06-28 12:09:01 -0700 | [diff] [blame] | 253 | return None |
Kevin Cheng | 84a71ba | 2016-07-14 11:03:57 -0700 | [diff] [blame] | 254 | return os_labels[0][len(full_os_prefix):] |