blob: 69f769cba0499f092431e1559d6e1557cd345351 [file] [log] [blame]
Chris Masone6a0680f2012-03-02 08:40:00 -08001# Copyright (c) 2012 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.
J. Richard Barnette3cbd76b2013-11-27 12:11:25 -08004
Simran Basi87d7a212012-09-27 10:41:05 -07005import logging
Simran Basiaf9b8e72012-10-12 15:02:36 -07006import os
Fang Deng7c2be102012-08-27 16:20:25 -07007import re
Simran Basiaf9b8e72012-10-12 15:02:36 -07008import signal
Scott Zawalski347a0b82012-03-30 16:39:21 -04009import socket
Simran Basiaf9b8e72012-10-12 15:02:36 -070010import time
beeps60aec242013-06-26 14:47:48 -070011import urllib2
Chris Masone6a0680f2012-03-02 08:40:00 -080012
Simran Basiaf9b8e72012-10-12 15:02:36 -070013from autotest_lib.client.common_lib import base_utils, error, global_config
beepsc4fb1472013-05-08 21:49:48 -070014from autotest_lib.client.cros import constants
Simran Basiaf9b8e72012-10-12 15:02:36 -070015
16
17# Keep checking if the pid is alive every second until the timeout (in seconds)
18CHECK_PID_IS_ALIVE_TIMEOUT = 6
19
Simran Basi22aa9fe2012-12-07 16:37:09 -080020_LOCAL_HOST_LIST = ('localhost', '127.0.0.1')
21
Fang Deng3197b392013-06-26 11:42:02 -070022
Chris Masone6a0680f2012-03-02 08:40:00 -080023def ping(host, deadline=None, tries=None, timeout=60):
24 """Attempt to ping |host|.
25
26 Shell out to 'ping' to try to reach |host| for |timeout| seconds.
27 Returns exit code of ping.
28
29 Per 'man ping', if you specify BOTH |deadline| and |tries|, ping only
30 returns 0 if we get responses to |tries| pings within |deadline| seconds.
31
32 Specifying |deadline| or |count| alone should return 0 as long as
33 some packets receive responses.
34
beepsfda8f412013-05-02 19:08:20 -070035 @param host: the host to ping.
Chris Masone6a0680f2012-03-02 08:40:00 -080036 @param deadline: seconds within which |tries| pings must succeed.
37 @param tries: number of pings to send.
38 @param timeout: number of seconds after which to kill 'ping' command.
39 @return exit code of ping command.
40 """
41 args = [host]
42 if deadline:
43 args.append('-w%d' % deadline)
44 if tries:
45 args.append('-c%d' % tries)
46 return base_utils.run('ping', args=args,
47 ignore_status=True, timeout=timeout,
Scott Zawalskiae843542012-03-20 09:51:29 -040048 stdout_tee=base_utils.TEE_TO_LOGS,
49 stderr_tee=base_utils.TEE_TO_LOGS).exit_status
Scott Zawalski347a0b82012-03-30 16:39:21 -040050
51
52def host_is_in_lab_zone(hostname):
53 """Check if the host is in the CROS.dns_zone.
54
55 @param hostname: The hostname to check.
56 @returns True if hostname.dns_zone resolves, otherwise False.
57 """
58 host_parts = hostname.split('.')
59 dns_zone = global_config.global_config.get_config_value('CROS', 'dns_zone',
60 default=None)
61 fqdn = '%s.%s' % (host_parts[0], dns_zone)
62 try:
63 socket.gethostbyname(fqdn)
64 return True
65 except socket.gaierror:
66 return False
Fang Deng7c2be102012-08-27 16:20:25 -070067
68
beepsc4fb1472013-05-08 21:49:48 -070069def get_chrome_version(job_views):
70 """
71 Retrieves the version of the chrome binary associated with a job.
72
73 When a test runs we query the chrome binary for it's version and drop
74 that value into a client keyval. To retrieve the chrome version we get all
75 the views associated with a test from the db, including those of the
76 server and client jobs, and parse the version out of the first test view
77 that has it. If we never ran a single test in the suite the job_views
78 dictionary will not contain a chrome version.
79
80 This method cannot retrieve the chrome version from a dictionary that
81 does not conform to the structure of an autotest tko view.
82
83 @param job_views: a list of a job's result views, as returned by
84 the get_detailed_test_views method in rpc_interface.
85 @return: The chrome version string, or None if one can't be found.
86 """
87
88 # Aborted jobs have no views.
89 if not job_views:
90 return None
91
92 for view in job_views:
93 if (view.get('attributes')
94 and constants.CHROME_VERSION in view['attributes'].keys()):
95
96 return view['attributes'].get(constants.CHROME_VERSION)
97
98 logging.warning('Could not find chrome version for failure.')
99 return None
100
101
Fang Deng7c2be102012-08-27 16:20:25 -0700102def get_current_board():
103 """Return the current board name.
104
105 @return current board name, e.g "lumpy", None on fail.
106 """
107 with open('/etc/lsb-release') as lsb_release_file:
108 for line in lsb_release_file:
109 m = re.match(r'^CHROMEOS_RELEASE_BOARD=(.+)$', line)
110 if m:
111 return m.group(1)
112 return None
Simran Basi87d7a212012-09-27 10:41:05 -0700113
114
115# TODO(petermayo): crosbug.com/31826 Share this with _GsUpload in
116# //chromite.git/buildbot/prebuilt.py somewhere/somehow
117def gs_upload(local_file, remote_file, acl, result_dir=None,
118 transfer_timeout=300, acl_timeout=300):
119 """Upload to GS bucket.
120
121 @param local_file: Local file to upload
122 @param remote_file: Remote location to upload the local_file to.
123 @param acl: name or file used for controlling access to the uploaded
124 file.
125 @param result_dir: Result directory if you want to add tracing to the
126 upload.
beepsfda8f412013-05-02 19:08:20 -0700127 @param transfer_timeout: Timeout for this upload call.
128 @param acl_timeout: Timeout for the acl call needed to confirm that
129 the uploader has permissions to execute the upload.
Simran Basi87d7a212012-09-27 10:41:05 -0700130
131 @raise CmdError: the exit code of the gsutil call was not 0.
132
133 @returns True/False - depending on if the upload succeeded or failed.
134 """
135 # https://developers.google.com/storage/docs/accesscontrol#extension
136 CANNED_ACLS = ['project-private', 'private', 'public-read',
137 'public-read-write', 'authenticated-read',
138 'bucket-owner-read', 'bucket-owner-full-control']
139 _GSUTIL_BIN = 'gsutil'
140 acl_cmd = None
141 if acl in CANNED_ACLS:
142 cmd = '%s cp -a %s %s %s' % (_GSUTIL_BIN, acl, local_file, remote_file)
143 else:
144 # For private uploads we assume that the overlay board is set up
145 # properly and a googlestore_acl.xml is present, if not this script
146 # errors
147 cmd = '%s cp -a private %s %s' % (_GSUTIL_BIN, local_file, remote_file)
148 if not os.path.exists(acl):
149 logging.error('Unable to find ACL File %s.', acl)
150 return False
151 acl_cmd = '%s setacl %s %s' % (_GSUTIL_BIN, acl, remote_file)
152 if not result_dir:
153 base_utils.run(cmd, timeout=transfer_timeout, verbose=True)
154 if acl_cmd:
155 base_utils.run(acl_cmd, timeout=acl_timeout, verbose=True)
156 return True
157 with open(os.path.join(result_dir, 'tracing'), 'w') as ftrace:
158 ftrace.write('Preamble\n')
159 base_utils.run(cmd, timeout=transfer_timeout, verbose=True,
160 stdout_tee=ftrace, stderr_tee=ftrace)
161 if acl_cmd:
162 ftrace.write('\nACL setting\n')
163 # Apply the passed in ACL xml file to the uploaded object.
164 base_utils.run(acl_cmd, timeout=acl_timeout, verbose=True,
165 stdout_tee=ftrace, stderr_tee=ftrace)
166 ftrace.write('Postamble\n')
167 return True
Simran Basiaf9b8e72012-10-12 15:02:36 -0700168
169
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800170def gs_ls(uri_pattern):
171 """Returns a list of URIs that match a given pattern.
172
173 @param uri_pattern: a GS URI pattern, may contain wildcards
174
175 @return A list of URIs matching the given pattern.
176
177 @raise CmdError: the gsutil command failed.
178
179 """
180 gs_cmd = ' '.join(['gsutil', 'ls', uri_pattern])
181 result = base_utils.system_output(gs_cmd).splitlines()
182 return [path.rstrip() for path in result if path]
183
184
Simran Basiaf9b8e72012-10-12 15:02:36 -0700185def nuke_pids(pid_list, signal_queue=[signal.SIGTERM, signal.SIGKILL]):
186 """
187 Given a list of pid's, kill them via an esclating series of signals.
188
189 @param pid_list: List of PID's to kill.
190 @param signal_queue: Queue of signals to send the PID's to terminate them.
191 """
192 for sig in signal_queue:
193 logging.debug('Sending signal %s to the following pids:', sig)
194 for pid in pid_list:
195 logging.debug('Pid %d', pid)
196 try:
197 os.kill(pid, sig)
198 except OSError:
199 # The process may have died from a previous signal before we
200 # could kill it.
201 pass
202 time.sleep(CHECK_PID_IS_ALIVE_TIMEOUT)
203 failed_list = []
204 if signal.SIGKILL in signal_queue:
205 return
206 for pid in pid_list:
207 if base_utils.pid_is_alive(pid):
208 failed_list.append('Could not kill %d for process name: %s.' % pid,
Simran Basi62723202013-01-22 15:24:49 -0800209 base_utils.get_process_name(pid))
Simran Basiaf9b8e72012-10-12 15:02:36 -0700210 if failed_list:
211 raise error.AutoservRunError('Following errors occured: %s' %
212 failed_list, None)
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800213
214
215def externalize_host(host):
216 """Returns an externally accessible host name.
217
218 @param host: a host name or address (string)
219
220 @return An externally visible host name or address
221
222 """
223 return socket.gethostname() if host in _LOCAL_HOST_LIST else host
Simran Basi22aa9fe2012-12-07 16:37:09 -0800224
225
beeps60aec242013-06-26 14:47:48 -0700226def urlopen_socket_timeout(url, data=None, timeout=5):
227 """
228 Wrapper to urllib2.urlopen with a socket timeout.
229
230 This method will convert all socket timeouts to
231 TimeoutExceptions, so we can use it in conjunction
232 with the rpc retry decorator and continue to handle
233 other URLErrors as we see fit.
234
235 @param url: The url to open.
236 @param data: The data to send to the url (eg: the urlencoded dictionary
237 used with a POST call).
238 @param timeout: The timeout for this urlopen call.
239
240 @return: The response of the urlopen call.
241
242 @raises: error.TimeoutException when a socket timeout occurs.
Dan Shi6c00dde2013-07-29 17:47:29 -0700243 urllib2.URLError for errors that not caused by timeout.
244 urllib2.HTTPError for errors like 404 url not found.
beeps60aec242013-06-26 14:47:48 -0700245 """
246 old_timeout = socket.getdefaulttimeout()
247 socket.setdefaulttimeout(timeout)
248 try:
249 return urllib2.urlopen(url, data=data)
250 except urllib2.URLError as e:
251 if type(e.reason) is socket.timeout:
252 raise error.TimeoutException(str(e))
Dan Shi6c00dde2013-07-29 17:47:29 -0700253 raise
beeps60aec242013-06-26 14:47:48 -0700254 finally:
255 socket.setdefaulttimeout(old_timeout)
Luis Lozano40b7d0d2014-01-17 15:12:06 -0800256
257
258def parse_chrome_version(version_string):
259 """
260 Parse a chrome version string and return version and milestone.
261
262 Given a chrome version of the form "W.X.Y.Z", return "W.X.Y.Z" as
263 the version and "W" as the milestone.
264
265 @param version_string: Chrome version string.
266 @return: a tuple (chrome_version, milestone). If the incoming version
267 string is not of the form "W.X.Y.Z", chrome_version will
268 be set to the incoming "version_string" argument and the
269 milestone will be set to the empty string.
270 """
271 match = re.search('(\d+)\.\d+\.\d+\.\d+', version_string)
272 ver = match.group(0) if match else version_string
273 milestone = match.group(1) if match else ''
274 return ver, milestone