Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ decd5f45

History | View | Annotate | Download (24 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
import sys
23
import os
24
import itertools
25
from optparse import make_option
26
from cStringIO import StringIO
27

    
28
from ganeti.cli import *
29
from ganeti import opcodes
30
from ganeti import logger
31
from ganeti import constants
32
from ganeti import utils
33
from ganeti import errors
34

    
35

    
36
_SHUTDOWN_CLUSTER = "cluster"
37
_SHUTDOWN_NODES_BOTH = "nodes"
38
_SHUTDOWN_NODES_PRI = "nodes-pri"
39
_SHUTDOWN_NODES_SEC = "nodes-sec"
40
_SHUTDOWN_INSTANCES = "instances"
41

    
42
def _ExpandMultiNames(mode, names):
43
  """Expand the given names using the passed mode.
44

    
45
  Args:
46
    - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
47
      _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
48
    - names, which is a list of names; for cluster, it must be empty,
49
      and for node and instance it must be a list of valid item
50
      names (short names are valid as usual, e.g. node1 instead of
51
      node1.example.com)
52

    
53
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
54
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
55
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
56
  instances having those nodes as either primary or secondary will be
57
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
58
  returned.
59

    
60
  """
61
  if mode == _SHUTDOWN_CLUSTER:
62
    if names:
63
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
64
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
65
    idata = SubmitOpCode(op)
66
    inames = [row[0] for row in idata]
67

    
68
  elif mode in (_SHUTDOWN_NODES_BOTH,
69
                _SHUTDOWN_NODES_PRI,
70
                _SHUTDOWN_NODES_SEC):
71
    if not names:
72
      raise errors.OpPrereqError("No node names passed")
73
    op = opcodes.OpQueryNodes(output_fields=["name", "pinst_list",
74
                                             "sinst_list"], names=names)
75
    ndata = SubmitOpCode(op)
76
    ipri = [row[1] for row in ndata]
77
    pri_names = list(itertools.chain(*ipri))
78
    isec = [row[2] for row in ndata]
79
    sec_names = list(itertools.chain(*isec))
80
    if mode == _SHUTDOWN_NODES_BOTH:
81
      inames = pri_names + sec_names
82
    elif mode == _SHUTDOWN_NODES_PRI:
83
      inames = pri_names
84
    elif mode == _SHUTDOWN_NODES_SEC:
85
      inames = sec_names
86
    else:
87
      raise errors.ProgrammerError("Unhandled shutdown type")
88

    
89
  elif mode == _SHUTDOWN_INSTANCES:
90
    if not names:
91
      raise errors.OpPrereqError("No instance names passed")
92
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
93
    idata = SubmitOpCode(op)
94
    inames = [row[0] for row in idata]
95

    
96
  else:
97
    raise errors.OpPrereqError("Unknown mode '%s'" % mode)
98

    
99
  return inames
100

    
101

    
102
def ListInstances(opts, args):
103
  """List nodes and their properties.
104

    
105
  """
106
  if opts.output is None:
107
    selected_fields = ["name", "os", "pnode", "admin_state",
108
                       "oper_state", "oper_ram"]
109
  else:
110
    selected_fields = opts.output.split(",")
111

    
112
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
113
  output = SubmitOpCode(op)
114

    
115
  if not opts.no_headers:
116
    headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
117
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
118
               "oper_state": "Status", "admin_ram": "Configured_memory",
119
               "oper_ram": "Memory", "disk_template": "Disk_template",
120
               "ip": "IP Address", "mac": "MAC Address",
121
               "bridge": "Bridge",
122
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
123
  else:
124
    headers = None
125

    
126
  if opts.human_readable:
127
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
128
  else:
129
    unitfields = None
130

    
131
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
132

    
133
  # change raw values to nicer strings
134
  for row in output:
135
    for idx, field in enumerate(selected_fields):
136
      val = row[idx]
137
      if field == "snodes":
138
        val = ",".join(val) or "-"
139
      elif field == "admin_state":
140
        if val:
141
          val = "yes"
142
        else:
143
          val = "no"
144
      elif field == "oper_state":
145
        if val is None:
146
          val = "(node down)"
147
        elif val: # True
148
          val = "running"
149
        else:
150
          val = "stopped"
151
      elif field == "oper_ram":
152
        if val is None:
153
          val = "(node down)"
154
      elif field == "sda_size" or field == "sdb_size":
155
        if val is None:
156
          val = "N/A"
157
      row[idx] = str(val)
158

    
159
  data = GenerateTable(separator=opts.separator, headers=headers,
160
                       fields=selected_fields, unitfields=unitfields,
161
                       numfields=numfields, data=output)
162

    
163
  for line in data:
164
    logger.ToStdout(line)
165

    
166
  return 0
167

    
168

    
169
def AddInstance(opts, args):
170
  """Add an instance to the cluster.
171

    
172
  Args:
173
    opts - class with options as members
174
    args - list with a single element, the instance name
175
  Opts used:
176
    mem - amount of memory to allocate to instance (MiB)
177
    size - amount of disk space to allocate to instance (MiB)
178
    os - which OS to run on instance
179
    node - node to run new instance on
180

    
181
  """
182
  instance = args[0]
183

    
184
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
185
                                disk_size=opts.size, swap_size=opts.swap,
186
                                disk_template=opts.disk_template,
187
                                mode=constants.INSTANCE_CREATE,
188
                                os_type=opts.os, pnode=opts.node,
189
                                snode=opts.snode, vcpus=opts.vcpus,
190
                                ip=opts.ip, bridge=opts.bridge, start=True,
191
                                wait_for_sync=opts.wait_for_sync)
