42 |
42 |
from ganeti import utils
|
43 |
43 |
|
44 |
44 |
|
|
45 |
_GROUPS_MERGE = "merge"
|
|
46 |
_GROUPS_RENAME = "rename"
|
|
47 |
_CLUSTERMERGE_ECID = "clustermerge-ecid"
|
|
48 |
|
45 |
49 |
PAUSE_PERIOD_OPT = cli.cli_option("-p", "--watcher-pause-period", default=1800,
|
46 |
50 |
action="store", type="int",
|
47 |
51 |
dest="pause_period",
|
48 |
52 |
help=("Amount of time in seconds watcher"
|
49 |
53 |
" should be suspended from running"))
|
50 |
|
_CLUSTERMERGE_ECID = "clustermerge-ecid"
|
|
54 |
GROUPS_OPT = cli.cli_option("--groups", default=None, metavar="STRATEGY",
|
|
55 |
choices=(_GROUPS_MERGE, _GROUPS_RENAME), dest="groups",
|
|
56 |
help=("How to handle groups that have the"
|
|
57 |
" same name (One of: %s/%s)" %
|
|
58 |
(_GROUPS_MERGE, _GROUPS_RENAME)))
|
51 |
59 |
|
52 |
60 |
|
53 |
61 |
def Flatten(unflattened_list):
|
... | ... | |
92 |
100 |
"""Handling the merge.
|
93 |
101 |
|
94 |
102 |
"""
|
95 |
|
def __init__(self, clusters, pause_period):
|
|
103 |
def __init__(self, clusters, pause_period, groups):
|
96 |
104 |
"""Initialize object with sane defaults and infos required.
|
97 |
105 |
|
98 |
106 |
@param clusters: The list of clusters to merge in
|
99 |
107 |
@param pause_period: The time watcher shall be disabled for
|
|
108 |
@param groups: How to handle group conflicts
|
100 |
109 |
|
101 |
110 |
"""
|
102 |
111 |
self.merger_data = []
|
... | ... | |
105 |
114 |
self.work_dir = tempfile.mkdtemp(suffix="cluster-merger")
|
106 |
115 |
(self.cluster_name, ) = cli.GetClient().QueryConfigValues(["cluster_name"])
|
107 |
116 |
self.ssh_runner = ssh.SshRunner(self.cluster_name)
|
|
117 |
self.groups = groups
|
108 |
118 |
|
109 |
119 |
def Setup(self):
|
110 |
120 |
"""Sets up our end so we can do the merger.
|
... | ... | |
304 |
314 |
ConfigWriter.AddNodeGroup takes care of making sure there are no conflicts.
|
305 |
315 |
"""
|
306 |
316 |
# pylint: disable-msg=R0201
|
307 |
|
for grp in other_config.GetAllNodeGroupsInfo().values():
|
|
317 |
logging.info("Node group conflict strategy: %s" % self.groups)
|
|
318 |
|
|
319 |
my_grps = my_config.GetAllNodeGroupsInfo().values()
|
|
320 |
other_grps = other_config.GetAllNodeGroupsInfo().values()
|
|
321 |
|
|
322 |
# Check for node group naming conflicts:
|
|
323 |
conflicts = []
|
|
324 |
for other_grp in other_grps:
|
|
325 |
for my_grp in my_grps:
|
|
326 |
if other_grp.name == my_grp.name:
|
|
327 |
conflicts.append(other_grp)
|
|
328 |
|
|
329 |
if conflicts:
|
|
330 |
conflict_names = utils.CommaJoin([g.name for g in conflicts])
|
|
331 |
logging.info("Node groups in both local and remote cluster: %s" %
|
|
332 |
conflict_names)
|
|
333 |
|
|
334 |
# User hasn't specified how to handle conflicts
|
|
335 |
if not self.groups:
|
|
336 |
raise errors.CommandError("The following node group(s) are in both"
|
|
337 |
" clusters, and no merge strategy has been"
|
|
338 |
" supplied (see the --groups option): %s" %
|
|
339 |
conflict_names)
|
|
340 |
|
|
341 |
# User wants to rename conflicts
|
|
342 |
if self.groups == _GROUPS_RENAME:
|
|
343 |
for grp in conflicts:
|
|
344 |
new_name = "%s-%s" % (grp.name, other_config.GetClusterName())
|
|
345 |
logging.info("Renaming remote node group from %s to %s"
|
|
346 |
" to resolve conflict" % (grp.name, new_name))
|
|
347 |
grp.name = new_name
|
|
348 |
|
|
349 |
for grp in other_grps:
|
308 |
350 |
#TODO: handle node group conflicts
|
309 |
351 |
my_config.AddNodeGroup(grp, _CLUSTERMERGE_ECID)
|
310 |
352 |
|
... | ... | |
483 |
525 |
"""
|
484 |
526 |
program = os.path.basename(sys.argv[0])
|
485 |
527 |
|
486 |
|
parser = optparse.OptionParser(usage=("%prog [--debug|--verbose]"
|
|
528 |
parser = optparse.OptionParser(usage=("%%prog [--debug|--verbose]"
|
487 |
529 |
" [--watcher-pause-period SECONDS]"
|
488 |
|
" <cluster> <cluster...>"),
|
|
530 |
" [--groups [%s|%s]]"
|
|
531 |
" <cluster> [<cluster...>]" %
|
|
532 |
(_GROUPS_MERGE, _GROUPS_RENAME)),
|
489 |
533 |
prog=program)
|
490 |
534 |
parser.add_option(cli.DEBUG_OPT)
|
491 |
535 |
parser.add_option(cli.VERBOSE_OPT)
|
492 |
536 |
parser.add_option(PAUSE_PERIOD_OPT)
|
|
537 |
parser.add_option(GROUPS_OPT)
|
493 |
538 |
|
494 |
539 |
(options, args) = parser.parse_args()
|
495 |
540 |
|
... | ... | |
498 |
543 |
if not args:
|
499 |
544 |
parser.error("No clusters specified")
|
500 |
545 |
|
501 |
|
cluster_merger = Merger(utils.UniqueSequence(args), options.pause_period)
|
|
546 |
cluster_merger = Merger(utils.UniqueSequence(args), options.pause_period,
|
|
547 |
options.groups)
|
502 |
548 |
try:
|
503 |
549 |
try:
|
504 |
550 |
cluster_merger.Setup()
|