Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ ce735215

History | View | Annotate | Download (22.4 kB)

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 = opts.hvparams
73
  if hvparams:
74
    # a list of (name, dict) we can pass directly to dict()
75
    hvparams = dict(opts.hvparams)
76
  else:
77
    # otherwise init as empty dict
78
    hvparams = {}
79

    
80
  beparams = opts.beparams
81
  # check for invalid parameters
82
  for parameter in beparams:
83
    if parameter not in constants.BES_PARAMETERS:
84
      ToStderr("Invalid backend parameter: %s", parameter)
85
      return 1
86

    
87
  # prepare beparams dict
88
  for parameter in constants.BES_PARAMETERS:
89
    if parameter not in beparams:
90
      beparams[parameter] = constants.BEC_DEFAULTS[parameter]
91

    
92
  # type wrangling
93
  try:
94
    beparams[constants.BE_VCPUS] = int(beparams[constants.BE_VCPUS])
95
  except ValueError:
96
    ToStderr("%s must be an integer", constants.BE_VCPUS)
97
    return 1
98

    
99
  if not isinstance(beparams[constants.BE_MEMORY], int):
100
    beparams[constants.BE_MEMORY] = utils.ParseUnit(
101
        beparams[constants.BE_MEMORY])
102

    
103
  # prepare hvparams dict
104
  for hv in constants.HYPER_TYPES:
105
    if hv not in hvparams:
106
      hvparams[hv] = {}
107
    for parameter in constants.HVC_DEFAULTS[hv]:
108
      if parameter not in hvparams[hv]:
109
        hvparams[hv][parameter] = constants.HVC_DEFAULTS[hv][parameter]
110

    
111
  for hv in hvlist:
112
    if hv not in constants.HYPER_TYPES:
113
      ToStderr("invalid hypervisor: %s", hv)
114
      return 1
115

    
116
  bootstrap.InitCluster(cluster_name=args[0],
117
                        secondary_ip=opts.secondary_ip,
118
                        vg_name=vg_name,
119
                        mac_prefix=opts.mac_prefix,
120
                        def_bridge=opts.def_bridge,
121
                        master_netdev=opts.master_netdev,
122
                        file_storage_dir=opts.file_storage_dir,
123
                        enabled_hypervisors=hvlist,
124
                        default_hypervisor=opts.default_hypervisor,
125
                        hvparams=hvparams,
126
                        beparams=beparams,
127
                        candidate_pool_size=opts.candidate_pool_size,
128
                        )
129
  return 0
130

    
131

    
132
@UsesRPC
133
def DestroyCluster(opts, args):
134
  """Destroy the cluster.
135

    
136
  @param opts: the command line options selected by the user
137
  @type args: list
138
  @param args: should be an empty list
139
  @rtype: int
140
  @return: the desired exit code
141

    
142
  """
143
  if not opts.yes_do_it:
144
    ToStderr("Destroying a cluster is irreversible. If you really want"
145
             " destroy this cluster, supply the --yes-do-it option.")
146
    return 1
147

    
148
  op = opcodes.OpDestroyCluster()
149
  master = SubmitOpCode(op)
150
  # if we reached this, the opcode didn't fail; we can proceed to
151
  # shutdown all the daemons
152
  bootstrap.FinalizeClusterDestroy(master)
153
  return 0
154

    
155

    
156
def RenameCluster(opts, args):
157
  """Rename the cluster.
158

    
159
  @param opts: the command line options selected by the user
160
  @type args: list
161
  @param args: should contain only one element, the new cluster name
162
  @rtype: int
163
  @return: the desired exit code
164

    
165
  """
166
  name = args[0]
167
  if not opts.force:
168
    usertext = ("This will rename the cluster to '%s'. If you are connected"
169
                " over the network to the cluster name, the operation is very"
170
                " dangerous as the IP address will be removed from the node"
171
                " and the change may not go through. Continue?") % name
172
    if not AskUser(usertext):
173
      return 1
174

    
175
  op = opcodes.OpRenameCluster(name=name)
176
  SubmitOpCode(op)
177
  return 0
178

    
179

    
180
def ShowClusterVersion(opts, args):
181
  """Write version of ganeti software to the standard output.
182

    
183
  @param opts: the command line options selected by the user
184
  @type args: list
185
  @param args: should be an empty list
186
  @rtype: int
187
  @return: the desired exit code
188

    
189
  """