192
  SubmitOpCode(op)
193
  return 0
194

    
195

    
196
def ReinstallInstance(opts, args):
197
  """Reinstall an instance.
198

    
199
  Args:
200
    opts - class with options as members
201
    args - list containing a single element, the instance name
202

    
203
  """
204
  instance_name = args[0]
205

    
206
  if not opts.force:
207
    usertext = ("This will reinstall the instance %s and remove "
208
                "all data. Continue?") % instance_name
209
    if not opts._ask_user(usertext):
210
      return 1
211

    
212
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
213
                                   os_type=opts.os)
214
  SubmitOpCode(op)
215

    
216
  return 0
217

    
218

    
219
def RemoveInstance(opts, args):
220
  """Remove an instance.
221

    
222
  Args:
223
    opts - class with options as members
224
    args - list containing a single element, the instance name
225

    
226
  """
227
  instance_name = args[0]
228
  force = opts.force
229

    
230
  if not force:
231
    usertext = ("This will remove the volumes of the instance %s"
232
                " (including mirrors), thus removing all the data"
233
                " of the instance. Continue?") % instance_name
234
    if not opts._ask_user(usertext):
235
      return 1
236

    
237
  op = opcodes.OpRemoveInstance(instance_name=instance_name)
238
  SubmitOpCode(op)
239
  return 0
240

    
241

    
242
def RenameInstance(opts, args):
243
  """Reinstall an instance.
244

    
245
  Args:
246
    opts - class with options as members
247
    args - list containing two elements, the instance name and the new name
248

    
249
  """
250
  op = opcodes.OpRenameInstance(instance_name=args[0],
251
                                new_name=args[1],
252
                                ignore_ip=opts.ignore_ip)
253
  SubmitOpCode(op)
254

    
255
  return 0
256

    
257

    
258
def ActivateDisks(opts, args):
259
  """Activate an instance's disks.
260

    
261
  This serves two purposes:
262
    - it allows one (as long as the instance is not running) to mount
263
    the disks and modify them from the node
264
    - it repairs inactive secondary drbds
265

    
266
  """
267
  instance_name = args[0]
268
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
269
  disks_info = SubmitOpCode(op)
270
  for host, iname, nname in disks_info:
