Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 066f465d

History | View | Annotate | Download (23.3 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
from ganeti import objects
38

    
39

    
40
@UsesRPC
41
def InitCluster(opts, args):
42
  """Initialize the cluster.
43

    
44
  @param opts: the command line options selected by the user
45
  @type args: list
46
  @param args: should contain only one element, the desired
47
      cluster name
48
  @rtype: int
49
  @return: the desired exit code
50

    
51
  """
52
  if not opts.lvm_storage and opts.vg_name:
53
    ToStderr("Options --no-lvm-storage and --vg-name conflict.")
54
    return 1
55

    
56
  vg_name = opts.vg_name
57
  if opts.lvm_storage and not opts.vg_name:
58
    vg_name = constants.DEFAULT_VG
59

    
60
  hvlist = opts.enabled_hypervisors
61
  hvlist = hvlist.split(",")
62

    
63
  hvparams = dict(opts.hvparams)
64
  beparams = opts.beparams
65
  nicparams = opts.nicparams
66

    
67
  # prepare beparams dict
68
  beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams)
69
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
70

    
71
  # prepare nicparams dict
72
  nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams)
73
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
74

    
75
  # prepare hvparams dict
76
  for hv in constants.HYPER_TYPES:
77
    if hv not in hvparams:
78
      hvparams[hv] = {}
79
    hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv])
80
    utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
81

    
82
  for hv in hvlist:
83
    if hv not in constants.HYPER_TYPES:
84
      ToStderr("invalid hypervisor: %s", hv)
85
      return 1
86

    
87
  bootstrap.InitCluster(cluster_name=args[0],
88
                        secondary_ip=opts.secondary_ip,
89
                        vg_name=vg_name,
90
                        mac_prefix=opts.mac_prefix,
91
                        master_netdev=opts.master_netdev,
92
                        file_storage_dir=opts.file_storage_dir,
93
                        enabled_hypervisors=hvlist,
94
                        hvparams=hvparams,
95
                        beparams=beparams,
96
                        nicparams=nicparams,
97
                        candidate_pool_size=opts.candidate_pool_size,
98
                        modify_etc_hosts=opts.modify_etc_hosts,
99
                        )
100
  return 0
101

    
102

    
103
@UsesRPC
104
def DestroyCluster(opts, args):
105
  """Destroy the cluster.
106

    
107
  @param opts: the command line options selected by the user
108
  @type args: list
109
  @param args: should be an empty list
110
  @rtype: int
111
  @return: the desired exit code
112

    
113
  """
114
  if not opts.yes_do_it:
115
    ToStderr("Destroying a cluster is irreversible. If you really want"
116
             " destroy this cluster, supply the --yes-do-it option.")
117
    return 1
118

    
119
  op = opcodes.OpDestroyCluster()
120
  master = SubmitOpCode(op)
121
  # if we reached this, the opcode didn't fail; we can proceed to
122
  # shutdown all the daemons
123
  bootstrap.FinalizeClusterDestroy(master)
124
  return 0
125

    
126

    
127
def RenameCluster(opts, args):
128
  """Rename the cluster.
129

    
130
  @param opts: the command line options selected by the user
131
  @type args: list
132
  @param args: should contain only one element, the new cluster name
133
  @rtype: int
134
  @return: the desired exit code
135

    
136
  """
137
  name = args[0]
138
  if not opts.force:
139
    usertext = ("This will rename the cluster to '%s'. If you are connected"
140
                " over the network to the cluster name, the operation is very"
141
                " dangerous as the IP address will be removed from the node"
142
                " and the change may not go through. Continue?") % name
143
    if not AskUser(usertext):
144
      return 1
145

    
146
  op = opcodes.OpRenameCluster(name=name)
147
  SubmitOpCode(op)
148
  return 0
149

    
150

    
151
def RedistributeConfig(opts, args):
152
  """Forces push of the cluster configuration.
153

    
154
  @param opts: the command line options selected by the user
155
  @type args: list
156
  @param args: empty list
157
  @rtype: int
158
  @return: the desired exit code
159

    
160
  """
161
  op = opcodes.OpRedistributeConfig()
162
  SubmitOrSend(op, opts)
163
  return 0
164

    
165

    
166
def ShowClusterVersion(opts, args):
167
  """Write version of ganeti software to the standard output.
168

    
169
  @param opts: the command line options selected by the user
170
  @type args: list
171
  @param args: should be an empty list
172
  @rtype: int
173
  @return: the desired exit code
174

    
175
  """
176
  cl = GetClient()
177
  result = cl.QueryClusterInfo()
178
  ToStdout("Software version: %s", result["software_version"])
179
  ToStdout("Internode protocol: %s", result["protocol_version"])
180
  ToStdout("Configuration format: %s", result["config_version"])
181
  ToStdout("OS api version: %s", result["os_api_version"])
182
  ToStdout("Export interface: %s", result["export_version"])
183
  return 0
184

    
185

    
186
def ShowClusterMaster(opts, args):
187
  """Write name of master node to the standard output.
188

    
189
  @param opts: the command line options selected by the user
190
  @type args: list
191
  @param args: should be an empty list
192
  @rtype: int
193
  @return: the desired exit code
194

    
195
  """
196
  master = bootstrap.GetMaster()
197
  ToStdout(master)
198
  return 0
199

    
200
def _PrintGroupedParams(paramsdict):
201
  """Print Grouped parameters (be, nic, disk) by group.
202

    
203
  @type paramsdict: dict of dicts
204
  @param paramsdict: {group: {param: value, ...}, ...}
205

    
206
  """
207
  for gr_name, gr_dict in paramsdict.items():
208
    ToStdout("  - %s:", gr_name)