190
  op = opcodes.OpQueryClusterInfo()
191
  result = SubmitOpCode(op)
192
  ToStdout("Software version: %s", result["software_version"])
193
  ToStdout("Internode protocol: %s", result["protocol_version"])
194
  ToStdout("Configuration format: %s", result["config_version"])
195
  ToStdout("OS api version: %s", result["os_api_version"])
196
  ToStdout("Export interface: %s", result["export_version"])
197
  return 0
198

    
199

    
200
def ShowClusterMaster(opts, args):
201
  """Write name of master node to the standard output.
202

    
203
  @param opts: the command line options selected by the user
204
  @type args: list
205
  @param args: should be an empty list
206
  @rtype: int
207
  @return: the desired exit code
208

    
209
  """
210
  ToStdout("%s", GetClient().QueryConfigValues(["master_node"])[0])
211
  return 0
212

    
213

    
214
def ShowClusterConfig(opts, args):
215
  """Shows cluster information.
216

    
217
  @param opts: the command line options selected by the user
218
  @type args: list
219
  @param args: should be an empty list
220
  @rtype: int
221
  @return: the desired exit code
222

    
223
  """
224
  op = opcodes.OpQueryClusterInfo()
225
  result = SubmitOpCode(op)
226

    
227
  ToStdout("Cluster name: %s", result["name"])
228

    
229
  ToStdout("Master node: %s", result["master"])
230

    
231
  ToStdout("Architecture (this node): %s (%s)",
232
           result["architecture"][0], result["architecture"][1])
233

    
234
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
235
  ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
236

    
237
  ToStdout("Hypervisor parameters:")
238
  for hv_name, hv_dict in result["hvparams"].items():
239
    ToStdout("  - %s:", hv_name)
240
    for item, val in hv_dict.iteritems():
241
      ToStdout("      %s: %s", item, val)
242

    
243
  ToStdout("Cluster parameters:")
244
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
245

    
246
  ToStdout("Default instance parameters:")
247
  for gr_name, gr_dict in result["beparams"].items():
248
    ToStdout("  - %s:", gr_name)
249
    for item, val in gr_dict.iteritems():
250
      ToStdout("      %s: %s", item, val)
251

    
252
  return 0
253

    
254

    
255
def ClusterCopyFile(opts, args):
256
  """Copy a file from master to some nodes.
257

    
258
  @param opts: the command line options selected by the user
259
  @type args: list
260
  @param args: should contain only one element, the path of
261
      the file to be copied
262
  @rtype: int
263
  @return: the desired exit code
264

    
265
  """
266
  filename = args[0]
267
  if not os.path.exists(filename):
268
    raise errors.OpPrereqError("No such filename '%s'" % filename)
269

    
270
  cl = GetClient()
271

    
272
  myname = utils.HostInfo().name
273

    
274
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
275

    
276
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
277
  results = [row[0] for row in SubmitOpCode(op, cl=cl) if row[0] != myname]
278

    
279
  srun = ssh.SshRunner(cluster_name=cluster_name)
280
  for node in results:
281
    if not srun.CopyFileToNode(node, filename):
282
      ToStderr("Copy of file %s to node %s failed", filename, node)
283

    
284
  return 0
285

    
286

    
287
def RunClusterCommand(opts, args):
288
  """Run a command on some nodes.
289

    
290
  @param opts: the command line options selected by the user
291
  @type args: list
292
  @param args: should contain the command to be run and its arguments
293
  @rtype: int
294
  @return: the desired exit code
295

    
296
  """
297
  cl = GetClient()
298

    
299
  command = " ".join(args)
300
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
301
  nodes = [row[0] for row in SubmitOpCode(op, cl=cl)]
302

    
303
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
304
                                                    "master_node"])
305

    
306
  srun = ssh.SshRunner(cluster_name=cluster_name)
307

    
308
  # Make sure master node is at list end
309
  if master_node in nodes:
310
    nodes.remove(master_node)
311
    nodes.append(master_node)
312

    
313
  for name in nodes:
314
    result = srun.Run(name, "root", command)
315
    ToStdout("------------------------------------------------")
316
    ToStdout("node: %s", name)
317
    ToStdout("%s", result.output)
318
    ToStdout("return code = %s", result.exit_code)
319

    
320
  return 0