271
    print "%s:%s:%s" % (host, iname, nname)
272
  return 0
273

    
274

    
275
def DeactivateDisks(opts, args):
276
  """Command-line interface for _ShutdownInstanceBlockDevices.
277

    
278
  This function takes the instance name, looks for its primary node
279
  and the tries to shutdown its block devices on that node.
280

    
281
  """
282
  instance_name = args[0]
283
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
284
  SubmitOpCode(op)
285
  return 0
286

    
287

    
288
def StartupInstance(opts, args):
289
  """Startup an instance.
290

    
291
  Args:
292
    opts - class with options as members
293
    args - list containing a single element, the instance name
294

    
295
  """
296
  if opts.multi_mode is None:
297
    opts.multi_mode = _SHUTDOWN_INSTANCES
298
  inames = _ExpandMultiNames(opts.multi_mode, args)
299
  multi_on = len(inames) > 1
300
  for name in inames:
301
    op = opcodes.OpStartupInstance(instance_name=name,
302
                                   force=opts.force,
303
                                   extra_args=opts.extra_args)
304
    if multi_on:
305
      logger.ToStdout("Starting up %s" % name)
306
    SubmitOpCode(op)
307
  return 0
308

    
309

    
310
def ShutdownInstance(opts, args):
311
  """Shutdown an instance.
312

    
313
  Args:
314
    opts - class with options as members
315
    args - list containing a single element, the instance name
316

    
317
  """
318
  if opts.multi_mode is None:
319
    opts.multi_mode = _SHUTDOWN_INSTANCES
320
  inames = _ExpandMultiNames(opts.multi_mode, args)
321
  multi_on = len(inames) > 1
322
  for name in inames:
323
    op = opcodes.OpShutdownInstance(instance_name=name)
324
    if multi_on:
325
      logger.ToStdout("Shutting down %s" % name)
326
    SubmitOpCode(op)
327
  return 0
328

    
329

    
330
def AddMDDRBDComponent(opts, args):
331
  """Add a new component to a remote_raid1 disk.
332

    
333
  Args:
334
    opts - class with options as members
335
    args - list with a single element, the instance name
336

    
337
  """
338
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
339
                                    disk_name=opts.disk,
340
                                    remote_node=opts.node)
341
  SubmitOpCode(op)
342
  return 0
343

    
344

    
345
def RemoveMDDRBDComponent(opts, args):
346
  """Remove a component from a remote_raid1 disk.
347

    
348
  Args:
349
    opts - class with options as members
350
    args - list with a single element, the instance name
351

    
352
  """
353
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
354
                                       disk_name=opts.disk,
355
                                       disk_id=opts.port)
356
  SubmitOpCode(op)
357
  return 0
358

    
359

    
360
def ReplaceDisks(opts, args):
361
  """Replace the disks of an instance
362

    
363
  Args:
364
    opts - class with options as members
365
    args - list with a single element, the instance name
366

    
367
  """
368
  instance_name = args[0]
369
  new_secondary = opts.new_secondary
370
  op = opcodes.OpReplaceDisks(instance_name=args[0],
371
                              remote_node=opts.new_secondary)
372
  SubmitOpCode(op)
373
  return 0
374

    
375

    
376
def FailoverInstance(opts, args):
377
  """Failover an instance.
378

    
379
  The failover is done by shutting it down on its present node and
380
  starting it on the secondary.
381

    
382
  Args:
383
    opts - class with options as members
384
    args - list with a single element, the instance name
385
  Opts used:
386
    force - whether to failover without asking questions.
387

    
388
  """
389
  instance_name = args[0]
390
  force = opts.force
391

    
392
  if not force:
393
    usertext = ("Failover will happen to image %s."
394
                " This requires a shutdown of the instance. Continue?" %
395
                (instance_name,))
396
    if not opts._ask_user(usertext):
397
      return 1
398

    
399
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
400
                                  ignore_consistency=opts.ignore_consistency)
