Move InitCluster opcode into a single function
authorMichael Hanselmann <hansmi@google.com>
Thu, 12 Jun 2008 13:05:27 +0000 (13:05 +0000)
committerMichael Hanselmann <hansmi@google.com>
Thu, 12 Jun 2008 13:05:27 +0000 (13:05 +0000)
This allows us to initialize a new cluster. The code certainly contains
bugs and hooks aren't implemented yet.

Reviewed-by: iustinp

lib/Makefile.am
lib/bootstrap.py [new file with mode: 0644]
lib/cmdlib.py
lib/mcpu.py
lib/opcodes.py
scripts/gnt-cluster

index a2537eb..726362b 100644 (file)
@@ -5,7 +5,8 @@ nodist_pkgpython_PYTHON = _autoconf.py
 pkgpython_PYTHON = __init__.py backend.py cli.py cmdlib.py config.py \
        objects.py errors.py logger.py ssh.py utils.py rpc.py \
        bdev.py opcodes.py mcpu.py constants.py \
-       ssconf.py locking.py luxi.py jqueue.py serializer.py
+       ssconf.py locking.py luxi.py jqueue.py serializer.py \
+       bootstrap.py
 python_files = $(pkgpython_PYTHON)
 
 all-local: _autoconf.py
diff --git a/lib/bootstrap.py b/lib/bootstrap.py
new file mode 100644 (file)
index 0000000..3565131
--- /dev/null
@@ -0,0 +1,226 @@
+#
+#
+
+# Copyright (C) 2006, 2007, 2008 Google Inc.
+#
+# This program is free software; you can redistribute it and/or modify
+# it under the terms of the GNU General Public License as published by
+# the Free Software Foundation; either version 2 of the License, or
+# (at your option) any later version.
+#
+# This program is distributed in the hope that it will be useful, but
+# WITHOUT ANY WARRANTY; without even the implied warranty of
+# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+# General Public License for more details.
+#
+# You should have received a copy of the GNU General Public License
+# along with this program; if not, write to the Free Software
+# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
+# 02110-1301, USA.
+
+
+"""Functions to bootstrap a new cluster.
+
+"""
+
+import os
+import os.path
+import sha
+import re
+
+from ganeti import rpc
+from ganeti import ssh
+from ganeti import utils
+from ganeti import errors
+from ganeti import config
+from ganeti import constants
+from ganeti import ssconf
+
+
+def _InitSSHSetup(node):
+  """Setup the SSH configuration for the cluster.
+
+
+  This generates a dsa keypair for root, adds the pub key to the
+  permitted hosts and adds the hostkey to its own known hosts.
+
+  Args:
+    node: the name of this host as a fqdn
+
+  """
+  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
+
+  for name in priv_key, pub_key:
+    if os.path.exists(name):
+      utils.CreateBackup(name)
+    utils.RemoveFile(name)
+
+  result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
+                         "-f", priv_key,
+                         "-q", "-N", ""])
+  if result.failed:
+    raise errors.OpExecError("Could not generate ssh keypair, error %s" %
+                             result.output)
+
+  f = open(pub_key, 'r')
+  try:
+    utils.AddAuthorizedKey(auth_keys, f.read(8192))
+  finally:
+    f.close()
+
+
+def _InitGanetiServerSetup(ss):
+  """Setup the necessary configuration for the initial node daemon.
+
+  This creates the nodepass file containing the shared password for
+  the cluster and also generates the SSL certificate.
+
+  """
+  # Create pseudo random password
+  randpass = sha.new(os.urandom(64)).hexdigest()
+  # and write it into sstore
+  ss.SetKey(ss.SS_NODED_PASS, randpass)
+
+  result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
+                         "-days", str(365*5), "-nodes", "-x509",
+                         "-keyout", constants.SSL_CERT_FILE,
+                         "-out", constants.SSL_CERT_FILE, "-batch"])
+  if result.failed:
+    raise errors.OpExecError("could not generate server ssl cert, command"
+                             " %s had exitcode %s and error message %s" %
+                             (result.cmd, result.exit_code, result.output))
+
+  os.chmod(constants.SSL_CERT_FILE, 0400)
+
+  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
+
+  if result.failed:
+    raise errors.OpExecError("Could not start the node daemon, command %s"
+                             " had exitcode %s and error %s" %
+                             (result.cmd, result.exit_code, result.output))
+
+
+def InitCluster(cluster_name, hypervisor_type, mac_prefix, def_bridge,
+                master_netdev, file_storage_dir,
+                secondary_ip=None,
+                vg_name=None):
+  """Initialise the cluster.
+
+  """
+  if config.ConfigWriter.IsCluster():
+    raise errors.OpPrereqError("Cluster is already initialised")
+
+  if hypervisor_type == constants.HT_XEN_HVM31:
+    if not os.path.exists(constants.VNC_PASSWORD_FILE):
+      raise errors.OpPrereqError("Please prepare the cluster VNC"
+                                 "password file %s" %
+                                 constants.VNC_PASSWORD_FILE)
+
+  hostname = utils.HostInfo()
+
+  if hostname.ip.startswith("127."):
+    raise errors.OpPrereqError("This host's IP resolves to the private"
+                               " range (%s). Please fix DNS or %s." %
+                               (hostname.ip, constants.ETC_HOSTS))
+
+  if not utils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT,
+                       source=constants.LOCALHOST_IP_ADDRESS):
+    raise errors.OpPrereqError("Inconsistency: this host's name resolves"
+                               " to %s,\nbut this ip address does not"
+                               " belong to this host."
+                               " Aborting." % hostname.ip)
+
+  clustername = utils.HostInfo(cluster_name)
+
+  if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
+                   timeout=5):
+    raise errors.OpPrereqError("Cluster IP already active. Aborting.")
+
+  if secondary_ip:
+    if not utils.IsValidIP(secondary_ip):
+      raise errors.OpPrereqError("Invalid secondary ip given")
+    if (secondary_ip != hostname.ip and
+        (not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
+                           source=constants.LOCALHOST_IP_ADDRESS))):
+      raise errors.OpPrereqError("You gave %s as secondary IP,"
+                                 " but it does not belong to this host." %
+                                 secondary_ip)
+
+  if vg_name is not None:
+    # Check if volume group is valid
+    vgstatus = utils.CheckVolumeGroupSize(utils.ListVolumeGroups(), vg_name,
+                                          constants.MIN_VG_SIZE)
+    if vgstatus:
+      raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
+                                 " you are not using lvm" % vgstatus)
+
+  file_storage_dir = os.path.normpath(file_storage_dir)
+
+  if not os.path.isabs(file_storage_dir):
+    raise errors.OpPrereqError("The file storage directory you passed is"
+                               " not an absolute path.")
+
+  if not os.path.exists(file_storage_dir):
+    try:
+      os.makedirs(file_storage_dir, 0750)
+    except OSError, err:
+      raise errors.OpPrereqError("Cannot create file storage directory"
+                                 " '%s': %s" %
+                                 (file_storage_dir, err))
+
+  if not os.path.isdir(file_storage_dir):
+    raise errors.OpPrereqError("The file storage directory '%s' is not"
+                               " a directory." % file_storage_dir)
+
+  if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$", mac_prefix):
+    raise errors.OpPrereqError("Invalid mac prefix given '%s'" % mac_prefix)
+
+  if hypervisor_type not in constants.HYPER_TYPES:
+    raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
+                               hypervisor_type)
+
+  result = utils.RunCmd(["ip", "link", "show", "dev", master_netdev])
+  if result.failed:
+    raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
+                               (master_netdev,
+                                result.output.strip()))
+
+  if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
+          os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
+    raise errors.OpPrereqError("Init.d script '%s' missing or not"
+                               " executable." % constants.NODE_INITD_SCRIPT)
+
+  # set up the simple store
+  ss = ssconf.SimpleStore()
+  ss.SetKey(ss.SS_HYPERVISOR, hypervisor_type)
+  ss.SetKey(ss.SS_MASTER_NODE, hostname.name)
+  ss.SetKey(ss.SS_MASTER_IP, clustername.ip)
+  ss.SetKey(ss.SS_MASTER_NETDEV, master_netdev)
+  ss.SetKey(ss.SS_CLUSTER_NAME, clustername.name)
+  ss.SetKey(ss.SS_FILE_STORAGE_DIR, file_storage_dir)
+  ss.SetKey(ss.SS_CONFIG_VERSION, constants.CONFIG_VERSION)
+
+  # set up the inter-node password and certificate
+  _InitGanetiServerSetup(ss)
+
+  # start the master ip
+  # TODO: Review rpc call from bootstrap
+  rpc.call_node_start_master(hostname.name)
+
+  # set up ssh config and /etc/hosts
+  f = open(constants.SSH_HOST_RSA_PUB, 'r')
+  try:
+    sshline = f.read()
+  finally:
+    f.close()
+  sshkey = sshline.split(" ")[1]
+
+  utils.AddHostToEtcHosts(hostname.name)
+  _InitSSHSetup(hostname.name)
+
+  # init of cluster config file
+  cfg = config.ConfigWriter()
+  cfg.InitConfig(hostname.name, hostname.ip, secondary_ip, sshkey,
+                 mac_prefix, vg_name, def_bridge)
+
+  ssh.WriteKnownHostsFile(cfg, ss, constants.SSH_KNOWN_HOSTS_FILE)
index d0ad271..dc71d4f 100644 (file)
@@ -305,69 +305,6 @@ def _BuildInstanceHookEnvByObject(instance, override=None):
   return _BuildInstanceHookEnv(**args)
 
 