321

    
322

    
323
def VerifyCluster(opts, args):
324
  """Verify integrity of cluster, performing various test on nodes.
325

    
326
  @param opts: the command line options selected by the user
327
  @type args: list
328
  @param args: should be an empty list
329
  @rtype: int
330
  @return: the desired exit code
331

    
332
  """
333
  skip_checks = []
334
  if opts.skip_nplusone_mem:
335
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
336
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
337
  if SubmitOpCode(op):
338
    return 0
339
  else:
340
    return 1
341

    
342

    
343
def VerifyDisks(opts, args):
344
  """Verify integrity of cluster disks.
345

    
346
  @param opts: the command line options selected by the user
347
  @type args: list
348
  @param args: should be an empty list
349
  @rtype: int
350
  @return: the desired exit code
351

    
352
  """
353
  op = opcodes.OpVerifyDisks()
354
  result = SubmitOpCode(op)
355
  if not isinstance(result, (list, tuple)) or len(result) != 4:
356
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
357

    
358
  nodes, nlvm, instances, missing = result
359

    
360
  if nodes:
361
    ToStdout("Nodes unreachable or with bad data:")
362
    for name in nodes:
363
      ToStdout("\t%s", name)
364
  retcode = constants.EXIT_SUCCESS
365

    
366
  if nlvm:
367
    for node, text in nlvm.iteritems():
368
      ToStdout("Error on node %s: LVM error: %s",
369
               node, text[-400:].encode('string_escape'))
370
      retcode |= 1
371
      ToStdout("You need to fix these nodes first before fixing instances")
372

    
373
  if instances:
374
    for iname in instances:
375
      if iname in missing:
376
        continue
377
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
378
      try:
379
        ToStdout("Activating disks for instance '%s'", iname)
380
        SubmitOpCode(op)
381
      except errors.GenericError, err:
382
        nret, msg = FormatError(err)
383
        retcode |= nret
384
        ToStderr("Error activating disks for instance %s: %s", iname, msg)
385

    
386
  if missing:
387
    for iname, ival in missing.iteritems():
388
      all_missing = utils.all(ival, lambda x: x[0] in nlvm)
389
      if all_missing:
390
        ToStdout("Instance %s cannot be verified as it lives on"
391
                 " broken nodes", iname)
392
      else:
393
        ToStdout("Instance %s has missing logical volumes:", iname)
394
        ival.sort()
395
        for node, vol in ival:
396
          if node in nlvm:
397
            ToStdout("\tbroken node %s /dev/xenvg/%s", node, vol)
398
          else:
399
            ToStdout("\t%s /dev/xenvg/%s", node, vol)
400
    ToStdout("You need to run replace_disks for all the above"
401
           " instances, if this message persist after fixing nodes.")
402
    retcode |= 1
403

    
404
  return retcode
405

    
406

    
407
@UsesRPC
408
def MasterFailover(opts, args):
409
  """Failover the master node.
410

    
411
  This command, when run on a non-master node, will cause the current
412
  master to cease being master, and the non-master to become new
413
  master.
414

    
415
  @param opts: the command line options selected by the user
416
  @type args: list
417
  @param args: should be an empty list
418
  @rtype: int
419
  @return: the desired exit code
420

    
421
  """
422
  return bootstrap.MasterFailover()
423

    
424

    
425
def SearchTags(opts, args):
426
  """Searches the tags on all the cluster.
427

    
428
  @param opts: the command line options selected by the user
429
  @type args: list
430
  @param args: should contain only one element, the tag pattern
431
  @rtype: int
432
  @return: the desired exit code
433

    
434
  """
435
  op = opcodes.OpSearchTags(pattern=args[0])
436
  result = SubmitOpCode(op)
437
  if not result:
438
    return 1
439
  result = list(result)
440
  result.sort()
441
  for path, tag in result:
442
    ToStdout("%s %s", path, tag)
443

    
444

    
445
def SetClusterParams(opts, args):
446
  """Modify the cluster.
447

    
448
  @param opts: the command line options selected by the user
449
  @type args: list
450
  @param args: should be an empty list
451
  @rtype: int
452
  @return: the desired exit code
453

    
454
  """
455
  if not (not opts.lvm_storage or opts.vg_name or
456
          opts.enabled_hypervisors or opts.hvparams or
457
          opts.beparams or opts.candidate_pool_size is not None):
458
    ToStderr("Please give at least one of the parameters.")
459
    return 1
460

    
461
  vg_name = opts.vg_name
462
  if not opts.lvm_storage and opts.vg_name:
463
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
464
    return 1
465

    
466
  hvlist = opts.enabled_hypervisors
467
  if hvlist is not None:
468
    hvlist = hvlist.split(",")
469

    
470
  hvparams = opts.hvparams
471
  if hvparams:
472
    # a list of (name, dict) we can pass directly to dict()
473
    hvparams = dict(opts.hvparams)
474

    
475
  beparams = opts.beparams
476

    
477
  op = opcodes.OpSetClusterParams(vg_name=opts.vg_name,
478
                                  enabled_hypervisors=hvlist,
479
                                  hvparams=hvparams,
480
                                  beparams=beparams,
481
                                  candidate_pool_size=opts.candidate_pool_size)
482
  SubmitOpCode(op)
483
  return 0
484

    
485

    
486
def QueueOps(opts, args):
487
  """Queue operations.
488

    
489
  @param opts: the command line options selected by the user
490
  @type args: list
491
  @param args: should contain only one element, the subcommand
492
  @rtype: int
493
  @return: the desired exit code
494

    
495
  """
496
  command = args[0]
497
  client = GetClient()
498
  if command in ("drain", "undrain"):
499
    drain_flag = command == "drain"
500
    client.SetQueueDrainFlag(drain_flag)
501
  elif command == "info":
502
    result = client.QueryConfigValues(["drain_flag"])
503
    if result[0]:
504
      val = "set"
505
    else:
506
      val = "unset"
507
    ToStdout("The drain flag is %s" % val)
508
  return 0
509

    
510
# this is an option common to more than one command, so we declare
511
# it here and reuse it
512
node_option = make_option("-n", "--node", action="append", dest="nodes",
513
                          help="Node to copy to (if not given, all nodes),"
514
                               " can be given multiple times",
515
                          metavar="<node>", default=[])
516

    
517
commands = {
518
  'init': (InitCluster, ARGS_ONE,
519
           [DEBUG_OPT,
520
            make_option("-s", "--secondary-ip", dest="secondary_ip",
521
                        help="Specify the secondary ip for this node;"
522
                        " if given, the entire cluster must have secondary"
523
                        " addresses",
524
                        metavar="ADDRESS", default=None),
525
            make_option("-m", "--mac-prefix", dest="mac_prefix",
526
                        help="Specify the mac prefix for the instance IP"
527
                        " addresses, in the format XX:XX:XX",
528
                        metavar="PREFIX",
529
                        default="aa:00:00",),
530
            make_option("-g", "--vg-name", dest="vg_name",
531
                        help="Specify the volume group name "
532
                        " (cluster-wide) for disk allocation [xenvg]",
533
                        metavar="VG",
534
                        default=None,),
535
            make_option("-b", "--bridge", dest="def_bridge",
536
                        help="Specify the default bridge name (cluster-wide)"
537
                          " to connect the instances to [%s]" %
538
                          constants.DEFAULT_BRIDGE,
539
                        metavar="BRIDGE",
540
                        default=constants.DEFAULT_BRIDGE,),
541
            make_option("--master-netdev", dest="master_netdev",
542
                        help="Specify the node interface (cluster-wide)"
543
                          " on which the master IP address will be added "
544
                          " [%s]" % constants.DEFAULT_BRIDGE,
545
                        metavar="NETDEV",
546
                        default=constants.DEFAULT_BRIDGE,),
547
            make_option("--file-storage-dir", dest="file_storage_dir",
548
                        help="Specify the default directory (cluster-wide)"
549
                             " for storing the file-based disks [%s]" %
550
                             constants.DEFAULT_FILE_STORAGE_DIR,
551
                        metavar="DIR",
552
                        default=constants.DEFAULT_FILE_STORAGE_DIR,),
553
            make_option("--no-lvm-storage", dest="lvm_storage",
554
                        help="No support for lvm based instances"
555
                             " (cluster-wide)",
556
                        action="store_false", default=True,),
557
            make_option("--enabled-hypervisors", dest="enabled_hypervisors",
558
                        help="Comma-separated list of hypervisors",
559
                        type="string", default=None),
560
            make_option("-t", "--default-hypervisor",
561
                        dest="default_hypervisor",
562
                        help="Default hypervisor to use for instance creation",
563
                        choices=list(constants.HYPER_TYPES),
564
                        default=constants.DEFAULT_ENABLED_HYPERVISOR),
565
            ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
566
                       help="Hypervisor and hypervisor options, in the"
567
                         " format"
568
                       " hypervisor:option=value,option=value,...",
569
                       default=[],
570
                       action="append",
571
                       type="identkeyval"),
572
            keyval_option("-B", "--backend-parameters", dest="beparams",
573
                          type="keyval", default={},
574
                          help="Backend parameters"),
575
            make_option("-C", "--candidate-pool-size",
576
                        default=constants.MASTER_POOL_SIZE_DEFAULT,
577
                        help="Set the candidate pool size",
578
                        dest="candidate_pool_size", type="int"),
579
            ],
580
           "[opts...] <cluster_name>",
581
           "Initialises a new cluster configuration"),
