blob: 5519f1d8f5b36b13c5c37cec531aef28a895c1f3 [file] [log] [blame]
Chris Masone44e4d6c2012-08-15 14:25:53 -07001# 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 Guthrie44668f92012-10-18 09:44:49 -07006import random
Chris Sosab76e0ee2013-05-22 16:55:41 -07007import re
Wade Guthrie44668f92012-10-18 09:44:49 -07008
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -07009import common
10
Chris Masone44e4d6c2012-08-15 14:25:53 -070011from autotest_lib.client.common_lib import global_config
Chris Masone44e4d6c2012-08-15 14:25:53 -070012
13
14_CONFIG = global_config.global_config
15
16
17def image_url_pattern():
Chris Sosab76e0ee2013-05-22 16:55:41 -070018 """Returns image_url_pattern from global_config."""
Chris Masone44e4d6c2012-08-15 14:25:53 -070019 return _CONFIG.get_config_value('CROS', 'image_url_pattern', type=str)
20
21
Vadim Bendeburyab14bf12012-12-28 13:51:46 -080022def firmware_url_pattern():
Chris Sosab76e0ee2013-05-22 16:55:41 -070023 """Returns firmware_url_pattern from global_config."""
Vadim Bendeburyab14bf12012-12-28 13:51:46 -080024 return _CONFIG.get_config_value('CROS', 'firmware_url_pattern', type=str)
25
26
beepse539be02013-07-31 21:57:39 -070027def factory_image_url_pattern():
28 """Returns path to factory image after it's been staged."""
29 return _CONFIG.get_config_value('CROS', 'factory_image_url_pattern',
30 type=str)
31
32
Chris Masone44e4d6c2012-08-15 14:25:53 -070033def sharding_factor():
Chris Sosab76e0ee2013-05-22 16:55:41 -070034 """Returns sharding_factor from global_config."""
Chris Masone44e4d6c2012-08-15 14:25:53 -070035 return _CONFIG.get_config_value('CROS', 'sharding_factor', type=int)
36
37
Chris Sosa66dfb372013-01-29 16:36:19 -080038def infrastructure_user():
Chris Sosab76e0ee2013-05-22 16:55:41 -070039 """Returns infrastructure_user from global_config."""
Chris Sosa66dfb372013-01-29 16:36:19 -080040 return _CONFIG.get_config_value('CROS', 'infrastructure_user', type=str)
Chris Masonee99bcf22012-08-17 15:09:49 -070041
42
Chris Masone44e4d6c2012-08-15 14:25:53 -070043def package_url_pattern():
Chris Sosab76e0ee2013-05-22 16:55:41 -070044 """Returns package_url_pattern from global_config."""
Chris Masone44e4d6c2012-08-15 14:25:53 -070045 return _CONFIG.get_config_value('CROS', 'package_url_pattern', type=str)
46
47
Alex Millerf8aafe62013-02-25 14:39:46 -080048def try_job_timeout_mins():
Chris Sosab76e0ee2013-05-22 16:55:41 -070049 """Returns try_job_timeout_mins from global_config."""
Alex Millerf8aafe62013-02-25 14:39:46 -080050 return _CONFIG.get_config_value('SCHEDULER', 'try_job_timeout_mins',
51 type=int, default=4*60)
52
53
Chris Sosaaccb5ce2012-08-30 17:29:15 -070054def get_package_url(devserver_url, build):
55 """Returns the package url from the |devserver_url| and |build|.
56
57 @param devserver_url: a string specifying the host to contact e.g.
58 http://my_host:9090.
59 @param build: the build/image string to use e.g. mario-release/R19-123.0.1.
60 @return the url where you can find the packages for the build.
61 """
Chris Masone44e4d6c2012-08-15 14:25:53 -070062 return package_url_pattern() % (devserver_url, build)
63
64
Chris Sosab76e0ee2013-05-22 16:55:41 -070065def get_devserver_build_from_package_url(package_url):
66 """The inverse method of get_package_url.
67
68 @param package_url: a string specifying the package url.
69
70 @return tuple containing the devserver_url, build.
71 """
72 pattern = package_url_pattern()
73 re_pattern = pattern.replace('%s', '(\S+)')
joychen03eaad92013-06-26 09:55:21 -070074
75 devserver_build_tuple = re.search(re_pattern, package_url).groups()
76
77 # TODO(beeps): This is a temporary hack around the fact that all
78 # job_repo_urls in the database currently contain 'archive'. Remove
79 # when all hosts have been reimaged at least once. Ref: crbug.com/214373.
80 return (devserver_build_tuple[0],
81 devserver_build_tuple[1].replace('archive/', ''))
Chris Sosab76e0ee2013-05-22 16:55:41 -070082
83
Wade Guthrie44668f92012-10-18 09:44:49 -070084def get_random_best_host(afe, host_list, require_usable_hosts=True):
85 """
86 Randomly choose the 'best' host from host_list, using fresh status.
87
88 Hit the AFE to get latest status for the listed hosts. Then apply
89 the following heuristic to pick the 'best' set:
90
91 Remove unusable hosts (not tools.is_usable()), then
92 'Ready' > 'Running, Cleaning, Verifying, etc'
93
94 If any 'Ready' hosts exist, return a random choice. If not, randomly
95 choose from the next tier. If there are none of those either, None.
96
97 @param afe: autotest front end that holds the hosts being managed.
98 @param host_list: an iterable of Host objects, per server/frontend.py
99 @param require_usable_hosts: only return hosts currently in a usable
100 state.
101 @return a Host object, or None if no appropriate host is found.
102 """
103 if not host_list:
104 return None
105 hostnames = [host.hostname for host in host_list]
106 updated_hosts = afe.get_hosts(hostnames=hostnames)
107 usable_hosts = [host for host in updated_hosts if is_usable(host)]
108 ready_hosts = [host for host in usable_hosts if host.status == 'Ready']
109 unusable_hosts = [h for h in updated_hosts if not is_usable(h)]
110 if ready_hosts:
111 return random.choice(ready_hosts)
112 if usable_hosts:
113 return random.choice(usable_hosts)
114 if not require_usable_hosts and unusable_hosts:
115 return random.choice(unusable_hosts)
116 return None
117
118
Chris Masone44e4d6c2012-08-15 14:25:53 -0700119def inject_vars(vars, control_file_in):
120 """
121 Inject the contents of |vars| into |control_file_in|.
122
123 @param vars: a dict to shoehorn into the provided control file string.
124 @param control_file_in: the contents of a control file to munge.
125 @return the modified control file string.
126 """
127 control_file = ''
128 for key, value in vars.iteritems():
129 # None gets injected as 'None' without this check; same for digits.
130 if isinstance(value, str):
Aviv Keshet7cd12312013-07-25 10:25:55 -0700131 control_file += "%s=%s\n" % (key, repr(value))
Chris Masone44e4d6c2012-08-15 14:25:53 -0700132 else:
133 control_file += "%s=%r\n" % (key, value)
134 return control_file + control_file_in
Chris Masone8906ab12012-07-23 15:37:56 -0700135
136
137def is_usable(host):
138 """
139 Given a host, determine if the host is usable right now.
140
141 @param host: Host instance (as in server/frontend.py)
142 @return True if host is alive and not incorrectly locked. Else, False.
143 """
144 return alive(host) and not incorrectly_locked(host)
145
146
147def alive(host):
148 """
149 Given a host, determine if the host is alive.
150
151 @param host: Host instance (as in server/frontend.py)
152 @return True if host is not under, or in need of, repair. Else, False.
153 """
154 return host.status not in ['Repair Failed', 'Repairing']
155
156
157def incorrectly_locked(host):
158 """
159 Given a host, determine if the host is locked by some user.
160
161 If the host is unlocked, or locked by the test infrastructure,
Chris Sosa66dfb372013-01-29 16:36:19 -0800162 this will return False. There is only one system user defined as part
163 of the test infrastructure and is listed in global_config.ini under the
164 [CROS] section in the 'infrastructure_user' field.
Chris Masone8906ab12012-07-23 15:37:56 -0700165
166 @param host: Host instance (as in server/frontend.py)
167 @return False if the host is not locked, or locked by the infra.
Chris Sosa66dfb372013-01-29 16:36:19 -0800168 True if the host is locked by the infra user.
Chris Masone8906ab12012-07-23 15:37:56 -0700169 """
Chris Sosa66dfb372013-01-29 16:36:19 -0800170 return (host.locked and host.locked_by != infrastructure_user())
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700171
172
173def _testname_to_keyval_key(testname):
174 """Make a test name acceptable as a keyval key.
175
176 @param testname Test name that must be converted.
177 @return A string with selected bad characters replaced
178 with allowable characters.
179 """
180 # Characters for keys in autotest keyvals are restricted; in
181 # particular, '/' isn't allowed. Alas, in the case of an
182 # aborted job, the test name will be a path that includes '/'
183 # characters. We want to file bugs for aborted jobs, so we
184 # apply a transform here to avoid trouble.
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700185 return testname.replace('/', '_')
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700186
187
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700188_BUG_ID_KEYVAL = '-Bug_Id'
189_BUG_COUNT_KEYVAL = '-Bug_Count'
190
191
192def create_bug_keyvals(testname, bug_info):
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700193 """Create keyvals to record a bug filed against a test failure.
194
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700195 @param testname Name of the test for which to record a bug.
196 @param bug_info Pair with the id of the bug and the count of
197 the number of times the bug has been seen.
198 @return Keyvals to be recorded for the given test.
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700199 """
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700200 keyval_base = _testname_to_keyval_key(testname)
201 return {
202 keyval_base + _BUG_ID_KEYVAL: bug_info[0],
203 keyval_base + _BUG_COUNT_KEYVAL: bug_info[1]
204 }
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700205
206
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700207def get_test_failure_bug_info(keyvals, testname):
208 """Extract information about a bug filed against a test failure.
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700209
210 @param keyvals Keyvals associated with a suite job.
211 @param testname Name of a test from the suite.
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700212 @return None if there is no bug info, or a pair with the
213 id of the bug, and the count of the number of
214 times the bug has been seen.
J. Richard Barnettee7b98bb2013-08-21 16:34:16 -0700215 """
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700216 keyval_base = _testname_to_keyval_key(testname)
217 bug_id = keyvals.get(keyval_base + _BUG_ID_KEYVAL)
218 if bug_id is not None:
219 bug_count = keyvals.get(keyval_base + _BUG_COUNT_KEYVAL)
J. Richard Barnettebfa720c2013-09-06 14:09:09 -0700220 return int(bug_id), int(bug_count)
J. Richard Barnetteb9c911d2013-08-23 11:24:21 -0700221 return bug_id