Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 3552cd2e

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
  bootstrap.InitCluster(cluster_name=args[0],
83
                        secondary_ip=opts.secondary_ip,
84
                        vg_name=vg_name,
85
                        mac_prefix=opts.mac_prefix,
86
                        master_netdev=opts.master_netdev,
87
                        file_storage_dir=opts.file_storage_dir,
88
                        enabled_hypervisors=hvlist,
89
                        hvparams=hvparams,
90
                        beparams=beparams,
91
                        nicparams=nicparams,
92
                        candidate_pool_size=opts.candidate_pool_size,
93
                        modify_etc_hosts=opts.modify_etc_hosts,
94
                        )
95
  op = opcodes.OpPostInitCluster()
96
  SubmitOpCode(op)
97
  return 0
98

    
99

    
100
@UsesRPC
101
def DestroyCluster(opts, args):
102
  """Destroy the cluster.
103

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

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

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

    
123

    
124
def RenameCluster(opts, args):
125
  """Rename the cluster.
126

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

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

    
143
  op = opcodes.OpRenameCluster(name=name)
144
  SubmitOpCode(op)
145
  return 0
146

    
147

    
148
def RedistributeConfig(opts, args):
149
  """Forces push of the cluster configuration.
150

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

    
157
  """
158
  op = opcodes.OpRedistributeConfig()
159
  SubmitOrSend(op, opts)
160
  return 0
161

    
162

    
163
def ShowClusterVersion(opts, args):
164
  """Write version of ganeti software to the standard output.
165

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

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

    
182

    
183
def ShowClusterMaster(opts, args):
184
  """Write name of master node to the standard output.
185

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

    
192
  """
193
  master = bootstrap.GetMaster()
194
  ToStdout(master)
195
  return 0
196

    
197
def _PrintGroupedParams(paramsdict):
198
  """Print Grouped parameters (be, nic, disk) by group.
199

    
200
  @type paramsdict: dict of dicts
201
  @param paramsdict: {group: {param: value, ...}, ...}
202

    
203
  """
204
  for gr_name, gr_dict in paramsdict.items():
205
    ToStdout("  - %s:", gr_name)
206
    for item, val in gr_dict.iteritems():
207
      ToStdout("      %s: %s", item, val)
208

    
209
def ShowClusterConfig(opts, args):
210
  """Shows cluster information.
211

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

    
218
  """
219
  cl = GetClient()
220
  result = cl.QueryClusterInfo()
221

    
222
  ToStdout("Cluster name: %s", result["name"])
223

    
224
  ToStdout("Master node: %s", result["master"])
225

    
226
  ToStdout("Architecture (this node): %s (%s)",
227
           result["architecture"][0], result["architecture"][1])
228

    
229
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
230
  ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
231

    
232
  ToStdout("Hypervisor parameters:")
233
  _PrintGroupedParams(result["hvparams"])
234

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

    
241
  ToStdout("Default instance parameters:")
242
  _PrintGroupedParams(result["beparams"])
243

    
244
  ToStdout("Default nic parameters:")
245
  _PrintGroupedParams(result["nicparams"])
246

    
247
  return 0
248

    
249

    
250
def ClusterCopyFile(opts, args):
251
  """Copy a file from master to some nodes.
252

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

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

    
265
  cl = GetClient()
266

    
267
  myname = utils.HostInfo().name
268

    
269
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
270

    
271
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
272
  results = [name for name in results if name != myname]
273

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

    
279
  return 0
280

    
281

    
282
def RunClusterCommand(opts, args):
283
  """Run a command on some nodes.
284

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

    
291
  """
292
  cl = GetClient()
293

    
294
  command = " ".join(args)
295

    
296
  nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
297

    
298
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
299
                                                    "master_node"])
300

    
301
  srun = ssh.SshRunner(cluster_name=cluster_name)
302

    
303
  # Make sure master node is at list end
304
  if master_node in nodes:
305
    nodes.remove(master_node)
306
    nodes.append(master_node)
307

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

    
315
  return 0
316

    
317

    
318
def VerifyCluster(opts, args):
319
  """Verify integrity of cluster, performing various test on nodes.
320

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

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

    
337

    
338
def VerifyDisks(opts, args):
339
  """Verify integrity of cluster disks.
340

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

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

    
353
  bad_nodes, instances, missing = result
354

    
355
  retcode = constants.EXIT_SUCCESS
356

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

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

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

    
395
  return retcode
396

    
397

    
398
@UsesRPC
399
def MasterFailover(opts, args):
400
  """Failover the master node.
401

    
402
  This command, when run on a non-master node, will cause the current
403
  master to cease being master, and the non-master to become new
404
  master.
405

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

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

    
421
  return bootstrap.MasterFailover(no_voting=opts.no_voting)
422

    
423

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

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

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

    
443

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

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

    
453
  """
454
  if not (not opts.lvm_storage or opts.vg_name or
455
          opts.enabled_hypervisors or opts.hvparams or
456
          opts.beparams or opts.nicparams or
457
          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
  elif not opts.lvm_storage:
466
    vg_name = ''
467

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

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

    
477
  beparams = opts.beparams
478
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
479

    
480
  nicparams = opts.nicparams
481
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
482

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

    
492

    
493
def QueueOps(opts, args):
494
  """Queue operations.
495

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

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

    
518
  return 0
519

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

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

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