582
  'destroy': (DestroyCluster, ARGS_NONE,
583
              [DEBUG_OPT,
584
               make_option("--yes-do-it", dest="yes_do_it",
585
                           help="Destroy cluster",
586
                           action="store_true"),
587
              ],
588
              "", "Destroy cluster"),
589
  'rename': (RenameCluster, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
590
               "<new_name>",
591
               "Renames the cluster"),
592
  'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT,
593
             make_option("--no-nplus1-mem", dest="skip_nplusone_mem",
594
                         help="Skip N+1 memory redundancy tests",
595
                         action="store_true",
596
                         default=False,),
597
             ],
598
             "", "Does a check on the cluster configuration"),
599
  'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
600
                   "", "Does a check on the cluster disk status"),
601
  'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT],
602
                     "", "Makes the current node the master"),
603
  'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
604
              "", "Shows the cluster version"),
605
  'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
606
                "", "Shows the cluster master"),
607
  'copyfile': (ClusterCopyFile, ARGS_ONE, [DEBUG_OPT, node_option],
608
               "[-n node...] <filename>",
609
               "Copies a file to all (or only some) nodes"),
610
  'command': (RunClusterCommand, ARGS_ATLEAST(1), [DEBUG_OPT, node_option],
611
              "[-n node...] <command>",
612
              "Runs a command on all (or only some) nodes"),
613
  'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
614
                 "", "Show cluster configuration"),
615
  'list-tags': (ListTags, ARGS_NONE,
616
                [DEBUG_OPT], "", "List the tags of the cluster"),
617
  'add-tags': (AddTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
618
               "tag...", "Add tags to the cluster"),
619
  'remove-tags': (RemoveTags, ARGS_ANY, [DEBUG_OPT, TAG_SRC_OPT],
620
                  "tag...", "Remove tags from the cluster"),
621
  'search-tags': (SearchTags, ARGS_ONE,
622
                  [DEBUG_OPT], "", "Searches the tags on all objects on"
623
                  " the cluster for a given pattern (regex)"),
624
  'queue': (QueueOps, ARGS_ONE, [DEBUG_OPT],
625
            "drain|undrain|info", "Change queue properties"),
626
  'modify': (SetClusterParams, ARGS_NONE,
627
             [DEBUG_OPT,
628
              make_option("-g", "--vg-name", dest="vg_name",
629
                          help="Specify the volume group name "
630
                          " (cluster-wide) for disk allocation "
631
                          "and enable lvm based storage",
632
                          metavar="VG",),
633
              make_option("--no-lvm-storage", dest="lvm_storage",
634
                          help="Disable support for lvm based instances"
635
                               " (cluster-wide)",
636
                          action="store_false", default=True,),
637
              make_option("--enabled-hypervisors", dest="enabled_hypervisors",
638
                          help="Comma-separated list of hypervisors",
639
                          type="string", default=None),
640
              ikv_option("-H", "--hypervisor-parameters", dest="hvparams",
641
                         help="Hypervisor and hypervisor options, in the"
642
                         " format"
643
                         " hypervisor:option=value,option=value,...",
644
                         default=[],
645
                         action="append",
646
                         type="identkeyval"),
647
              keyval_option("-B", "--backend-parameters", dest="beparams",
648
                            type="keyval", default={},
649
                            help="Backend parameters"),
650
              make_option("-C", "--candidate-pool-size", default=None,
651
                          help="Set the candidate pool size",
652
                          dest="candidate_pool_size", type="int"),
653
              ],
654
             "[opts...]",
655
             "Alters the parameters of the cluster"),
656
  }
657

    
658
if __name__ == '__main__':
659
  sys.exit(GenericMain(commands, override={"tag_type": constants.TAG_CLUSTER}))