Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-cluster @ 28b498cd

History | View | Annotate | Download (25.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
import os.path
28
import time
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("Creation time: %s", utils.FormatTime(result["ctime"]))
225
  ToStdout("Modification time: %s", utils.FormatTime(result["mtime"]))
226

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

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

    
232
  if result["tags"]:
233
    tags = ", ".join(utils.NiceSort(result["tags"]))
234
  else:
235
    tags = "(none)"
236

    
237
  ToStdout("Tags: %s", tags)
238

    
239
  ToStdout("Default hypervisor: %s", result["default_hypervisor"])
240
  ToStdout("Enabled hypervisors: %s", ", ".join(result["enabled_hypervisors"]))
241

    
242
  ToStdout("Hypervisor parameters:")
243
  _PrintGroupedParams(result["hvparams"])
244

    
245
  ToStdout("Cluster parameters:")
246
  ToStdout("  - candidate pool size: %s", result["candidate_pool_size"])
247
  ToStdout("  - master netdev: %s", result["master_netdev"])
248
  ToStdout("  - lvm volume group: %s", result["volume_group_name"])
249
  ToStdout("  - file storage path: %s", result["file_storage_dir"])
250

    
251
  ToStdout("Default instance parameters:")
252
  _PrintGroupedParams(result["beparams"])
253

    
254
  ToStdout("Default nic parameters:")
255
  _PrintGroupedParams(result["nicparams"])
256

    
257
  return 0
258

    
259

    
260
def ClusterCopyFile(opts, args):
261
  """Copy a file from master to some nodes.
262

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

    
270
  """
271
  filename = args[0]
272
  if not os.path.exists(filename):
273
    raise errors.OpPrereqError("No such filename '%s'" % filename)
274

    
275
  cl = GetClient()
276

    
277
  myname = utils.HostInfo().name
278

    
279
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]
280

    
281
  results = GetOnlineNodes(nodes=opts.nodes, cl=cl)
282
  results = [name for name in results if name != myname]
283

    
284
  srun = ssh.SshRunner(cluster_name=cluster_name)
285
  for node in results:
286
    if not srun.CopyFileToNode(node, filename):
287
      ToStderr("Copy of file %s to node %s failed", filename, node)
288

    
289
  return 0
290

    
291

    
292
def RunClusterCommand(opts, args):
293
  """Run a command on some nodes.
294

    
295
  @param opts: the command line options selected by the user
296
  @type args: list
297
  @param args: should contain the command to be run and its arguments
298
  @rtype: int
299
  @return: the desired exit code
300

    
301
  """
302
  cl = GetClient()
303

    
304
  command = " ".join(args)
305

    
306
  nodes = GetOnlineNodes(nodes=opts.nodes, cl=cl)
307

    
308
  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
309
                                                    "master_node"])
310

    
311
  srun = ssh.SshRunner(cluster_name=cluster_name)
312

    
313
  # Make sure master node is at list end
314
  if master_node in nodes:
315
    nodes.remove(master_node)
316
    nodes.append(master_node)
317

    
318
  for name in nodes:
319
    result = srun.Run(name, "root", command)
320
    ToStdout("------------------------------------------------")
321
    ToStdout("node: %s", name)
322
    ToStdout("%s", result.output)
323
    ToStdout("return code = %s", result.exit_code)
324

    
325
  return 0
326

    
327

    
328
def VerifyCluster(opts, args):
329
  """Verify integrity of cluster, performing various test on nodes.
330

    
331
  @param opts: the command line options selected by the user
332
  @type args: list
333
  @param args: should be an empty list
334
  @rtype: int
335
  @return: the desired exit code
336

    
337
  """
338
  skip_checks = []
339
  if opts.skip_nplusone_mem:
340
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
341
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
342
  if SubmitOpCode(op):
343
    return 0
344
  else:
345
    return 1
346

    
347

    
348
def VerifyDisks(opts, args):
349
  """Verify integrity of cluster disks.
350

    
351
  @param opts: the command line options selected by the user
352
  @type args: list
353
  @param args: should be an empty list
354
  @rtype: int
355
  @return: the desired exit code
356

    
357
  """
358
  op = opcodes.OpVerifyDisks()
359
  result = SubmitOpCode(op)
360
  if not isinstance(result, (list, tuple)) or len(result) != 3:
361
    raise errors.ProgrammerError("Unknown result type for OpVerifyDisks")
362

    
363
  bad_nodes, instances, missing = result
364

    
365
  retcode = constants.EXIT_SUCCESS
366

    
367
  if bad_nodes:
368
    for node, text in bad_nodes.items():
369
      ToStdout("Error gathering data on node %s: %s",
370
               node, utils.SafeEncode(text[-400:]))
371
      retcode |= 1
372
      ToStdout("You need to fix these nodes first before fixing instances")
373

    
374
  if instances:
375
    for iname in instances:
376
      if iname in missing:
377
        continue
378
      op = opcodes.OpActivateInstanceDisks(instance_name=iname)
379
      try:
380
        ToStdout("Activating disks for instance '%s'", iname)
381
        SubmitOpCode(op)
382
      except errors.GenericError, err:
383
        nret, msg = FormatError(err)
384
        retcode |= nret
385
        ToStderr("Error activating disks for instance %s: %s", iname, msg)
386

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

    
405
  return retcode
406

    
407

    
408
def RepairDiskSizes(opts, args):
409
  """Verify sizes of cluster disks.
410

    
411
  @param opts: the command line options selected by the user
412
  @type args: list
413
  @param args: optional list of instances to restrict check to
414
  @rtype: int
415
  @return: the desired exit code
416

    
417
  """
418
  op = opcodes.OpRepairDiskSizes(instances=args)
419
  SubmitOpCode(op)
420

    
421

    
422
@UsesRPC
423
def MasterFailover(opts, args):
424
  """Failover the master node.
425

    
426
  This command, when run on a non-master node, will cause the current
427
  master to cease being master, and the non-master to become new
428
  master.
429

    
430
  @param opts: the command line options selected by the user
431
  @type args: list
432
  @param args: should be an empty list
433
  @rtype: int
434
  @return: the desired exit code
435

    
436
  """
437
  if opts.no_voting:
438
    usertext = ("This will perform the failover even if most other nodes"
439
                " are down, or if this node is outdated. This is dangerous"
440
                " as it can lead to a non-consistent cluster. Check the"
441
                " gnt-cluster(8) man page before proceeding. Continue?")
442
    if not AskUser(usertext):
443
      return 1
444

    
445
  return bootstrap.MasterFailover(no_voting=opts.no_voting)
446

    
447

    
448
def SearchTags(opts, args):
449
  """Searches the tags on all the cluster.
450

    
451
  @param opts: the command line options selected by the user
452
  @type args: list
453
  @param args: should contain only one element, the tag pattern
454
  @rtype: int
455
  @return: the desired exit code
456

    
457
  """
458
  op = opcodes.OpSearchTags(pattern=args[0])
459
  result = SubmitOpCode(op)
460
  if not result:
461
    return 1
462
  result = list(result)
463
  result.sort()
464
  for path, tag in result:
465
    ToStdout("%s %s", path, tag)
466

    
467

    
468
def SetClusterParams(opts, args):
469
  """Modify the cluster.
470

    
471
  @param opts: the command line options selected by the user
472
  @type args: list
473
  @param args: should be an empty list
474
  @rtype: int
475
  @return: the desired exit code
476

    
477
  """
478
  if not (not opts.lvm_storage or opts.vg_name or
479
          opts.enabled_hypervisors or opts.hvparams or
480
          opts.beparams or opts.nicparams or
481
          opts.candidate_pool_size is not None):
482
    ToStderr("Please give at least one of the parameters.")
483
    return 1
484

    
485
  vg_name = opts.vg_name
486
  if not opts.lvm_storage and opts.vg_name:
487
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
488
    return 1
489
  elif not opts.lvm_storage:
490
    vg_name = ''
491

    
492
  hvlist = opts.enabled_hypervisors
493
  if hvlist is not None:
494
    hvlist = hvlist.split(",")
495

    
496
  # a list of (name, dict) we can pass directly to dict() (or [])
497
  hvparams = dict(opts.hvparams)
498
  for hv, hv_params in hvparams.iteritems():
499
    utils.ForceDictType(hv_params, constants.HVS_PARAMETER_TYPES)
500

    
501
  beparams = opts.beparams
502
  utils.ForceDictType(beparams, constants.BES_PARAMETER_TYPES)
503

    
504
  nicparams = opts.nicparams
505
  utils.ForceDictType(nicparams, constants.NICS_PARAMETER_TYPES)
506

    
507
  op = opcodes.OpSetClusterParams(vg_name=vg_name,
508
                                  enabled_hypervisors=hvlist,
509
                                  hvparams=hvparams,
510
                                  beparams=beparams,
511
                                  nicparams=nicparams,
512
                                  candidate_pool_size=opts.candidate_pool_size)
513
  SubmitOpCode(op)
514
  return 0
515

    
516

    
517
def QueueOps(opts, args):
518
  """Queue operations.
519

    
520
  @param opts: the command line options selected by the user
521
  @type args: list
522
  @param args: should contain only one element, the subcommand
523
  @rtype: int
524
  @return: the desired exit code
525

    
526
  """
527
  command = args[0]
528
  client = GetClient()
529
  if command in ("drain", "undrain"):
530
    drain_flag = command == "drain"
531
    client.SetQueueDrainFlag(drain_flag)
532
  elif command == "info":
533
    result = client.QueryConfigValues(["drain_flag"])
534
    if result[0]:
535
      val = "set"
536
    else:
537
      val = "unset"
538
    ToStdout("The drain flag is %s" % val)
539
  else:
540
    raise errors.OpPrereqError("Command '%s' is not valid." % command)
541

    
542
  return 0
543

    
544

    
545
def _ShowWatcherPause(until):
546
  if until is None or until < time.time():
547
    ToStdout("The watcher is not paused.")
548
  else:
549
    ToStdout("The watcher is paused until %s.", time.ctime(until))
550

    
551

    
552
def WatcherOps(opts, args):
553
  """Watcher operations.
554

    
555
  @param opts: the command line options selected by the user
556
  @type args: list
557
  @param args: should contain only one element, the subcommand
558
  @rtype: int
559
  @return: the desired exit code
560

    
561
  """
562
  command = args[0]
563
  client = GetClient()
564

    
565
  if command == "continue":
566
    client.SetWatcherPause(None)
567
    ToStdout("The watcher is no longer paused.")
568

    
569
  elif command == "pause":
570
    if len(args) < 2:
571
      raise errors.OpPrereqError("Missing pause duration")
572

    
573
    result = client.SetWatcherPause(time.time() + ParseTimespec(args[1]))
574
    _ShowWatcherPause(result)
575

    
576
  elif command == "info":
577
    result = client.QueryConfigValues(["watcher_pause"])
578
    _ShowWatcherPause(result)
579

    
580
  else:
581
    raise errors.OpPrereqError("Command '%s' is not valid." % command)
582

    
583
  return 0
584

    
585

    
586
# this is an option common to more than one command, so we declare
587
# it here and reuse it
588
node_option = cli_option("-n", "--node", action="append", dest="nodes",
589
                         help="Node to copy to (if not given, all nodes),"
590
                              " can be given multiple times",
591
                         metavar="<node>", default=[])
