Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ ec0652ad

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
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("  - default bridge: %s", result["default_bridge"])
253
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
254
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
255

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

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

    
262
  return 0
263

    
264

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

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

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

    
280
  cl = GetClient()
281

    
282
  myname = utils.HostInfo().name
283

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

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

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

    
294
  return 0
295

    
296

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

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

    
306
  """
307
  cl = GetClient()
308

    
309
  command = " ".join(args)
310

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

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

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

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

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

    
330
  return 0
331

    
332

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

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

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

    
352

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

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

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

    
368
  nodes, nlvm, instances, missing = result
369

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

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

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

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

    
414
  return retcode
415

    
416

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

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

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

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

    
434

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

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

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

    
454

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

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

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

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

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

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

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

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

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

    
503

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

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

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

    
529
  return 0
530

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

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

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