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 |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 14 | from autotest_lib.server import utils |
| 15 | from autotest_lib.server.cros.dynamic_suite import frontend_wrappers |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 16 | |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 17 | |
| 18 | AFE = frontend_wrappers.RetryingAFE(timeout_min=5, delay_sec=10) |
| 19 | |
| 20 | |
| 21 | def host_in_lab(host): |
| 22 | """Check if the host is in the lab and an object the AFE knows. |
| 23 | |
| 24 | This check ensures that autoserv and the host's current job is running |
| 25 | inside a fully Autotest instance, aka a lab environment. If this is the |
| 26 | case it then verifies the host is registed with the configured AFE |
| 27 | instance. |
| 28 | |
| 29 | @param host: Host object to verify. |
| 30 | |
| 31 | @returns The host model object. |
| 32 | """ |
Simran Basi | aa467ad | 2016-02-03 16:56:22 -0800 | [diff] [blame] | 33 | if not host.job or not host.job.in_lab: |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 34 | return False |
| 35 | return AFE.get_hosts(hostname=host.hostname) |
| 36 | |
| 37 | |
| 38 | def get_build(host): |
Dan Shi | b3b6db3 | 2016-02-03 14:54:05 -0800 | [diff] [blame] | 39 | """Retrieve the current build for a given host from the AFE. |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 40 | |
| 41 | Looks through a host's labels in the AFE to determine its build. |
| 42 | |
Dan Shi | b3b6db3 | 2016-02-03 14:54:05 -0800 | [diff] [blame] | 43 | @param host: Host object to get build. |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 44 | |
| 45 | @returns The current build or None if it could not find it or if there |
| 46 | were multiple build labels assigned to the host. |
| 47 | """ |
| 48 | if not host_in_lab(host): |
| 49 | return None |
| 50 | return utils.get_build_from_afe(host.hostname, AFE) |
| 51 | |
| 52 | |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 53 | def get_board(host): |
| 54 | """Retrieve the board for a given host from the AFE. |
| 55 | |
| 56 | Contacts the AFE to retrieve the board for a given host. |
| 57 | |
| 58 | @param host: Host object to get board. |
| 59 | |
| 60 | @returns The current board or None if it could not find it. |
| 61 | """ |
| 62 | if not host_in_lab(host): |
| 63 | return None |
| 64 | return utils.get_board_from_afe(host.hostname, AFE) |
| 65 | |
| 66 | |
Simran Basi | 5ace6f2 | 2016-01-06 17:30:44 -0800 | [diff] [blame] | 67 | def clear_version_labels(host): |
| 68 | """Clear version labels for a given host. |
| 69 | |
| 70 | @param host: Host whose version labels to clear. |
| 71 | """ |
| 72 | if not host_in_lab(host): |
| 73 | return |
| 74 | |
| 75 | host_list = [host.hostname] |
| 76 | labels = AFE.get_labels( |
| 77 | name__startswith=host.VERSION_PREFIX, |
| 78 | host__hostname=host.hostname) |
| 79 | |
| 80 | for label in labels: |
| 81 | label.remove_hosts(hosts=host_list) |
| 82 | |
| 83 | |
| 84 | def add_version_label(host, image_name): |
| 85 | """Add version labels to a host. |
| 86 | |
| 87 | @param host: Host to add the version label for. |
| 88 | @param image_name: Name of the build version to add to the host. |
| 89 | """ |
| 90 | if not host_in_lab(host): |
| 91 | return |
| 92 | label = '%s:%s' % (host.VERSION_PREFIX, image_name) |
| 93 | AFE.run('label_add_hosts', id=label, hosts=[host.hostname]) |
| 94 | |
| 95 | |
Simran Basi | beb2bb2 | 2016-02-03 15:25:48 -0800 | [diff] [blame] | 96 | def get_stable_version(board, android=False): |
| 97 | """Retrieves a board's stable version from the AFE. |
| 98 | |
| 99 | @param board: Board to lookup. |
| 100 | @param android: If True, indicates we are looking up a Android/Brillo-based |
| 101 | board. There is no default version that works for all |
| 102 | Android/Brillo boards. If False, we are looking up a Chrome |
| 103 | OS based board. |
| 104 | |
| 105 | @returns Stable version of the given board. |
| 106 | """ |
| 107 | return AFE.run('get_stable_version', board=board, android=android) |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 108 | |
| 109 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 110 | def lookup_job_repo_url(host): |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 111 | """Looks up the job_repo_url for the host. |
| 112 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 113 | The method is kept for backwards compatibility with test |
| 114 | autoupdate_EndToEndTest in existing builds. It should not be used for new |
| 115 | code. |
| 116 | TODO(dshi): Update autoupdate_EndToEndTest to use get_host_attribute after |
| 117 | lab is updated. After R50 is in stable channel, this method can be removed. |
| 118 | |
| 119 | @param host: A Host object to lookup for job_repo_url. |
| 120 | |
| 121 | @returns Host attribute `job_repo_url` of the given host. |
| 122 | """ |
| 123 | return get_host_attribute(host, host.job_repo_url_attribute) |
| 124 | |
| 125 | |
| 126 | def get_host_attribute(host, attribute): |
| 127 | """Looks up the value of host attribute for the host. |
| 128 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 129 | @param host: A Host object to lookup for attribute value. |
| 130 | @param attribute: Name of the host attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 131 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 132 | @returns value for the given attribute or None if not found. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 133 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 134 | @raises KeyError if the host does not have the given attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 135 | """ |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 136 | local_value = host.host_attributes.get(attribute) |
| 137 | if not host_in_lab(host): |
| 138 | return local_value |
| 139 | |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 140 | hosts = AFE.get_hosts(hostname=host.hostname) |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 141 | if hosts and attribute in hosts[0].attributes: |
| 142 | return hosts[0].attributes[attribute] |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 143 | else: |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 144 | return local_value |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 145 | |
| 146 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 147 | def clear_host_attributes_before_provision(host): |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 148 | """Clear host attributes before provision, e.g., job_repo_url. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 149 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 150 | @param host: A Host object to clear attributes before provision. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 151 | """ |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 152 | attributes = host.get_attributes_to_clear_before_provision() |
| 153 | for attribute in attributes: |
| 154 | if attribute in host.host_attributes: |
| 155 | del host.host_attributes[attribute] |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 156 | if not host_in_lab(host): |
| 157 | return |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 158 | |
| 159 | for attribute in attributes: |
| 160 | update_host_attribute(host, attribute, None) |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 161 | |
| 162 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 163 | def update_host_attribute(host, attribute, value): |
| 164 | """Updates the host attribute with given value. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 165 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 166 | @param host: A Host object to update attribute value. |
| 167 | @param attribute: Name of the host attribute. |
| 168 | @param value: Value for the host attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 169 | |
Dan Shi | e4256c8 | 2016-02-18 00:23:49 -0800 | [diff] [blame] | 170 | @raises AutoservError: If we failed to update the attribute. |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 171 | """ |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 172 | host.host_attributes[attribute] = value |
Dan Shi | 8190eb8 | 2016-02-11 17:15:58 -0800 | [diff] [blame] | 173 | if not host_in_lab(host): |
| 174 | return |
| 175 | |
Dan Shi | be3636a | 2016-02-14 22:48:01 -0800 | [diff] [blame] | 176 | AFE.set_host_attribute(attribute, value, hostname=host.hostname) |
| 177 | if get_host_attribute(host, attribute) != value: |
| 178 | raise error.AutoservError( |
| 179 | 'Failed to update host attribute `%s` with %s, host %s' % |
| 180 | (attribute, value, host.hostname)) |
| 181 | |
| 182 | |
| 183 | def machine_install_and_update_labels(host, *args, **dargs): |
| 184 | """Calls machine_install and updates the version labels on a host. |
| 185 | |
| 186 | @param host: Host object to run machine_install on. |
| 187 | @param *args: Args list to pass to machine_install. |
| 188 | @param **dargs: dargs dict to pass to machine_install. |
| 189 | """ |
| 190 | clear_version_labels(host) |
| 191 | clear_host_attributes_before_provision(host) |
| 192 | image_name, host_attributes = host.machine_install(*args, **dargs) |
| 193 | add_version_label(host, image_name) |
| 194 | for attribute, value in host_attributes.items(): |
| 195 | update_host_attribute(host, attribute, value) |
Dan Shi | 6450e14 | 2016-03-11 11:52:20 -0800 | [diff] [blame^] | 196 | |
| 197 | |
| 198 | def get_labels(host, prefix): |
| 199 | """Get labels of a host with name started with given prefix. |
| 200 | |
| 201 | @param prefix: Prefix of label names. |
| 202 | """ |
| 203 | return AFE.get_labels(name__startswith=prefix, host__hostname=host.hostname) |