Commit 1a615be0 authored by Stephen Shirley's avatar Stephen Shirley
Browse files

Add option to rename groups on conflict

Signed-off-by: default avatarStephen Shirley <>
Reviewed-by: default avatarMichael Hanselmann <>
parent fecbc0b6
......@@ -23,8 +23,8 @@ clusters into.
The usage of ``cluster-merge`` is as follows::
cluster-merge [--debug|--verbose] [--watcher-pause-period SECONDS] <cluster> \
cluster-merge [--debug|--verbose] [--watcher-pause-period SECONDS] \
[--groups [merge|rename]] <cluster> [<cluster...>]
You can provide multiple clusters. The tool will then go over every
cluster in serial and perform the steps to merge it into the invoking
......@@ -39,6 +39,15 @@ These options can be used to control the behaviour of the tool:
Define the period of time in seconds the watcher shall be disabled,
default is 1800 seconds (30 minutes).
This option controls how ``cluster-merge`` handles duplicate node
group names on the merging clusters. If ``merge`` is specified then
all node groups with the same name will be merged into one. If
``rename`` is specified, then conflicting node groups on the remove
clusters will have their cluster name appended to the group name. If
this option is not speicifed, then ``cluster-merge`` will refuse to
continue if it finds conflicting group names, otherwise it will
proceed as normal.
......@@ -42,12 +42,20 @@ from ganeti import ssh
from ganeti import utils
_GROUPS_MERGE = "merge"
_GROUPS_RENAME = "rename"
_CLUSTERMERGE_ECID = "clustermerge-ecid"
PAUSE_PERIOD_OPT = cli.cli_option("-p", "--watcher-pause-period", default=1800,
action="store", type="int",
help=("Amount of time in seconds watcher"
" should be suspended from running"))
_CLUSTERMERGE_ECID = "clustermerge-ecid"
GROUPS_OPT = cli.cli_option("--groups", default=None, metavar="STRATEGY",
choices=(_GROUPS_MERGE, _GROUPS_RENAME), dest="groups",
help=("How to handle groups that have the"
" same name (One of: %s/%s)" %
def Flatten(unflattened_list):
......@@ -92,11 +100,12 @@ class Merger(object):
"""Handling the merge.
def __init__(self, clusters, pause_period):
def __init__(self, clusters, pause_period, groups):
"""Initialize object with sane defaults and infos required.
@param clusters: The list of clusters to merge in
@param pause_period: The time watcher shall be disabled for
@param groups: How to handle group conflicts
self.merger_data = []
......@@ -105,6 +114,7 @@ class Merger(object):
self.work_dir = tempfile.mkdtemp(suffix="cluster-merger")
(self.cluster_name, ) = cli.GetClient().QueryConfigValues(["cluster_name"])
self.ssh_runner = ssh.SshRunner(self.cluster_name)
self.groups = groups
def Setup(self):
"""Sets up our end so we can do the merger.
......@@ -304,7 +314,39 @@ class Merger(object):
ConfigWriter.AddNodeGroup takes care of making sure there are no conflicts.
# pylint: disable-msg=R0201
for grp in other_config.GetAllNodeGroupsInfo().values():"Node group conflict strategy: %s" % self.groups)
my_grps = my_config.GetAllNodeGroupsInfo().values()
other_grps = other_config.GetAllNodeGroupsInfo().values()
# Check for node group naming conflicts:
conflicts = []
for other_grp in other_grps:
for my_grp in my_grps:
if ==
if conflicts:
conflict_names = utils.CommaJoin([ for g in conflicts])"Node groups in both local and remote cluster: %s" %
# User hasn't specified how to handle conflicts
if not self.groups:
raise errors.CommandError("The following node group(s) are in both"
" clusters, and no merge strategy has been"
" supplied (see the --groups option): %s" %
# User wants to rename conflicts
if self.groups == _GROUPS_RENAME:
for grp in conflicts:
new_name = "%s-%s" % (, other_config.GetClusterName())"Renaming remote node group from %s to %s"
" to resolve conflict" % (, new_name)) = new_name
for grp in other_grps:
#TODO: handle node group conflicts
my_config.AddNodeGroup(grp, _CLUSTERMERGE_ECID)
......@@ -483,13 +525,16 @@ def main():
program = os.path.basename(sys.argv[0])
parser = optparse.OptionParser(usage=("%prog [--debug|--verbose]"
parser = optparse.OptionParser(usage=("%%prog [--debug|--verbose]"
" [--watcher-pause-period SECONDS]"
" <cluster> <cluster...>"),
" [--groups [%s|%s]]"
" <cluster> [<cluster...>]" %
(options, args) = parser.parse_args()
......@@ -498,7 +543,8 @@ def main():
if not args:
parser.error("No clusters specified")
cluster_merger = Merger(utils.UniqueSequence(args), options.pause_period)
cluster_merger = Merger(utils.UniqueSequence(args), options.pause_period,
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