blob: be3861dbb7ab1d30c205cc3352b97699b53dbd4d [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
24
25class SSHHost(base_classes.RemoteHost):
mbligh7d2bde82007-08-02 16:26:10 +000026 """
27 This class represents a remote machine controlled through an ssh
mblighdcd57a82007-07-11 23:06:47 +000028 session on which you can run programs.
mbligh7d2bde82007-08-02 16:26:10 +000029
mblighdcd57a82007-07-11 23:06:47 +000030 It is not the machine autoserv is running on. The machine must be
31 configured for password-less login, for example through public key
32 authentication.
mbligh7d2bde82007-08-02 16:26:10 +000033
mbligh3409ee72007-10-16 23:58:33 +000034 It includes support for controlling the machine through a serial
35 console on which you can run programs. If such a serial console is
36 set up on the machine then capabilities such as hard reset and
37 boot strap monitoring are available. If the machine does not have a
38 serial console available then ordinary SSH-based commands will
39 still be available, but attempts to use extensions such as
40 console logging or hard reset will fail silently.
41
mblighdcd57a82007-07-11 23:06:47 +000042 Implementation details:
43 This is a leaf class in an abstract class hierarchy, it must
44 implement the unimplemented methods in parent classes.
45 """
mbligh7d2bde82007-08-02 16:26:10 +000046
mbligh0faf91f2007-10-18 03:10:48 +000047 SSH_BASE_COMMAND = 'ssh -a'
48
mblighde384372007-10-17 04:25:37 +000049 def __init__(self, hostname, user="root", port=22, initialize=True,
mblighe6c995f2007-10-26 19:43:01 +000050 conmux_log="console.log", conmux_warnings="warning.log",
51 conmux_server=None, conmux_attach=None,
52 netconsole_log=None, netconsole_port=6666):
mbligh7d2bde82007-08-02 16:26:10 +000053 """
54 Construct a SSHHost object
mblighdcd57a82007-07-11 23:06:47 +000055
56 Args:
57 hostname: network hostname or address of remote machine
58 user: user to log in as on the remote machine
59 port: port the ssh daemon is listening on on the remote
60 machine
mbligh9708f732007-10-18 03:18:54 +000061 """
mblighdcd57a82007-07-11 23:06:47 +000062 self.hostname= hostname
63 self.user= user
64 self.port= port
65 self.tmp_dirs= []
mbligh137a05c2007-10-04 15:56:51 +000066 self.initialize = initialize
mbligh91334902007-09-28 01:47:59 +000067
mbligh9708f732007-10-18 03:18:54 +000068 super(SSHHost, self).__init__()
69
mbligh3409ee72007-10-16 23:58:33 +000070 self.conmux_server = conmux_server
71 self.conmux_attach = self.__find_console_attach(conmux_attach)
72 self.logger_pid = None
mblighde384372007-10-17 04:25:37 +000073 self.__start_console_log(conmux_log)
mblighe6c995f2007-10-26 19:43:01 +000074 self.warning_pid = None
75 self.__start_warning_log(conmux_warnings)
mbligh3409ee72007-10-16 23:58:33 +000076
mbligha0452c82007-08-08 20:24:57 +000077 self.bootloader = bootloader.Bootloader(self)
mbligh7d2bde82007-08-02 16:26:10 +000078
mblighde384372007-10-17 04:25:37 +000079 self.__init_netconsole_params(netconsole_port)
80 self.netlogger_pid = None
81 self.__start_netconsole_log(netconsole_log, netconsole_port)
82 self.__load_netconsole_module()
83
mbligh7d2bde82007-08-02 16:26:10 +000084
mblighdcd57a82007-07-11 23:06:47 +000085 def __del__(self):
mbligh7d2bde82007-08-02 16:26:10 +000086 """
87 Destroy a SSHHost object
mblighdcd57a82007-07-11 23:06:47 +000088 """
89 for dir in self.tmp_dirs:
90 try:
91 self.run('rm -rf "%s"' % (utils.sh_escape(dir)))
92 except errors.AutoservRunError:
93 pass
mblighde384372007-10-17 04:25:37 +000094 # kill the console logger
mbligh7364ae42007-10-18 03:20:34 +000095 if getattr(self, 'logger_pid', None):
mbligh3409ee72007-10-16 23:58:33 +000096 try:
97 pgid = os.getpgid(self.logger_pid)
98 os.killpg(pgid, signal.SIGTERM)
99 except OSError:
100 pass
mblighde384372007-10-17 04:25:37 +0000101 # kill the netconsole logger
mbligh7364ae42007-10-18 03:20:34 +0000102 if getattr(self, 'netlogger_pid', None):
mblighe6c995f2007-10-26 19:43:01 +0000103 self.__unload_netconsole_module()
mblighde384372007-10-17 04:25:37 +0000104 try:
105 os.kill(self.netlogger_pid, signal.SIGTERM)
106 except OSError:
107 pass
mblighe6c995f2007-10-26 19:43:01 +0000108 # kill the warning logger
109 if getattr(self, 'warning_pid', None):
110 try:
111 pgid = os.getpgid(self.warning_pid)
112 os.killpg(pgid, signal.SIGTERM)
113 except OSError:
114 pass
mblighde384372007-10-17 04:25:37 +0000115
116
117 def __init_netconsole_params(self, port):
118 """
119 Connect to the remote machine and determine the values to use for the
120 required netconsole parameters.
121 """
122 self.__netconsole_param = ""
123 # PROBLEM: on machines with multiple IPs this may not make any sense
124 # It also doesn't work with IPv6
125 remote_ip = socket.gethostbyname(self.hostname)
126 local_ip = socket.gethostbyname(socket.gethostname())
127 # Get the gateway of the remote machine
128 try:
129 traceroute = self.run('traceroute -n %s' % local_ip)
130 except errors.AutoservRunError:
131 return
132 first_node = traceroute.stdout.split("\n")[0]
133 match = re.search(r'\s+((\d+\.){3}\d+)\s+', first_node)
134 if match:
135 router_ip = match.group(1)
136 else:
137 return
138 # Look up the MAC address of the gateway
139 try:
140 self.run('ping -c 1 %s' % router_ip)
141 arp = self.run('arp -n -a %s' % router_ip)
142 except errors.AutoservRunError:
143 return
144 match = re.search(r'\s+(([0-9A-F]{2}:){5}[0-9A-F]{2})\s+', arp.stdout)
145 if match:
146 gateway_mac = match.group(1)
147 else:
148 return
149 self.__netconsole_param = 'netconsole=@%s/,%s@%s/%s' % (remote_ip,
150 port,
151 local_ip,
152 gateway_mac)
153
154
155 def __start_netconsole_log(self, logfilename, port):
156 """
157 Log the output of netconsole to a specified file
158 """
159 if logfilename == None:
160 return
161 cmd = ['nc', '-u', '-l', '-p', str(port)]
mblighd2fc50f2007-10-23 22:38:00 +0000162 logger = subprocess.Popen(cmd, stdout=open(logfilename, "a", 0))
mblighde384372007-10-17 04:25:37 +0000163 self.netlogger_pid = logger.pid
164
165
166 def __load_netconsole_module(self):
167 """
168 Make a best effort to load the netconsole module.
169
170 Note that loading the module can fail even when the remote machine is
171 working correctly if netconsole is already compiled into the kernel
172 and started.
173 """
174 try:
175 self.run('modprobe netconsole %s' % self.__netconsole_param)
176 except errors.AutoservRunError:
177 # if it fails there isn't much we can do, just keep going
178 pass
179
180
181 def __unload_netconsole_module(self):
182 try:
183 self.run('modprobe -r netconsole')
184 except errors.AutoservRunError:
185 pass
mbligh3409ee72007-10-16 23:58:33 +0000186
187
mbligh6a0010f2007-10-25 15:45:21 +0000188 def _wait_for_restart(self, timeout):
mbligh3409ee72007-10-16 23:58:33 +0000189 self.wait_down(60) # Make sure he's dead, Jim
190 self.wait_up(timeout)
191 time.sleep(2) # this is needed for complete reliability
mbligh87c5d882007-10-29 17:07:24 +0000192 if not self.wait_up(timeout):
193 sys.stderr.write("REBOOT ERROR\n")
mbligh3409ee72007-10-16 23:58:33 +0000194 print "Reboot complete"
195
196
197 def hardreset(self, timeout=600, wait=True):
198 """
199 Reach out and slap the box in the power switch
200 """
mblighba81c682007-10-25 15:35:59 +0000201 command_ran = self.__console_run(r"'~$hardreset'")
202 if not command_ran:
203 raise errors.AutoservUnsupportedError
204 if wait:
mbligh6a0010f2007-10-25 15:45:21 +0000205 self._wait_for_restart(timeout)
mbligh3409ee72007-10-16 23:58:33 +0000206
207
mblighe6c995f2007-10-26 19:43:01 +0000208 def __conmux_hostname(self):
209 if self.conmux_server:
210 return '%s/%s' % (self.conmux_server, self.hostname)
211 else:
212 return self.hostname
213
214
mbligh3409ee72007-10-16 23:58:33 +0000215 def __start_console_log(self, logfilename):
216 """
217 Log the output of the console session to a specified file
218 """
219 if logfilename == None:
220 return
221 if not self.conmux_attach or not os.path.exists(self.conmux_attach):
222 return
mblighe6c995f2007-10-26 19:43:01 +0000223 cmd = [self.conmux_attach, self.__conmux_hostname(), 'cat - >> %s' % logfilename]
mbligh3409ee72007-10-16 23:58:33 +0000224 logger = subprocess.Popen(cmd,
225 stderr=open('/dev/null', 'w'),
226 preexec_fn=lambda: os.setpgid(0, 0))
227 self.logger_pid = logger.pid
228
229
mblighe6c995f2007-10-26 19:43:01 +0000230 def __start_warning_log(self, logfilename):
231 """
232 Log the output of the warning monitor to a specified file
233 """
234 if logfilename == None:
235 return
236 script_path = os.path.join(self.serverdir, 'warning_monitor')
237 script_cmd = 'expect %s %s' % (script_path, self.hostname)
238 if self.conmux_server:
239 to = '%s/%s'
240 cmd = [self.conmux_attach, self.__conmux_hostname(), script_cmd]
241 logger = subprocess.Popen(cmd,
242 stdout=open(logfilename, 'a', 0),
243 stderr=subprocess.STDOUT,
244 preexec_fn=lambda: os.setpgid(0, 0))
245 self.warning_pid = logger.pid
246
247
mbligh3409ee72007-10-16 23:58:33 +0000248 def __find_console_attach(self, conmux_attach):
249 if conmux_attach:
250 return conmux_attach
251 try:
252 res = utils.run('which conmux-attach')
253 if res.exit_status == 0:
254 return res.stdout.strip()
255 except errors.AutoservRunError, e:
256 pass
mbligh9708f732007-10-18 03:18:54 +0000257 autotest_conmux = os.path.join(self.serverdir, '..',
mbligh3409ee72007-10-16 23:58:33 +0000258 'conmux', 'conmux-attach')
mbligh9708f732007-10-18 03:18:54 +0000259 autotest_conmux_alt = os.path.join(self.serverdir,
mbligh3409ee72007-10-16 23:58:33 +0000260 '..', 'autotest',
261 'conmux', 'conmux-attach')
262 locations = [autotest_conmux,
263 autotest_conmux_alt,
264 '/usr/local/conmux/bin/conmux-attach',
265 '/usr/bin/conmux-attach']
266 for l in locations:
267 if os.path.exists(l):
268 return l
269
270 print "WARNING: conmux-attach not found on autoserv server"
271 return None
272
273
274 def __console_run(self, cmd):
275 """
276 Send a command to the conmux session
277 """
278 if not self.conmux_attach or not os.path.exists(self.conmux_attach):
279 return False
mbligh3409ee72007-10-16 23:58:33 +0000280 cmd = '%s %s echo %s 2> /dev/null' % (self.conmux_attach,
mblighe6c995f2007-10-26 19:43:01 +0000281 self.__conmux_hostname(),
mbligh3409ee72007-10-16 23:58:33 +0000282 cmd)
283 result = os.system(cmd)
284 return result == 0
mbligh7d2bde82007-08-02 16:26:10 +0000285
286
mblighe6647d12007-10-17 00:00:01 +0000287 def ssh_command(self):
288 """Construct an ssh command with proper args for this host."""
mbligh0faf91f2007-10-18 03:10:48 +0000289 return r'%s -l %s -p %d %s' % (self.SSH_BASE_COMMAND,
290 self.user,
291 self.port,
292 self.hostname)
mblighe6647d12007-10-17 00:00:01 +0000293
294
mblighcf965b02007-07-25 16:49:45 +0000295 def run(self, command, timeout=None, ignore_status=False):
mbligh7d2bde82007-08-02 16:26:10 +0000296 """
297 Run a command on the remote host.
mblighdcd57a82007-07-11 23:06:47 +0000298
299 Args:
300 command: the command line string
301 timeout: time limit in seconds before attempting to
302 kill the running process. The run() function
303 will take a few seconds longer than 'timeout'
304 to complete if it has to kill the process.
mbligh8b85dfb2007-08-28 09:50:31 +0000305 ignore_status: do not raise an exception, no matter
306 what the exit code of the command is.
mblighdcd57a82007-07-11 23:06:47 +0000307
308 Returns:
309 a hosts.base_classes.CmdResult object
310
311 Raises:
312 AutoservRunError: the exit code of the command
313 execution was not 0
314 """
315 #~ print "running %s" % (command,)
mblighe6647d12007-10-17 00:00:01 +0000316 result= utils.run(r'%s "%s"' % (self.ssh_command(),
317 utils.sh_escape(command)),
318 timeout, ignore_status)
mblighdcd57a82007-07-11 23:06:47 +0000319 return result
mbligh7d2bde82007-08-02 16:26:10 +0000320
321
mbligha0452c82007-08-08 20:24:57 +0000322 def reboot(self, timeout=600, label=None, kernel_args=None, wait=True):
mbligh7d2bde82007-08-02 16:26:10 +0000323 """
324 Reboot the remote host.
mbligh8b85dfb2007-08-28 09:50:31 +0000325
mbligha0452c82007-08-08 20:24:57 +0000326 Args:
327 timeout
mbligh8b85dfb2007-08-28 09:50:31 +0000328 """
mblighde384372007-10-17 04:25:37 +0000329 # forcibly include the "netconsole" kernel arg
330 if self.__netconsole_param:
331 if kernel_args is None:
332 kernel_args = self.__netconsole_param
333 else:
334 kernel_args += " " + self.__netconsole_param
335 # unload the (possibly loaded) module to avoid shutdown issues
336 self.__unload_netconsole_module()
mbligha0452c82007-08-08 20:24:57 +0000337 if label or kernel_args:
338 self.bootloader.install_boottool()
339 if label:
340 self.bootloader.set_default(label)
341 if kernel_args:
342 if not label:
343 default = int(self.bootloader.get_default())
344 label = self.bootloader.get_titles()[default]
345 self.bootloader.add_args(label, kernel_args)
mblighd742a222007-09-30 01:27:06 +0000346 print "Reboot: initiating reboot"
mbligh87c5d882007-10-29 17:07:24 +0000347 sys.stderr.write("REBOOT\n")
mbligha0452c82007-08-08 20:24:57 +0000348 self.run('reboot')
349 if wait:
mbligh6a0010f2007-10-25 15:45:21 +0000350 self._wait_for_restart(timeout)
mblighde384372007-10-17 04:25:37 +0000351 self.__load_netconsole_module() # if the builtin fails
mbligha0452c82007-08-08 20:24:57 +0000352
mbligh7d2bde82007-08-02 16:26:10 +0000353
mblighdcd57a82007-07-11 23:06:47 +0000354 def get_file(self, source, dest):
mbligh7d2bde82007-08-02 16:26:10 +0000355 """
356 Copy files from the remote host to a local path.
mblighdcd57a82007-07-11 23:06:47 +0000357
358 Directories will be copied recursively.
359 If a source component is a directory with a trailing slash,
360 the content of the directory will be copied, otherwise, the
361 directory itself and its content will be copied. This
362 behavior is similar to that of the program 'rsync'.
363
364 Args:
365 source: either
366 1) a single file or directory, as a string
367 2) a list of one or more (possibly mixed)
368 files or directories
369 dest: a file or a directory (if source contains a
370 directory or more than one element, you must
371 supply a directory dest)
372
373 Raises:
374 AutoservRunError: the scp command failed
375 """
376 if isinstance(source, types.StringTypes):
377 source= [source]
378
379 processed_source= []
380 for entry in source:
381 if entry.endswith('/'):
382 format_string= '%s@%s:"%s*"'
383 else:
384 format_string= '%s@%s:"%s"'
385 entry= format_string % (self.user, self.hostname,
386 utils.scp_remote_escape(entry))
387 processed_source.append(entry)
388
389 processed_dest= os.path.abspath(dest)
390 if os.path.isdir(dest):
391 processed_dest= "%s/" % (utils.sh_escape(processed_dest),)
392 else:
393 processed_dest= utils.sh_escape(processed_dest)
394
395 utils.run('scp -rpq %s "%s"' % (
396 " ".join(processed_source),
397 processed_dest))
mbligh7d2bde82007-08-02 16:26:10 +0000398
399
mblighdcd57a82007-07-11 23:06:47 +0000400 def send_file(self, source, dest):
mbligh7d2bde82007-08-02 16:26:10 +0000401 """
402 Copy files from a local path to the remote host.
mblighdcd57a82007-07-11 23:06:47 +0000403
404 Directories will be copied recursively.
405 If a source component is a directory with a trailing slash,
406 the content of the directory will be copied, otherwise, the
407 directory itself and its content will be copied. This
408 behavior is similar to that of the program 'rsync'.
409
410 Args:
411 source: either
412 1) a single file or directory, as a string
413 2) a list of one or more (possibly mixed)
414 files or directories
415 dest: a file or a directory (if source contains a
416 directory or more than one element, you must
417 supply a directory dest)
418
419 Raises:
420 AutoservRunError: the scp command failed
421 """
422 if isinstance(source, types.StringTypes):
423 source= [source]
424
425 processed_source= []
426 for entry in source:
427 if entry.endswith('/'):
428 format_string= '"%s/"*'
429 else:
430 format_string= '"%s"'
431 entry= format_string % (utils.sh_escape(os.path.abspath(entry)),)
432 processed_source.append(entry)
mbligh7d2bde82007-08-02 16:26:10 +0000433
mblighe6647d12007-10-17 00:00:01 +0000434 result = utils.run(r'%s rsync -h' % self.ssh_command(),
435 ignore_status=True)
mblighd5669092007-08-27 19:01:05 +0000436
mbligh0faf91f2007-10-18 03:10:48 +0000437 remote_dest = '%s@%s:"%s"' % (
438 self.user, self.hostname,
439 utils.scp_remote_escape(dest))
mblighd5669092007-08-27 19:01:05 +0000440 if result.exit_status == 0:
mbligh0faf91f2007-10-18 03:10:48 +0000441 utils.run('rsync --rsh="%s" -az %s %s' % (
442 self.SSH_BASE_COMMAND, " ".join(processed_source),
443 remote_dest))
mblighd5669092007-08-27 19:01:05 +0000444 else:
mbligh0faf91f2007-10-18 03:10:48 +0000445 utils.run('scp -rpq %s %s' % (
446 " ".join(processed_source),
447 remote_dest))
mbligh7d2bde82007-08-02 16:26:10 +0000448
mblighdcd57a82007-07-11 23:06:47 +0000449 def get_tmp_dir(self):
mbligh7d2bde82007-08-02 16:26:10 +0000450 """
451 Return the pathname of a directory on the host suitable
mblighdcd57a82007-07-11 23:06:47 +0000452 for temporary file storage.
453
454 The directory and its content will be deleted automatically
455 on the destruction of the Host object that was used to obtain
456 it.
457 """
mbligha25b29e2007-08-26 13:58:04 +0000458 dir_name= self.run("mktemp -d /tmp/autoserv-XXXXXX").stdout.rstrip(" \n")
mblighdcd57a82007-07-11 23:06:47 +0000459 self.tmp_dirs.append(dir_name)
460 return dir_name
mbligh7d2bde82007-08-02 16:26:10 +0000461
462
mblighdcd57a82007-07-11 23:06:47 +0000463 def is_up(self):
mbligh7d2bde82007-08-02 16:26:10 +0000464 """
465 Check if the remote host is up.
mblighdcd57a82007-07-11 23:06:47 +0000466
467 Returns:
468 True if the remote host is up, False otherwise
469 """
470 try:
471 result= self.run("true", timeout=10)
472 except errors.AutoservRunError:
473 return False
474 else:
475 if result.exit_status == 0:
476 return True
477 else:
mbligh7d2bde82007-08-02 16:26:10 +0000478
mblighdcd57a82007-07-11 23:06:47 +0000479 return False
mbligh7d2bde82007-08-02 16:26:10 +0000480
mblighdcd57a82007-07-11 23:06:47 +0000481 def wait_up(self, timeout=None):
mbligh7d2bde82007-08-02 16:26:10 +0000482 """
483 Wait until the remote host is up or the timeout expires.
mblighdcd57a82007-07-11 23:06:47 +0000484
485 In fact, it will wait until an ssh connection to the remote
486 host can be established.
487
488 Args:
489 timeout: time limit in seconds before returning even
490 if the host is not up.
491
492 Returns:
493 True if the host was found to be up, False otherwise
494 """
495 if timeout:
496 end_time= time.time() + timeout
497
498 while not timeout or time.time() < end_time:
499 try:
mblighe9cf9d42007-08-31 08:56:00 +0000500 run_timeout= 10
mblighdcd57a82007-07-11 23:06:47 +0000501 result= self.run("true", timeout=run_timeout)
502 except errors.AutoservRunError:
503 pass
504 else:
505 if result.exit_status == 0:
506 return True
507 time.sleep(1)
508
509 return False
mbligh7d2bde82007-08-02 16:26:10 +0000510
511
mblighdcd57a82007-07-11 23:06:47 +0000512 def wait_down(self, timeout=None):
mbligh7d2bde82007-08-02 16:26:10 +0000513 """
514 Wait until the remote host is down or the timeout expires.
mblighdcd57a82007-07-11 23:06:47 +0000515
516 In fact, it will wait until an ssh connection to the remote
517 host fails.
518
519 Args:
520 timeout: time limit in seconds before returning even
521 if the host is not up.
522
523 Returns:
524 True if the host was found to be down, False otherwise
525 """
526 if timeout:
527 end_time= time.time() + timeout
528
529 while not timeout or time.time() < end_time:
530 try:
mbligh7e1e9642007-07-31 18:00:45 +0000531 run_timeout= 10
mblighdcd57a82007-07-11 23:06:47 +0000532 result= self.run("true", timeout=run_timeout)
533 except errors.AutoservRunError:
534 return True
535 else:
536 if result.aborted:
537 return True
538 time.sleep(1)
539
540 return False
mbligh7d2bde82007-08-02 16:26:10 +0000541
542
mblighdbe4a382007-07-26 19:41:28 +0000543 def ensure_up(self):
mbligh7d2bde82007-08-02 16:26:10 +0000544 """
545 Ensure the host is up if it is not then do not proceed;
546 this prevents cacading failures of tests
547 """
mbligha0452c82007-08-08 20:24:57 +0000548 print 'Ensuring that %s is up before continuing' % self.hostname
549 if hasattr(self, 'hardreset') and not self.wait_up(300):
mblighdbe4a382007-07-26 19:41:28 +0000550 print "Performing a hardreset on %s" % self.hostname
551 self.hardreset()
mbligha9563b92007-10-25 14:45:56 +0000552 if not self.wait_up(60 * 30):
553 # 30 minutes should be more than enough
554 raise errors.AutoservHostError
mbligha0452c82007-08-08 20:24:57 +0000555 print 'Host up, continuing'
mbligh7d2bde82007-08-02 16:26:10 +0000556
557
mblighdcd57a82007-07-11 23:06:47 +0000558 def get_num_cpu(self):
mbligh7d2bde82007-08-02 16:26:10 +0000559 """
560 Get the number of CPUs in the host according to
mblighdcd57a82007-07-11 23:06:47 +0000561 /proc/cpuinfo.
562
563 Returns:
564 The number of CPUs
565 """
566
mbligh5f876ad2007-10-12 23:59:53 +0000567 proc_cpuinfo = self.run("cat /proc/cpuinfo").stdout
mblighdcd57a82007-07-11 23:06:47 +0000568 cpus = 0
569 for line in proc_cpuinfo.splitlines():
570 if line.startswith('processor'):
571 cpus += 1
572 return cpus
mbligh5f876ad2007-10-12 23:59:53 +0000573
574
575 def check_uptime(self):
576 """
577 Check that uptime is available and monotonically increasing.
578 """
579 if not self.ping():
580 raise "Client is not pingable"
581 result = self.run("/bin/cat /proc/uptime", 30)
582 return result.stdout.strip().split()[0]
583
584
585 def get_arch(self):
586 """
587 Get the hardware architecture of the remote machine
588 """
589 arch = self.run('/bin/uname -m').stdout.rstrip()
590 if re.match(r'i\d86$', arch):
591 arch = 'i386'
592 return arch
593
594
595 def get_kernel_ver(self):
596 """
597 Get the kernel version of the remote machine
598 """
599 return self.run('/bin/uname -r').stdout.rstrip()
600
601
602 def get_cmdline(self):
603 """
604 Get the kernel command line of the remote machine
605 """
606 return self.run('cat /proc/cmdline').stdout.rstrip()
607
608
609 def ping(self):
610 """
611 Ping the remote system, and return whether it's available
612 """
613 fpingcmd = "%s -q %s" % ('/usr/bin/fping', self.hostname)
614 rc = utils.system(fpingcmd, ignore_status = 1)
615 return (rc == 0)