Merge branch 'devel-2.7'
[ganeti-local] / lib / bootstrap.py
index dfd415b..69ba1ef 100644 (file)
@@ -28,6 +28,7 @@ import os.path
 import re
 import logging
 import time
 import re
 import logging
 import time
+import tempfile
 
 from ganeti import rpc
 from ganeti import ssh
 
 from ganeti import rpc
 from ganeti import ssh
@@ -255,6 +256,61 @@ def _WaitForMasterDaemon():
                              " %s seconds" % _DAEMON_READY_TIMEOUT)
 
 
                              " %s seconds" % _DAEMON_READY_TIMEOUT)
 
 
+def RunNodeSetupCmd(cluster_name, node, basecmd, debug, verbose,
+                    use_cluster_key, ask_key, strict_host_check, data):
+  """Runs a command to configure something on a remote machine.
+
+  @type cluster_name: string
+  @param cluster_name: Cluster name
+  @type node: string
+  @param node: Node name
+  @type basecmd: string
+  @param basecmd: Base command (path on the remote machine)
+  @type debug: bool
+  @param debug: Enable debug output
+  @type verbose: bool
+  @param verbose: Enable verbose output
+  @type use_cluster_key: bool
+  @param use_cluster_key: See L{ssh.SshRunner.BuildCmd}
+  @type ask_key: bool
+  @param ask_key: See L{ssh.SshRunner.BuildCmd}
+  @type strict_host_check: bool
+  @param strict_host_check: See L{ssh.SshRunner.BuildCmd}
+  @param data: JSON-serializable input data for script (passed to stdin)
+
+  """
+  cmd = [basecmd]
+
+  # Pass --debug/--verbose to the external script if set on our invocation
+  if debug:
+    cmd.append("--debug")
+
+  if verbose:
+    cmd.append("--verbose")
+
+  family = ssconf.SimpleStore().GetPrimaryIPFamily()
+  srun = ssh.SshRunner(cluster_name,
+                       ipv6=(family == netutils.IP6Address.family))
+  scmd = srun.BuildCmd(node, constants.SSH_LOGIN_USER,
+                       utils.ShellQuoteArgs(cmd),
+                       batch=False, ask_key=ask_key, quiet=False,
+                       strict_host_check=strict_host_check,
+                       use_cluster_key=use_cluster_key)
+
+  tempfh = tempfile.TemporaryFile()
+  try:
+    tempfh.write(serializer.DumpJson(data))
+    tempfh.seek(0)
+
+    result = utils.RunCmd(scmd, interactive=True, input_fd=tempfh)
+  finally:
+    tempfh.close()
+
+  if result.failed:
+    raise errors.OpExecError("Command '%s' failed: %s" %
+                             (result.cmd, result.fail_reason))
+
+
 def _InitFileStorage(file_storage_dir):
   """Initialize if needed the file storage.
 
 def _InitFileStorage(file_storage_dir):
   """Initialize if needed the file storage.
 
@@ -329,7 +385,7 @@ def InitCluster(cluster_name, mac_prefix, # pylint: disable=R0913, R0914
   if ipcls.IsLoopback(hostname.ip):
     raise errors.OpPrereqError("This host's IP (%s) resolves to a loopback"
                                " address. Please fix DNS or %s." %
   if ipcls.IsLoopback(hostname.ip):
     raise errors.OpPrereqError("This host's IP (%s) resolves to a loopback"
                                " address. Please fix DNS or %s." %
-                               (hostname.ip, constants.ETC_HOSTS),
+                               (hostname.ip, pathutils.ETC_HOSTS),
                                errors.ECODE_ENVIRON)
 
   if not ipcls.Own(hostname.ip):
                                errors.ECODE_ENVIRON)
 
   if not ipcls.Own(hostname.ip):
@@ -392,6 +448,13 @@ def InitCluster(cluster_name, mac_prefix, # pylint: disable=R0913, R0914
                                                              curr_helper),
                                  errors.ECODE_INVAL)
 
                                                              curr_helper),
                                  errors.ECODE_INVAL)
 
