blob: e5c3dbcd11897e063a0ee56eeef0d433c1c1615c [file] [log] [blame]
mblighdcd57a82007-07-11 23:06:47 +00001#!/usr/bin/python
2#
3# Copyright 2007 Google Inc. Released under the GPL v2
4
mbligh7d2bde82007-08-02 16:26:10 +00005"""
6This module defines the SSHHost class.
mblighdcd57a82007-07-11 23:06:47 +00007
8Implementation details:
9You should import the "hosts" package instead of importing each type of host.
10
11 SSHHost: a remote machine with a ssh access
12"""
13
mbligh7d2bde82007-08-02 16:26:10 +000014__author__ = """
15mbligh@google.com (Martin J. Bligh),
mblighdcd57a82007-07-11 23:06:47 +000016poirier@google.com (Benjamin Poirier),
mbligh7d2bde82007-08-02 16:26:10 +000017stutsman@google.com (Ryan Stutsman)
18"""
mblighdcd57a82007-07-11 23:06:47 +000019
20
mblighde384372007-10-17 04:25:37 +000021import types, os, sys, signal, subprocess, time, re, socket
mbligh5f876ad2007-10-12 23:59:53 +000022import base_classes, utils, errors, bootloader
mblighdcd57a82007-07-11 23:06:47 +000023
mbligh80d20772007-10-29 17:10:10 +000024DEFAULT_REBOOT_TIMEOUT = 1800
25
mblighdcd57a82007-07-11 23:06:47 +000026
27class SSHHost(base_classes.RemoteHost):
mbligh7d2bde82007-08-02 16:26:10 +000028 """
29 This class represents a remote machine controlled through an ssh
mblighdcd57a82007-07-11 23:06:47 +000030 session on which you can run programs.
mbligh7d2bde82007-08-02 16:26:10 +000031
mblighdcd57a82007-07-11 23:06:47 +000032 It is not the machine autoserv is running on. The machine must be
33 configured for password-less login, for example through public key
34 authentication.
mbligh7d2bde82007-08-02 16:26:10 +000035
mbligh3409ee72007-10-16 23:58:33 +000036 It includes support for controlling the machine through a serial
37 console on which you can run programs. If such a serial console is
38 set up on the machine then capabilities such as hard reset and
39 boot strap monitoring are available. If the machine does not have a
40 serial console available then ordinary SSH-based commands will
41 still be available, but attempts to use extensions such as
42 console logging or hard reset will fail silently.
43
mblighdcd57a82007-07-11 23:06:47 +000044 Implementation details:
45 This is a leaf class in an abstract class hierarchy, it must
46 implement the unimplemented methods in parent classes.
47 """
mbligh7d2bde82007-08-02 16:26:10 +000048
mbligh0faf91f2007-10-18 03:10:48 +000049 SSH_BASE_COMMAND = 'ssh -a'
50
mblighde384372007-10-17 04:25:37 +000051 def __init__(self, hostname, user="root", port=22, initialize=True,
mblighe6c995f2007-10-26 19:43:01 +000052 conmux_log="console.log", conmux_warnings="warning.log",
53 conmux_server=None, conmux_attach=None,
54 netconsole_log=None, netconsole_port=6666):
mbligh7d2bde82007-08-02 16:26:10 +000055 """
56 Construct a SSHHost object
mblighdcd57a82007-07-11 23:06:47 +000057
58 Args:
59 hostname: network hostname or address of remote machine
60 user: user to log in as on the remote machine
61 port: port the ssh daemon is listening on on the remote
62 machine
mbligh9708f732007-10-18 03:18:54 +000063 """
mblighdcd57a82007-07-11 23:06:47 +000064 self.hostname= hostname
65 self.user= user
66 self.port= port
67 self.tmp_dirs= []
mbligh137a05c2007-10-04 15:56:51 +000068 self.initialize = initialize
mbligh91334902007-09-28 01:47:59 +000069
mbligh9708f732007-10-18 03:18:54 +000070 super(SSHHost, self).__init__()
71
mbligh3409ee72007-10-16 23:58:33 +000072 self.conmux_server = conmux_server
73 self.conmux_attach = self.__find_console_attach(conmux_attach)
74 self.logger_pid = None
mblighde384372007-10-17 04:25:37 +000075 self.__start_console_log(conmux_log)
mblighe6c995f2007-10-26 19:43:01 +000076 self.warning_pid = None
77 self.__start_warning_log(conmux_warnings)
mbligh3409ee72007-10-16 23:58:33 +000078
mbligha0452c82007-08-08 20:24:57 +000079 self.bootloader = bootloader.Bootloader(self)
mbligh7d2bde82007-08-02 16:26:10 +000080
mblighde384372007-10-17 04:25:37 +000081 self.__init_netconsole_params(netconsole_port)
82 self.netlogger_pid = None
83 self.__start_netconsole_log(netconsole_log, netconsole_port)
84 self.__load_netconsole_module()
85
mbligh7d2bde82007-08-02 16:26:10 +000086
mblighdcd57a82007-07-11 23:06:47 +000087 def __del__(self):
mbligh7d2bde82007-08-02 16:26:10 +000088 """
89 Destroy a SSHHost object
mblighdcd57a82007-07-11 23:06:47 +000090 """
91 for dir in self.tmp_dirs:
92 try:
93 self.run('rm -rf "%s"' % (utils.sh_escape(dir)))
94 except errors.AutoservRunError:
95 pass
mblighde384372007-10-17 04:25:37 +000096 # kill the console logger
mbligh7364ae42007-10-18 03:20:34 +000097 if getattr(self, 'logger_pid', None):
mbligh3409ee72007-10-16 23:58:33 +000098 try:
99 pgid = os.getpgid(self.logger_pid)
100 os.killpg(pgid, signal.SIGTERM)
101 except OSError:
102 pass
mblighde384372007-10-17 04:25:37 +0000103 # kill the netconsole logger
mbligh7364ae42007-10-18 03:20:34 +0000104 if getattr(self, 'netlogger_pid', None):
mblighe6c995f2007-10-26 19:43:01 +0000105 self.__unload_netconsole_module()
mblighde384372007-10-17 04:25:37 +0000106 try:
107 os.kill(self.netlogger_pid, signal.SIGTERM)
108 except OSError:
109 pass
mblighe6c995f2007-10-26 19:43:01 +0000110 # kill the warning logger
111 if getattr(self, 'warning_pid', None):
112 try:
113 pgid = os.getpgid(self.warning_pid)
114 os.killpg(pgid, signal.SIGTERM)
115 except OSError:
116 pass
mblighde384372007-10-17 04:25:37 +0000117
118
119 def __init_netconsole_params(self, port):
120 """
121 Connect to the remote machine and determine the values to use for the
122 required netconsole parameters.
123 """
124 self.__netconsole_param = ""
125 # PROBLEM: on machines with multiple IPs this may not make any sense
126 # It also doesn't work with IPv6
127 remote_ip = socket.gethostbyname(self.hostname)
128 local_ip = socket.gethostbyname(socket.gethostname())
129 # Get the gateway of the remote machine
130 try:
131 traceroute = self.run('traceroute -n %s' % local_ip)
132 except errors.AutoservRunError:
133 return
134 first_node = traceroute.stdout.split("\n")[0]
135 match = re.search(r'\s+((\d+\.){3}\d+)\s+', first_node)
136 if match:
137 router_ip = match.group(1)
138 else:
139 return
140 # Look up the MAC address of the gateway
141 try:
142 self.run('ping -c 1 %s' % router_ip)
143 arp = self.run('arp -n -a %s' % router_ip)
144 except errors.AutoservRunError:
145 return
146 match = re.search(r'\s+(([0-9A-F]{2}:){5}[0-9A-F]{2})\s+', arp.stdout)
147 if match:
148 gateway_mac = match.group(1)
149 else:
150 return
151 self.__netconsole_param = 'netconsole=@%s/,%s@%s/%s' % (remote_ip,
152 port,
153 local_ip,
154 gateway_mac)
155
156
157 def __start_netconsole_log(self, logfilename, port):
158 """
159 Log the output of netconsole to a specified file
160 """
161 if logfilename == None:
162 return
163 cmd = ['nc', '-u', '-l', '-p', str(port)]
mblighd2fc50f2007-10-23 22:38:00 +0000164 logger = subprocess.Popen(cmd, stdout=open(logfilename, "a", 0))
mblighde384372007-10-17 04:25:37 +0000165 self.netlogger_pid = logger.pid
166
167
168 def __load_netconsole_module(self):
169 """
170 Make a best effort to load the netconsole module.
171
172 Note that loading the module can fail even when the remote machine is
173 working correctly if netconsole is already compiled into the kernel
174 and started.
175 """
176 try:
177 self.run('modprobe netconsole %s' % self.__netconsole_param)
178 except errors.AutoservRunError:
179 # if it fails there isn't much we can do, just keep going
180 pass
181
182
183 def __unload_netconsole_module(self):
184 try:
185 self.run('modprobe -r netconsole')
186 except errors.AutoservRunError:
187 pass
mbligh3409ee72007-10-16 23:58:33 +0000188
189
mbligh6a0010f2007-10-25 15:45:21 +0000190 def _wait_for_restart(self, timeout):
mblighd567f722007-10-30 15:37:33 +0000191 if not self.wait_down(300): # Make sure he's dead, Jim
192 sys.stderr.write("REBOOT ERROR\n")
193 raise errors.AutoservRebootError("Host would not shut down")
mbligh3409ee72007-10-16 23:58:33 +0000194 self.wait_up(timeout)
195 time.sleep(2) # this is needed for complete reliability
mbligh87c5d882007-10-29 17:07:24 +0000196 if not self.wait_up(timeout):
197 sys.stderr.write("REBOOT ERROR\n")
mbligh3409ee72007-10-16 23:58:33 +0000198 print "Reboot complete"
199
200
mbligh80d20772007-10-29 17:10:10 +0000201 def hardreset(self, timeout=DEFAULT_REBOOT_TIMEOUT, wait=True):
mbligh3409ee72007-10-16 23:58:33 +0000202 """
203 Reach out and slap the box in the power switch
204 """
mblighba81c682007-10-25 15:35:59 +0000205 command_ran = self.__console_run(r"'~$hardreset'")
206 if not command_ran:
207 raise errors.AutoservUnsupportedError
208 if wait:
mbligh6a0010f2007-10-25 15:45:21 +0000209 self._wait_for_restart(timeout)
mbligh3409ee72007-10-16 23:58:33 +0000210
211
mblighe6c995f2007-10-26 19:43:01 +0000212 def __conmux_hostname(self):
213 if self.conmux_server:
214 return '%s/%s' % (self.conmux_server, self.hostname)
215 else:
216 return self.hostname
217
218
mbligh3409ee72007-10-16 23:58:33 +0000219 def __start_console_log(self, logfilename):
220 """
221 Log the output of the console session to a specified file
222 """
223 if logfilename == None:
224 return
225 if not self.conmux_attach or not os.path.exists(self.conmux_attach):
226 return
mblighe6c995f2007-10-26 19:43:01 +0000227 cmd = [self.conmux_attach, self.__conmux_hostname(), 'cat - >> %s' % logfilename]
mbligh3409ee72007-10-16 23:58:33 +0000228 logger = subprocess.Popen(cmd,
229 stderr=open('/dev/null', 'w'),
230 preexec_fn=lambda: os.setpgid(0, 0))
231 self.logger_pid = logger.pid
232
233
mblighe6c995f2007-10-26 19:43:01 +0000234 def __start_warning_log(self, logfilename):
235 """
236 Log the output of the warning monitor to a specified file
237 """
238 if logfilename == None:
239 return
240 script_path = os.path.join(self.serverdir, 'warning_monitor')
241 script_cmd = 'expect %s %s' % (script_path, self.hostname)
242 if self.conmux_server:
243 to = '%s/%s'
244 cmd = [self.conmux_attach, self.__conmux_hostname(), script_cmd]
245 logger = subprocess.Popen(cmd,
246 stdout=open(logfilename, 'a', 0),
247 stderr=subprocess.STDOUT,
248 preexec_fn=lambda: os.setpgid(0, 0))
249 self.warning_pid = logger.pid
250
251
mbligh3409ee72007-10-16 23:58:33 +0000252 def __find_console_attach(self, conmux_attach):
253 if conmux_attach:
254 return conmux_attach
255 try:
256 res = utils.run('which conmux-attach')
257 if res.exit_status == 0:
258 return res.stdout.strip()
259 except errors.AutoservRunError, e:
260 pass
mbligh9708f732007-10-18 03:18:54 +0000261 autotest_conmux = os.path.join(self.serverdir, '..',
mbligh3409ee72007-10-16 23:58:33 +0000262 'conmux', 'conmux-attach')
mbligh9708f732007-10-18 03:18:54 +0000263 autotest_conmux_alt = os.path.join(self.serverdir,
mbligh3409ee72007-10-16 23:58:33 +0000264 '..', 'autotest',
265 'conmux', 'conmux-attach')
266 locations = [autotest_conmux,
267 autotest_conmux_alt,
268 '/usr/local/conmux/bin/conmux-attach',
269 '/usr/bin/conmux-attach']
270 for l in locations:
271 if os.path.exists(l):
272 return l
273
274 print "WARNING: conmux-attach not found on autoserv server"
275 return None
276
277
278 def __console_run(self, cmd):
279 """
280 Send a command to the conmux session
281 """
282 if not self.conmux_attach or not os.path.exists(self.conmux_attach):
283 return False
mbligh3409ee72007-10-16 23:58:33 +0000284 cmd = '%s %s echo %s 2> /dev/null' % (self.conmux_attach,
mblighe6c995f2007-10-26 19:43:01 +0000285 self.__conmux_hostname(),
mbligh3409ee72007-10-16 23:58:33 +0000286 cmd)
287 result = os.system(cmd)
288 return result == 0
mbligh7d2bde82007-08-02 16:26:10 +0000289
290
mblighe6647d12007-10-17 00:00:01 +0000291 def ssh_command(self):
292 """Construct an ssh command with proper args for this host."""
mbligh0faf91f2007-10-18 03:10:48 +0000293 return r'%s -l %s -p %d %s' % (self.SSH_BASE_COMMAND,
294 self.user,
295 self.port,
296 self.hostname)
mblighe6647d12007-10-17 00:00:01 +0000297
298
mblighcf965b02007-07-25 16:49:45 +0000299 def run(self, command, timeout=None, ignore_status=False):
mbligh7d2bde82007-08-02 16:26:10 +0000300 """
301 Run a command on the remote host.
mblighdcd57a82007-07-11 23:06:47 +0000302
303 Args:
304 command: the command line string
305 timeout: time limit in seconds before attempting to
306 kill the running process. The run() function
307 will take a few seconds longer than 'timeout'
308 to complete if it has to kill the process.
mbligh8b85dfb2007-08-28 09:50:31 +0000309 ignore_status: do not raise an exception, no matter
310 what the exit code of the command is.
mblighdcd57a82007-07-11 23:06:47 +0000311
312 Returns:
313 a hosts.base_classes.CmdResult object
314
315 Raises:
316 AutoservRunError: the exit code of the command
317 execution was not 0
318 """
319 #~ print "running %s" % (command,)
mblighe6647d12007-10-17 00:00:01 +0000320 result= utils.run(r'%s "%s"' % (self.ssh_command(),
321 utils.sh_escape(command)),
322 timeout, ignore_status)
mblighdcd57a82007-07-11 23:06:47 +0000323 return result
mbligh7d2bde82007-08-02 16:26:10 +0000324
325
mbligh80d20772007-10-29 17:10:10 +0000326 def reboot(self, timeout=DEFAULT_REBOOT_TIMEOUT, label=None,
327 kernel_args=None, wait=True):
mbligh7d2bde82007-08-02 16:26:10 +0000328 """
329 Reboot the remote host.
mbligh8b85dfb2007-08-28 09:50:31 +0000330
mbligha0452c82007-08-08 20:24:57 +0000331 Args:
332 timeout
mbligh8b85dfb2007-08-28 09:50:31 +0000333 """
mblighde384372007-10-17 04:25:37 +0000334 # forcibly include the "netconsole" kernel arg
335 if self.__netconsole_param:
336 if kernel_args is None:
337 kernel_args = self.__netconsole_param
338 else:
339 kernel_args += " " + self.__netconsole_param
340 # unload the (possibly loaded) module to avoid shutdown issues
341 self.__unload_netconsole_module()
mbligha0452c82007-08-08 20:24:57 +0000342 if label or kernel_args:
343 self.bootloader.install_boottool()
344 if label:
345 self.bootloader.set_default(label)
346 if kernel_args:
347 if not label:
348 default = int(self.bootloader.get_default())
349 label = self.bootloader.get_titles()[default]
350 self.bootloader.add_args(label, kernel_args)
mblighd742a222007-09-30 01:27:06 +0000351 print "Reboot: initiating reboot"
mbligh87c5d882007-10-29 17:07:24 +0000352 sys.stderr.write("REBOOT\n")
mblighd2e46052007-11-05 18:31:00 +0000353 self.run('(sleep 5; reboot) >/dev/null 2>&1 &')
mbligha0452c82007-08-08 20:24:57 +0000354 if wait:
mbligh6a0010f2007-10-25 15:45:21 +0000355 self._wait_for_restart(timeout)
mblighde384372007-10-17 04:25:37 +0000356 self.__load_netconsole_module() # if the builtin fails
mbligha0452c82007-08-08 20:24:57 +0000357
mbligh7d2bde82007-08-02 16:26:10 +0000358
mblighdcd57a82007-07-11 23:06:47 +0000359 def get_file(self, source, dest):
mbligh7d2bde82007-08-02 16:26:10 +0000360 """
361 Copy files from the remote host to a local path.
mblighdcd57a82007-07-11 23:06:47 +0000362
363 Directories will be copied recursively.
364 If a source component is a directory with a trailing slash,
365 the content of the directory will be copied, otherwise, the
366 directory itself and its content will be copied. This
367 behavior is similar to that of the program 'rsync'.
368
369 Args:
370 source: either
371 1) a single file or directory, as a string
372 2) a list of one or more (possibly mixed)
373 files or directories
374 dest: a file or a directory (if source contains a
375 directory or more than one element, you must
376 supply a directory dest)
377
378 Raises:
379 AutoservRunError: the scp command failed
380 """
381 if isinstance(source, types.StringTypes):
382 source= [source]
383
384 processed_source= []
385 for entry in source:
386 if entry.endswith('/'):
387 format_string= '%s@%s:"%s*"'
388 else:
389 format_string= '%s@%s:"%s"'
390 entry= format_string % (self.user, self.hostname,
391 utils.scp_remote_escape(entry))
392 processed_source.append(entry)
393
394 processed_dest= os.path.abspath(dest)
395 if os.path.isdir(dest):
396 processed_dest= "%s/" % (utils.sh_escape(processed_dest),)
397 else:
398 processed_dest= utils.sh_escape(processed_dest)
399
400 utils.run('scp -rpq %s "%s"' % (
401 " ".join(processed_source),
402 processed_dest))
mbligh7d2bde82007-08-02 16:26:10 +0000403
404
mblighdcd57a82007-07-11 23:06:47 +0000405 def send_file(self, source, dest):
mbligh7d2bde82007-08-02 16:26:10 +0000406 """
407 Copy files from a local path to the remote host.
mblighdcd57a82007-07-11 23:06:47 +0000408
409 Directories will be copied recursively.
410 If a source component is a directory with a trailing slash,
411 the content of the directory will be copied, otherwise, the
412 directory itself and its content will be copied. This
413 behavior is similar to that of the program 'rsync'.
414
415 Args:
416 source: either
417 1) a single file or directory, as a string
418 2) a list of one or more (possibly mixed)
419 files or directories
420 dest: a file or a directory (if source contains a
421 directory or more than one element, you must
422 supply a directory dest)
423
424 Raises:
425 AutoservRunError: the scp command failed
426 """
427 if isinstance(source, types.StringTypes):
428 source= [source]
429
430 processed_source= []
431 for entry in source:
432 if entry.endswith('/'):
433 format_string= '"%s/"*'
434 else:
435 format_string= '"%s"'
436 entry= format_string % (utils.sh_escape(os.path.abspath(entry)),)
437 processed_source.append(entry)
mbligh7d2bde82007-08-02 16:26:10 +0000438
mblighe6647d12007-10-17 00:00:01 +0000439 result = utils.run(r'%s rsync -h' % self.ssh_command(),
440 ignore_status=True)
mblighd5669092007-08-27 19:01:05 +0000441
mbligh0faf91f2007-10-18 03:10:48 +0000442 remote_dest = '%s@%s:"%s"' % (
443 self.user, self.hostname,
444 utils.scp_remote_escape(dest))
mblighd5669092007-08-27 19:01:05 +0000445 if result.exit_status == 0:
mbligh0faf91f2007-10-18 03:10:48 +0000446 utils.run('rsync --rsh="%s" -az %s %s' % (
447 self.SSH_BASE_COMMAND, " ".join(processed_source),
448 remote_dest))
mblighd5669092007-08-27 19:01:05 +0000449 else:
mbligh0faf91f2007-10-18 03:10:48 +0000450 utils.run('scp -rpq %s %s' % (
451 " ".join(processed_source),
452 remote_dest))
mbligh7d2bde82007-08-02 16:26:10 +0000453
mblighdcd57a82007-07-11 23:06:47 +0000454 def get_tmp_dir(self):
mbligh7d2bde82007-08-02 16:26:10 +0000455 """
456 Return the pathname of a directory on the host suitable
mblighdcd57a82007-07-11 23:06:47 +0000457 for temporary file storage.
458
459 The directory and its content will be deleted automatically
460 on the destruction of the Host object that was used to obtain
461 it.
462 """
mbligha25b29e2007-08-26 13:58:04 +0000463 dir_name= self.run("mktemp -d /tmp/autoserv-XXXXXX").stdout.rstrip(" \n")
mblighdcd57a82007-07-11 23:06:47 +0000464 self.tmp_dirs.append(dir_name)
465 return dir_name
mbligh7d2bde82007-08-02 16:26:10 +0000466
467
mblighdcd57a82007-07-11 23:06:47 +0000468 def is_up(self):
mbligh7d2bde82007-08-02 16:26:10 +0000469 """
470 Check if the remote host is up.
mblighdcd57a82007-07-11 23:06:47 +0000471
472 Returns:
473 True if the remote host is up, False otherwise
474 """
475 try:
476 result= self.run("true", timeout=10)
477 except errors.AutoservRunError:
478 return False
479 else:
480 if result.exit_status == 0:
481 return True
482 else:
mbligh7d2bde82007-08-02 16:26:10 +0000483
mblighdcd57a82007-07-11 23:06:47 +0000484 return False
mbligh7d2bde82007-08-02 16:26:10 +0000485
mblighdcd57a82007-07-11 23:06:47 +0000486 def wait_up(self, timeout=None):
mbligh7d2bde82007-08-02 16:26:10 +0000487 """
488 Wait until the remote host is up or the timeout expires.
mblighdcd57a82007-07-11 23:06:47 +0000489
490 In fact, it will wait until an ssh connection to the remote
491 host can be established.
492
493 Args:
494 timeout: time limit in seconds before returning even
495 if the host is not up.
496
497 Returns:
498 True if the host was found to be up, False otherwise
499 """
500 if timeout:
501 end_time= time.time() + timeout
502
503 while not timeout or time.time() < end_time:
504 try:
mblighe9cf9d42007-08-31 08:56:00 +0000505 run_timeout= 10
mblighdcd57a82007-07-11 23:06:47 +0000506 result= self.run("true", timeout=run_timeout)
507 except errors.AutoservRunError:
508 pass
509 else:
510 if result.exit_status == 0:
511 return True
512 time.sleep(1)
513
514 return False
mbligh7d2bde82007-08-02 16:26:10 +0000515
516
mblighdcd57a82007-07-11 23:06:47 +0000517 def wait_down(self, timeout=None):
mbligh7d2bde82007-08-02 16:26:10 +0000518 """
519 Wait until the remote host is down or the timeout expires.
mblighdcd57a82007-07-11 23:06:47 +0000520
521 In fact, it will wait until an ssh connection to the remote
522 host fails.
523
524 Args:
525 timeout: time limit in seconds before returning even
526 if the host is not up.
527
528 Returns:
529 True if the host was found to be down, False otherwise
530 """
531 if timeout:
532 end_time= time.time() + timeout
533
534 while not timeout or time.time() < end_time:
535 try:
mbligh7e1e9642007-07-31 18:00:45 +0000536 run_timeout= 10
mblighdcd57a82007-07-11 23:06:47 +0000537 result= self.run("true", timeout=run_timeout)
538 except errors.AutoservRunError:
539 return True
540 else:
541 if result.aborted:
542 return True
543 time.sleep(1)
544
545 return False
mbligh7d2bde82007-08-02 16:26:10 +0000546
547
mblighdbe4a382007-07-26 19:41:28 +0000548 def ensure_up(self):
mbligh7d2bde82007-08-02 16:26:10 +0000549 """
550 Ensure the host is up if it is not then do not proceed;
551 this prevents cacading failures of tests
552 """
mbligha0452c82007-08-08 20:24:57 +0000553 print 'Ensuring that %s is up before continuing' % self.hostname
554 if hasattr(self, 'hardreset') and not self.wait_up(300):
mblighdbe4a382007-07-26 19:41:28 +0000555 print "Performing a hardreset on %s" % self.hostname
556 self.hardreset()
mbligha9563b92007-10-25 14:45:56 +0000557 if not self.wait_up(60 * 30):
558 # 30 minutes should be more than enough
559 raise errors.AutoservHostError
mbligha0452c82007-08-08 20:24:57 +0000560 print 'Host up, continuing'
mbligh7d2bde82007-08-02 16:26:10 +0000561
562
mblighdcd57a82007-07-11 23:06:47 +0000563 def get_num_cpu(self):
mbligh7d2bde82007-08-02 16:26:10 +0000564 """
565 Get the number of CPUs in the host according to
mblighdcd57a82007-07-11 23:06:47 +0000566 /proc/cpuinfo.
567
568 Returns:
569 The number of CPUs
570 """
571
mbligh5f876ad2007-10-12 23:59:53 +0000572 proc_cpuinfo = self.run("cat /proc/cpuinfo").stdout
mblighdcd57a82007-07-11 23:06:47 +0000573 cpus = 0
574 for line in proc_cpuinfo.splitlines():
575 if line.startswith('processor'):
576 cpus += 1
577 return cpus
mbligh5f876ad2007-10-12 23:59:53 +0000578
579
580 def check_uptime(self):
581 """
582 Check that uptime is available and monotonically increasing.
583 """
584 if not self.ping():
585 raise "Client is not pingable"
586 result = self.run("/bin/cat /proc/uptime", 30)
587 return result.stdout.strip().split()[0]
588
589
590 def get_arch(self):
591 """
592 Get the hardware architecture of the remote machine
593 """
594 arch = self.run('/bin/uname -m').stdout.rstrip()
595 if re.match(r'i\d86$', arch):
596 arch = 'i386'
597 return arch
598
599
600 def get_kernel_ver(self):
601 """
602 Get the kernel version of the remote machine
603 """
604 return self.run('/bin/uname -r').stdout.rstrip()
605
606
607 def get_cmdline(self):
608 """
609 Get the kernel command line of the remote machine
610 """
611 return self.run('cat /proc/cmdline').stdout.rstrip()
612
613
614 def ping(self):
615 """
616 Ping the remote system, and return whether it's available
617 """
618 fpingcmd = "%s -q %s" % ('/usr/bin/fping', self.hostname)
619 rc = utils.system(fpingcmd, ignore_status = 1)
620 return (rc == 0)
mblighd2e46052007-11-05 18:31:00 +0000621
622
623 def ssh_ping(self):
624 self.run('ls', timeout=30)