-def _InitSSHSetup(node):
-  """Setup the SSH configuration for the cluster.
-
-
-  This generates a dsa keypair for root, adds the pub key to the
-  permitted hosts and adds the hostkey to its own known hosts.
-
-  Args:
-    node: the name of this host as a fqdn
-
-  """
-  priv_key, pub_key, auth_keys = ssh.GetUserFiles(constants.GANETI_RUNAS)
-
-  for name in priv_key, pub_key:
-    if os.path.exists(name):
-      utils.CreateBackup(name)
-    utils.RemoveFile(name)
-
-  result = utils.RunCmd(["ssh-keygen", "-t", "dsa",
-                         "-f", priv_key,
-                         "-q", "-N", ""])
-  if result.failed:
-    raise errors.OpExecError("Could not generate ssh keypair, error %s" %
-                             result.output)
-
-  f = open(pub_key, 'r')
-  try:
-    utils.AddAuthorizedKey(auth_keys, f.read(8192))
-  finally:
-    f.close()
-
-
-def _InitGanetiServerSetup(ss):
-  """Setup the necessary configuration for the initial node daemon.
-
-  This creates the nodepass file containing the shared password for
-  the cluster and also generates the SSL certificate.
-
-  """
-  # Create pseudo random password
-  randpass = sha.new(os.urandom(64)).hexdigest()
-  # and write it into sstore
-  ss.SetKey(ss.SS_NODED_PASS, randpass)
-
-  result = utils.RunCmd(["openssl", "req", "-new", "-newkey", "rsa:1024",
-                         "-days", str(365*5), "-nodes", "-x509",
-                         "-keyout", constants.SSL_CERT_FILE,
-                         "-out", constants.SSL_CERT_FILE, "-batch"])
-  if result.failed:
-    raise errors.OpExecError("could not generate server ssl cert, command"
-                             " %s had exitcode %s and error message %s" %
-                             (result.cmd, result.exit_code, result.output))
-
-  os.chmod(constants.SSL_CERT_FILE, 0400)
-
-  result = utils.RunCmd([constants.NODE_INITD_SCRIPT, "restart"])
-
-  if result.failed:
-    raise errors.OpExecError("Could not start the node daemon, command %s"
-                             " had exitcode %s and error %s" %
-                             (result.cmd, result.exit_code, result.output))
-
-
 def _CheckInstanceBridgesExist(instance):
   """Check that the brigdes needed by an instance exist.
 
@@ -380,162 +317,6 @@ def _CheckInstanceBridgesExist(instance):
                                (brlist, instance.primary_node))
 
 
-class LUInitCluster(LogicalUnit):
-  """Initialise the cluster.
-
-  """
-  HPATH = "cluster-init"
-  HTYPE = constants.HTYPE_CLUSTER
-  _OP_REQP = ["cluster_name", "hypervisor_type", "mac_prefix",
-              "def_bridge", "master_netdev", "file_storage_dir"]
-  REQ_CLUSTER = False
-
-  def BuildHooksEnv(self):
-    """Build hooks env.
-
-    Notes: Since we don't require a cluster, we must manually add
-    ourselves in the post-run node list.
-
-    """
-    env = {"OP_TARGET": self.op.cluster_name}
-    return env, [], [self.hostname.name]
-
-  def CheckPrereq(self):
-    """Verify that the passed name is a valid one.
-
-    """
-    if config.ConfigWriter.IsCluster():
-      raise errors.OpPrereqError("Cluster is already initialised")
-
-    if self.op.hypervisor_type == constants.HT_XEN_HVM31:
-      if not os.path.exists(constants.VNC_PASSWORD_FILE):
-        raise errors.OpPrereqError("Please prepare the cluster VNC"
-                                   "password file %s" %
-                                   constants.VNC_PASSWORD_FILE)
-
-    self.hostname = hostname = utils.HostInfo()
-
-    if hostname.ip.startswith("127."):
-      raise errors.OpPrereqError("This host's IP resolves to the private"
-                                 " range (%s). Please fix DNS or %s." %
-                                 (hostname.ip, constants.ETC_HOSTS))
-
-    if not utils.TcpPing(hostname.ip, constants.DEFAULT_NODED_PORT,
-                         source=constants.LOCALHOST_IP_ADDRESS):
-      raise errors.OpPrereqError("Inconsistency: this host's name resolves"
-                                 " to %s,\nbut this ip address does not"
-                                 " belong to this host."
-                                 " Aborting." % hostname.ip)
-
-    self.clustername = clustername = utils.HostInfo(self.op.cluster_name)
-
-    if utils.TcpPing(clustername.ip, constants.DEFAULT_NODED_PORT,
-                     timeout=5):
-      raise errors.OpPrereqError("Cluster IP already active. Aborting.")
-
-    secondary_ip = getattr(self.op, "secondary_ip", None)
-    if secondary_ip and not utils.IsValidIP(secondary_ip):
-      raise errors.OpPrereqError("Invalid secondary ip given")
-    if (secondary_ip and
-        secondary_ip != hostname.ip and
-        (not utils.TcpPing(secondary_ip, constants.DEFAULT_NODED_PORT,
-                           source=constants.LOCALHOST_IP_ADDRESS))):
-      raise errors.OpPrereqError("You gave %s as secondary IP,"
-                                 " but it does not belong to this host." %
-                                 secondary_ip)
-    self.secondary_ip = secondary_ip
-
-    if not hasattr(self.op, "vg_name"):
-      self.op.vg_name = None
-    # if vg_name not None, checks if volume group is valid
-    if self.op.vg_name:
-      vgstatus = utils.CheckVolumeGroupSize(utils.ListVolumeGroups(), vg_name,
-                                            constants.MIN_VG_SIZE)
-      if vgstatus:
-        raise errors.OpPrereqError("Error: %s\nspecify --no-lvm-storage if"
-                                   " you are not using lvm" % vgstatus)
-
-    self.op.file_storage_dir = os.path.normpath(self.op.file_storage_dir)
-
-    if not os.path.isabs(self.op.file_storage_dir):
-      raise errors.OpPrereqError("The file storage directory you have is"
-                                 " not an absolute path.")
-
-    if not os.path.exists(self.op.file_storage_dir):
-      try:
-        os.makedirs(self.op.file_storage_dir, 0750)
-      except OSError, err:
-        raise errors.OpPrereqError("Cannot create file storage directory"
-                                   " '%s': %s" %
-                                   (self.op.file_storage_dir, err))
-
-    if not os.path.isdir(self.op.file_storage_dir):
-      raise errors.OpPrereqError("The file storage directory '%s' is not"
-                                 " a directory." % self.op.file_storage_dir)
-
-    if not re.match("^[0-9a-z]{2}:[0-9a-z]{2}:[0-9a-z]{2}$",
-                    self.op.mac_prefix):
-      raise errors.OpPrereqError("Invalid mac prefix given '%s'" %
-                                 self.op.mac_prefix)
-
-    if self.op.hypervisor_type not in constants.HYPER_TYPES:
-      raise errors.OpPrereqError("Invalid hypervisor type given '%s'" %
-                                 self.op.hypervisor_type)
-
-    result = utils.RunCmd(["ip", "link", "show", "dev", self.op.master_netdev])
-    if result.failed:
-      raise errors.OpPrereqError("Invalid master netdev given (%s): '%s'" %
-                                 (self.op.master_netdev,
-                                  result.output.strip()))
-
-    if not (os.path.isfile(constants.NODE_INITD_SCRIPT) and
-            os.access(constants.NODE_INITD_SCRIPT, os.X_OK)):
-      raise errors.OpPrereqError("Init.d script '%s' missing or not"
-                                 " executable." % constants.NODE_INITD_SCRIPT)
-
-  def Exec(self, feedback_fn):
-    """Initialize the cluster.
-
-    """
-    clustername = self.clustername
-    hostname = self.hostname
-
-    # set up the simple store
-    self.sstore = ss = ssconf.SimpleStore()
-    ss.SetKey(ss.SS_HYPERVISOR, self.op.hypervisor_type)
-    ss.SetKey(ss.SS_MASTER_NODE, hostname.name)
-    ss.SetKey(ss.SS_MASTER_IP, clustername.ip)
-    ss.SetKey(ss.SS_MASTER_NETDEV, self.op.master_netdev)
-    ss.SetKey(ss.SS_CLUSTER_NAME, clustername.name)
-    ss.SetKey(ss.SS_FILE_STORAGE_DIR, self.op.file_storage_dir)
-    ss.SetKey(ss.SS_CONFIG_VERSION, constants.CONFIG_VERSION)
-
-    # set up the inter-node password and certificate
-    _InitGanetiServerSetup(ss)
-
-    # start the master ip
-    rpc.call_node_start_master(hostname.name)
-
-    # set up ssh config and /etc/hosts
-    f = open(constants.SSH_HOST_RSA_PUB, 'r')
-    try:
-      sshline = f.read()
-    finally:
-      f.close()
-    sshkey = sshline.split(" ")[1]
-
-    utils.AddHostToEtcHosts(hostname.name)
-    _InitSSHSetup(hostname.name)
-
-    # init of cluster config file
-    self.cfg = cfgw = config.ConfigWriter()
-    cfgw.InitConfig(hostname.name, hostname.ip, self.secondary_ip,
-                    sshkey, self.op.mac_prefix,
-                    self.op.vg_name, self.op.def_bridge)
-
-    ssh.WriteKnownHostsFile(cfgw, ss, constants.SSH_KNOWN_HOSTS_FILE)
-
-
 class LUDestroyCluster(NoHooksLU):
   """Logical unit for destroying the cluster.
 
