4 # Copyright (C) 2006, 2007 Google Inc.
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
14 # General Public License for more details.
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
22 """Module encapsulating ssh functionality.
29 from ganeti import logger
30 from ganeti import utils
31 from ganeti import errors
32 from ganeti import constants
35 __all__ = ["SSHCall", "CopyFileToNode", "VerifyNodeHostname",
36 "KNOWN_HOSTS_OPTS", "BATCH_MODE_OPTS", "ASK_KEY_OPTS"]
40 "-oGlobalKnownHostsFile=%s" % constants.SSH_KNOWN_HOSTS_FILE,
41 "-oUserKnownHostsFile=/dev/null",
44 # Note: BATCH_MODE conflicts with ASK_KEY
48 "-oStrictHostKeyChecking=yes",
52 "-oStrictHostKeyChecking=ask",
54 "-oHashKnownHosts=no",
58 def BuildSSHCmd(hostname, user, command, batch=True, ask_key=False):
59 """Build an ssh string to execute a command on a remote node.
62 hostname: the target host, string
65 batch: if true, ssh will run in batch mode with no prompting
66 ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
67 we can connect to an unknown host (not valid in batch mode)
70 The ssh call to run 'command' on the remote host.
74 argv.extend(KNOWN_HOSTS_OPTS)
76 # if we are in batch mode, we can't ask the key
78 raise errors.ProgrammerError("SSH call requested conflicting options")
79 argv.extend(BATCH_MODE_OPTS)
81 argv.extend(ASK_KEY_OPTS)
82 argv.extend(["%s@%s" % (user, hostname), command])
86 def SSHCall(hostname, user, command, batch=True, ask_key=False):
87 """Execute a command on a remote node.
89 This method has the same return value as `utils.RunCmd()`, which it
93 hostname: the target host, string
96 batch: if true, ssh will run in batch mode with no prompting
97 ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
98 we can connect to an unknown host (not valid in batch mode)
101 `utils.RunResult` as for `utils.RunCmd()`
104 return utils.RunCmd(BuildSSHCmd(hostname, user, command, batch=batch, ask_key=ask_key))
107 def CopyFileToNode(node, filename):
108 """Copy a file to another node with scp.
111 node: node in the cluster
112 filename: absolute pathname of a local file
118 if not os.path.isfile(filename):
119 logger.Error("file %s does not exist" % (filename))
122 if not os.path.isabs(filename):
123 logger.Error("file %s must be an absolute path" % (filename))
126 command = ["scp", "-q", "-p"]
127 command.extend(KNOWN_HOSTS_OPTS)
128 command.extend(BATCH_MODE_OPTS)
129 command.append(filename)
130 command.append("%s:%s" % (node, filename))
132 result = utils.RunCmd(command)
135 logger.Error("copy to node %s failed (%s) error %s,"
137 (node, result.fail_reason, result.output, result.cmd))
139 return not result.failed
142 def VerifyNodeHostname(node):
143 """Verify hostname consistency via SSH.
146 This functions connects via ssh to a node and compares the hostname
147 reported by the node to the name with have (the one that we
150 This is used to detect problems in ssh known_hosts files
151 (conflicting known hosts) and incosistencies between dns/hosts
152 entries and local machine names
155 node: nodename of a host to check. can be short or full qualified hostname
161 detail: String with details
164 retval = SSHCall(node, 'root', 'hostname')
168 output = retval.output
170 msg += ": %s" % output
173 remotehostname = retval.stdout.strip()
175 if not remotehostname or remotehostname != node:
176 return False, "hostname mismatch, got %s" % remotehostname
178 return True, "host matches"