Statistics
| Branch: | Tag: | Revision:

root / lib / ssh.py @ 00003458

History | View | Annotate | Download (4.7 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007 Google Inc.
5
#
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.
10
#
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.
15
#
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
19
# 02110-1301, USA.
20

    
21

    
22
"""Module encapsulating ssh functionality.
23

24
"""
25

    
26

    
27
import os
28

    
29
from ganeti import logger
30
from ganeti import utils
31
from ganeti import errors
32
from ganeti import constants
33

    
34

    
35
__all__ = ["SSHCall", "CopyFileToNode", "VerifyNodeHostname",
36
           "KNOWN_HOSTS_OPTS", "BATCH_MODE_OPTS", "ASK_KEY_OPTS"]
37

    
38

    
39
KNOWN_HOSTS_OPTS = [
40
  "-oGlobalKnownHostsFile=%s" % constants.SSH_KNOWN_HOSTS_FILE,
41
  "-oUserKnownHostsFile=/dev/null",
42
  ]
43

    
44
# Note: BATCH_MODE conflicts with ASK_KEY
45
BATCH_MODE_OPTS = [
46
  "-oEscapeChar=none",
47
  "-oBatchMode=yes",
48
  "-oStrictHostKeyChecking=yes",
49
  ]
50

    
51
ASK_KEY_OPTS = [
52
  "-oStrictHostKeyChecking=ask",
53
  "-oEscapeChar=none",
54
  "-oHashKnownHosts=no",
55
  ]
56

    
57
def BuildSSHCmd(hostname, user, command, batch=True, ask_key=False):
58
  """Build an ssh string to execute a command on a remote node.
59

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

68
  Returns:
69
    The ssh call to run 'command' on the remote host.
70

71
  """
72
  argv = ["ssh", "-q"]
73
  argv.extend(KNOWN_HOSTS_OPTS)
74
  if batch:
75
    # if we are in batch mode, we can't ask the key
76
    if ask_key:
77
      raise errors.ProgrammerError("SSH call requested conflicting options")
78
    argv.extend(BATCH_MODE_OPTS)
79
  elif ask_key:
80
    argv.extend(ASK_KEY_OPTS)
81
  argv.extend(["%s@%s" % (user, hostname), "'%s'" % command])
82
  return argv
83

    
84

    
85
def SSHCall(hostname, user, command, batch=True, ask_key=False):
86
  """Execute a command on a remote node.
87

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

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

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

102
  """
103
  return utils.RunCmd(BuildSSHCmd(hostname, user, command, batch=batch, ask_key=ask_key))
104

    
105

    
106
def CopyFileToNode(node, filename):
107
  """Copy a file to another node with scp.
108

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

113
  Returns:
114
    success: True/False
115

116
  """
117
  if not os.path.isfile(filename):
118
    logger.Error("file %s does not exist" % (filename))
119
    return False
120

    
121
  if not os.path.isabs(filename):
122
    logger.Error("file %s must be an absolute path" % (filename))
123
    return False
124

    
125
  command = ["scp", "-q", "-p"]
126
  command.extend(KNOWN_HOSTS_OPTS)
127
  command.extend(BATCH_MODE_OPTS)
128
  command.append(filename)
129
  command.append("%s:%s" % (node, filename))
130

    
131
  result = utils.RunCmd(command)
132

    
133
  if result.failed:
134
    logger.Error("copy to node %s failed (%s) error %s,"
135
                 " command was %s" %
136
                 (node, result.fail_reason, result.output, result.cmd))
137

    
138
  return not result.failed
139

    
140

    
141
def VerifyNodeHostname(node):
142
  """Verify hostname consistency via SSH.
143

144

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

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

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

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

162
  """
163
  retval = SSHCall(node, 'root', 'hostname')
164

    
165
  if retval.failed:
166
    msg = "ssh problem"
167
    output = retval.output
168
    if output:
169
      msg += ": %s" % output
170
    return False, msg
171

    
172
  remotehostname = retval.stdout.strip()
173

    
174
  if not remotehostname or remotehostname != node:
175
    return False, "hostname mismatch, got %s" % remotehostname
176

    
177
  return True, "host matches"