Revision d3b18b8e

b/tools/setup-ssh
38 38
from ganeti import constants
39 39
from ganeti import errors
40 40
from ganeti import netutils
41
from ganeti import ssconf
41 42
from ganeti import ssh
42 43
from ganeti import utils
43 44

  
......
48 49
  """
49 50

  
50 51

  
52
class JoinCheckError(errors.GenericError):
53
  """Exception raised if join check fails.
54

  
55
  """
56

  
57

  
58
def _CheckJoin(transport):
59
  """Checks if a join is safe or dangerous.
60

  
61
  Note: This function relies on the fact, that all
62
  hosts have the same configuration at compile time of
63
  Ganeti. So that the constants do not mismatch.
64

  
65
  @param transport: The paramiko transport instance
66
  @return: True if the join is safe; False otherwise
67

  
68
  """
69
  sftp = transport.open_sftp_client()
70
  ss = ssconf.SimpleStore()
71
  ss_cluster_name_path = ss.KeyToFilename(constants.SS_CLUSTER_NAME)
72

  
73
  cluster_files = {
74
    ss_cluster_name_path: utils.ReadFile(ss_cluster_name_path),
75
    constants.NODED_CERT_FILE: utils.ReadFile(constants.NODED_CERT_FILE),
76
    }
77

  
78
  try:
79
    remote_noded_file = _ReadSftpFile(sftp, constants.NODED_CERT_FILE)
80
  except IOError:
81
    # We can just assume that the file doesn't exist as such error reporting
82
    # is lacking from paramiko
83
    #
84
    # We don't have the noded certificate. As without the cert, the
85
    # noded is not running, we are on the safe bet to say that this
86
    # node doesn't belong to a cluster
87
    return True
88

  
89
  try:
90
    remote_cluster_name = _ReadSftpFile(sftp, ss_cluster_name_path)
91
  except IOError:
92
    # This can indicate that a previous join was not successful
93
    # So if the noded cert was found and matches we are fine
94
    return cluster_files[constants.NODED_CERT_FILE] == remote_noded_file
95

  
96
  return (cluster_files[constants.NODED_CERT_FILE] == remote_noded_file and
97
          cluster_files[ss_cluster_name_path] == remote_cluster_name)
98

  
99

  
51 100
def _RunRemoteCommand(transport, command):
52 101
  """Invokes and wait for the command over SSH.
53 102

  
......
84 133
  _RunRemoteCommand(transport, "%s %s" % (constants.DAEMON_UTIL, command))
85 134

  
86 135

  
136
def _ReadSftpFile(sftp, filename):
137
  """Reads a file over sftp.
138

  
139
  @param sftp: An open paramiko SFTP client
140
  @param filename: The filename of the file to read
141
  @return: The content of the file
142

  
143
  """
144
  remote_file = sftp.open(filename, "r")
145
  try:
146
    return remote_file.read()
147
  finally:
148
    remote_file.close()
149

  
150

  
87 151
def _WriteSftpFile(sftp, name, perm, data):
88 152
  """SFTPs data to a remote file.
89 153

  
......
159 223
  """
160 224
  program = os.path.basename(sys.argv[0])
161 225

  
162
  parser = optparse.OptionParser(usage=("%prog [--debug|--verbose] <node>"
163
                                        " <node...>"), prog=program)
226
  parser = optparse.OptionParser(usage=("%prog [--debug|--verbose] [--force]"
227
                                        " <node> <node...>"), prog=program)
164 228
  parser.add_option(cli.DEBUG_OPT)
165 229
  parser.add_option(cli.VERBOSE_OPT)
166 230
  parser.add_option(cli.NOSSH_KEYCHECK_OPT)
......
172 236
  parser.add_option(optparse.Option("--key-type", dest="key_type",
173 237
                                    choices=("rsa", "dsa"), default="dsa",
174 238
                                    help="The private key type (rsa or dsa)"))
239
  parser.add_option(optparse.Option("-j", "--force-join", dest="force_join",
240
                                    action="store_true", default=False,
241
                                    help="Force the join of the host"))
175 242

  
176 243
  (options, args) = parser.parse_args()
177 244

  
......
368 435
      continue
369 436
    try:
370 437
      try:
438
        if not _CheckJoin(transport):
439
          if options.force_join:
440
            logging.warning("Host %s failed join check, forced to continue",
441
                            host)
442
          else:
443
            raise JoinCheckError("Host %s failed join check" % host)
371 444
        SetupSSH(transport)
372 445
      except errors.GenericError, err:
373 446
        logging.error("While doing setup on host %s an error occured: %s",

Also available in: Unified diff