+  logging.debug("Stopping daemons (if any are running)")
+  result = utils.RunCmd([pathutils.DAEMON_UTIL, "stop-all"])
+  if result.failed:
+    raise errors.OpExecError("Could not stop daemons, command %s"
+                             " had exitcode %s and error '%s'" %
+                             (result.cmd, result.exit_code, result.output))
+
   if constants.ENABLE_FILE_STORAGE:
     file_storage_dir = _InitFileStorage(file_storage_dir)
   else:
   if constants.ENABLE_FILE_STORAGE:
     file_storage_dir = _InitFileStorage(file_storage_dir)
   else:
@@ -603,6 +666,7 @@ def InitConfig(version, cluster_config, master_node_config,
                                    nodegroups=nodegroups,
                                    nodes=nodes,
                                    instances={},
                                    nodegroups=nodegroups,
                                    nodes=nodes,
                                    instances={},
+                                   networks={},
                                    serial_no=1,
                                    ctime=now, mtime=now)
   utils.WriteFile(cfg_file,
                                    serial_no=1,
                                    ctime=now, mtime=now)
   utils.WriteFile(cfg_file,
@@ -643,7 +707,7 @@ def FinalizeClusterDestroy(master):
                     " the node: %s", msg)
 
 
                     " the node: %s", msg)
 
 
-def SetupNodeDaemon(cluster_name, node, ssh_key_check):
+def SetupNodeDaemon(opts, cluster_name, node):
   """Add a node to the cluster.
 
   This function must be called before the actual opcode, and will ssh
   """Add a node to the cluster.
 
   This function must be called before the actual opcode, and will ssh
@@ -652,36 +716,19 @@ def SetupNodeDaemon(cluster_name, node, ssh_key_check):
 
   @param cluster_name: the cluster name
   @param node: the name of the new node
 
   @param cluster_name: the cluster name
   @param node: the name of the new node
-  @param ssh_key_check: whether to do a strict key check
 
   """
 
   """
-  sstore = ssconf.SimpleStore()
-  family = sstore.GetPrimaryIPFamily()
-  sshrunner = ssh.SshRunner(cluster_name,
-                            ipv6=(family == netutils.IP6Address.family))
-
-  # set up inter-node password and certificate and restarts the node daemon
-  # and then connect with ssh to set password and start ganeti-noded
-  # note that all the below variables are sanitized at this point,
-  # either by being constants or by the checks above
-  sshrunner.CopyFileToNode(node, pathutils.NODED_CERT_FILE)
-  sshrunner.CopyFileToNode(node, pathutils.RAPI_CERT_FILE)
-  sshrunner.CopyFileToNode(node, pathutils.SPICE_CERT_FILE)
-  sshrunner.CopyFileToNode(node, pathutils.SPICE_CACERT_FILE)
-  sshrunner.CopyFileToNode(node, pathutils.CONFD_HMAC_KEY)
-  for filename in sstore.GetFileList():
-    sshrunner.CopyFileToNode(node, filename)
-  mycommand = ("%s stop-all; %s start %s" %
-               (pathutils.DAEMON_UTIL, pathutils.DAEMON_UTIL, constants.NODED))
-
-  result = sshrunner.Run(node, constants.SSH_LOGIN_USER, mycommand, batch=False,
-                         ask_key=ssh_key_check,
-                         use_cluster_key=True,
-                         strict_host_check=ssh_key_check)
-  if result.failed:
-    raise errors.OpExecError("Remote command on node %s, error: %s,"
-                             " output: %s" %
-                             (node, result.fail_reason, result.output))
+  data = {
+    constants.NDS_CLUSTER_NAME: cluster_name,
+    constants.NDS_NODE_DAEMON_CERTIFICATE:
+      utils.ReadFile(pathutils.NODED_CERT_FILE),
+    constants.NDS_SSCONF: ssconf.SimpleStore().ReadAll(),
+    constants.NDS_START_NODE_DAEMON: True,
+    }
+
+  RunNodeSetupCmd(cluster_name, node, pathutils.NODE_DAEMON_SETUP,
+                  opts.debug, opts.verbose,
+                  True, opts.ssh_key_check, opts.ssh_key_check, data)
 
   _WaitForNodeDaemon(node)
 
 
   _WaitForNodeDaemon(node)