index 6619273..360eaf2 100644 (file)
@@ -43,7 +43,6 @@ class Processor(object):
   """Object which runs OpCodes"""
   DISPATCH_TABLE = {
     # Cluster
-    opcodes.OpInitCluster: cmdlib.LUInitCluster,
     opcodes.OpDestroyCluster: cmdlib.LUDestroyCluster,
     opcodes.OpQueryClusterInfo: cmdlib.LUQueryClusterInfo,
     opcodes.OpClusterCopyFile: cmdlib.LUClusterCopyFile,
index dbae63c..094d085 100644 (file)
@@ -159,14 +159,6 @@ class OpCode(BaseJO):
     return op
 
 
-class OpInitCluster(OpCode):
-  """Initialise the cluster."""
-  OP_ID = "OP_CLUSTER_INIT"
-  __slots__ = ["cluster_name", "secondary_ip", "hypervisor_type",
-               "vg_name", "mac_prefix", "def_bridge", "master_netdev",
-               "file_storage_dir"]
-
-
 class OpDestroyCluster(OpCode):
   """Destroy the cluster."""
   OP_ID = "OP_CLUSTER_DESTROY"
index 59437de..9f4b493 100755 (executable)
@@ -28,6 +28,7 @@ from ganeti import opcodes
 from ganeti import constants
 from ganeti import errors
 from ganeti import utils
+from ganeti import bootstrap
 
 
 def InitCluster(opts, args):
@@ -46,15 +47,14 @@ def InitCluster(opts, args):
   if opts.lvm_storage and not opts.vg_name:
     vg_name = constants.DEFAULT_VG
 
-  op = opcodes.OpInitCluster(cluster_name=args[0],
-                             secondary_ip=opts.secondary_ip,
-                             hypervisor_type=opts.hypervisor_type,
-                             vg_name=vg_name,
-                             mac_prefix=opts.mac_prefix,
-                             def_bridge=opts.def_bridge,
-                             master_netdev=opts.master_netdev,
-                             file_storage_dir=opts.file_storage_dir)
-  SubmitOpCode(op)
+  bootstrap.InitCluster(cluster_name=args[0],
+                        secondary_ip=opts.secondary_ip,
+                        hypervisor_type=opts.hypervisor_type,
+                        vg_name=vg_name,
+                        mac_prefix=opts.mac_prefix,
+                        def_bridge=opts.def_bridge,
+                        master_netdev=opts.master_netdev,
+                        file_storage_dir=opts.file_storage_dir)
   return 0