401
  SubmitOpCode(op)
402
  return 0
403

    
404

    
405
def ConnectToInstanceConsole(opts, args):
406
  """Connect to the console of an instance.
407

    
408
  Args:
409
    opts - class with options as members
410
    args - list with a single element, the instance name
411

    
412
  """
413
  instance_name = args[0]
414

    
415
  op = opcodes.OpConnectConsole(instance_name=instance_name)
416
  cmd, argv = SubmitOpCode(op)
417
  # drop lock and exec so other commands can run while we have console
418
  utils.Unlock("cmd")
419
  try:
420
    os.execvp(cmd, argv)
421
  finally:
422
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
423
                     (cmd, " ".join(argv)))
424
    os._exit(1)
425

    
426

    
427
def _FormatBlockDevInfo(buf, dev, indent_level):
428
  """Show block device information.
429

    
430
  This is only used by ShowInstanceConfig(), but it's too big to be
431
  left for an inline definition.
432

    
433
  """
434
  def helper(buf, dtype, status):
435
    """Format one line for phsyical device status."""
436
    if not status:
437
      buf.write("not active\n")
438
    else:
439
      (path, major, minor, syncp, estt, degr) = status
440
      buf.write("%s (%d:%d)" % (path, major, minor))
441
      if dtype in ("md_raid1", "drbd"):
442
        if syncp is not None:
443
          sync_text = "*RECOVERING* %5.2f%%," % syncp
444
          if estt:
445
            sync_text += " ETA %ds" % estt
446
          else:
447
            sync_text += " ETA unknown"
448
        else:
449
          sync_text = "in sync"
450
        if degr:
451
          degr_text = "*DEGRADED*"
452
        else:
453
          degr_text = "ok"
454
        buf.write(" %s, status %s" % (sync_text, degr_text))
455
      buf.write("\n")
456

    
457
  if dev["iv_name"] is not None:
458
    data = "  - %s, " % dev["iv_name"]
459
  else:
460
    data = "  - "
461
  data += "type: %s" % dev["dev_type"]
462
  if dev["logical_id"] is not None:
463
    data += ", logical_id: %s" % (dev["logical_id"],)
464
  elif dev["physical_id"] is not None:
465
    data += ", physical_id: %s" % (dev["physical_id"],)
466
  buf.write("%*s%s\n" % (2*indent_level, "", data))
467
  buf.write("%*s    primary:   " % (2*indent_level, ""))
468
  helper(buf, dev["dev_type"], dev["pstatus"])
469

    
470
  if dev["sstatus"]:
471
    buf.write("%*s    secondary: " % (2*indent_level, ""))
472
    helper(buf, dev["dev_type"], dev["sstatus"])
473

    
474
  if dev["children"]:
475
    for child in dev["children"]:
476
      _FormatBlockDevInfo(buf, child, indent_level+1)
477

    
478

    
479
def ShowInstanceConfig(opts, args):
480
  """Compute instance run-time status.
481

    
482
  """
483
  retcode = 0
484
  op = opcodes.OpQueryInstanceData(instances=args)
485
  result = SubmitOpCode(op)
486

    
487
  if not result:
488
    logger.ToStdout("No instances.")
489
    return 1
490

    
491
  buf = StringIO()
492
  retcode = 0
493
  for instance_name in result:
494
    instance = result[instance_name]
495
    buf.write("Instance name: %s\n" % instance["name"])
496
    buf.write("State: configured to be %s, actual state is %s\n" %
497
              (instance["config_state"], instance["run_state"]))
498
    buf.write("  Nodes:\n")
499
    buf.write("    - primary: %s\n" % instance["pnode"])
500
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
501
    buf.write("  Operating system: %s\n" % instance["os"])
502
    buf.write("  Hardware:\n")
503
    buf.write("    - memory: %dMiB\n" % instance["memory"])
504
    buf.write("    - NICs: %s\n" %
505
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
506
                   (mac, ip, bridge)
507
                     for mac, ip, bridge in instance["nics"]]))
