Statistics
| Branch: | Tag: | Revision:

root / lib / ssh.py @ 72f0f7fd

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 72f0f7fd Iustin Pop
58 00003458 Guido Trotter
def BuildSSHCmd(hostname, user, command, batch=True, ask_key=False):
59 00003458 Guido Trotter
  """Build an ssh string to execute a command on a remote node.
60 a8083063 Iustin Pop

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

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

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

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

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

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

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

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

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

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

145 a8083063 Iustin Pop

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

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

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

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

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