Add cluster-init --no-etc-hosts parameter
[ganeti-local] / scripts / gnt-cluster
1 #!/usr/bin/python
2 #
3
4 # Copyright (C) 2006, 2007 Google Inc.
5 #
6 # This program is free software; you can redistribute it and/or modify
7 # it under the terms of the GNU General Public License as published by
8 # the Free Software Foundation; either version 2 of the License, or
9 # (at your option) any later version.
10 #
11 # This program is distributed in the hope that it will be useful, but
12 # WITHOUT ANY WARRANTY; without even the implied warranty of
13 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14 # General Public License for more details.
15 #
16 # You should have received a copy of the GNU General Public License
17 # along with this program; if not, write to the Free Software
18 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19 # 02110-1301, USA.
20
21
22 # pylint: disable-msg=W0401,W0614
23 # W0401: Wildcard import ganeti.cli
24 # W0614: Unused import %s from wildcard import (since we need cli)
25
26 import sys
27 from optparse import make_option
28 import os.path
29
30 from ganeti.cli import *
31 from ganeti import opcodes
32 from ganeti import constants
33 from ganeti import errors
34 from ganeti import utils
35 from ganeti import bootstrap
36 from ganeti import ssh
37
38
39 @UsesRPC
40 def InitCluster(opts, args):
41   """Initialize the cluster.
42
43   @param opts: the command line options selected by the user
44   @type args: list
45   @param args: should contain only one element, the desired
46       cluster name
47   @rtype: int
48   @return: the desired exit code
49
50   """
51   if not opts.lvm_storage and opts.vg_name:
52     ToStderr("Options --no-lvm-storage and --vg-name conflict.")
53     return 1
54
55   vg_name = opts.vg_name
56   if opts.lvm_storage and not opts.vg_name:
57     vg_name = constants.DEFAULT_VG
58
59   hvlist = opts.enabled_hypervisors
60   if hvlist is not None:
61     hvlist = hvlist.split(",")
62   else:
63     hvlist = [opts.default_hypervisor]
64
65   # avoid an impossible situation
66   if opts.default_hypervisor not in hvlist:
67     ToStderr("The default hypervisor requested (%s) is not"
68              " within the enabled hypervisor list (%s)" %
69              (opts.default_hypervisor, hvlist))
70     return 1
71
72   hvparams = dict(opts.hvparams)
73
74   beparams = opts.beparams
75   # check for invalid parameters
76   for parameter in beparams:
77     if parameter not in constants.BES_PARAMETERS:
78       ToStderr("Invalid backend parameter: %s", parameter)
79       return 1
80
81   # prepare beparams dict
82   for parameter in constants.BES_PARAMETERS:
83     if parameter not in beparams:
84       beparams[parameter] = constants.BEC_DEFAULTS[parameter]
85   utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
86
87   # prepare hvparams dict
88   for hv in constants.HYPER_TYPES:
89     if hv not in hvparams:
90       hvparams[hv] = {}
91     for parameter in constants.HVC_DEFAULTS[hv]:
92       if parameter not in hvparams[hv]:
93         hvparams[hv][parameter] = constants.HVC_DEFAULTS[hv][parameter]
94     utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
95
96   bootstrap.InitCluster(cluster_name=args[0],
97                         secondary_ip=opts.secondary_ip,
98                         vg_name=vg_name,
99                         mac_prefix=opts.mac_prefix,
100                         def_bridge=opts.def_bridge,
101                         master_netdev=opts.master_netdev,
102                         file_storage_dir=opts.file_storage_dir,
103                         enabled_hypervisors=hvlist,
104                         default_hypervisor=opts.default_hypervisor,
105                         hvparams=hvparams,
106                         beparams=beparams,
107                         candidate_pool_size=opts.candidate_pool_size,
108                         modify_etc_hosts=opts.modify_etc_hosts,
109                         )
110   return 0
111
112
113 @UsesRPC
114 def DestroyCluster(opts, args):
115   """Destroy the cluster.
116
117   @param opts: the command line options selected by the user
118   @type args: list
119   @param args: should be an empty list
120   @rtype: int
121   @return: the desired exit code
122
123   """
124   if not opts.yes_do_it:
125     ToStderr("Destroying a cluster is irreversible. If you really want"
126              " destroy this cluster, supply the --yes-do-it option.")
127     return 1
128
129   op = opcodes.OpDestroyCluster()
130   master = SubmitOpCode(op)
131   # if we reached this, the opcode didn't fail; we can proceed to
132   # shutdown all the daemons
133   bootstrap.FinalizeClusterDestroy(master)
134   return 0
135
136
137 def RenameCluster(opts, args):
138   """Rename the cluster.
139
140   @param opts: the command line options selected by the user
141   @type args: list
142   @param args: should contain only one element, the new cluster name
143   @rtype: int
144   @return: the desired exit code
145
146   """
147   name = args[0]
148   if not opts.force:
149     usertext = ("This will rename the cluster to '%s'. If you are connected"
150                 " over the network to the cluster name, the operation is very"
151                 " dangerous as the IP address will be removed from the node"
152                 " and the change may not go through. Continue?") % name
153     if not AskUser(usertext):
154       return 1
155
156   op = opcodes.OpRenameCluster(name=name)
157   SubmitOpCode(op)
158   return 0
159
160
161 def RedistributeConfig(opts, args):
162   """Forces push of the cluster configuration.
163
164   @param opts: the command line options selected by the user
165   @type args: list
166   @param args: empty list
167   @rtype: int
168   @return: the desired exit code
169
170   """
171   op = opcodes.OpRedistributeConfig()
172   SubmitOrSend(op, opts)
173   return 0
174
175
176 def ShowClusterVersion(opts, args):
177   """Write version of ganeti software to the standard output.
178
179   @param opts: the command line options selected by the user
180   @type args: list
181   @param args: should be an empty list
182   @rtype: int
183   @return: the desired exit code
184
185   """
186   cl = GetClient()
187   result = cl.QueryClusterInfo()
188   ToStdout("Software version: %s", result["software_version"])
189   ToStdout("Internode protocol: %s", result["protocol_version"])
190   ToStdout("Configuration format: %s", result["config_version"])
191   ToStdout("OS api version: %s", result["os_api_version"])
192   ToStdout("Export interface: %s", result["export_version"])
193   return 0
194
195
196 def ShowClusterMaster(opts, args):
197   """Write name of master node to the standard output.
198
199   @param opts: the command line options selected by the user
200   @type args: list
201   @param args: should be an empty list
202   @rtype: int
203   @return: the desired exit code
204
205   """
206   master = bootstrap.GetMaster()
207   ToStdout(master)
208   return 0
209
210
211 def ShowClusterConfig(opts, args):
212   """Shows cluster information.
213
214   @param opts: the command line options selected by the user
215   @type args: list
216   @param args: should be an empty list
217   @rtype: int
218   @return: the desired exit code
219
220   """
221   cl = GetClient()
222   result = cl.QueryClusterInfo()
223
224   ToStdout("Cluster name: %s", result["name"])
225
226   ToStdout("Master node: %s", result["master"])
227
228   ToStdout("Architecture (this node): %s (%s)",
229            result["architecture"][0], result["architecture"][1])
230
231   ToStdout("Default hypervisor: %s", result["default_hypervisor"])
232   ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
233
234   ToStdout("Hypervisor parameters:")
235   for hv_name, hv_dict in result["hvparams"].items():
236     ToStdout("  - %s:", hv_name)
237     for item, val in hv_dict.iteritems():
238       ToStdout("      %s: %s", item, val)
239
240   ToStdout("Cluster parameters:")
241   ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
242   ToStdout("  - master netdev: %s", result["master_netdev"])
243   ToStdout("  - default bridge: %s", result["default_bridge"])
244   ToStdout("  - lvm volume group: %s", result["volume_group_name"])
245   ToStdout("  - file storage path: %s", result["file_storage_dir"])
246
247   ToStdout("Default instance parameters:")
248   for gr_name, gr_dict in result["beparams"].items():
249     ToStdout("  - %s:", gr_name)
250     for item, val in gr_dict.iteritems():
251       ToStdout("      %s: %s", item, val)
252
253   return 0
254
255
256 def ClusterCopyFile(opts, args):
257   """Copy a file from master to some nodes.
258
259   @param opts: the command line options selected by the user
260   @type args: list
261   @param args: should contain only one element, the path of
262       the file to be copied
263   @rtype: int
264   @return: the desired exit code
265
266   """
267   filename = args[0]
268   if not os.path.exists(filename):
269     raise errors.OpPrereqError("No such filename '%s'" % filename)
270
271   cl = GetClient()
272
273   myname = utils.HostInfo().name
274
275   cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
276
277   results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
278   results = [name for name in results if name != myname]
279
280   srun = ssh.SshRunner(cluster_name=cluster_name)
281   for node in results:
282     if not srun.CopyFileToNode(node, filename):
283       ToStderr("Copy of file %s to node %s failed", filename, node)
284
285   return 0
286
287
288 def RunClusterCommand(opts, args):
289   """Run a command on some nodes.
290
291   @param opts: the command line options selected by the user
292   @type args: list
293   @param args: should contain the command to be run and its arguments
294   @rtype: int
295   @return: the desired exit code
296
297   """
298   cl = GetClient()
299
300   command = " ".join(args)
301
302   nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
303
304   cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
305                                                     "master_node"])
306
307   srun = ssh.SshRunner(cluster_name=cluster_name)
308
309   # Make sure master node is at list end
310   if master_node in nodes:
311     nodes.remove(master_node)
312     nodes.append(master_node)
313
314   for name in nodes:
315     result = srun.Run(name, "root", command)
316     ToStdout("------------------------------------------------")
317     ToStdout("node: %s", name)
318     ToStdout("%s", result.output)
319     ToStdout("return code = %s", result.exit_code)
320
321   return 0
322
323
324 def VerifyCluster(opts, args):
325   """Verify integrity of cluster, performing various test on nodes.
326
327   @param opts: the command line options selected by the user
328   @type args: list
329   @param args: should be an empty list
330   @rtype: int
331   @return: the desired exit code
332
333   """
334   skip_checks = []
335   if opts.skip_nplusone_mem:
336     skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
337   op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
338   if SubmitOpCode(op):
339     return 0
340   else:
341     return 1
342
343
344 def VerifyDisks(opts, args):
345   """Verify integrity of cluster disks.
346
347   @param opts: the command line options selected by the user
348   @type args: list
349   @param args: should be an empty list
350   @rtype: int
351   @return: the desired exit code
352
353   """
354   op = opcodes.OpVerifyDisks()
355   result = SubmitOpCode(op)
356   if not isinstance(result, (list, tuple)) or len(result) != 4:
357     raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
358
359   nodes, nlvm, instances, missing = result
360
361   if nodes:
362     ToStdout("Nodes unreachable or with bad data:")
363     for name in nodes:
364       ToStdout("\t%s", name)
365   retcode = constants.EXIT_SUCCESS
366
367   if nlvm:
368     for node, text in nlvm.iteritems():
369       ToStdout("Error on node %s: LVM error: %s",
370                node, utils.SafeEncode(text[-400:]))
371       retcode |= 1
372       ToStdout("You need to fix these nodes first before fixing instances")
373
374   if instances:
375     for iname in instances:
376       if iname in missing:
377         continue
378       op = opcodes.OpActivateInstanceDisks(instance_name=iname)
379       try:
380         ToStdout("Activating disks for instance '%s'", iname)
381         SubmitOpCode(op)
382       except errors.GenericError, err:
383         nret, msg = FormatError(err)
384         retcode |= nret
385         ToStderr("Error activating disks for instance %s: %s", iname, msg)
386
387   if missing:
388     for iname, ival in missing.iteritems():
389       all_missing = utils.all(ival, lambda x: x[0] in nlvm)
390       if all_missing:
391         ToStdout("Instance %s cannot be verified as it lives on"
392                  " broken nodes", iname)
393       else:
394         ToStdout("Instance %s has missing logical volumes:", iname)
395         ival.sort()
396         for node, vol in ival:
397           if node in nlvm:
398             ToStdout("\tbroken node %s /dev/xenvg/%s", node, vol)
399           else:
400             ToStdout("\t%s /dev/xenvg/%s", node, vol)
401     ToStdout("You need to run replace_disks for all the above"
402            " instances, if this message persist after fixing nodes.")
403     retcode |= 1
404
405   return retcode
406
407
408 def RepairDiskSizes(opts, args):
409   """Verify sizes of cluster disks.
410
411   @param opts: the command line options selected by the user
412   @type args: list
413   @param args: optional list of instances to restrict check to
414   @rtype: int
415   @return: the desired exit code
416
417   """
418   op = opcodes.OpRepairDiskSizes(instances=args)
419   SubmitOpCode(op)
420
421
422 @UsesRPC
423 def MasterFailover(opts, args):
424   """Failover the master node.
425
426   This command, when run on a non-master node, will cause the current
427   master to cease being master, and the non-master to become new
428   master.
429
430   @param opts: the command line options selected by the user
431   @type args: list
432   @param args: should be an empty list
433   @rtype: int
434   @return: the desired exit code
435
436   """
437   if opts.no_voting:
438     usertext = ("This will perform the failover even if most other nodes"
439                 " are down, or if this node is outdated. This is dangerous"
440                 " as it can lead to a non-consistent cluster. Check the"
441                 " gnt-cluster(8) man page before proceeding. Continue?")
442     if not AskUser(usertext):
443       return 1
444
445   return bootstrap.MasterFailover(no_voting=opts.no_voting)
446
447
448 def SearchTags(opts, args):
449   """Searches the tags on all the cluster.
450
451   @param opts: the command line options selected by the user
452   @type args: list
453   @param args: should contain only one element, the tag pattern
454   @rtype: int
455   @return: the desired exit code
456
457   """
458   op = opcodes.OpSearchTags(pattern=args[0])
459   result = SubmitOpCode(op)
460   if not result:
461     return 1
462   result = list(result)
463   result.sort()
464   for path, tag in result:
465     ToStdout("%s %s", path, tag)
466
467
468 def SetClusterParams(opts, args):
469   """Modify the cluster.
470
471   @param opts: the command line options selected by the user
472   @type args: list
473   @param args: should be an empty list
474   @rtype: int
475   @return: the desired exit code
476
477   """
478   if not (not opts.lvm_storage or opts.vg_name or
479           opts.enabled_hypervisors or opts.hvparams or
480           opts.beparams or opts.candidate_pool_size is not None):
481     ToStderr("Please give at least one of the parameters.")
482     return 1
483
484   vg_name = opts.vg_name
485   if not opts.lvm_storage and opts.vg_name:
486     ToStdout("Options --no-lvm-storage and --vg-name conflict.")
487     return 1
488   elif not opts.lvm_storage:
489     vg_name = ''
490
491   hvlist = opts.enabled_hypervisors
492   if hvlist is not None:
493     hvlist = hvlist.split(",")
494
495   # a list of (name, dict) we can pass directly to dict() (or [])
496   hvparams = dict(opts.hvparams)
497   for hv, hv_params in hvparams.iteritems():
498     utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
499
500   beparams = opts.beparams
501   utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
502
503   op = opcodes.OpSetClusterParams(vg_name=vg_name,
504                                   enabled_hypervisors=hvlist,
505                                   hvparams=hvparams,
506                                   beparams=beparams,
507                                   candidate_pool_size=opts.candidate_pool_size)
508   SubmitOpCode(op)
509   return 0
510
511
512 def QueueOps(opts, args):
513   """Queue operations.
514
515   @param opts: the command line options selected by the user
516   @type args: list
517   @param args: should contain only one element, the subcommand
518   @rtype: int
519   @return: the desired exit code
520
521   """
522   command = args[0]
523   client = GetClient()
524   if command in ("drain", "undrain"):
525     drain_flag = command == "drain"
526     client.SetQueueDrainFlag(drain_flag)
527   elif command == "info":
528     result = client.QueryConfigValues(["drain_flag"])
529     if result[0]:
530       val = "set"
531     else:
532       val = "unset"
533     ToStdout("The drain flag is %s" % val)
534   else:
535     raise errors.OpPrereqError("Command '%s' is not valid." % command)
536
537   return 0
538
539 # this is an option common to more than one command, so we declare
540 # it here and reuse it
541 node_option = make_option("-n", "--node", action="append", dest="nodes",
542                           help="Node to copy to (if not given, all nodes),"
543                                " can be given multiple times",
544                           metavar="<node>", default=[])
545
546 commands = {
547   'init': (InitCluster, ARGS_ONE,
548            [DEBUG_OPT,
549             make_option("-s", "--secondary-ip", dest="secondary_ip",
550                         help="Specify the secondary ip for this node;"
551                         " if given, the entire cluster must have secondary"
552                         " addresses",
553                         metavar="ADDRESS", default=None),
554             make_option("-m", "--mac-prefix", dest="mac_prefix",
555                         help="Specify the mac prefix for the instance IP"
556                         " addresses, in the format XX:XX:XX",
557                         metavar="PREFIX",
558                         default=constants.DEFAULT_MAC_PREFIX,),
559             make_option("-g", "--vg-name", dest="vg_name",
560                         help="Specify the volume group name "
561                         " (cluster-wide) for disk allocation [xenvg]",
562                         metavar="VG",
563                         default=None,),
564             make_option("-b", "--bridge", dest="def_bridge",
565                         help="Specify the default bridge name (cluster-wide)"
566                           " to connect the instances to [%s]" %
567                           constants.DEFAULT_BRIDGE,
568                         metavar="BRIDGE",
569                         default=constants.DEFAULT_BRIDGE,),
570             make_option("--master-netdev", dest="master_netdev",
571                         help="Specify the node interface (cluster-wide)"
572                           " on which the master IP address will be added "
573                           " [%s]" % constants.DEFAULT_BRIDGE,
574                         metavar="NETDEV",
575                         default=constants.DEFAULT_BRIDGE,),
576             make_option("--file-storage-dir", dest="file_storage_dir",
577                         help="Specify the default directory (cluster-wide)"
578                              " for storing the file-based disks [%s]" %
579                              constants.DEFAULT_FILE_STORAGE_DIR,
580                         metavar="DIR",
581                         default=constants.DEFAULT_FILE_STORAGE_DIR,),
582             make_option("--no-lvm-storage", dest="lvm_storage",
583                         help="No support for lvm based instances"
584                              " (cluster-wide)",
585                         action="store_false", default=True,),
586             make_option("--no-etc-hosts", dest="modify_etc_hosts",
587                         help="Don't modify /etc/hosts"
588                              " (cluster-wide)",
589                         action="store_false", default=True,),
590             make_option("--enabled-hypervisors", dest="enabled_hypervisors",
591                         help="Comma-separated list of hypervisors",
592                         type="string", default=None),
593             make_option("-t", "--default-hypervisor",
594                         dest="default_hypervisor",
595                         help="Default hypervisor to use for instance creation",
596                         choices=list(constants.HYPER_TYPES),
597                         default=constants.DEFAULT_ENABLED_HYPERVISOR),
598             ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
599                        help="Hypervisor and hypervisor options, in the"
600                          " format"
601                        " hypervisor:option=value,option=value,...",
602                        default=[],
603                        action="append",
604                        type="identkeyval"),
605             keyval_option("-B", "--backend-parameters", dest="beparams",
606                           type="keyval", default={},
607                           help="Backend parameters"),
608             make_option("-C", "--candidate-pool-size",
609                         default=constants.MASTER_POOL_SIZE_DEFAULT,
610                         help="Set the candidate pool size",
611                         dest="candidate_pool_size", type="int"),
612             ],
613            "[opts...] <cluster_name>",
614            "Initialises a new cluster configuration"),
615   'destroy': (DestroyCluster, ARGS_NONE,
616               [DEBUG_OPT,
617                make_option("--yes-do-it", dest="yes_do_it",
618                            help="Destroy cluster",
619                            action="store_true"),
620               ],
621               "", "Destroy cluster"),
622   'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
623                "<new_name>",
624                "Renames the cluster"),
625   'redist-conf': (RedistributeConfig, ARGS_NONE, [DEBUG_OPT, SUBMIT_OPT],
626                   "",
627                   "Forces a push of the configuration file and ssconf files"
628                   " to the nodes in the cluster"),
629   'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT,
630              make_option("--no-nplus1-mem", dest="skip_nplusone_mem",
631                          help="Skip N+1 memory redundancy tests",
632                          action="store_true",
633                          default=False,),
634              ],
635              "", "Does a check on the cluster configuration"),
636   'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
637                    "", "Does a check on the cluster disk status"),
638   'repair-disk-sizes': (RepairDiskSizes, ARGS_ANY, [DEBUG_OPT],
639                    "", "Updates mismatches in recorded disk sizes"),
640   'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT,
641                      make_option("--no-voting", dest="no_voting",
642                                  help="Skip node agreement check (dangerous)",
643                                  action="store_true",
644                                  default=False,),
645                      ],
646                      "", "Makes the current node the master"),
647   'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
648               "", "Shows the cluster version"),
649   'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
650                 "", "Shows the cluster master"),
651   'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
652                "[-n node...] <filename>",
653                "Copies a file to all (or only some) nodes"),
654   'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
655               "[-n node...] <command>",
656               "Runs a command on all (or only some) nodes"),
657   'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
658                  "", "Show cluster configuration"),
659   'list-tags': (ListTags, ARGS_NONE,
660                 [DEBUG_OPT], "", "List the tags of the cluster"),
661   'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
662                "tag...", "Add tags to the cluster"),
663   'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
664                   "tag...", "Remove tags from the cluster"),
665   'search-tags': (SearchTags, ARGS_ONE,
666                   [DEBUG_OPT], "", "Searches the tags on all objects on"
667                   " the cluster for a given pattern (regex)"),
668   'queue': (QueueOps, ARGS_ONE, [DEBUG_OPT],
669             "drain|undrain|info", "Change queue properties"),
670   'modify': (SetClusterParams, ARGS_NONE,
671              [DEBUG_OPT,
672               make_option("-g", "--vg-name", dest="vg_name",
673                           help="Specify the volume group name "
674                           " (cluster-wide) for disk allocation "
675                           "and enable lvm based storage",
676                           metavar="VG",),
677               make_option("--no-lvm-storage", dest="lvm_storage",
678                           help="Disable support for lvm based instances"
679                                " (cluster-wide)",
680                           action="store_false", default=True,),
681               make_option("--enabled-hypervisors", dest="enabled_hypervisors",
682                           help="Comma-separated list of hypervisors",
683                           type="string", default=None),
684               ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
685                          help="Hypervisor and hypervisor options, in the"
686                          " format"
687                          " hypervisor:option=value,option=value,...",
688                          default=[],
689                          action="append",
690                          type="identkeyval"),
691               keyval_option("-B", "--backend-parameters", dest="beparams",
692                             type="keyval", default={},
693                             help="Backend parameters"),
694               make_option("-C", "--candidate-pool-size", default=None,
695                           help="Set the candidate pool size",
696                           dest="candidate_pool_size", type="int"),
697               ],
698              "[opts...]",
699              "Alters the parameters of the cluster"),
700   }
701
702 if __name__ == '__main__':
703   sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}))