508
    buf.write("  Block devices:\n")
509

    
510
    for device in instance["disks"]:
511
      _FormatBlockDevInfo(buf, device, 1)
512

    
513
  logger.ToStdout(buf.getvalue().rstrip('\n'))
514
  return retcode
515

    
516

    
517
def SetInstanceParms(opts, args):
518
  """Modifies an instance.
519

    
520
  All parameters take effect only at the next restart of the instance.
521

    
522
  Args:
523
    opts - class with options as members
524
    args - list with a single element, the instance name
525
  Opts used:
526
    memory - the new memory size
527
    vcpus - the new number of cpus
528

    
529
  """
530
  if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
531
    logger.ToStdout("Please give at least one of the parameters.")
532
    return 1
533

    
534
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
535
                                  vcpus=opts.vcpus, ip=opts.ip,
536
                                  bridge=opts.bridge)
537
  result = SubmitOpCode(op)
538

    
539
  if result:
540
    logger.ToStdout("Modified instance %s" % args[0])
541
    for param, data in result:
542
      logger.ToStdout(" - %-5s -> %s" % (param, data))
543
    logger.ToStdout("Please don't forget that these parameters take effect"
544
                    " only at the next start of the instance.")
545
  return 0
546

    
547

    
548
# options used in more than one cmd
549
node_opt = make_option("-n", "--node", dest="node", help="Target node",
550
                       metavar="<node>")
551

    
552
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
553
                    metavar="<os>")
554

    
555
# multi-instance selection options
556
m_pri_node_opt = make_option("--primary", dest="multi_mode",
557
                             help="Filter by nodes (primary only)",
558
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
559

    
560
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
561
                             help="Filter by nodes (secondary only)",
562
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
563

    
564
m_node_opt = make_option("--node", dest="multi_mode",
565
                         help="Filter by nodes (primary and secondary)",
566
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
567

    
568
m_clust_opt = make_option("--all", dest="multi_mode",
569
                          help="Select all instances in the cluster",
570
                          const=_SHUTDOWN_CLUSTER, action="store_const")
571

    
572
m_inst_opt = make_option("--instance", dest="multi_mode",
573
                         help="Filter by instance name [default]",
574
                         const=_SHUTDOWN_INSTANCES, action="store_const")
575

    
576

    
577
# this is defined separately due to readability only
578
add_opts = [
579
  DEBUG_OPT,
580
  node_opt,
581
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
582
             " a suffix is used",
583
             default=20 * 1024, type="unit", metavar="<size>"),
584
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
585
             " suffix is used",
586
             default=4 * 1024, type="unit", metavar="<size>"),
587
  os_opt,
588
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
589
              default=128, type="unit", metavar="<mem>"),
590
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
591
              default=1, type="int", metavar="<PROC>"),
592
  make_option("-t", "--disk-template", dest="disk_template",
593
              help="Custom disk setup (diskless, plain, local_raid1 or"
594
              " remote_raid1)", default=None, metavar="TEMPL"),
595
  make_option("-i", "--ip", dest="ip",
596
              help="IP address ('none' [default], 'auto', or specify address)",
597
              default='none', type="string", metavar="<ADDRESS>"),
598
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
599
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
600
  make_option("--secondary-node", dest="snode",
601
              help="Secondary node for remote_raid1 disk layout",
602
              metavar="<node>"),
603
  make_option("-b", "--bridge", dest="bridge",
604
              help="Bridge to connect this instance to",
605
              default=None, metavar="<bridge>")
606
  ]
607

    
608
commands = {
609
  'add': (AddInstance, ARGS_ONE, add_opts,
610
          "[opts...] <name>",
611
          "Creates and adds a new instance to the cluster"),
612
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
613
                [DEBUG_OPT, node_opt,
614
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
615
                             help=("The name of the instance disk for which to"
616
                                   " add the mirror"))],
617
                "-n node -b disk <instance>",
