Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ b119bccb

History | View | Annotate | Download (23.5 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 = dict(opts.hvparams)
73

    
74
  beparams = opts.beparams
75
  # check for invalid parameters
76
  for parameter in beparams:
77
    if parameter not in constants.BES_PARAMETERS:
78
      ToStderr("Invalid backend parameter: %s", parameter)
79
      return 1
80

    
81
  # prepare beparams dict
82
  for parameter in constants.BES_PARAMETERS:
83
    if parameter not in beparams:
84
      beparams[parameter] = constants.BEC_DEFAULTS[parameter]
85
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
86

    
87
  # prepare hvparams dict
88
  for hv in constants.HYPER_TYPES:
89
    if hv not in hvparams:
90
      hvparams[hv] = {}
91
    for parameter in constants.HVC_DEFAULTS[hv]:
92
      if parameter not in hvparams[hv]:
93
        hvparams[hv][parameter] = constants.HVC_DEFAULTS[hv][parameter]
94
    utils.ForceDictType(hvparams[hv], constants.HVS_PARAMETER_TYPES)
95

    
96
  bootstrap.InitCluster(cluster_name=args[0],
97
                        secondary_ip=opts.secondary_ip,
98
                        vg_name=vg_name,
99
                        mac_prefix=opts.mac_prefix,
100
                        def_bridge=opts.def_bridge,
101
                        master_netdev=opts.master_netdev,
102
                        file_storage_dir=opts.file_storage_dir,
103
                        enabled_hypervisors=hvlist,
104
                        default_hypervisor=opts.default_hypervisor,
105
                        hvparams=hvparams,
106
                        beparams=beparams,
107
                        candidate_pool_size=opts.candidate_pool_size,
108
                        )
109
  return 0
110

    
111

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

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

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

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

    
135

    
136
def RenameCluster(opts, args):
137
  """Rename the cluster.
138

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

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

    
155
  op = opcodes.OpRenameCluster(name=name)
156
  SubmitOpCode(op)
157
  return 0
158

    
159

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

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

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

    
174

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

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

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

    
194

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

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

    
204
  """
205
  master = bootstrap.GetMaster()
206
  ToStdout(master)
207
  return 0
208

    
209

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

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

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

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

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

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

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

    
233
  ToStdout("Hypervisor parameters:")
234
  for hv_name, hv_dict in result["hvparams"].items():
235
    ToStdout("  - %s:", hv_name)
236
    for item, val in hv_dict.iteritems():
237
      ToStdout("      %s: %s", item, val)
238

    
239
  ToStdout("Cluster parameters:")
240
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
241
  ToStdout("  - master netdev: %s", result["master_netdev"])
242
  ToStdout("  - default bridge: %s", result["default_bridge"])
243
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
244
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
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
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
277
  results = [name for name in results if name != 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

    
301
  nodes = GetOnlineNodes(nodes=opts.nodes, 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, utils.SafeEncode(text[-400:]))
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
  if opts.no_voting:
423
    usertext = ("This will perform the failover even if most other nodes"
424
                " are down, or if this node is outdated. This is dangerous"
425
                " as it can lead to a non-consistent cluster. Check the"
426
                " gnt-cluster(8) man page before proceeding. Continue?")
427
    if not AskUser(usertext):
428
      return 1
429

    
430
  return bootstrap.MasterFailover(no_voting=opts.no_voting)
431

    
432

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

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

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

    
452

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

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

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

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

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

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

    
485
  beparams = opts.beparams
486
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
487

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

    
496

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

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

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

    
522
  return 0
523

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

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

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