209
    for item, val in gr_dict.iteritems():
210
      ToStdout("      %s: %s", item, val)
211

    
212
def ShowClusterConfig(opts, args):
213
  """Shows cluster information.
214

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

    
221
  """
222
  cl = GetClient()
223
  result = cl.QueryClusterInfo()
224

    
225
  ToStdout("Cluster name: %s", result["name"])
226

    
227
  ToStdout("Master node: %s", result["master"])
228

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

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

    
235
  ToStdout("Hypervisor parameters:")
236
  _PrintGroupedParams(result["hvparams"])
237

    
238
  ToStdout("Cluster parameters:")
239
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
240
  ToStdout("  - master netdev: %s", result["master_netdev"])
241
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
242
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
243

    
244
  ToStdout("Default instance parameters:")
245
  _PrintGroupedParams(result["beparams"])
246

    
247
  ToStdout("Default nic parameters:")
248
  _PrintGroupedParams(result["nicparams"])
249

    
250
  return 0
251

    
252

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

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

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

    
268
  cl = GetClient()
269

    
270
  myname = utils.HostInfo().name
271

    
272
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
273

    
274
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
275
  results = [name for name in results if name != myname]
276

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

    
282
  return 0
283

    
284

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

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

    
294
  """
295
  cl = GetClient()
296

    
297
  command = " ".join(args)
298

    
299
  nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
300

    
301
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
302
                                                    "master_node"])
303

    
304
  srun = ssh.SshRunner(cluster_name=cluster_name)
305

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

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

    
318
  return 0
319

    
320

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

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

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

    
340

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

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

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

    
356
  bad_nodes, instances, missing = result
357

    
358
  retcode = constants.EXIT_SUCCESS
359

    
360
  if bad_nodes:
361
    for node, text in bad_nodes.items():
362
      ToStdout("Error gathering data on node %s: %s",
363
               node, utils.SafeEncode(text[-400:]))
364
      retcode |= 1
365
      ToStdout("You need to fix these nodes first before fixing instances")
366

    
367
  if instances:
368
    for iname in instances:
369
      if iname in missing:
370
        continue
371
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
372
      try:
373
        ToStdout("Activating disks for instance '%s'", iname)
374
        SubmitOpCode(op)
375
      except errors.GenericError, err:
376
        nret, msg = FormatError(err)
377
        retcode |= nret
378
        ToStderr("Error activating disks for instance %s: %s", iname, msg)
379

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

    
398
  return retcode
399

    
400

    
401
@UsesRPC
402
def MasterFailover(opts, args):
403
  """Failover the master node.
404

    
405
  This command, when run on a non-master node, will cause the current
406
  master to cease being master, and the non-master to become new
407
  master.
408

    
409
  @param opts: the command line options selected by the user
410
  @type args: list
411
  @param args: should be an empty list
412
  @rtype: int
413
  @return: the desired exit code
414

    
415
  """
416
  if opts.no_voting:
417
    usertext = ("This will perform the failover even if most other nodes"
418
                " are down, or if this node is outdated. This is dangerous"
419
                " as it can lead to a non-consistent cluster. Check the"
420
                " gnt-cluster(8) man page before proceeding. Continue?")
421
    if not AskUser(usertext):
422
      return 1
423

    
424
  return bootstrap.MasterFailover(no_voting=opts.no_voting)
425

    
426

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

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

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

    
446

    
447
def SetClusterParams(opts, args):
448
  """Modify the cluster.
449

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

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

    
464
  vg_name = opts.vg_name
465
  if not opts.lvm_storage and opts.vg_name:
466
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
467
    return 1
468
  elif not opts.lvm_storage:
469
    vg_name = ''
470

    
471
  hvlist = opts.enabled_hypervisors
472
  if hvlist is not None:
473
    hvlist = hvlist.split(",")
474

    
475
  # a list of (name, dict) we can pass directly to dict() (or [])
476
  hvparams = dict(opts.hvparams)
477
  for hv, hv_params in hvparams.iteritems():
478
    utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
479

    
480
  beparams = opts.beparams
481
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
482

    
483
  nicparams = opts.nicparams
484
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
485

    
486
  op = opcodes.OpSetClusterParams(vg_name=vg_name,
487
                                  enabled_hypervisors=hvlist,
488
                                  hvparams=hvparams,
489
                                  beparams=beparams,
490
                                  nicparams=nicparams,
491
                                  candidate_pool_size=opts.candidate_pool_size)
492
  SubmitOpCode(op)
493
  return 0
494

    
495

    
496
def QueueOps(opts, args):
497
  """Queue operations.
498

    
499
  @param opts: the command line options selected by the user
500
  @type args: list
501
  @param args: should contain only one element, the subcommand
502
  @rtype: int
503
  @return: the desired exit code
504

    
505
  """
506
  command = args[0]
507
  client = GetClient()
508
  if command in ("drain", "undrain"):
509
    drain_flag = command == "drain"
510
    client.SetQueueDrainFlag(drain_flag)
511
  elif command == "info":
512
    result = client.QueryConfigValues(["drain_flag"])
513
    if result[0]:
514
      val = "set"
515
    else:
516
      val = "unset"
517
    ToStdout("The drain flag is %s" % val)
518
  else:
519
    raise errors.OpPrereqError("Command '%s' is not valid." % command)
520

    
521
  return 0
522

    
523
# this is an option common to more than one command, so we declare
524
# it here and reuse it
525
node_option = make_option("-n", "--node", action="append", dest="nodes",
526
                          help="Node to copy to (if not given, all nodes),"
527
                               " can be given multiple times",
528
                          metavar="<node>", default=[])
529

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

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