blob: 1c7ec8a49f6a0dc787671a8d81f5e3a60d14c7d3 [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.
Simran Basi22aa9fe2012-12-07 16:37:09 -08004import json
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
Simran Basi22aa9fe2012-12-07 16:37:09 -080011import urllib
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
14
15
16# Keep checking if the pid is alive every second until the timeout (in seconds)
17CHECK_PID_IS_ALIVE_TIMEOUT = 6
18
Chris Masone6a0680f2012-03-02 08:40:00 -080019
Gilad Arnold0ed760c2012-11-05 23:42:53 -080020
Simran Basi22aa9fe2012-12-07 16:37:09 -080021_LOCAL_HOST_LIST = ('localhost', '127.0.0.1')
22
23LAB_GOOD_STATES = ('open', 'throttled')
Gilad Arnold0ed760c2012-11-05 23:42:53 -080024
25
Chris Masone6a0680f2012-03-02 08:40:00 -080026def ping(host, deadline=None, tries=None, timeout=60):
27 """Attempt to ping |host|.
28
29 Shell out to 'ping' to try to reach |host| for |timeout| seconds.
30 Returns exit code of ping.
31
32 Per 'man ping', if you specify BOTH |deadline| and |tries|, ping only
33 returns 0 if we get responses to |tries| pings within |deadline| seconds.
34
35 Specifying |deadline| or |count| alone should return 0 as long as
36 some packets receive responses.
37
38 @param deadline: seconds within which |tries| pings must succeed.
39 @param tries: number of pings to send.
40 @param timeout: number of seconds after which to kill 'ping' command.
41 @return exit code of ping command.
42 """
43 args = [host]
44 if deadline:
45 args.append('-w%d' % deadline)
46 if tries:
47 args.append('-c%d' % tries)
48 return base_utils.run('ping', args=args,
49 ignore_status=True, timeout=timeout,
Scott Zawalskiae843542012-03-20 09:51:29 -040050 stdout_tee=base_utils.TEE_TO_LOGS,
51 stderr_tee=base_utils.TEE_TO_LOGS).exit_status
Scott Zawalski347a0b82012-03-30 16:39:21 -040052
53
54def host_is_in_lab_zone(hostname):
55 """Check if the host is in the CROS.dns_zone.
56
57 @param hostname: The hostname to check.
58 @returns True if hostname.dns_zone resolves, otherwise False.
59 """
60 host_parts = hostname.split('.')
61 dns_zone = global_config.global_config.get_config_value('CROS', 'dns_zone',
62 default=None)
63 fqdn = '%s.%s' % (host_parts[0], dns_zone)
64 try:
65 socket.gethostbyname(fqdn)
66 return True
67 except socket.gaierror:
68 return False
Fang Deng7c2be102012-08-27 16:20:25 -070069
70
71def get_current_board():
72 """Return the current board name.
73
74 @return current board name, e.g "lumpy", None on fail.
75 """
76 with open('/etc/lsb-release') as lsb_release_file:
77 for line in lsb_release_file:
78 m = re.match(r'^CHROMEOS_RELEASE_BOARD=(.+)$', line)
79 if m:
80 return m.group(1)
81 return None
Simran Basi87d7a212012-09-27 10:41:05 -070082
83
84# TODO(petermayo): crosbug.com/31826 Share this with _GsUpload in
85# //chromite.git/buildbot/prebuilt.py somewhere/somehow
86def gs_upload(local_file, remote_file, acl, result_dir=None,
87 transfer_timeout=300, acl_timeout=300):
88 """Upload to GS bucket.
89
90 @param local_file: Local file to upload
91 @param remote_file: Remote location to upload the local_file to.
92 @param acl: name or file used for controlling access to the uploaded
93 file.
94 @param result_dir: Result directory if you want to add tracing to the
95 upload.
96
97 @raise CmdError: the exit code of the gsutil call was not 0.
98
99 @returns True/False - depending on if the upload succeeded or failed.
100 """
101 # https://developers.google.com/storage/docs/accesscontrol#extension
102 CANNED_ACLS = ['project-private', 'private', 'public-read',
103 'public-read-write', 'authenticated-read',
104 'bucket-owner-read', 'bucket-owner-full-control']
105 _GSUTIL_BIN = 'gsutil'
106 acl_cmd = None
107 if acl in CANNED_ACLS:
108 cmd = '%s cp -a %s %s %s' % (_GSUTIL_BIN, acl, local_file, remote_file)
109 else:
110 # For private uploads we assume that the overlay board is set up
111 # properly and a googlestore_acl.xml is present, if not this script
112 # errors
113 cmd = '%s cp -a private %s %s' % (_GSUTIL_BIN, local_file, remote_file)
114 if not os.path.exists(acl):
115 logging.error('Unable to find ACL File %s.', acl)
116 return False
117 acl_cmd = '%s setacl %s %s' % (_GSUTIL_BIN, acl, remote_file)
118 if not result_dir:
119 base_utils.run(cmd, timeout=transfer_timeout, verbose=True)
120 if acl_cmd:
121 base_utils.run(acl_cmd, timeout=acl_timeout, verbose=True)
122 return True
123 with open(os.path.join(result_dir, 'tracing'), 'w') as ftrace:
124 ftrace.write('Preamble\n')
125 base_utils.run(cmd, timeout=transfer_timeout, verbose=True,
126 stdout_tee=ftrace, stderr_tee=ftrace)
127 if acl_cmd:
128 ftrace.write('\nACL setting\n')
129 # Apply the passed in ACL xml file to the uploaded object.
130 base_utils.run(acl_cmd, timeout=acl_timeout, verbose=True,
131 stdout_tee=ftrace, stderr_tee=ftrace)
132 ftrace.write('Postamble\n')
133 return True
Simran Basiaf9b8e72012-10-12 15:02:36 -0700134
135
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800136def gs_ls(uri_pattern):
137 """Returns a list of URIs that match a given pattern.
138
139 @param uri_pattern: a GS URI pattern, may contain wildcards
140
141 @return A list of URIs matching the given pattern.
142
143 @raise CmdError: the gsutil command failed.
144
145 """
146 gs_cmd = ' '.join(['gsutil', 'ls', uri_pattern])
147 result = base_utils.system_output(gs_cmd).splitlines()
148 return [path.rstrip() for path in result if path]
149
150
Simran Basiaf9b8e72012-10-12 15:02:36 -0700151def nuke_pids(pid_list, signal_queue=[signal.SIGTERM, signal.SIGKILL]):
152 """
153 Given a list of pid's, kill them via an esclating series of signals.
154
155 @param pid_list: List of PID's to kill.
156 @param signal_queue: Queue of signals to send the PID's to terminate them.
157 """
158 for sig in signal_queue:
159 logging.debug('Sending signal %s to the following pids:', sig)
160 for pid in pid_list:
161 logging.debug('Pid %d', pid)
162 try:
163 os.kill(pid, sig)
164 except OSError:
165 # The process may have died from a previous signal before we
166 # could kill it.
167 pass
168 time.sleep(CHECK_PID_IS_ALIVE_TIMEOUT)
169 failed_list = []
170 if signal.SIGKILL in signal_queue:
171 return
172 for pid in pid_list:
173 if base_utils.pid_is_alive(pid):
174 failed_list.append('Could not kill %d for process name: %s.' % pid,
Simran Basi62723202013-01-22 15:24:49 -0800175 base_utils.get_process_name(pid))
Simran Basiaf9b8e72012-10-12 15:02:36 -0700176 if failed_list:
177 raise error.AutoservRunError('Following errors occured: %s' %
178 failed_list, None)
Gilad Arnold0ed760c2012-11-05 23:42:53 -0800179
180
181def externalize_host(host):
182 """Returns an externally accessible host name.
183
184 @param host: a host name or address (string)
185
186 @return An externally visible host name or address
187
188 """
189 return socket.gethostname() if host in _LOCAL_HOST_LIST else host
Simran Basi22aa9fe2012-12-07 16:37:09 -0800190
191
192def get_lab_status():
193 """Grabs the current lab status and message.
194
195 @returns a dict with keys 'lab_is_up' and 'message'. lab_is_up points
196 to a boolean and message points to a string.
197 """
198 result = {'lab_is_up' : True, 'message' : ''}
199 status_url = global_config.global_config.get_config_value('CROS',
200 'lab_status_url')
201 max_attempts = 5
202 retry_waittime = 1
203 for _ in range(max_attempts):
204 try:
205 response = urllib.urlopen(status_url)
206 except IOError as e:
207 logging.debug('Error occured when grabbing the lab status: %s.',
208 e)
209 time.sleep(retry_waittime)
210 continue
211 # Check for successful response code.
212 if response.getcode() == 200:
213 data = json.load(response)
214 result['lab_is_up'] = data['general_state'] in LAB_GOOD_STATES
215 result['message'] = data['message']
216 return result
217 time.sleep(retry_waittime)
218 # We go ahead and say the lab is open if we can't get the status.
219 logging.warn('Could not get a status from %s', status_url)
220 return result
221
222
Simran Basi41bfae42013-01-09 10:50:47 -0800223def check_lab_status(board=None):
Simran Basi22aa9fe2012-12-07 16:37:09 -0800224 """Check if the lab is up and if we can schedule suites to run.
225
Simran Basi41bfae42013-01-09 10:50:47 -0800226 Also checks if the lab is disabled for that particular board, and if so
227 will raise an error to prevent new suites from being scheduled for that
228 board.
229
230 @param board: board name that we want to check the status of.
231
Simran Basi22aa9fe2012-12-07 16:37:09 -0800232 @raises error.LabIsDownException if the lab is not up.
Simran Basi41bfae42013-01-09 10:50:47 -0800233 @raises error.BoardIsDisabledException if the desired board is currently
234 disabled.
Simran Basi22aa9fe2012-12-07 16:37:09 -0800235 """
236 # Ensure we are trying to schedule on the actual lab.
237 if not (global_config.global_config.get_config_value('SERVER',
238 'hostname').startswith('cautotest')):
239 return
240
Simran Basi41bfae42013-01-09 10:50:47 -0800241 # First check if the lab is up.
Simran Basi22aa9fe2012-12-07 16:37:09 -0800242 lab_status = get_lab_status()
243 if not lab_status['lab_is_up']:
244 raise error.LabIsDownException('Chromium OS Lab is currently not up: '
245 '%s.' % lab_status['message'])
Simran Basi41bfae42013-01-09 10:50:47 -0800246
247 # Check if the board we wish to use is disabled.
248 # Lab messages should be in the format of:
249 # Lab is 'status' [boards not to be ran] (comment). Example:
250 # Lab is Open [stumpy, kiev, x86-alex] (power_resume rtc causing duts to go
251 # down)
252 boards_are_disabled = re.search('\[(.*)\]', lab_status['message'])
253 if board and boards_are_disabled:
254 if board in boards_are_disabled.group(1):
255 raise error.BoardIsDisabledException('Chromium OS Lab is '
256 'currently not allowing suites to be scheduled on board '
257 '%s: %s' % (board, lab_status['message']))
Simran Basi22aa9fe2012-12-07 16:37:09 -0800258 return