gnt-cluster 21.6 KB
Newer Older
Iustin Pop's avatar
Iustin Pop committed
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
#!/usr/bin/python
#

# Copyright (C) 2006, 2007 Google Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful, but
# WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
# General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
# 02110-1301, USA.


22
23
24
25
# pylint: disable-msg=W0401,W0614
# W0401: Wildcard import ganeti.cli
# W0614: Unused import %s from wildcard import (since we need cli)

Iustin Pop's avatar
Iustin Pop committed
26
27
import sys
from optparse import make_option
28
import os.path
Iustin Pop's avatar
Iustin Pop committed
29
30
31

from ganeti.cli import *
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
Iustin Pop's avatar
Iustin Pop committed
37
38
39
40
41


def InitCluster(opts, args):
  """Initialize the cluster.

42
43
44
45
46
47
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the desired
      cluster name
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
48
49

  """
50
  if not opts.lvm_storage and opts.vg_name:
51
    ToStderr("Options --no-lvm-storage and --vg-name conflict.")
52
53
54
55
56
57
    return 1

  vg_name = opts.vg_name
  if opts.lvm_storage and not opts.vg_name:
    vg_name = constants.DEFAULT_VG

58
59
60
61
  hvlist = opts.enabled_hypervisors
  if hvlist is not None:
    hvlist = hvlist.split(",")
  else:
62
63
64
65
66
67
68
    hvlist = [constants.DEFAULT_ENABLED_HYPERVISOR]

  # avoid an impossible situation
  if opts.default_hypervisor in hvlist:
    default_hypervisor = opts.default_hypervisor
  else:
    default_hypervisor = hvlist[0]
69
70
71
72
73
74
75
76
77
78
79
80
81

  hvparams = opts.hvparams
  if hvparams:
    # a list of (name, dict) we can pass directly to dict()
    hvparams = dict(opts.hvparams)
  else:
    # otherwise init as empty dict
    hvparams = {}

  beparams = opts.beparams
  # check for invalid parameters
  for parameter in beparams:
    if parameter not in constants.BES_PARAMETERS:
82
      ToStderr("Invalid backend parameter: %s", parameter)
83
84
85
86
87
88
89
90
91
92
93
      return 1

  # prepare beparams dict
  for parameter in constants.BES_PARAMETERS:
    if parameter not in beparams:
      beparams[parameter] = constants.BEC_DEFAULTS[parameter]

  # type wrangling
  try:
    beparams[constants.BE_VCPUS] = int(beparams[constants.BE_VCPUS])
  except ValueError:
94
    ToStderr("%s must be an integer", constants.BE_VCPUS)
95
96
    return 1

97
98
99
  if not isinstance(beparams[constants.BE_MEMORY], int):
    beparams[constants.BE_MEMORY] = utils.ParseUnit(
        beparams[constants.BE_MEMORY])
100
101
102
103
104
105
106
107
108
109
110

  # prepare hvparams dict
  for hv in constants.HYPER_TYPES:
    if hv not in hvparams:
      hvparams[hv] = {}
    for parameter in constants.HVC_DEFAULTS[hv]:
      if parameter not in hvparams[hv]:
        hvparams[hv][parameter] = constants.HVC_DEFAULTS[hv][parameter]

  for hv in hvlist:
    if hv not in constants.HYPER_TYPES:
111
      ToStderr("invalid hypervisor: %s", hv)
112
113
      return 1

114
115
116
117
118
119
  bootstrap.InitCluster(cluster_name=args[0],
                        secondary_ip=opts.secondary_ip,
                        vg_name=vg_name,
                        mac_prefix=opts.mac_prefix,
                        def_bridge=opts.def_bridge,
                        master_netdev=opts.master_netdev,
120
121
                        file_storage_dir=opts.file_storage_dir,
                        enabled_hypervisors=hvlist,
122
                        default_hypervisor=default_hypervisor,
123
124
                        hvparams=hvparams,
                        beparams=beparams)
Iustin Pop's avatar
Iustin Pop committed
125
126
127
128
129
130
  return 0


def DestroyCluster(opts, args):
  """Destroy the cluster.

131
132
133
134
135
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
136

Iustin Pop's avatar
Iustin Pop committed
137
138
  """
  if not opts.yes_do_it:
139
140
    ToStderr("Destroying a cluster is irreversible. If you really want"
             " destroy this cluster, supply the --yes-do-it option.")
Iustin Pop's avatar
Iustin Pop committed
141
142
143
    return 1

  op = opcodes.OpDestroyCluster()
Iustin Pop's avatar
Iustin Pop committed
144
145
146
147
  master = SubmitOpCode(op)
  # if we reached this, the opcode didn't fail; we can proceed to
  # shutdown all the daemons
  bootstrap.FinalizeClusterDestroy(master)
Iustin Pop's avatar
Iustin Pop committed
148
149
150
  return 0


151
152
153
def RenameCluster(opts, args):
  """Rename the cluster.

154
155
156
157
158
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the new cluster name
  @rtype: int
  @return: the desired exit code
159
160
161
162
163
164
165
166

  """
  name = args[0]
  if not opts.force:
    usertext = ("This will rename the cluster to '%s'. If you are connected"
                " over the network to the cluster name, the operation is very"
                " dangerous as the IP address will be removed from the node"
                " and the change may not go through. Continue?") % name
167
    if not AskUser(usertext):
168
169
170
171
172
173
174
      return 1

  op = opcodes.OpRenameCluster(name=name)
  SubmitOpCode(op)
  return 0


Iustin Pop's avatar
Iustin Pop committed
175
176
177
def ShowClusterVersion(opts, args):
  """Write version of ganeti software to the standard output.

178
179
180
181
182
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
183
184
185
186

  """
  op = opcodes.OpQueryClusterInfo()
  result = SubmitOpCode(op)
187
188
189
190
191
  ToStdout("Software version: %s", result["software_version"])
  ToStdout("Internode protocol: %s", result["protocol_version"])
  ToStdout("Configuration format: %s", result["config_version"])
  ToStdout("OS api version: %s", result["os_api_version"])
  ToStdout("Export interface: %s", result["export_version"])
Iustin Pop's avatar
Iustin Pop committed
192
193
194
195
196
197
  return 0


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

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

  """
205
  ToStdout("%s", GetClient().QueryConfigValues(["master_node"])[0])
Iustin Pop's avatar
Iustin Pop committed
206
207
208
209
210
211
  return 0


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

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

Iustin Pop's avatar
Iustin Pop committed
218
219
220
221
  """
  op = opcodes.OpQueryClusterInfo()
  result = SubmitOpCode(op)

222
  ToStdout("Cluster name: %s", result["name"])
Iustin Pop's avatar
Iustin Pop committed
223

224
  ToStdout("Master node: %s", result["master"])
Iustin Pop's avatar
Iustin Pop committed
225

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

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

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

238
  ToStdout("Cluster parameters:")
239
  for gr_name, gr_dict in result["beparams"].items():
240
    ToStdout("  - %s:", gr_name)
241
    for item, val in gr_dict.iteritems():
242
      ToStdout("      %s: %s", item, val)
243

Iustin Pop's avatar
Iustin Pop committed
244
245
246
247
248
249
  return 0


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

250
251
252
253
254
255
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the path of
      the file to be copied
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
256
257

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

Iustin Pop's avatar
Iustin Pop committed
262
263
  cl = GetClient()

264
265
  myname = utils.HostInfo().name

Iustin Pop's avatar
Iustin Pop committed
266
267
  cluster_name = cl.QueryConfigValues(["cluster_name"])[0]

268
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
Iustin Pop's avatar
Iustin Pop committed
269
  results = [row[0] for row in SubmitOpCode(op, cl=cl) if row[0] != myname]
Michael Hanselmann's avatar
Michael Hanselmann committed
270

Iustin Pop's avatar
Iustin Pop committed
271
  srun = ssh.SshRunner(cluster_name=cluster_name)
272
273
  for node in results:
    if not srun.CopyFileToNode(node, filename):
274
      ToStderr("Copy of file %s to node %s failed", filename, node)
275

Iustin Pop's avatar
Iustin Pop committed
276
277
278
279
280
281
  return 0


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

282
283
284
285
286
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain the command to be run and its arguments
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
287
288

  """
Iustin Pop's avatar
Iustin Pop committed
289
  cl = GetClient()
Michael Hanselmann's avatar
Michael Hanselmann committed
290

Iustin Pop's avatar
Iustin Pop committed
291
  command = " ".join(args)
292
  op = opcodes.OpQueryNodes(output_fields=["name"], names=opts.nodes)
Iustin Pop's avatar
Iustin Pop committed
293
294
295
296
  nodes = [row[0] for row in SubmitOpCode(op, cl=cl)]

  cluster_name, master_node = cl.QueryConfigValues(["cluster_name",
                                                    "master_node"])
297

Iustin Pop's avatar
Iustin Pop committed
298
  srun = ssh.SshRunner(cluster_name=cluster_name)
299

Michael Hanselmann's avatar
Michael Hanselmann committed
300
  # Make sure master node is at list end
301
302
303
304
305
306
  if master_node in nodes:
    nodes.remove(master_node)
    nodes.append(master_node)

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

  return 0
Iustin Pop's avatar
Iustin Pop committed
313
314
315
316
317


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

318
319
320
321
322
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
Iustin Pop's avatar
Iustin Pop committed
323
324

  """
Iustin Pop's avatar
Iustin Pop committed
325
  skip_checks = []
326
327
328
  if opts.skip_nplusone_mem:
    skip_checks.append(constants.VERIFY_NPLUSONE_MEM)
  op = opcodes.OpVerifyCluster(skip_checks=skip_checks)
329
330
331
332
  if SubmitOpCode(op):
    return 0
  else:
    return 1
Iustin Pop's avatar
Iustin Pop committed
333
334


335
336
337
def VerifyDisks(opts, args):
  """Verify integrity of cluster disks.

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

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

350
351
  nodes, nlvm, instances, missing = result

352
  if nodes:
353
    ToStdout("Nodes unreachable or with bad data:")
354
    for name in nodes:
355
      ToStdout("\t%s", name)
356
  retcode = constants.EXIT_SUCCESS
357
358
359

  if nlvm:
    for node, text in nlvm.iteritems():
360
361
      ToStdout("Error on node %s: LVM error: %s",
               node, text[-400:].encode('string_escape'))
362
      retcode |= 1
363
      ToStdout("You need to fix these nodes first before fixing instances")
364

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

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

  return retcode


Iustin Pop's avatar
Iustin Pop committed
399
400
401
402
403
404
405
def MasterFailover(opts, args):
  """Failover the master node.

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

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

Iustin Pop's avatar
Iustin Pop committed
412
  """
413
  return bootstrap.MasterFailover()
Iustin Pop's avatar
Iustin Pop committed
414
415


Iustin Pop's avatar
Iustin Pop committed
416
417
418
def SearchTags(opts, args):
  """Searches the tags on all the cluster.

419
420
421
422
423
424
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the tag pattern
  @rtype: int
  @return: the desired exit code

Iustin Pop's avatar
Iustin Pop committed
425
426
427
428
429
430
431
432
  """
  op = opcodes.OpSearchTags(pattern=args[0])
  result = SubmitOpCode(op)
  if not result:
    return 1
  result = list(result)
  result.sort()
  for path, tag in result:
433
    ToStdout("%s %s", path, tag)
Iustin Pop's avatar
Iustin Pop committed
434
435


436
437
438
def SetClusterParams(opts, args):
  """Modify the cluster.

439
440
441
442
443
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should be an empty list
  @rtype: int
  @return: the desired exit code
444
445

  """
446
447
448
  if not (not opts.lvm_storage or opts.vg_name or
          opts.enabled_hypervisors or opts.hvparams or
          opts.beparams):
449
    ToStderr("Please give at least one of the parameters.")
450
451
452
453
    return 1

  vg_name = opts.vg_name
  if not opts.lvm_storage and opts.vg_name:
454
    ToStdout("Options --no-lvm-storage and --vg-name conflict.")
455
456
    return 1

457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
  hvlist = opts.enabled_hypervisors
  if hvlist is not None:
    hvlist = hvlist.split(",")

  hvparams = opts.hvparams
  if hvparams:
    # a list of (name, dict) we can pass directly to dict()
    hvparams = dict(opts.hvparams)

  beparams = opts.beparams

  op = opcodes.OpSetClusterParams(vg_name=opts.vg_name,
                                  enabled_hypervisors=hvlist,
                                  hvparams=hvparams,
                                  beparams=beparams)
472
473
474
475
  SubmitOpCode(op)
  return 0


476
477
478
def QueueOps(opts, args):
  """Queue operations.

479
480
481
482
483
484
  @param opts: the command line options selected by the user
  @type args: list
  @param args: should contain only one element, the subcommand
  @rtype: int
  @return: the desired exit code

485
486
487
488
489
490
491
492
493
  """
  command = args[0]
  client = GetClient()
  if command in ("drain", "undrain"):
    drain_flag = command == "drain"
    client.SetQueueDrainFlag(drain_flag)
  elif command == "info":
    result = client.QueryConfigValues(["drain_flag"])
    if result[0]:
494
      val = "set"
495
    else:
496
497
      val = "unset"
    ToStdout("The drain flag is %s" % val)
498
499
  return 0

Iustin Pop's avatar
Iustin Pop committed
500
501
502
# this is an option common to more than one command, so we declare
# it here and reuse it
node_option = make_option("-n", "--node", action="append", dest="nodes",
503
504
505
                          help="Node to copy to (if not given, all nodes),"
                               " can be given multiple times",
                          metavar="<node>", default=[])
Iustin Pop's avatar
Iustin Pop committed
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523

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

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