Commit 07bd8a51 authored by Iustin Pop's avatar Iustin Pop
Browse files

Implement cluster rename operation

This patch adds a new OpCode (and corresponding LU) that implements the
cluster rename functionality.

This is done by shutting down the master role, making the needed sstore
modifications and distributing the changed files to all nodes, and then
re-enabling the master role.

The modification to the man page of gnt-cluster also moves the section
on gnt-cluster destroy in order to correct alphabetical ordering.

Reviewed-by: imsnah
parent 4ab0b9e3
......@@ -944,6 +944,87 @@ class LUVerifyCluster(NoHooksLU):
return int(bad)
class LURenameCluster(LogicalUnit):
"""Rename the cluster.
"""
HPATH = "cluster-rename"
HTYPE = constants.HTYPE_CLUSTER
_OP_REQP = ["name"]
def BuildHooksEnv(self):
"""Build hooks env.
"""
env = {
"NEW_NAME": self.op.name,
}
mn = self.sstore.GetMasterNode()
return env, [mn], [mn]
def CheckPrereq(self):
"""Verify that the passed name is a valid one.
"""
hostname = utils.LookupHostname(self.op.name)
if not hostname:
raise errors.OpPrereqError("Cannot resolve the new cluster name ('%s')" %
self.op.name)
new_name = hostname["hostname"]
self.ip = new_ip = hostname["ip"]
old_name = self.sstore.GetClusterName()
old_ip = self.sstore.GetMasterIP()
if new_name == old_name and new_ip == old_ip:
raise errors.OpPrereqError("Neither the name nor the IP address of the"
" cluster has changed")
if new_ip != old_ip:
result = utils.RunCmd(["fping", "-q", new_ip])
if not result.failed:
raise errors.OpPrereqError("The given cluster IP address (%s) is"
" reachable on the network. Aborting." %
new_ip)
self.op.name = new_name
def Exec(self, feedback_fn):
"""Rename the cluster.
"""
clustername = self.op.name
ip = self.ip
ss = self.sstore
# shutdown the master IP
master = ss.GetMasterNode()
if not rpc.call_node_stop_master(master):
raise errors.OpExecError("Could not disable the master role")
try:
# modify the sstore
ss.SetKey(ss.SS_MASTER_IP, ip)
ss.SetKey(ss.SS_CLUSTER_NAME, clustername)
# Distribute updated ss config to all nodes
myself = self.cfg.GetNodeInfo(master)
dist_nodes = self.cfg.GetNodeList()
if myself.name in dist_nodes:
dist_nodes.remove(myself.name)
logger.Debug("Copying updated ssconf data to all nodes")
for keyname in [ss.SS_CLUSTER_NAME, ss.SS_MASTER_IP]:
fname = ss.KeyToFilename(keyname)
result = rpc.call_upload_file(dist_nodes, fname)
for to_node in dist_nodes:
if not result[to_node]:
logger.Error("copy of file %s to node %s failed" %
(fname, to_node))
finally:
if not rpc.call_node_start_master(master):
logger.Error("Could not re-enable the master role on the master,\n"
"please restart manually.")
def _WaitForSync(cfgw, instance, oneshot=False, unlock=False):
"""Sleep and poll for an instance's disk to sync.
......
......@@ -49,6 +49,7 @@ class Processor(object):
opcodes.OpVerifyCluster: cmdlib.LUVerifyCluster,
opcodes.OpMasterFailover: cmdlib.LUMasterFailover,
opcodes.OpDumpClusterConfig: cmdlib.LUDumpClusterConfig,
opcodes.OpRenameCluster: cmdlib.LURenameCluster,
# node lu
opcodes.OpAddNode: cmdlib.LUAddNode,
opcodes.OpQueryNodes: cmdlib.LUQueryNodes,
......
......@@ -98,6 +98,14 @@ class OpDumpClusterConfig(OpCode):
__slots__ = []
class OpRenameCluster(OpCode):
"""Rename the cluster."""
OP_ID = "OP_CLUSTER_RENAME"
__slots__ = ["name"]
# node opcodes
class OpRemoveNode(OpCode):
"""Remove a node."""
OP_ID = "OP_NODE_REMOVE"
......
......@@ -110,6 +110,15 @@
current node to the two named nodes.
</para>
<cmdsynopsis>
<command>destroy</command>
</cmdsynopsis>
<para>
Remove all configuration files related to the cluster, so that a
<command>gnt-cluster init</command> can be done again afterwards.
</para>
<cmdsynopsis>
<command>getmaster</command>
</cmdsynopsis>
......@@ -178,12 +187,22 @@
</para>
<cmdsynopsis>
<command>destroy</command>
<command>rename</command>
<arg>-f</arg>
<arg choice="req"><replaceable>name</replaceable></arg>
</cmdsynopsis>
<para>
Remove all configuration files related to the cluster, so that a
<command>gnt-cluster init</command> can be done again afterwards.
Renames the cluster and in the process updates the master IP
address to the one the new name resolves to. At least one of
either the name or the IP address must be different, otherwise
the operation will be aborted.
</para>
<para>
Note that since this command can be dangerous (especially when
run over SSH), the command will require confirmation unless run
with the <option>-f</option> option.
</para>
<cmdsynopsis>
......
......@@ -64,6 +64,28 @@ def DestroyCluster(opts, args):
return 0
def RenameCluster(opts, args):
"""Rename the cluster.
Args:
opts - class with options as members, we use force only
args - list of arguments, expected to be [new_name]
"""
name = args[0]
if not opts.force:
usertext = ("This will rename the cluster to '%s'. If you are connected"
" over the network to the cluster name, the operation is very"
" dangerous as the IP address will be removed from the node"
" and the change may not go through. Continue?") % name
if not opts._ask_user(usertext):
return 1
op = opcodes.OpRenameCluster(name=name)
SubmitOpCode(op)
return 0
def ShowClusterVersion(opts, args):
"""Write version of ganeti software to the standard output.
......@@ -222,6 +244,9 @@ commands = {
action="store_true"),
],
"", "Destroy cluster"),
'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
"<new_name>",
"Renames the cluster"),
'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT],
"", "Does a check on the cluster configuration"),
'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
......
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment