Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 9ebe9556

History | View | Annotate | Download (23.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
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
  if hvlist is not None:
62
    hvlist = hvlist.split(",")
63
  else:
64
    hvlist = [opts.default_hypervisor]
65

    
66
  # avoid an impossible situation
67
  if opts.default_hypervisor not in hvlist:
68
    ToStderr("The default hypervisor requested (%s) is not"
69
             " within the enabled hypervisor list (%s)" %
70
             (opts.default_hypervisor, hvlist))
71
    return 1
72

    
73
  hvparams = dict(opts.hvparams)
74
  beparams = opts.beparams
75
  nicparams = opts.nicparams
76

    
77
  # prepare beparams dict
78
  beparams = objects.FillDict(constants.BEC_DEFAULTS, beparams)
79
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
80

    
81
  # prepare nicparams dict
82
  nicparams = objects.FillDict(constants.NICC_DEFAULTS, nicparams)
83
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
84

    
85
  # prepare hvparams dict
86
  for hv in constants.HYPER_TYPES:
87
    if hv not in hvparams:
88
      hvparams[hv] = {}
89
    hvparams[hv] = objects.FillDict(constants.HVC_DEFAULTS[hv], hvparams[hv])
90
    utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
91

    
92
  for hv in hvlist:
93
    if hv not in constants.HYPER_TYPES:
94
      ToStderr("invalid hypervisor: %s", hv)
95
      return 1
96

    
97
  bootstrap.InitCluster(cluster_name=args[0],
98
                        secondary_ip=opts.secondary_ip,
99
                        vg_name=vg_name,
100
                        mac_prefix=opts.mac_prefix,
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
                        nicparams=nicparams,
108
                        candidate_pool_size=opts.candidate_pool_size,
109
                        modify_etc_hosts=opts.modify_etc_hosts,
110
                        )
111
  return 0
112

    
113

    
114
@UsesRPC
115
def DestroyCluster(opts, args):
116
  """Destroy the cluster.
117

    
118
  @param opts: the command line options selected by the user
119
  @type args: list
120
  @param args: should be an empty list
121
  @rtype: int
122
  @return: the desired exit code
123

    
124
  """
125
  if not opts.yes_do_it:
126
    ToStderr("Destroying a cluster is irreversible. If you really want"
127
             " destroy this cluster, supply the --yes-do-it option.")
128
    return 1
129

    
130
  op = opcodes.OpDestroyCluster()
131
  master = SubmitOpCode(op)
132
  # if we reached this, the opcode didn't fail; we can proceed to
133
  # shutdown all the daemons
134
  bootstrap.FinalizeClusterDestroy(master)
135
  return 0
136

    
137

    
138
def RenameCluster(opts, args):
139
  """Rename the cluster.
140

    
141
  @param opts: the command line options selected by the user
142
  @type args: list
143
  @param args: should contain only one element, the new cluster name
144
  @rtype: int
145
  @return: the desired exit code
146

    
147
  """
148
  name = args[0]
149
  if not opts.force:
150
    usertext = ("This will rename the cluster to '%s'. If you are connected"
151
                " over the network to the cluster name, the operation is very"
152
                " dangerous as the IP address will be removed from the node"
153
                " and the change may not go through. Continue?") % name
154
    if not AskUser(usertext):
155
      return 1
156

    
157
  op = opcodes.OpRenameCluster(name=name)
158
  SubmitOpCode(op)
159
  return 0
160

    
161

    
162
def RedistributeConfig(opts, args):
163
  """Forces push of the cluster configuration.
164

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

    
171
  """
172
  op = opcodes.OpRedistributeConfig()
173
  SubmitOrSend(op, opts)
174
  return 0
175

    
176

    
177
def ShowClusterVersion(opts, args):
178
  """Write version of ganeti software to the standard output.
179

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

    
186
  """
187
  cl = GetClient()
188
  result = cl.QueryClusterInfo()
189
  ToStdout("Software version: %s", result["software_version"])
190
  ToStdout("Internode protocol: %s", result["protocol_version"])
191
  ToStdout("Configuration format: %s", result["config_version"])
192
  ToStdout("OS api version: %s", result["os_api_version"])
193
  ToStdout("Export interface: %s", result["export_version"])
194
  return 0
195

    
196

    
197
def ShowClusterMaster(opts, args):
198
  """Write name of master node to the standard output.
199

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

    
206
  """
207
  master = bootstrap.GetMaster()
208
  ToStdout(master)
209
  return 0
210

    
211
def _PrintGroupedParams(paramsdict):
212
  """Print Grouped parameters (be, nic, disk) by group.
213

    
214
  @type paramsdict: dict of dicts
215
  @param paramsdict: {group: {param: value, ...}, ...}
216

    
217
  """
218
  for gr_name, gr_dict in paramsdict.items():
219
    ToStdout("  - %s:", gr_name)
220
    for item, val in gr_dict.iteritems():
221
      ToStdout("      %s: %s", item, val)
222

    
223
def ShowClusterConfig(opts, args):
224
  """Shows cluster information.
225

    
226
  @param opts: the command line options selected by the user
227
  @type args: list
228
  @param args: should be an empty list
229
  @rtype: int
230
  @return: the desired exit code
231

    
232
  """
233
  cl = GetClient()
234
  result = cl.QueryClusterInfo()
235

    
236
  ToStdout("Cluster name: %s", result["name"])
237

    
238
  ToStdout("Master node: %s", result["master"])
239

    
240
  ToStdout("Architecture (this node): %s (%s)",
241
           result["architecture"][0], result["architecture"][1])
242

    
243
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
244
  ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
245

    
246
  ToStdout("Hypervisor parameters:")
247
  _PrintGroupedParams(result["hvparams"])
248

    
249
  ToStdout("Cluster parameters:")
250
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
251
  ToStdout("  - master netdev: %s", result["master_netdev"])
252
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
253
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
254

    
255
  ToStdout("Default instance parameters:")
256
  _PrintGroupedParams(result["beparams"])
257

    
258
  ToStdout("Default nic parameters:")
259
  _PrintGroupedParams(result["nicparams"])
260

    
261
  return 0
262

    
263

    
264
def ClusterCopyFile(opts, args):
265
  """Copy a file from master to some nodes.
266

    
267
  @param opts: the command line options selected by the user
268
  @type args: list
269
  @param args: should contain only one element, the path of
270
      the file to be copied
271
  @rtype: int
272
  @return: the desired exit code
273

    
274
  """
275
  filename = args[0]
276
  if not os.path.exists(filename):
277
    raise errors.OpPrereqError("No such filename '%s'" % filename)
278

    
279
  cl = GetClient()
280

    
281
  myname = utils.HostInfo().name
282

    
283
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
284

    
285
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
286
  results = [name for name in results if name != myname]
287

    
288
  srun = ssh.SshRunner(cluster_name=cluster_name)
289
  for node in results:
290
    if not srun.CopyFileToNode(node, filename):
291
      ToStderr("Copy of file %s to node %s failed", filename, node)
292

    
293
  return 0
294

    
295

    
296
def RunClusterCommand(opts, args):
297
  """Run a command on some nodes.
298

    
299
  @param opts: the command line options selected by the user
300
  @type args: list
301
  @param args: should contain the command to be run and its arguments
302
  @rtype: int
303
  @return: the desired exit code
304

    
305
  """
306
  cl = GetClient()
307

    
308
  command = " ".join(args)
309

    
310
  nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
311

    
312
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
313
                                                    "master_node"])
