Merged ConmuxSSHHost with SSHHost. If the remote machine has a serial
console you can now use SSHHost to hard reset the machine and to log
console output to a file. The class still works file when using a
machine without a serial console, with the caveat that hard reset and
console logging will fail silently.
The change also fixes a couple of issues with the original ConmuxSSHHost
implementation, specifically:
- it properly terminates all the console logging subprocesses
- hardreset can wait for the machine to come back up
From: jadmanski@google.com
git-svn-id: http://test.kernel.org/svn/autotest/trunk@811 592f7852-d20e-0410-864c-8624ca9c26a4
diff --git a/server/hosts/__init__.py b/server/hosts/__init__.py
index 41adf94..f849375 100644
--- a/server/hosts/__init__.py
+++ b/server/hosts/__init__.py
@@ -20,7 +20,6 @@
# host implementation classes
from ssh_host import SSHHost
-from conmux_ssh_host import ConmuxSSHHost
from guest import Guest
from kvm_guest import KVMGuest
diff --git a/server/hosts/conmux_ssh_host.py b/server/hosts/conmux_ssh_host.py
deleted file mode 100644
index f8bdabd..0000000
--- a/server/hosts/conmux_ssh_host.py
+++ /dev/null
@@ -1,128 +0,0 @@
-#!/usr/bin/python
-#
-# Copyright 2007 Google Inc. Released under the GPL v2
-
-"""
-This module defines the ConmuxSSHHost
-
-Implementation details:
-You should import the "hosts" package instead of importing each type of host.
-
- ConmuxSSHHost: a remote machine controlled through a serial console
-"""
-
-__author__ = """
-mbligh@google.com (Martin J. Bligh),
-poirier@google.com (Benjamin Poirier),
-stutsman@google.com (Ryan Stutsman)
-"""
-
-import os
-import os.path
-import sys
-import signal
-import subprocess
-
-import utils
-import ssh_host
-import errors
-
-class ConmuxSSHHost(ssh_host.SSHHost):
- """
- This class represents a remote machine controlled through a serial
- console on which you can run programs. It is not the machine autoserv
- is running on.
-
- For a machine controlled in this way, it may be possible to support
- hard reset, boot strap monitoring or other operations not possible
- on a machine controlled through ssh, telnet, ...
- """
-
-
- def __init__(self,
- hostname,
- logfilename=None,
- server=None,
- attach=None, *args, **kwargs):
- super(ConmuxSSHHost, self).__init__(hostname, *args, **kwargs)
- self.server = server
- self.attach = attach
- self.attach = self.__find_console_attach()
- self.pid = None
- self.__start_console_log(logfilename)
-
-
- def hardreset(self):
- """
- Reach out and slap the box in the power switch
- """
- self.__console_run(r"'~$hardreset'")
-
-
- def __start_console_log(self, logfilename):
- """
- Log the output of the console session to a specified file
- """
- if logfilename == None:
- return
- if not self.attach or not os.path.exists(self.attach):
- return
- if self.server:
- to = '%s/%s' % (self.server, self.hostname)
- else:
- to = self.hostname
- cmd = [self.attach, to, 'cat - > %s' % logfilename]
- self.pid = subprocess.Popen(cmd).pid
-
-
- def __find_console_attach(self):
- if self.attach:
- return self.attach
- try:
- res = utils.run('which conmux-attach')
- if res.exit_status == 0:
- return res.stdout.strip()
- except errors.AutoservRunError, e:
- pass
- autoserv_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
- autotest_conmux = os.path.join(autoserv_dir, '..',
- 'conmux', 'conmux-attach')
- autotest_conmux_alt = os.path.join(autoserv_dir,
- '..', 'autotest',
- 'conmux', 'conmux-attach')
- locations = [autotest_conmux,
- autotest_conmux_alt,
- '/usr/local/conmux/bin/conmux-attach',
- '/usr/bin/conmux-attach']
- for l in locations:
- if os.path.exists(l):
- return l
-
- print "WARNING: conmux-attach not found on autoserv server"
- return None
-
-
- def __console_run(self, cmd):
- """
- Send a command to the conmux session
- """
- if not self.attach or not os.path.exists(self.attach):
- return
- if self.server:
- to = '%s/%s' % (self.server, self.hostname)
- else:
- to = self.hostname
- cmd = '%s %s echo %s' % (self.attach,
- to,
- cmd)
- os.system(cmd)
-
-
- def __del__(self):
- if self.pid:
- try:
- os.kill(self.pid, signal.SIGTERM)
- except OSError:
- pass
- super(ConmuxSSHHost, self).__del__()
-
diff --git a/server/hosts/guest.py b/server/hosts/guest.py
index f75e90b..0d8270c 100644
--- a/server/hosts/guest.py
+++ b/server/hosts/guest.py
@@ -60,7 +60,7 @@
self.controlling_hypervisor.delete_guest(self.hostname)
- def hardreset(self):
+ def hardreset(self, timeout=600, wait=True):
"""
Perform a "hardreset" of the guest.
@@ -68,4 +68,3 @@
even if the guest otherwise innaccessible through ssh.
"""
return self.controlling_hypervisor.reset_guest(self.hostname)
-
diff --git a/server/hosts/ssh_host.py b/server/hosts/ssh_host.py
index 463546f..9aff6c6 100644
--- a/server/hosts/ssh_host.py
+++ b/server/hosts/ssh_host.py
@@ -18,7 +18,7 @@
"""
-import types, os, time, re
+import types, os, sys, signal, subprocess, time, re
import base_classes, utils, errors, bootloader
@@ -31,12 +31,20 @@
configured for password-less login, for example through public key
authentication.
+ It includes support for controlling the machine through a serial
+ console on which you can run programs. If such a serial console is
+ set up on the machine then capabilities such as hard reset and
+ boot strap monitoring are available. If the machine does not have a
+ serial console available then ordinary SSH-based commands will
+ still be available, but attempts to use extensions such as
+ console logging or hard reset will fail silently.
+
Implementation details:
This is a leaf class in an abstract class hierarchy, it must
implement the unimplemented methods in parent classes.
"""
- def __init__(self, hostname, user="root", port=22, initialize=True):
+ def __init__(self, hostname, user="root", port=22, initialize=True, logfilename=None, conmux_server=None, conmux_attach=None):
"""
Construct a SSHHost object
@@ -52,6 +60,11 @@
self.tmp_dirs= []
self.initialize = initialize
+ self.conmux_server = conmux_server
+ self.conmux_attach = self.__find_console_attach(conmux_attach)
+ self.logger_pid = None
+ self.__start_console_log(logfilename)
+
super(SSHHost, self).__init__()
self.bootloader = bootloader.Bootloader(self)
@@ -65,6 +78,93 @@
self.run('rm -rf "%s"' % (utils.sh_escape(dir)))
except errors.AutoservRunError:
pass
+ if self.logger_pid:
+ try:
+ pgid = os.getpgid(self.logger_pid)
+ os.killpg(pgid, signal.SIGTERM)
+ except OSError:
+ pass
+
+
+ def __wait_for_restart(self, timeout):
+ self.wait_down(60) # Make sure he's dead, Jim
+ self.wait_up(timeout)
+ time.sleep(2) # this is needed for complete reliability
+ self.wait_up(timeout)
+ print "Reboot complete"
+
+
+ def hardreset(self, timeout=600, wait=True):
+ """
+ Reach out and slap the box in the power switch
+ """
+ result = self.__console_run(r"'~$hardreset'")
+ if wait:
+ self.__wait_for_restart(timeout)
+ return result
+
+
+ def __start_console_log(self, logfilename):
+ """
+ Log the output of the console session to a specified file
+ """
+ if logfilename == None:
+ return
+ if not self.conmux_attach or not os.path.exists(self.conmux_attach):
+ return
+ if self.conmux_server:
+ to = '%s/%s' % (self.conmux_server, self.hostname)
+ else:
+ to = self.hostname
+ cmd = [self.conmux_attach, to, 'cat - > %s' % logfilename]
+ logger = subprocess.Popen(cmd,
+ stderr=open('/dev/null', 'w'),
+ preexec_fn=lambda: os.setpgid(0, 0))
+ self.logger_pid = logger.pid
+
+
+ def __find_console_attach(self, conmux_attach):
+ if conmux_attach:
+ return conmux_attach
+ try:
+ res = utils.run('which conmux-attach')
+ if res.exit_status == 0:
+ return res.stdout.strip()
+ except errors.AutoservRunError, e:
+ pass
+ autoserv_dir = os.path.dirname(os.path.abspath(sys.argv[0]))
+ autotest_conmux = os.path.join(autoserv_dir, '..',
+ 'conmux', 'conmux-attach')
+ autotest_conmux_alt = os.path.join(autoserv_dir,
+ '..', 'autotest',
+ 'conmux', 'conmux-attach')
+ locations = [autotest_conmux,
+ autotest_conmux_alt,
+ '/usr/local/conmux/bin/conmux-attach',
+ '/usr/bin/conmux-attach']
+ for l in locations:
+ if os.path.exists(l):
+ return l
+
+ print "WARNING: conmux-attach not found on autoserv server"
+ return None
+
+
+ def __console_run(self, cmd):
+ """
+ Send a command to the conmux session
+ """
+ if not self.conmux_attach or not os.path.exists(self.conmux_attach):
+ return False
+ if self.conmux_server:
+ to = '%s/%s' % (self.conmux_server, self.hostname)
+ else:
+ to = self.hostname
+ cmd = '%s %s echo %s 2> /dev/null' % (self.conmux_attach,
+ to,
+ cmd)
+ result = os.system(cmd)
+ return result == 0
def run(self, command, timeout=None, ignore_status=False):
@@ -113,12 +213,7 @@
print "Reboot: initiating reboot"
self.run('reboot')
if wait:
- self.wait_down(60) # Make sure he's dead, Jim
- print "Reboot: machine has gone down"
- self.wait_up(timeout)
- time.sleep(2) # this is needed for complete reliability
- self.wait_up(timeout)
- print "Reboot complete"
+ self.__wait_for_restart(timeout)
def get_file(self, source, dest):