diff --git a/lib/cmdlib.py b/lib/cmdlib.py index 5d18c2f7b908c66e0d2c796ee88af8c0e68b7f8f..873f216db95eda146ce9c1134dbe951db2dfbbfd 100644 --- a/lib/cmdlib.py +++ b/lib/cmdlib.py @@ -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): diff --git a/lib/opcodes.py b/lib/opcodes.py index 117757f9f2896889d858a41c7c092db3f4003f6b..c629e0c91a43ad4e781eabbc3cc2afaf11c0a98e 100644 --- a/lib/opcodes.py +++ b/lib/opcodes.py @@ -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): diff --git a/man/gnt-node.sgml b/man/gnt-node.sgml index 98ec3437a41015b78820739db56bfc86d78d6a6d..71f9dbfc3130e1052ee80c5a78fe7ac12a97b9a9 100644 --- a/man/gnt-node.sgml +++ b/man/gnt-node.sgml @@ -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> @@ -91,6 +93,11 @@ informations. </para> + <para> + In case you're readding a node after hardware failure, you + can use the <option>--readd</option> parameter. + </para> + <para> Example: <screen> diff --git a/qa/ganeti-qa.py b/qa/ganeti-qa.py index 01614e0246cc2ad5c21d36670b4219a61fc6abe6..47db0b79006901abb4297765125833d2c6504276 100755 --- a/qa/ganeti-qa.py +++ b/qa/ganeti-qa.py @@ -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) diff --git a/qa/qa-sample.yaml b/qa/qa-sample.yaml index c4aedf9843413ed132a64856cd874d8b2c10580e..4138ea242e7ad3402b1a79ef745466b09efe06f0 100644 --- a/qa/qa-sample.yaml +++ b/qa/qa-sample.yaml @@ -41,6 +41,7 @@ tests: node-info: True node-volumes: True + node-readd: True # This test needs at least three nodes node-evacuate: False diff --git a/qa/qa_node.py b/qa/qa_node.py index 26eccebceee635bbc71c5483cba0e62a9530e1bd..20840bde1c859a1accf13909d788579df5da481a 100644 --- a/qa/qa_node.py +++ b/qa/qa_node.py @@ -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""" diff --git a/scripts/gnt-node b/scripts/gnt-node index 442d8f232f5d5767a5de2ea0b11b755285e2bd2e..c8082c9019bbae34d1c3eab2d8e7954999eac623 100755 --- a/scripts/gnt-node +++ b/scripts/gnt-node @@ -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],