Commit c92b310a authored by Michael Hanselmann's avatar Michael Hanselmann
Browse files

Move SSH functions into a class

This renames some functions and does some minor codestyle cleanup.

Reviewed-by: ultrotter
parent 75a5f456
......@@ -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)",
......
......@@ -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
......
......@@ -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):
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment