Statistics
| Branch: | Tag: | Revision:

root / lib / ssh.py @ 00003458

History | View | Annotate | Download (4.7 kB)

1 a8083063 Iustin Pop
#!/usr/bin/python
2 a8083063 Iustin Pop
#
3 a8083063 Iustin Pop
4 a8083063 Iustin Pop
# Copyright (C) 2006, 2007 Google Inc.
5 a8083063 Iustin Pop
#
6 a8083063 Iustin Pop
# This program is free software; you can redistribute it and/or modify
7 a8083063 Iustin Pop
# it under the terms of the GNU General Public License as published by
8 a8083063 Iustin Pop
# the Free Software Foundation; either version 2 of the License, or
9 a8083063 Iustin Pop
# (at your option) any later version.
10 a8083063 Iustin Pop
#
11 a8083063 Iustin Pop
# This program is distributed in the hope that it will be useful, but
12 a8083063 Iustin Pop
# WITHOUT ANY WARRANTY; without even the implied warranty of
13 a8083063 Iustin Pop
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 a8083063 Iustin Pop
# General Public License for more details.
15 a8083063 Iustin Pop
#
16 a8083063 Iustin Pop
# You should have received a copy of the GNU General Public License
17 a8083063 Iustin Pop
# along with this program; if not, write to the Free Software
18 a8083063 Iustin Pop
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 a8083063 Iustin Pop
# 02110-1301, USA.
20 a8083063 Iustin Pop
21 a8083063 Iustin Pop
22 a8083063 Iustin Pop
"""Module encapsulating ssh functionality.
23 a8083063 Iustin Pop

24 a8083063 Iustin Pop
"""
25 a8083063 Iustin Pop
26 a8083063 Iustin Pop
27 a8083063 Iustin Pop
import os
28 a8083063 Iustin Pop
29 a8083063 Iustin Pop
from ganeti import logger
30 a8083063 Iustin Pop
from ganeti import utils
31 a8083063 Iustin Pop
from ganeti import errors
32 82122173 Iustin Pop
from ganeti import constants
33 82122173 Iustin Pop
34 82122173 Iustin Pop
35 82122173 Iustin Pop
__all__ = ["SSHCall", "CopyFileToNode", "VerifyNodeHostname",
36 82122173 Iustin Pop
           "KNOWN_HOSTS_OPTS", "BATCH_MODE_OPTS", "ASK_KEY_OPTS"]
37 82122173 Iustin Pop
38 82122173 Iustin Pop
39 82122173 Iustin Pop
KNOWN_HOSTS_OPTS = [
40 82122173 Iustin Pop
  "-oGlobalKnownHostsFile=%s" % constants.SSH_KNOWN_HOSTS_FILE,
41 82122173 Iustin Pop
  "-oUserKnownHostsFile=/dev/null",
42 82122173 Iustin Pop
  ]
43 82122173 Iustin Pop
44 82122173 Iustin Pop
# Note: BATCH_MODE conflicts with ASK_KEY
45 82122173 Iustin Pop
BATCH_MODE_OPTS = [
46 82122173 Iustin Pop
  "-oEscapeChar=none",
47 82122173 Iustin Pop
  "-oBatchMode=yes",
48 82122173 Iustin Pop
  "-oStrictHostKeyChecking=yes",
49 82122173 Iustin Pop
  ]
50 82122173 Iustin Pop
51 82122173 Iustin Pop
ASK_KEY_OPTS = [
52 82122173 Iustin Pop
  "-oStrictHostKeyChecking=ask",
53 82122173 Iustin Pop
  "-oEscapeChar=none",
54 82122173 Iustin Pop
  "-oHashKnownHosts=no",
55 82122173 Iustin Pop
  ]
56 82122173 Iustin Pop
57 00003458 Guido Trotter
def BuildSSHCmd(hostname, user, command, batch=True, ask_key=False):
58 00003458 Guido Trotter
  """Build an ssh string to execute a command on a remote node.
59 a8083063 Iustin Pop

60 a8083063 Iustin Pop
  Args:
61 a8083063 Iustin Pop
    hostname: the target host, string
62 a8083063 Iustin Pop
    user: user to auth as
63 a8083063 Iustin Pop
    command: the command
64 82122173 Iustin Pop
    batch: if true, ssh will run in batch mode with no prompting
65 82122173 Iustin Pop
    ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
66 82122173 Iustin Pop
             we can connect to an unknown host (not valid in batch mode)
67 a8083063 Iustin Pop

68 a8083063 Iustin Pop
  Returns:
69 00003458 Guido Trotter
    The ssh call to run 'command' on the remote host.
70 a8083063 Iustin Pop

71 a8083063 Iustin Pop
  """
72 82122173 Iustin Pop
  argv = ["ssh", "-q"]
73 82122173 Iustin Pop
  argv.extend(KNOWN_HOSTS_OPTS)
74 a8083063 Iustin Pop
  if batch:
75 a8083063 Iustin Pop
    # if we are in batch mode, we can't ask the key
76 a8083063 Iustin Pop
    if ask_key:
77 3ecf6786 Iustin Pop
      raise errors.ProgrammerError("SSH call requested conflicting options")
78 82122173 Iustin Pop
    argv.extend(BATCH_MODE_OPTS)
79 82122173 Iustin Pop
  elif ask_key:
80 82122173 Iustin Pop
    argv.extend(ASK_KEY_OPTS)
81 00003458 Guido Trotter
  argv.extend(["%s@%s" % (user, hostname), "'%s'" % command])
82 00003458 Guido Trotter
  return argv
