Revision 224ff0f7 lib/client/gnt_node.py

b/lib/client/gnt_node.py
27 27
# C0103: Invalid name gnt-node
28 28

  
29 29
import itertools
30
import errno
31
import tempfile
30 32

  
31 33
from ganeti.cli import *
32 34
from ganeti import cli
......
37 39
from ganeti import errors
38 40
from ganeti import netutils
39 41
from ganeti import pathutils
42
from ganeti import serializer
43
from ganeti import ssh
40 44
from cStringIO import StringIO
41 45

  
42 46
from ganeti import confd
......
134 138
                               errors.ECODE_INVAL)
135 139

  
136 140

  
137
def _RunSetupSSH(options, nodes):
138
  """Wrapper around utils.RunCmd to call setup-ssh
141
def _TryReadFile(path):
142
  """Tries to read a file.
139 143

  
140
  @param options: The command line options
141
  @param nodes: The nodes to setup
144
  If the file is not found, C{None} is returned.
145

  
146
  @type path: string
147
  @param path: Filename
148
  @rtype: None or string
149
  @todo: Consider adding a generic ENOENT wrapper
142 150

  
143 151
  """
152
  try:
153
    return utils.ReadFile(path)
154
  except EnvironmentError, err:
155
    if err.errno == errno.ENOENT:
156
      return None
157
    else:
158
      raise
159

  
144 160

  
145
  assert nodes, "Empty node list"
161
def _ReadSshKeys(keyfiles, _tostderr_fn=ToStderr):
162
  """Reads SSH keys according to C{keyfiles}.
163

  
164
  @type keyfiles: dict
165
  @param keyfiles: Dictionary with keys of L{constants.SSHK_ALL} and two-values
166
    tuples (private and public key file)
167
  @rtype: list
168
  @return: List of three-values tuples (L{constants.SSHK_ALL}, private and
169
    public key as strings)
170

  
171
  """
172
  result = []
146 173

  
147
  cmd = [pathutils.SETUP_SSH]
174
  for (kind, (private_file, public_file)) in keyfiles.items():
175
    private_key = _TryReadFile(private_file)
176
    public_key = _TryReadFile(public_file)
148 177

  
149
  # Pass --debug|--verbose to the external script if set on our invocation
150
  # --debug overrides --verbose
178
    if public_key and private_key:
179
      result.append((kind, private_key, public_key))
180
    elif public_key or private_key:
181
      _tostderr_fn("Couldn't find a complete set of keys for kind '%s'; files"
182
                   " '%s' and '%s'", kind, private_file, public_file)
183

  
184
  return result
185

  
186

  
187
def _SetupSSH(options, cluster_name, node):
188
  """Configures a destination node's SSH daemon.
189

  
190
  @param options: Command line options
191
  @type cluster_name
192
  @param cluster_name: Cluster name
193
  @type node: string
194
  @param node: Destination node name
195

  
196
  """
197
  if options.force_join:
198
    ToStderr("The \"--force-join\" option is no longer supported and will be"
199
             " ignored.")
200

  
201
  cmd = [pathutils.PREPARE_NODE_JOIN]
202

  
203
  # Pass --debug/--verbose to the external script if set on our invocation
151 204
  if options.debug:
152 205
    cmd.append("--debug")
153
  elif options.verbose:
206

  
207
  if options.verbose:
154 208
    cmd.append("--verbose")
155
  if not options.ssh_key_check:
156
    cmd.append("--no-ssh-key-check")
157
  if options.force_join:
158
    cmd.append("--force-join")
159 209

  
160
  cmd.extend(nodes)
210
  host_keys = _ReadSshKeys(constants.SSH_DAEMON_KEYFILES)
211

  
212
  (_, root_keyfiles) = \
213
    ssh.GetAllUserFiles(constants.SSH_LOGIN_USER, mkdir=False, dircheck=False)
214

  
215
  root_keys = _ReadSshKeys(root_keyfiles)
216

  
217
  (_, cert_pem) = \
218
    utils.ExtractX509Certificate(utils.ReadFile(pathutils.NODED_CERT_FILE))
219

  
220
  data = {
221
    constants.SSHS_CLUSTER_NAME: cluster_name,
222
    constants.SSHS_NODE_DAEMON_CERTIFICATE: cert_pem,
223
    constants.SSHS_SSH_HOST_KEY: host_keys,
224
    constants.SSHS_SSH_ROOT_KEY: root_keys,
225
    }
226

  
227
  srun = ssh.SshRunner(cluster_name)
228
  scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER,
229
                       utils.ShellQuoteArgs(cmd),
230
                       batch=False, ask_key=options.ssh_key_check,
231
                       strict_host_check=options.ssh_key_check, quiet=False,
232
                       use_cluster_key=False)
233

  
234
  tempfh = tempfile.TemporaryFile()
235
  try:
236
    tempfh.write(serializer.DumpJson(data))
237
    tempfh.seek(0)
161 238

  
162
  result = utils.RunCmd(cmd, interactive=True)
239
    result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh)
240
  finally:
241
    tempfh.close()
163 242

  
164 243
  if result.failed:
165
    errmsg = ("Command '%s' failed with exit code %s; output %r" %
166
              (result.cmd, result.exit_code, result.output))
167
    raise errors.OpExecError(errmsg)
244
    raise errors.OpExecError("Command '%s' failed: %s" %
245
                             (result.cmd, result.fail_reason))
168 246

  
169 247

  
170 248
@UsesRPC
......
206 284
    sip = opts.secondary_ip
207 285

  
208 286
  # read the cluster name from the master
209
  output = cl.QueryConfigValues(["cluster_name"])
210
  cluster_name = output[0]
287
  (cluster_name, ) = cl.QueryConfigValues(["cluster_name"])
211 288

  
212 289
  if not readd and opts.node_setup:
213 290
    ToStderr("-- WARNING -- \n"
......
218 295
             "and grant full intra-cluster ssh root access to/from it\n", node)
219 296

  
220 297
  if opts.node_setup:
221
    _RunSetupSSH(opts, [node])
298
    _SetupSSH(opts, cluster_name, node)
222 299

  
223 300
  bootstrap.SetupNodeDaemon(cluster_name, node, opts.ssh_key_check)
224 301

  

Also available in: Unified diff