Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 1 | # Copyright (c) 2012 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 | |
Wade Guthrie | 44668f9 | 2012-10-18 09:44:49 -0700 | [diff] [blame] | 6 | import random |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 7 | import re |
Wade Guthrie | 44668f9 | 2012-10-18 09:44:49 -0700 | [diff] [blame] | 8 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 9 | from autotest_lib.client.common_lib import global_config |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 10 | |
| 11 | |
| 12 | _CONFIG = global_config.global_config |
| 13 | |
| 14 | |
| 15 | def image_url_pattern(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 16 | """Returns image_url_pattern from global_config.""" |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 17 | return _CONFIG.get_config_value('CROS', 'image_url_pattern', type=str) |
| 18 | |
| 19 | |
Vadim Bendebury | ab14bf1 | 2012-12-28 13:51:46 -0800 | [diff] [blame] | 20 | def firmware_url_pattern(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 21 | """Returns firmware_url_pattern from global_config.""" |
Vadim Bendebury | ab14bf1 | 2012-12-28 13:51:46 -0800 | [diff] [blame] | 22 | return _CONFIG.get_config_value('CROS', 'firmware_url_pattern', type=str) |
| 23 | |
| 24 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 25 | def sharding_factor(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 26 | """Returns sharding_factor from global_config.""" |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 27 | return _CONFIG.get_config_value('CROS', 'sharding_factor', type=int) |
| 28 | |
| 29 | |
Chris Sosa | 66dfb37 | 2013-01-29 16:36:19 -0800 | [diff] [blame] | 30 | def infrastructure_user(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 31 | """Returns infrastructure_user from global_config.""" |
Chris Sosa | 66dfb37 | 2013-01-29 16:36:19 -0800 | [diff] [blame] | 32 | return _CONFIG.get_config_value('CROS', 'infrastructure_user', type=str) |
Chris Masone | e99bcf2 | 2012-08-17 15:09:49 -0700 | [diff] [blame] | 33 | |
| 34 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 35 | def package_url_pattern(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 36 | """Returns package_url_pattern from global_config.""" |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 37 | return _CONFIG.get_config_value('CROS', 'package_url_pattern', type=str) |
| 38 | |
| 39 | |
Alex Miller | f8aafe6 | 2013-02-25 14:39:46 -0800 | [diff] [blame] | 40 | def try_job_timeout_mins(): |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 41 | """Returns try_job_timeout_mins from global_config.""" |
Alex Miller | f8aafe6 | 2013-02-25 14:39:46 -0800 | [diff] [blame] | 42 | return _CONFIG.get_config_value('SCHEDULER', 'try_job_timeout_mins', |
| 43 | type=int, default=4*60) |
| 44 | |
| 45 | |
Chris Sosa | accb5ce | 2012-08-30 17:29:15 -0700 | [diff] [blame] | 46 | def get_package_url(devserver_url, build): |
| 47 | """Returns the package url from the |devserver_url| and |build|. |
| 48 | |
| 49 | @param devserver_url: a string specifying the host to contact e.g. |
| 50 | http://my_host:9090. |
| 51 | @param build: the build/image string to use e.g. mario-release/R19-123.0.1. |
| 52 | @return the url where you can find the packages for the build. |
| 53 | """ |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 54 | return package_url_pattern() % (devserver_url, build) |
| 55 | |
| 56 | |
Chris Sosa | b76e0ee | 2013-05-22 16:55:41 -0700 | [diff] [blame^] | 57 | def get_devserver_build_from_package_url(package_url): |
| 58 | """The inverse method of get_package_url. |
| 59 | |
| 60 | @param package_url: a string specifying the package url. |
| 61 | |
| 62 | @return tuple containing the devserver_url, build. |
| 63 | """ |
| 64 | pattern = package_url_pattern() |
| 65 | re_pattern = pattern.replace('%s', '(\S+)') |
| 66 | return re.search(re_pattern, package_url).groups() |
| 67 | |
| 68 | |
Wade Guthrie | 44668f9 | 2012-10-18 09:44:49 -0700 | [diff] [blame] | 69 | def get_random_best_host(afe, host_list, require_usable_hosts=True): |
| 70 | """ |
| 71 | Randomly choose the 'best' host from host_list, using fresh status. |
| 72 | |
| 73 | Hit the AFE to get latest status for the listed hosts. Then apply |
| 74 | the following heuristic to pick the 'best' set: |
| 75 | |
| 76 | Remove unusable hosts (not tools.is_usable()), then |
| 77 | 'Ready' > 'Running, Cleaning, Verifying, etc' |
| 78 | |
| 79 | If any 'Ready' hosts exist, return a random choice. If not, randomly |
| 80 | choose from the next tier. If there are none of those either, None. |
| 81 | |
| 82 | @param afe: autotest front end that holds the hosts being managed. |
| 83 | @param host_list: an iterable of Host objects, per server/frontend.py |
| 84 | @param require_usable_hosts: only return hosts currently in a usable |
| 85 | state. |
| 86 | @return a Host object, or None if no appropriate host is found. |
| 87 | """ |
| 88 | if not host_list: |
| 89 | return None |
| 90 | hostnames = [host.hostname for host in host_list] |
| 91 | updated_hosts = afe.get_hosts(hostnames=hostnames) |
| 92 | usable_hosts = [host for host in updated_hosts if is_usable(host)] |
| 93 | ready_hosts = [host for host in usable_hosts if host.status == 'Ready'] |
| 94 | unusable_hosts = [h for h in updated_hosts if not is_usable(h)] |
| 95 | if ready_hosts: |
| 96 | return random.choice(ready_hosts) |
| 97 | if usable_hosts: |
| 98 | return random.choice(usable_hosts) |
| 99 | if not require_usable_hosts and unusable_hosts: |
| 100 | return random.choice(unusable_hosts) |
| 101 | return None |
| 102 | |
| 103 | |
Chris Masone | 44e4d6c | 2012-08-15 14:25:53 -0700 | [diff] [blame] | 104 | def inject_vars(vars, control_file_in): |
| 105 | """ |
| 106 | Inject the contents of |vars| into |control_file_in|. |
| 107 | |
| 108 | @param vars: a dict to shoehorn into the provided control file string. |
| 109 | @param control_file_in: the contents of a control file to munge. |
| 110 | @return the modified control file string. |
| 111 | """ |
| 112 | control_file = '' |
| 113 | for key, value in vars.iteritems(): |
| 114 | # None gets injected as 'None' without this check; same for digits. |
| 115 | if isinstance(value, str): |
| 116 | control_file += "%s='%s'\n" % (key, value) |
| 117 | else: |
| 118 | control_file += "%s=%r\n" % (key, value) |
| 119 | return control_file + control_file_in |
Chris Masone | 8906ab1 | 2012-07-23 15:37:56 -0700 | [diff] [blame] | 120 | |
| 121 | |
| 122 | def is_usable(host): |
| 123 | """ |
| 124 | Given a host, determine if the host is usable right now. |
| 125 | |
| 126 | @param host: Host instance (as in server/frontend.py) |
| 127 | @return True if host is alive and not incorrectly locked. Else, False. |
| 128 | """ |
| 129 | return alive(host) and not incorrectly_locked(host) |
| 130 | |
| 131 | |
| 132 | def alive(host): |
| 133 | """ |
| 134 | Given a host, determine if the host is alive. |
| 135 | |
| 136 | @param host: Host instance (as in server/frontend.py) |
| 137 | @return True if host is not under, or in need of, repair. Else, False. |
| 138 | """ |
| 139 | return host.status not in ['Repair Failed', 'Repairing'] |
| 140 | |
| 141 | |
| 142 | def incorrectly_locked(host): |
| 143 | """ |
| 144 | Given a host, determine if the host is locked by some user. |
| 145 | |
| 146 | If the host is unlocked, or locked by the test infrastructure, |
Chris Sosa | 66dfb37 | 2013-01-29 16:36:19 -0800 | [diff] [blame] | 147 | this will return False. There is only one system user defined as part |
| 148 | of the test infrastructure and is listed in global_config.ini under the |
| 149 | [CROS] section in the 'infrastructure_user' field. |
Chris Masone | 8906ab1 | 2012-07-23 15:37:56 -0700 | [diff] [blame] | 150 | |
| 151 | @param host: Host instance (as in server/frontend.py) |
| 152 | @return False if the host is not locked, or locked by the infra. |
Chris Sosa | 66dfb37 | 2013-01-29 16:36:19 -0800 | [diff] [blame] | 153 | True if the host is locked by the infra user. |
Chris Masone | 8906ab1 | 2012-07-23 15:37:56 -0700 | [diff] [blame] | 154 | """ |
Chris Sosa | 66dfb37 | 2013-01-29 16:36:19 -0800 | [diff] [blame] | 155 | return (host.locked and host.locked_by != infrastructure_user()) |