314

    
315
  srun = ssh.SshRunner(cluster_name=cluster_name)
316

    
317
  # Make sure master node is at list end
318
  if master_node in nodes:
319
    nodes.remove(master_node)
320
    nodes.append(master_node)
321

    
322
  for name in nodes:
323
    result = srun.Run(name, "root", command)
324
    ToStdout("------------------------------------------------")
325
    ToStdout("node: %s", name)
326
    ToStdout("%s", result.output)
327
    ToStdout("return code = %s", result.exit_code)
328

    
329
  return 0
330

    
331

    
332
def VerifyCluster(opts, args):
333
  """Verify integrity of cluster, performing various test on nodes.
334

    
335
  @param opts: the command line options selected by the user
336
  @type args: list
337
  @param args: should be an empty list
338
  @rtype: int
339
  @return: the desired exit code
340

    
341
  """
342
  skip_checks = []
343
  if opts.skip_nplusone_mem:
344
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
345
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
346
  if SubmitOpCode(op):
347
    return 0
348
  else:
349
    return 1
350

    
351

    
352
def VerifyDisks(opts, args):
353
  """Verify integrity of cluster disks.
354

    
355
  @param opts: the command line options selected by the user
356
  @type args: list
357
  @param args: should be an empty list
358
  @rtype: int
359
  @return: the desired exit code
360

    
361
  """
362
  op = opcodes.OpVerifyDisks()
363
  result = SubmitOpCode(op)
364
  if not isinstance(result, (list, tuple)) or len(result) != 4:
365
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
366

    
367
  nodes, nlvm, instances, missing = result
368

    
369
  if nodes:
370
    ToStdout("Nodes unreachable or with bad data:")
371
    for name in nodes:
372
      ToStdout("\t%s", name)