618
                "Creates a new mirror for the instance"),
619
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
620
              "<instance>",
621
              "Opens a console on the specified instance"),
622
  'failover': (FailoverInstance, ARGS_ONE,
623
               [DEBUG_OPT, FORCE_OPT,
624
                make_option("--ignore-consistency", dest="ignore_consistency",
625
                            action="store_true", default=False,
626
                            help="Ignore the consistency of the disks on"
627
                            " the secondary"),
628
                ],
629
               "[-f] <instance>",
630
               "Stops the instance and starts it on the backup node, using"
631
               " the remote mirror (only for instances of type remote_raid1)"),
632
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
633
           "Show information on the specified instance"),
634
  'list': (ListInstances, ARGS_NONE,
635
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
636
           "", "Lists the instances and their status"),
637
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
638
                "[-f] <instance>", "Reinstall the instance"),
639
  'remove': (RemoveInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
640
             "[-f] <instance>", "Shuts down the instance and removes it"),
641
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
642
                   [DEBUG_OPT, node_opt,
643
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
644
                                help=("The name of the instance disk"
645
                                      " for which to add the mirror")),
646
                    make_option("-p", "--port", dest="port", metavar="PORT",
647
                                help=("The port of the drbd device"
648
                                      " which to remove from the mirror"),
649
                                type="int"),
650
                    ],
651
                   "-b disk -p port <instance>",
652
                   "Removes a mirror from the instance"),
653
  'rename': (RenameInstance, ARGS_FIXED(2),
654
             [DEBUG_OPT,
655
              make_option("--no-ip-check", dest="ignore_ip",
656
                          help="Do not check that the IP of the new name"
657
                          " is alive",
658
                          default=False, action="store_true"),
659
              ],
660
             "<instance> <new_name>", "Rename the instance"),
661
  'replace-disks': (ReplaceDisks, ARGS_ONE,
662
                    [DEBUG_OPT,
663
                     make_option("-n", "--new-secondary", dest="new_secondary",
664
                                 metavar="NODE",
665
                                 help=("New secondary node (if you want to"
666
                                       " change the secondary)"))],
667
                    "[-n NODE] <instance>",
668
                    "Replaces all disks for the instance"),
669
  'modify': (SetInstanceParms, ARGS_ONE,
670
             [DEBUG_OPT, FORCE_OPT,
671
              cli_option("-m", "--memory", dest="mem",
672
                         help="Memory size",
673
                         default=None, type="unit", metavar="<mem>"),
674
              make_option("-p", "--cpu", dest="vcpus",
675
                          help="Number of virtual CPUs",
676
                          default=None, type="int", metavar="<PROC>"),
677
              make_option("-i", "--ip", dest="ip",
678
                          help="IP address ('none' or numeric IP)",
679
                          default=None, type="string", metavar="<ADDRESS>"),
680
              make_option("-b", "--bridge", dest="bridge",
681
                          help="Bridge to connect this instance to",
682
                          default=None, type="string", metavar="<bridge>"),
683
              ],
684
             "<instance>", "Alters the parameters of an instance"),
685
  'shutdown': (ShutdownInstance, ARGS_ANY,
686
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
687
                m_clust_opt, m_inst_opt],
688
               "<instance>", "Stops an instance"),
689
  'startup': (StartupInstance, ARGS_ANY,
690
              [DEBUG_OPT, FORCE_OPT,
691
               make_option("-e", "--extra", dest="extra_args",
692
                           help="Extra arguments for the instance's kernel",
693
                           default=None, type="string", metavar="<PARAMS>"),
694
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
695
               m_clust_opt, m_inst_opt,
696
               ],
697
            "<instance>", "Starts an instance"),
698
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
699
                     "<instance>",
700
                     "Activate an instance's disks"),
701
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
702
                       "<instance>",
703
                       "Deactivate an instance's disks"),
704
  }
705

    
706
if __name__ == '__main__':
707
  sys.exit(GenericMain(commands))