592

    
593
commands = {
594
  'init': (InitCluster, [ArgHost(min=1, max=1)],
595
           [DEBUG_OPT,
596
            cli_option("-s", "--secondary-ip", dest="secondary_ip",
597
                       help="Specify the secondary ip for this node;"
598
                       " if given, the entire cluster must have secondary"
599
                       " addresses",
600
                       metavar="ADDRESS", default=None),
601
            cli_option("-m", "--mac-prefix", dest="mac_prefix",
602
                       help="Specify the mac prefix for the instance IP"
603
                       " addresses, in the format XX:XX:XX",
604
                       metavar="PREFIX",
605
                       default=constants.DEFAULT_MAC_PREFIX,),
606
            cli_option("-g", "--vg-name", dest="vg_name",
607
                       help="Specify the volume group name "
608
                       " (cluster-wide) for disk allocation [xenvg]",
609
                       metavar="VG",
610
                       default=None,),
611
            cli_option("--master-netdev", dest="master_netdev",
612
                       help="Specify the node interface (cluster-wide)"
613
                         " on which the master IP address will be added "
614
                         " [%s]" % constants.DEFAULT_BRIDGE,
615
                       metavar="NETDEV",
616
                       default=constants.DEFAULT_BRIDGE,),
617
            cli_option("--file-storage-dir", dest="file_storage_dir",
618
                       help="Specify the default directory (cluster-wide)"
619
                            " for storing the file-based disks [%s]" %
620
                            constants.DEFAULT_FILE_STORAGE_DIR,
621
                       metavar="DIR",
622
                       default=constants.DEFAULT_FILE_STORAGE_DIR,),
623
            cli_option("--no-lvm-storage", dest="lvm_storage",
624
                       help="No support for lvm based instances"
625
                            " (cluster-wide)",
626
                       action="store_false", default=True,),
627
            cli_option("--no-etc-hosts", dest="modify_etc_hosts",
628
                       help="Don't modify /etc/hosts"
629
                            " (cluster-wide)",
630
                       action="store_false", default=True,),
631
            cli_option("--enabled-hypervisors", dest="enabled_hypervisors",
632
                       help="Comma-separated list of hypervisors",
633
                       type="string",
634
                       default=constants.DEFAULT_ENABLED_HYPERVISOR),
635
            cli_option("-H", "--hypervisor-parameters", dest="hvparams",
636
                       help="Hypervisor and hypervisor options, in the format"
637
                            " hypervisor:option=value,option=value,...",
638
                       default=[],
639
                       action="append",
640
                       type="identkeyval"),
641
            cli_option("-B", "--backend-parameters", dest="beparams",
642
                       type="keyval", default={},
643
                       help="Backend parameters"),
644
            cli_option("-N", "--nic-parameters", dest="nicparams",
645
                       type="keyval", default={},
646
                       help="NIC parameters"),
647
            cli_option("-C", "--candidate-pool-size",
648
                       default=constants.MASTER_POOL_SIZE_DEFAULT,
649
                       help="Set the candidate pool size",
650
                       dest="candidate_pool_size", type="int"),
651
            ],
652
           "[opts...] <cluster_name>",
653
           "Initialises a new cluster configuration"),
654
  'destroy': (DestroyCluster, ARGS_NONE,
655
              [DEBUG_OPT,
656
               cli_option("--yes-do-it", dest="yes_do_it",
657
                          help="Destroy cluster",
658
                          action="store_true"),
659
              ],
660
              "", "Destroy cluster"),
661
  'rename': (RenameCluster, [ArgHost(min=1, max=1)],
662
             [DEBUG_OPT, FORCE_OPT],
663
             "<new_name>",
664
             "Renames the cluster"),
665
  'redist-conf': (RedistributeConfig, ARGS_NONE, [DEBUG_OPT, SUBMIT_OPT],
666
                  "",
667
                  "Forces a push of the configuration file and ssconf files"
668
                  " to the nodes in the cluster"),
669
  'verify': (VerifyCluster, ARGS_NONE, [DEBUG_OPT,
670
             cli_option("--no-nplus1-mem", dest="skip_nplusone_mem",
671
                        help="Skip N+1 memory redundancy tests",
672
                        action="store_true",
673
                        default=False,),
674
             ],
675
             "", "Does a check on the cluster configuration"),
676
  'verify-disks': (VerifyDisks, ARGS_NONE, [DEBUG_OPT],
677
                   "", "Does a check on the cluster disk status"),
678
  'repair-disk-sizes': (RepairDiskSizes, ARGS_MANY_INSTANCES, [DEBUG_OPT],
679
                   "", "Updates mismatches in recorded disk sizes"),
680
  'masterfailover': (MasterFailover, ARGS_NONE, [DEBUG_OPT,
681
                     cli_option("--no-voting", dest="no_voting",
682
                                help="Skip node agreement check (dangerous)",
683
                                action="store_true",
684
                                default=False,),
685
                     ],
686
                     "", "Makes the current node the master"),
687
  'version': (ShowClusterVersion, ARGS_NONE, [DEBUG_OPT],
688
              "", "Shows the cluster version"),
689
  'getmaster': (ShowClusterMaster, ARGS_NONE, [DEBUG_OPT],
690
                "", "Shows the cluster master"),
691
  'copyfile': (ClusterCopyFile, [ArgFile(min=1, max=1)],
692
               [DEBUG_OPT, node_option],
693
               "[-n node...] <filename>",
694
               "Copies a file to all (or only some) nodes"),
695
  'command': (RunClusterCommand, [ArgCommand(min=1)], [DEBUG_OPT, node_option],
696
              "[-n node...] <command>",
697
              "Runs a command on all (or only some) nodes"),
698
  'info': (ShowClusterConfig, ARGS_NONE, [DEBUG_OPT],
699
           "", "Show cluster configuration"),
700
  'list-tags': (ListTags, ARGS_NONE,
701
                [DEBUG_OPT], "", "List the tags of the cluster"),
702
  'add-tags': (AddTags, [ArgUnknown()], [DEBUG_OPT, TAG_SRC_OPT],
703
               "tag...", "Add tags to the cluster"),
704
  'remove-tags': (RemoveTags, [ArgUnknown()], [DEBUG_OPT, TAG_SRC_OPT],
705
                  "tag...", "Remove tags from the cluster"),
706
  'search-tags': (SearchTags, [ArgUnknown(min=1, max=1)],
707
                  [DEBUG_OPT], "", "Searches the tags on all objects on"
708
                  " the cluster for a given pattern (regex)"),
709
  'queue': (QueueOps,
710
            [ArgChoice(min=1, max=1, choices=["drain", "undrain", "info"])],
711
            [DEBUG_OPT],
712
            "drain|undrain|info", "Change queue properties"),
713
  'watcher': (WatcherOps,
714
              [ArgChoice(min=1, max=1,
715
                         choices=["pause", "continue", "info"]),
716
               ArgSuggest(min=0, max=1, choices=["30m", "1h", "4h"])],
717
              [DEBUG_OPT],
718
              "{pause <timespec>|continue|info}", "Change watcher properties"),
719
  'modify': (SetClusterParams, ARGS_NONE,
720
             [DEBUG_OPT,
721
              cli_option("-g", "--vg-name", dest="vg_name",
722
                         help="Specify the volume group name "
723
                         " (cluster-wide) for disk allocation "
724
                         "and enable lvm based storage",
725
                         metavar="VG",),
726
              cli_option("--no-lvm-storage", dest="lvm_storage",
727
                         help="Disable support for lvm based instances"
728
                              " (cluster-wide)",
729
                         action="store_false", default=True,),
730
              cli_option("--enabled-hypervisors", dest="enabled_hypervisors",
731
                         help="Comma-separated list of hypervisors",
732
                         type="string", default=None),
733
              cli_option("-H", "--hypervisor-parameters", dest="hvparams",
734
                         help="Hypervisor and hypervisor options, in the"
735
                         " format"
736
                         " hypervisor:option=value,option=value,...",
737
                         default=[],
738
                         action="append",
739
                         type="identkeyval"),
740
              cli_option("-B", "--backend-parameters", dest="beparams",
741
                         type="keyval", default={},
742
                         help="Backend parameters"),
743
              cli_option("-N", "--nic-parameters", dest="nicparams",
744
                         type="keyval", default={},
745
                         help="NIC parameters"),
746
              cli_option("-C", "--candidate-pool-size", default=None,
747
                         help="Set the candidate pool size",
748
                         dest="candidate_pool_size", type="int"),
749
              ],
750
             "[opts...]",
751
             "Alters the parameters of the cluster"),
752
  }
753

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