373
  retcode = constants.EXIT_SUCCESS
374

    
375
  if nlvm:
376
    for node, text in nlvm.iteritems():
377
      ToStdout("Error on node %s: LVM error: %s",
378
               node, utils.SafeEncode(text[-400:]))
379
      retcode |= 1
380
      ToStdout("You need to fix these nodes first before fixing instances")
381

    
382
  if instances:
383
    for iname in instances:
384
      if iname in missing:
385
        continue
386
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
387
      try:
388
        ToStdout("Activating disks for instance '%s'", iname)
389
        SubmitOpCode(op)
390
      except errors.GenericError, err:
391
        nret, msg = FormatError(err)
392
        retcode |= nret
393
        ToStderr("Error activating disks for instance %s: %s", iname, msg)
394

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

    
413
  return retcode
414

    
415

    
416
@UsesRPC
417
def MasterFailover(opts, args):
418
  """Failover the master node.
419

    
420
  This command, when run on a non-master node, will cause the current
421
  master to cease being master, and the non-master to become new
422
  master.
423

    
424
  @param opts: the command line options selected by the user
425
  @type args: list
426
  @param args: should be an empty list
427
  @rtype: int
428
  @return: the desired exit code
429

    
430
  """
431
  return bootstrap.MasterFailover()
432

    
433

    
434
def SearchTags(opts, args):
435
  """Searches the tags on all the cluster.
436

    
437
  @param opts: the command line options selected by the user
438
  @type args: list
439
  @param args: should contain only one element, the tag pattern
440
  @rtype: int
441
  @return: the desired exit code
442

    
443
  """
444
  op = opcodes.OpSearchTags(pattern=args[0])
445
  result = SubmitOpCode(op)
446
  if not result:
447
    return 1
448
  result = list(result)
449
  result.sort()
450
  for path, tag in result:
451
    ToStdout("%s %s", path, tag)
452

    
453

    
454
def SetClusterParams(opts, args):
455
  """Modify the cluster.
456

    
457
  @param opts: the command line options selected by the user
458
  @type args: list
459
  @param args: should be an empty list
460
  @rtype: int
461
  @return: the desired exit code
462

    
463
  """
464
  if not (not opts.lvm_storage or opts.vg_name or
465
          opts.enabled_hypervisors or opts.hvparams or
466
          opts.beparams or opts.nicparams or
467
          opts.candidate_pool_size is not None):
468
    ToStderr("Please give at least one of the parameters.")
469
    return 1
470

    
471
  vg_name = opts.vg_name
472
  if not opts.lvm_storage and opts.vg_name:
473
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
474
    return 1
475
  elif not opts.lvm_storage:
476
    vg_name = ''
477

    
478
  hvlist = opts.enabled_hypervisors
479
  if hvlist is not None:
480
    hvlist = hvlist.split(",")
481

    
482
  # a list of (name, dict) we can pass directly to dict() (or [])
483
  hvparams = dict(opts.hvparams)
484
  for hv, hv_params in hvparams.iteritems():
485
    utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
486

    
487
  beparams = opts.beparams
488
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
489

    
490
  nicparams = opts.nicparams
491
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
492

    
493
  op = opcodes.OpSetClusterParams(vg_name=vg_name,
494
                                  enabled_hypervisors=hvlist,
495
                                  hvparams=hvparams,
496
                                  beparams=beparams,
497
                                  nicparams=nicparams,
498
                                  candidate_pool_size=opts.candidate_pool_size)
499
  SubmitOpCode(op)
500
  return 0
501

    
502

    
503
def QueueOps(opts, args):
504
  """Queue operations.
505

    
506
  @param opts: the command line options selected by the user
507
  @type args: list
508
  @param args: should contain only one element, the subcommand
509
  @rtype: int
510
  @return: the desired exit code
511

    
512
  """
513
  command = args[0]
514
  client = GetClient()
515
  if command in ("drain", "undrain"):
516
    drain_flag = command == "drain"
517
    client.SetQueueDrainFlag(drain_flag)
518
  elif command == "info":
519
    result = client.QueryConfigValues(["drain_flag"])
520
    if result[0]:
521
      val = "set"
522
    else:
523
      val = "unset"
524
    ToStdout("The drain flag is %s" % val)
525
  else:
526
    raise errors.OpPrereqError("Command '%s' is not valid." % command)
527

    
528
  return 0
529

    
530
# this is an option common to more than one command, so we declare
531
# it here and reuse it
532
node_option = make_option("-n", "--node", action="append", dest="nodes",
533
                          help="Node to copy to (if not given, all nodes),"
534
                               " can be given multiple times",
535
                          metavar="<node>", default=[])
536

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

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