Add --readd option to “gnt-node add”
authorMichael Hanselmann <hansmi@google.com>
Wed, 16 Apr 2008 18:22:26 +0000 (18:22 +0000)
committerMichael Hanselmann <hansmi@google.com>
Wed, 16 Apr 2008 18:22:26 +0000 (18:22 +0000)
This allows us to readd a node after it failed and required a
reinstallation or replacement.

Reviewed-by: iustinp

lib/cmdlib.py
lib/opcodes.py
man/gnt-node.sgml
qa/ganeti-qa.py
qa/qa-sample.yaml
qa/qa_node.py
scripts/gnt-node

index 5d18c2f..873f216 100644 (file)
@@ -1,7 +1,7 @@
 #
 #
 
-# Copyright (C) 2006, 2007 Google Inc.
+# 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
@@ -1641,13 +1641,24 @@ class LUAddNode(LogicalUnit):
     if not utils.IsValidIP(secondary_ip):
       raise errors.OpPrereqError("Invalid secondary IP given")
     self.op.secondary_ip = secondary_ip
+
     node_list = cfg.GetNodeList()
-    if node in node_list:
-      raise errors.OpPrereqError("Node %s is already in the configuration"
-                                 % node)
+    if not self.op.readd and node in node_list:
+      raise errors.OpPrereqError("Node %s is already in the configuration" %
+                                 node)
+    elif self.op.readd and node not in node_list:
+      raise errors.OpPrereqError("Node %s is not in the configuration" % node)
 
     for existing_node_name in node_list:
       existing_node = cfg.GetNodeInfo(existing_node_name)
+
+      if self.op.readd and node == existing_node_name:
+        if (existing_node.primary_ip != primary_ip or
+            existing_node.secondary_ip != secondary_ip):
+          raise errors.OpPrereqError("Readded node doesn't have the same IP"
+                                     " address configuration as before")
+        continue
+
       if (existing_node.primary_ip == primary_ip or
           existing_node.secondary_ip == primary_ip or
           existing_node.primary_ip == secondary_ip or
@@ -1810,8 +1821,9 @@ class LUAddNode(LogicalUnit):
       if not self.ssh.CopyFileToNode(node, fname):
         logger.Error("could not copy file %s to node %s" % (fname, node))
 
-    logger.Info("adding node %s to cluster.conf" % node)
-    self.cfg.AddNode(new_node)
+    if not self.op.readd:
+      logger.Info("adding node %s to cluster.conf" % node)
+      self.cfg.AddNode(new_node)
 
 
 class LUMasterFailover(LogicalUnit):
index 117757f..c629e0c 100644 (file)
@@ -257,7 +257,7 @@ class OpRemoveNode(OpCode):
 class OpAddNode(OpCode):
   """Add a node."""
   OP_ID = "OP_NODE_ADD"
-  __slots__ = ["node_name", "primary_ip", "secondary_ip"]
+  __slots__ = ["node_name", "primary_ip", "secondary_ip", "readd"]
 
 
 class OpQueryNodes(OpCode):
index 98ec343..71f9dbf 100644 (file)
@@ -20,6 +20,7 @@
     <copyright>
       <year>2006</year>
       <year>2007</year>
+      <year>2008</year>
       <holder>Google Inc.</holder>
     </copyright>
     &dhdate;
@@ -60,6 +61,7 @@
 
       <cmdsynopsis>
         <command>add</command>
+        <arg>--readd</arg>
         <arg>-s <replaceable>secondary_ip</replaceable></arg>
         <arg choice="req"><replaceable>nodename</replaceable></arg>
       </cmdsynopsis>
       </para>
 
       <para>
+        In case you're readding a node after hardware failure, you
+        can use the <option>--readd</option> parameter.
+      </para>
+
+      <para>
         Example:
         <screen>
 # gnt-node add node5.example.com
index 01614e0..47db0b7 100755 (executable)
@@ -263,6 +263,9 @@ def main():
     if qa_config.TestEnabled('tags'):
       RunTest(qa_tags.TestNodeTags, pnode)
 
+    if qa_config.TestEnabled('node-readd'):
+      RunTest(qa_node.TestNodeReadd, pnode)
+
     if qa_config.TestEnabled('instance-add-plain-disk'):
       instance = RunTest(qa_instance.TestInstanceAddWithPlainDisk, pnode)
       RunCommonInstanceTests(instance)
index c4aedf9..4138ea2 100644 (file)
@@ -41,6 +41,7 @@ tests:
 
   node-info: True
   node-volumes: True
+  node-readd: True
 
   # This test needs at least three nodes
   node-evacuate: False
index 26ecceb..20840bd 100644 (file)
@@ -29,15 +29,19 @@ from qa_utils import AssertEqual, StartSSH
 
 
 @qa_utils.DefineHook('node-add')
-def _NodeAdd(node):
+def _NodeAdd(node, readd=False):
   master = qa_config.GetMasterNode()
 
-  if node.get('_added', False):
+  if not readd and node.get('_added', False):
     raise qa_error.Error("Node %s already in cluster" % node['primary'])
+  elif readd and not node.get('_added', False):
+    raise qa_error.Error("Node not yet %s in cluster" % node['primary'])
 
   cmd = ['gnt-node', 'add']
   if node.get('secondary', None):
     cmd.append('--secondary-ip=%s' % node['secondary'])
+  if readd:
+    cmd.append('--readd')
   cmd.append(node['primary'])
   AssertEqual(StartSSH(master['primary'],
                        utils.ShellQuoteArgs(cmd)).wait(), 0)
@@ -60,7 +64,7 @@ def TestNodeAddAll():
   master = qa_config.GetMasterNode()
   for node in qa_config.get('nodes'):
     if node != master:
-      _NodeAdd(node)
+      _NodeAdd(node, readd=False)
 
 
 def TestNodeRemoveAll():
@@ -71,6 +75,12 @@ def TestNodeRemoveAll():
       _NodeRemove(node)
 
 
+@qa_utils.DefineHook('node-readd')
+def TestNodeReadd(node):
+  """gnt-node add --readd"""
+  _NodeAdd(node, readd=True)
+
+
 @qa_utils.DefineHook('node-info')
 def TestNodeInfo():
   """gnt-node info"""
index 442d8f2..c8082c9 100755 (executable)
@@ -1,7 +1,7 @@
 #!/usr/bin/python
 #
 
-# Copyright (C) 2006, 2007 Google Inc.
+# 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
@@ -36,7 +36,8 @@ def AddNode(opts, args):
     "Performing this operation is going to replace the ssh daemon keypair\n"
     "on the target machine (%s) with the ones of the current one\n"
     "and grant full intra-cluster ssh root access to/from it\n" % args[0])
-  op = opcodes.OpAddNode(node_name=args[0], secondary_ip=opts.secondary_ip)
+  op = opcodes.OpAddNode(node_name=args[0], secondary_ip=opts.secondary_ip,
+                         readd=opts.readd)
   SubmitOpCode(op)
 
 
@@ -279,7 +280,11 @@ commands = {
           [DEBUG_OPT,
            make_option("-s", "--secondary-ip", dest="secondary_ip",
                        help="Specify the secondary ip for the node",
-                       metavar="ADDRESS", default=None),],
+                       metavar="ADDRESS", default=None),
+           make_option("--readd", dest="readd",
+                       default=False, action="store_true",
+                       help="Readd old node after replacing it"),
+           ],
           "[-s ip] <node_name>", "Add a node to the cluster"),
   'evacuate': (EvacuateNode, ARGS_FIXED(2),
                [DEBUG_OPT, FORCE_OPT],