Statistics
| Branch: | Tag: | Revision:

root / lib / ssh.py @ 4a72cc75

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

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

61
  Args:
62
    hostname: the target host, string
63
    user: user to auth as
64
    command: the command
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)
68

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

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

    
85

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

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

92
  Args:
93
    hostname: the target host, string
94
    user: user to auth as
95
    command: the command
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)
99

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

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

    
106

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

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

114
  Returns:
115
    success: True/False
116

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

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

    
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))
131

    
132
  result = utils.RunCmd(command)
133

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

    
139
  return not result.failed
140

    
141

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

145

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
148
  connected to).
149

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
153

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

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

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

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

    
173
  remotehostname = retval.stdout.strip()
174

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

    
178
  return True, "host matches"