From c92b310aa3ec7a63b3acb0410f5b6a4fca7ec2de Mon Sep 17 00:00:00 2001
From: Michael Hanselmann <hansmi@google.com>
Date: Tue, 18 Mar 2008 13:02:53 +0000
Subject: [PATCH] Move SSH functions into a class
This renames some functions and does some minor codestyle cleanup.
Reviewed-by: ultrotter
---
lib/backend.py | 14 ++--
lib/cmdlib.py | 24 ++++--
lib/ssh.py | 212 ++++++++++++++++++++++++-------------------------
3 files changed, 131 insertions(+), 119 deletions(-)
diff --git a/lib/backend.py b/lib/backend.py
index f82674ed8..9df5ddac9 100644
--- a/lib/backend.py
+++ b/lib/backend.py
@@ -42,6 +42,10 @@ from ganeti import objects
from ganeti import ssconf
+def _GetSshRunner():
+ return ssh.SshRunner()
+
+
def StartMaster():
"""Activate local node as master node.
@@ -197,7 +201,7 @@ def VerifyNode(what):
if 'nodelist' in what:
result['nodelist'] = {}
for node in what['nodelist']:
- success, message = ssh.VerifyNodeHostname(node)
+ success, message = _GetSshRunner().VerifyNodeHostname(node)
if not success:
result['nodelist'][node] = message
return result
@@ -1188,9 +1192,8 @@ def ExportSnapshot(disk, dest_node, instance):
destcmd = utils.BuildShellCmd("mkdir -p %s && cat > %s/%s",
destdir, destdir, destfile)
- remotecmd = ssh.BuildSSHCmd(dest_node, constants.GANETI_RUNAS, destcmd)
-
-
+ remotecmd = _GetSshRunner().BuildCmd(dest_node, constants.GANETI_RUNAS,
+ destcmd)
# all commands have been checked, so we're safe to combine them
command = '|'.join([expcmd, comprcmd, utils.ShellQuoteArgs(remotecmd)])
@@ -1331,7 +1334,8 @@ def ImportOSIntoInstance(instance, os_disk, swap_disk, src_node, src_image):
os.mkdir(constants.LOG_OS_DIR, 0750)
destcmd = utils.BuildShellCmd('cat %s', src_image)
- remotecmd = ssh.BuildSSHCmd(src_node, constants.GANETI_RUNAS, destcmd)
+ remotecmd = _GetSshRunner().BuildCmd(src_node, constants.GANETI_RUNAS,
+ destcmd)
comprcmd = "gunzip"
impcmd = utils.BuildShellCmd("(cd %s; %s -i %s -b %s -s %s &>%s)",
diff --git a/lib/cmdlib.py b/lib/cmdlib.py
index 15c1e1478..62710f1bd 100644
--- a/lib/cmdlib.py
+++ b/lib/cmdlib.py
@@ -74,6 +74,8 @@ class LogicalUnit(object):
self.op = op
self.cfg = cfg
self.sstore = sstore
+ self.__ssh = None
+
for attr_name in self._OP_REQP:
attr_val = getattr(op, attr_name, None)
if attr_val is None:
@@ -89,6 +91,16 @@ class LogicalUnit(object):
raise errors.OpPrereqError("Commands must be run on the master"
" node %s" % master)
+ def __GetSSH(self):
+ """Returns the SshRunner object
+
+ """
+ if not self.__ssh:
+ self.__ssh = ssh.SshRunner()
+ return self.__ssh
+
+ ssh = property(fget=__GetSSH)
+
def CheckPrereq(self):
"""Check prerequisites for this LU.
@@ -1229,7 +1241,7 @@ class LURemoveNode(LogicalUnit):
rpc.call_node_leave_cluster(node.name)
- ssh.SSHCall(node.name, 'root', "%s stop" % constants.NODE_INITD_SCRIPT)
+ self.ssh.Run(node.name, 'root', "%s stop" % constants.NODE_INITD_SCRIPT)
logger.Info("Removing node %s from config" % node.name)
@@ -1539,7 +1551,7 @@ class LUAddNode(LogicalUnit):
constants.SSL_CERT_FILE, gntpem,
constants.NODE_INITD_SCRIPT))
- result = ssh.SSHCall(node, 'root', mycommand, batch=False, ask_key=True)
+ result = self.ssh.Run(node, 'root', mycommand, batch=False, ask_key=True)
if result.failed:
raise errors.OpExecError("Remote command on node %s, error: %s,"
" output: %s" %
@@ -1597,7 +1609,7 @@ class LUAddNode(LogicalUnit):
" you gave (%s). Please fix and re-run this"
" command." % new_node.secondary_ip)
- success, msg = ssh.VerifyNodeHostname(node)
+ success, msg = self.ssh.VerifyNodeHostname(node)
if not success:
raise errors.OpExecError("Node '%s' claims it has a different hostname"
" than the one the resolver gives: %s."
@@ -1623,7 +1635,7 @@ class LUAddNode(LogicalUnit):
if self.sstore.GetHypervisorType() == constants.HT_XEN_HVM31:
to_copy.append(constants.VNC_PASSWORD_FILE)
for fname in to_copy:
- if not ssh.CopyFileToNode(node, fname):
+ if not self.ssh.CopyFileToNode(node, fname):
logger.Error("could not copy file %s to node %s" % (fname, node))
logger.Info("adding node %s to cluster.conf" % node)
@@ -1767,7 +1779,7 @@ class LUClusterCopyFile(NoHooksLU):
for node in self.nodes:
if node == myname:
continue
- if not ssh.CopyFileToNode(node, filename):
+ if not self.ssh.CopyFileToNode(node, filename):
logger.Error("Copy of file %s to node %s failed" % (filename, node))
@@ -1810,7 +1822,7 @@ class LURunClusterCommand(NoHooksLU):
"""
data = []
for node in self.nodes:
- result = ssh.SSHCall(node, "root", self.op.command)
+ result = self.ssh.Run(node, "root", self.op.command)
data.append((node, result.output, result.exit_code))
return data
diff --git a/lib/ssh.py b/lib/ssh.py
index 7cc055b75..1288c7ff2 100644
--- a/lib/ssh.py
+++ b/lib/ssh.py
@@ -32,10 +32,6 @@ from ganeti import errors
from ganeti import constants
-__all__ = ["SSHCall", "CopyFileToNode", "VerifyNodeHostname",
- "KNOWN_HOSTS_OPTS", "BATCH_MODE_OPTS", "ASK_KEY_OPTS"]
-
-
KNOWN_HOSTS_OPTS = [
"-oGlobalKnownHostsFile=%s" % constants.SSH_KNOWN_HOSTS_FILE,
"-oUserKnownHostsFile=/dev/null",
@@ -90,128 +86,128 @@ def GetUserFiles(user, mkdir=False):
for base in ["id_dsa", "id_dsa.pub", "authorized_keys"]]
-def BuildSSHCmd(hostname, user, command, batch=True, ask_key=False):
- """Build an ssh string to execute a command on a remote node.
-
- Args:
- hostname: the target host, string
- user: user to auth as
- command: the command
- batch: if true, ssh will run in batch mode with no prompting
- ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
- we can connect to an unknown host (not valid in batch mode)
-
- Returns:
- The ssh call to run 'command' on the remote host.
-
- """
- argv = ["ssh", "-q"]
- argv.extend(KNOWN_HOSTS_OPTS)
- if batch:
- # if we are in batch mode, we can't ask the key
- if ask_key:
- raise errors.ProgrammerError("SSH call requested conflicting options")
- argv.extend(BATCH_MODE_OPTS)
- elif ask_key:
- argv.extend(ASK_KEY_OPTS)
- argv.extend(["%s@%s" % (user, hostname), command])
- return argv
-
-
-def SSHCall(hostname, user, command, batch=True, ask_key=False):
- """Execute a command on a remote node.
-
- This method has the same return value as `utils.RunCmd()`, which it
- uses to launch ssh.
-
- Args:
- hostname: the target host, string
- user: user to auth as
- command: the command
- batch: if true, ssh will run in batch mode with no prompting
- ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
- we can connect to an unknown host (not valid in batch mode)
-
- Returns:
- `utils.RunResult` as for `utils.RunCmd()`
-
- """
- return utils.RunCmd(BuildSSHCmd(hostname, user, command,
- batch=batch, ask_key=ask_key))
-
-
-def CopyFileToNode(node, filename):
- """Copy a file to another node with scp.
-
- Args:
- node: node in the cluster
- filename: absolute pathname of a local file
-
- Returns:
- success: True/False
+class SshRunner:
+ """Wrapper for SSH commands.
"""
- if not os.path.isfile(filename):
- logger.Error("file %s does not exist" % (filename))
- return False
-
- if not os.path.isabs(filename):
- logger.Error("file %s must be an absolute path" % (filename))
- return False
+ def BuildCmd(self, hostname, user, command, batch=True, ask_key=False):
+ """Build an ssh command to execute a command on a remote node.
+
+ Args:
+ hostname: the target host, string
+ user: user to auth as
+ command: the command
+ batch: if true, ssh will run in batch mode with no prompting
+ ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
+ we can connect to an unknown host (not valid in batch mode)
+
+ Returns:
+ The ssh call to run 'command' on the remote host.
+
+ """
+ argv = ["ssh", "-q"]
+ argv.extend(KNOWN_HOSTS_OPTS)
+ if batch:
+ # if we are in batch mode, we can't ask the key
+ if ask_key:
+ raise errors.ProgrammerError("SSH call requested conflicting options")
+ argv.extend(BATCH_MODE_OPTS)
+ elif ask_key:
+ argv.extend(ASK_KEY_OPTS)
+ argv.extend(["%s@%s" % (user, hostname), command])
+ return argv
+
+ def Run(self, hostname, user, command, batch=True, ask_key=False):
+ """Runs a command on a remote node.
+
+ This method has the same return value as `utils.RunCmd()`, which it
+ uses to launch ssh.
+
+ Args:
+ hostname: the target host, string
+ user: user to auth as
+ command: the command
+ batch: if true, ssh will run in batch mode with no prompting
+ ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
+ we can connect to an unknown host (not valid in batch mode)
+
+ Returns:
+ `utils.RunResult` like `utils.RunCmd()`
+
+ """
+ return utils.RunCmd(self.BuildCmd(hostname, user, command, batch=batch,
+ ask_key=ask_key))
+
+ def CopyFileToNode(self, node, filename):
+ """Copy a file to another node with scp.
+
+ Args:
+ node: node in the cluster
+ filename: absolute pathname of a local file
+
+ Returns:
+ success: True/False
- command = ["scp", "-q", "-p"]
- command.extend(KNOWN_HOSTS_OPTS)
- command.extend(BATCH_MODE_OPTS)
- command.append(filename)
- command.append("%s:%s" % (node, filename))
+ """
+ if not os.path.isfile(filename):
+ logger.Error("file %s does not exist" % (filename))
+ return False
- result = utils.RunCmd(command)
+ if not os.path.isabs(filename):
+ logger.Error("file %s must be an absolute path" % (filename))
+ return False
- if result.failed:
- logger.Error("copy to node %s failed (%s) error %s,"
- " command was %s" %
- (node, result.fail_reason, result.output, result.cmd))
+ command = ["scp", "-q", "-p"]
+ command.extend(KNOWN_HOSTS_OPTS)
+ command.extend(BATCH_MODE_OPTS)
+ command.append(filename)
+ command.append("%s:%s" % (node, filename))
- return not result.failed
+ result = utils.RunCmd(command)
+ if result.failed:
+ logger.Error("copy to node %s failed (%s) error %s,"
+ " command was %s" %
+ (node, result.fail_reason, result.output, result.cmd))
-def VerifyNodeHostname(node):
- """Verify hostname consistency via SSH.
+ return not result.failed
+ def VerifyNodeHostname(self, node):
+ """Verify hostname consistency via SSH.
- This functions connects via ssh to a node and compares the hostname
- reported by the node to the name with have (the one that we
- connected to).
+ This functions connects via ssh to a node and compares the hostname
+ reported by the node to the name with have (the one that we
+ connected to).
- This is used to detect problems in ssh known_hosts files
- (conflicting known hosts) and incosistencies between dns/hosts
- entries and local machine names
+ This is used to detect problems in ssh known_hosts files
+ (conflicting known hosts) and incosistencies between dns/hosts
+ entries and local machine names
- Args:
- node: nodename of a host to check. can be short or full qualified hostname
+ Args:
+ node: nodename of a host to check. can be short or full qualified hostname
- Returns:
- (success, detail)
- where
- success: True/False
- detail: String with details
+ Returns:
+ (success, detail)
+ where
+ success: True/False
+ detail: String with details
- """
- retval = SSHCall(node, 'root', 'hostname')
+ """
+ retval = self.Run(node, 'root', 'hostname')
- if retval.failed:
- msg = "ssh problem"
- output = retval.output
- if output:
- msg += ": %s" % output
- return False, msg
+ if retval.failed:
+ msg = "ssh problem"
+ output = retval.output
+ if output:
+ msg += ": %s" % output
+ return False, msg
- remotehostname = retval.stdout.strip()
+ remotehostname = retval.stdout.strip()
- if not remotehostname or remotehostname != node:
- return False, "hostname mismatch, got %s" % remotehostname
+ if not remotehostname or remotehostname != node:
+ return False, "hostname mismatch, got %s" % remotehostname
- return True, "host matches"
+ return True, "host matches"
def WriteKnownHostsFile(cfg, sstore, file_name):
--
GitLab