83 00003458 Guido Trotter
84 00003458 Guido Trotter
85 00003458 Guido Trotter
def SSHCall(hostname, user, command, batch=True, ask_key=False):
86 00003458 Guido Trotter
  """Execute a command on a remote node.
87 00003458 Guido Trotter

88 00003458 Guido Trotter
  This method has the same return value as `utils.RunCmd()`, which it
89 00003458 Guido Trotter
  uses to launch ssh.
90 00003458 Guido Trotter

91 00003458 Guido Trotter
  Args:
92 00003458 Guido Trotter
    hostname: the target host, string
93 00003458 Guido Trotter
    user: user to auth as
94 00003458 Guido Trotter
    command: the command
95 00003458 Guido Trotter
    batch: if true, ssh will run in batch mode with no prompting
96 00003458 Guido Trotter
    ask_key: if true, ssh will run with StrictHostKeyChecking=ask, so that
97 00003458 Guido Trotter
             we can connect to an unknown host (not valid in batch mode)
98 00003458 Guido Trotter

99 00003458 Guido Trotter
  Returns:
100 00003458 Guido Trotter
    `utils.RunResult` as for `utils.RunCmd()`
101 00003458 Guido Trotter

102 00003458 Guido Trotter
  """
103 00003458 Guido Trotter
  return utils.RunCmd(BuildSSHCmd(hostname, user, command, batch=batch, ask_key=ask_key))
104 a8083063 Iustin Pop
105 a8083063 Iustin Pop
106 a8083063 Iustin Pop
def CopyFileToNode(node, filename):
107 a8083063 Iustin Pop
  """Copy a file to another node with scp.
108 a8083063 Iustin Pop

109 a8083063 Iustin Pop
  Args:
110 a8083063 Iustin Pop
    node: node in the cluster
111 a8083063 Iustin Pop
    filename: absolute pathname of a local file
112 a8083063 Iustin Pop

113 a8083063 Iustin Pop
  Returns:
114 a8083063 Iustin Pop
    success: True/False
115 a8083063 Iustin Pop

116 a8083063 Iustin Pop
  """
117 a8083063 Iustin Pop
  if not os.path.isfile(filename):
118 a8083063 Iustin Pop
    logger.Error("file %s does not exist" % (filename))
119 a8083063 Iustin Pop
    return False
120 a8083063 Iustin Pop
121 a8083063 Iustin Pop
  if not os.path.isabs(filename):
122 a8083063 Iustin Pop
    logger.Error("file %s must be an absolute path" % (filename))
123 a8083063 Iustin Pop
    return False
124 a8083063 Iustin Pop
125 82122173 Iustin Pop
  command = ["scp", "-q", "-p"]
126 82122173 Iustin Pop
  command.extend(KNOWN_HOSTS_OPTS)
127 82122173 Iustin Pop
  command.extend(BATCH_MODE_OPTS)
128 82122173 Iustin Pop
  command.append(filename)
129 82122173 Iustin Pop
  command.append("%s:%s" % (node, filename))
130 a8083063 Iustin Pop
131 a8083063 Iustin Pop
  result = utils.RunCmd(command)
132 a8083063 Iustin Pop
133 a8083063 Iustin Pop
  if result.failed:
134 a8083063 Iustin Pop
    logger.Error("copy to node %s failed (%s) error %s,"
135 a8083063 Iustin Pop
                 " command was %s" %
136 a8083063 Iustin Pop
                 (node, result.fail_reason, result.output, result.cmd))
137 a8083063 Iustin Pop
138 a8083063 Iustin Pop
  return not result.failed
139 a8083063 Iustin Pop
140 a8083063 Iustin Pop
141 a8083063 Iustin Pop
def VerifyNodeHostname(node):
142 a8083063 Iustin Pop
  """Verify hostname consistency via SSH.
143 a8083063 Iustin Pop

144 a8083063 Iustin Pop

145 a8083063 Iustin Pop
  This functions connects via ssh to a node and compares the hostname
146 a8083063 Iustin Pop
  reported by the node to the name with have (the one that we
147 a8083063 Iustin Pop
  connected to).
148 a8083063 Iustin Pop

149 a8083063 Iustin Pop
  This is used to detect problems in ssh known_hosts files
150 a8083063 Iustin Pop
  (conflicting known hosts) and incosistencies between dns/hosts
151 a8083063 Iustin Pop
  entries and local machine names
152 a8083063 Iustin Pop

153 a8083063 Iustin Pop
  Args:
154 a8083063 Iustin Pop
    node: nodename of a host to check. can be short or full qualified hostname
155 a8083063 Iustin Pop

156 a8083063 Iustin Pop
  Returns:
157 a8083063 Iustin Pop
    (success, detail)
158 a8083063 Iustin Pop
    where
159 a8083063 Iustin Pop
      success: True/False
160 a8083063 Iustin Pop
      detail: String with details
161 a8083063 Iustin Pop

162 a8083063 Iustin Pop
  """
163 a8083063 Iustin Pop
  retval = SSHCall(node, 'root', 'hostname')
164 a8083063 Iustin Pop
165 a8083063 Iustin Pop
  if retval.failed:
166 a8083063 Iustin Pop
    msg = "ssh problem"
167 a8083063 Iustin Pop
    output = retval.output
168 a8083063 Iustin Pop
    if output:
169 a8083063 Iustin Pop
      msg += ": %s" % output
170 a8083063 Iustin Pop
    return False, msg
171 a8083063 Iustin Pop
172 a8083063 Iustin Pop
  remotehostname = retval.stdout.strip()
173 a8083063 Iustin Pop
174 a8083063 Iustin Pop
  if not remotehostname or remotehostname != node:
175 a8083063 Iustin Pop
    return False, "hostname mismatch, got %s" % remotehostname
176 a8083063 Iustin Pop
177 a8083063 Iustin Pop
  return True, "host matches"