Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ fe96220b

History | View | Annotate | Download (28.6 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 _ConfirmOperation(inames, text):
103
  """Ask the user to confirm an operation on a list of instances.
104

    
105
  This function is used to request confirmation for doing an operation
106
  on a given list of instances.
107

    
108
  The inames argument is what the selection algorithm computed, and
109
  the text argument is the operation we should tell the user to
110
  confirm (e.g. 'shutdown' or 'startup').
111

    
112
  Returns: boolean depending on user's confirmation.
113

    
114
  """
115
  count = len(inames)
116
  msg = ("The %s will operate on %d instances.\n"
117
         "Do you want to continue?" % (text, count))
118
  affected = ("\nAffected instances:\n" +
119
              "\n".join(["  %s" % name for name in inames]))
120

    
121
  choices = [('y', True, 'Yes, execute the %s' % text),
122
             ('n', False, 'No, abort the %s' % text)]
123

    
124
  if count > 20:
125
    choices.insert(1, ('v', 'v', 'View the list of affected instances'))
126
    ask = msg
127
  else:
128
    ask = msg + affected
129

    
130
  choice = AskUser(ask, choices)
131
  if choice == 'v':
132
    choices.pop(1)
133
    choice = AskUser(choices, msg + affected)
134
  return choice
135

    
136

    
137
def ListInstances(opts, args):
138
  """List instances and their properties.
139

    
140
  """
141
  if opts.output is None:
142
    selected_fields = ["name", "os", "pnode", "admin_state",
143
                       "oper_state", "oper_ram"]
144
  else:
145
    selected_fields = opts.output.split(",")
146

    
147
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
148
  output = SubmitOpCode(op)
149

    
150
  if not opts.no_headers:
151
    headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
152
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
153
               "oper_state": "Status", "admin_ram": "Configured_memory",
154
               "oper_ram": "Memory", "disk_template": "Disk_template",
155
               "ip": "IP Address", "mac": "MAC Address",
156
               "bridge": "Bridge",
157
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
158
  else:
159
    headers = None
160

    
161
  if opts.human_readable:
162
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
163
  else:
164
    unitfields = None
165

    
166
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
167

    
168
  # change raw values to nicer strings
169
  for row in output:
170
    for idx, field in enumerate(selected_fields):
171
      val = row[idx]
172
      if field == "snodes":
173
        val = ",".join(val) or "-"
174
      elif field == "admin_state":
175
        if val:
176
          val = "yes"
177
        else:
178
          val = "no"
179
      elif field == "oper_state":
180
        if val is None:
181
          val = "(node down)"
182
        elif val: # True
183
          val = "running"
184
        else:
185
          val = "stopped"
186
      elif field == "oper_ram":
187
        if val is None:
188
          val = "(node down)"
189
      elif field == "sda_size" or field == "sdb_size":
190
        if val is None:
191
          val = "N/A"
192
      row[idx] = str(val)
193

    
194
  data = GenerateTable(separator=opts.separator, headers=headers,
195
                       fields=selected_fields, unitfields=unitfields,
196
                       numfields=numfields, data=output)
197

    
198
  for line in data:
199
    logger.ToStdout(line)
200

    
201
  return 0
202

    
203

    
204
def AddInstance(opts, args):
205
  """Add an instance to the cluster.
206

    
207
  Args:
208
    opts - class with options as members
209
    args - list with a single element, the instance name
210
  Opts used:
211
    mem - amount of memory to allocate to instance (MiB)
212
    size - amount of disk space to allocate to instance (MiB)
213
    os - which OS to run on instance
214
    node - node to run new instance on
215

    
216
  """
217
  instance = args[0]
218

    
219
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
220
                                disk_size=opts.size, swap_size=opts.swap,
221
                                disk_template=opts.disk_template,
222
                                mode=constants.INSTANCE_CREATE,
223
                                os_type=opts.os, pnode=opts.node,
224
                                snode=opts.snode, vcpus=opts.vcpus,
225
                                ip=opts.ip, bridge=opts.bridge,
226
                                start=opts.start, ip_check=opts.ip_check,
227
                                wait_for_sync=opts.wait_for_sync)
228
  SubmitOpCode(op)
229
  return 0
230

    
231

    
232
def ReinstallInstance(opts, args):
233
  """Reinstall an instance.
234

    
235
  Args:
236
    opts - class with options as members
237
    args - list containing a single element, the instance name
238

    
239
  """
240
  instance_name = args[0]
241

    
242
  if not opts.force:
243
    usertext = ("This will reinstall the instance %s and remove "
244
                "all data. Continue?") % instance_name
245
    if not AskUser(usertext):
246
      return 1
247

    
248
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
249
                                   os_type=opts.os)
250
  SubmitOpCode(op)
251

    
252
  return 0
253

    
254

    
255
def RemoveInstance(opts, args):
256
  """Remove an instance.
257

    
258
  Args:
259
    opts - class with options as members
260
    args - list containing a single element, the instance name
261

    
262
  """
263
  instance_name = args[0]
264
  force = opts.force
265

    
266
  if not force:
267
    usertext = ("This will remove the volumes of the instance %s"
268
                " (including mirrors), thus removing all the data"
269
                " of the instance. Continue?") % instance_name
270
    if not AskUser(usertext):
271
      return 1
272

    
273
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
274
                                ignore_failures=opts.ignore_failures)
275
  SubmitOpCode(op)
276
  return 0
277

    
278

    
279
def RenameInstance(opts, args):
280
  """Rename an instance.
281

    
282
  Args:
283
    opts - class with options as members
284
    args - list containing two elements, the instance name and the new name
285

    
286
  """
287
  op = opcodes.OpRenameInstance(instance_name=args[0],
288
                                new_name=args[1],
289
                                ignore_ip=opts.ignore_ip)
290
  SubmitOpCode(op)
291

    
292
  return 0
293

    
294

    
295
def ActivateDisks(opts, args):
296
  """Activate an instance's disks.
297

    
298
  This serves two purposes:
299
    - it allows one (as long as the instance is not running) to mount
300
    the disks and modify them from the node
301
    - it repairs inactive secondary drbds
302

    
303
  """
304
  instance_name = args[0]
305
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
306
  disks_info = SubmitOpCode(op)
307
  for host, iname, nname in disks_info:
308
    print "%s:%s:%s" % (host, iname, nname)
309
  return 0
310

    
311

    
312
def DeactivateDisks(opts, args):
313
  """Command-line interface for _ShutdownInstanceBlockDevices.
314

    
315
  This function takes the instance name, looks for its primary node
316
  and the tries to shutdown its block devices on that node.
317

    
318
  """
319
  instance_name = args[0]
320
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
321
  SubmitOpCode(op)
322
  return 0
323

    
324

    
325
def StartupInstance(opts, args):
326
  """Startup an instance.
327

    
328
  Args:
329
    opts - class with options as members
330
    args - list containing a single element, the instance name
331

    
332
  """
333
  if opts.multi_mode is None:
334
    opts.multi_mode = _SHUTDOWN_INSTANCES
335
  inames = _ExpandMultiNames(opts.multi_mode, args)
336
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
337
  if not (opts.force_multi or not multi_on
338
          or _ConfirmOperation(inames, "startup")):
339
    return 1
340
  for name in inames:
341
    op = opcodes.OpStartupInstance(instance_name=name,
342
                                   force=opts.force,
343
                                   extra_args=opts.extra_args)
344
    if multi_on:
345
      logger.ToStdout("Starting up %s" % name)
346
    SubmitOpCode(op)
347
  return 0
348

    
349
def RebootInstance(opts, args):
350
  """Reboot an instance
351

    
352
  Args:
353
    opts - class with options as members
354
    args - list containing a single element, the instance name
355

    
356
  """
357
  if opts.multi_mode is None:
358
    opts.multi_mode = _SHUTDOWN_INSTANCES
359
  inames = _ExpandMultiNames(opts.multi_mode, args)
360
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
361
  if not (opts.force_multi or not multi_on
362
          or _ConfirmOperation(inames, "reboot")):
363
    return 1
364
  for name in inames:
365
    op = opcodes.OpRebootInstance(instance_name=name,
366
                                  reboot_type=opts.reboot_type,
367
                                  ignore_secondaries=opts.ignore_secondaries)
368

    
369
    SubmitOpCode(op)
370
  return 0
371

    
372
def ShutdownInstance(opts, args):
373
  """Shutdown an instance.
374

    
375
  Args:
376
    opts - class with options as members
377
    args - list containing a single element, the instance name
378

    
379
  """
380
  if opts.multi_mode is None:
381
    opts.multi_mode = _SHUTDOWN_INSTANCES
382
  inames = _ExpandMultiNames(opts.multi_mode, args)
383
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
384
  if not (opts.force_multi or not multi_on
385
          or _ConfirmOperation(inames, "shutdown")):
386
    return 1
387
  for name in inames:
388
    op = opcodes.OpShutdownInstance(instance_name=name)
389
    if multi_on:
390
      logger.ToStdout("Shutting down %s" % name)
391
    SubmitOpCode(op)
392
  return 0
393

    
394

    
395
def AddMDDRBDComponent(opts, args):
396
  """Add a new component to a remote_raid1 disk.
397

    
398
  Args:
399
    opts - class with options as members
400
    args - list with a single element, the instance name
401

    
402
  """
403
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
404
                                    disk_name=opts.disk,
405
                                    remote_node=opts.node)
406
  SubmitOpCode(op)
407
  return 0
408

    
409

    
410
def RemoveMDDRBDComponent(opts, args):
411
  """Remove a component from a remote_raid1 disk.
412

    
413
  Args:
414
    opts - class with options as members
415
    args - list with a single element, the instance name
416

    
417
  """
418
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
419
                                       disk_name=opts.disk,
420
                                       disk_id=opts.port)
421
  SubmitOpCode(op)
422
  return 0
423

    
424

    
425
def ReplaceDisks(opts, args):
426
  """Replace the disks of an instance
427

    
428
  Args:
429
    opts - class with options as members
430
    args - list with a single element, the instance name
431

    
432
  """
433
  instance_name = args[0]
434
  new_secondary = opts.new_secondary
435
  op = opcodes.OpReplaceDisks(instance_name=args[0],
436
                              remote_node=opts.new_secondary)
437
  SubmitOpCode(op)
438
  return 0
439

    
440

    
441
def FailoverInstance(opts, args):
442
  """Failover an instance.
443

    
444
  The failover is done by shutting it down on its present node and
445
  starting it on the secondary.
446

    
447
  Args:
448
    opts - class with options as members
449
    args - list with a single element, the instance name
450
  Opts used:
451
    force - whether to failover without asking questions.
452

    
453
  """
454
  instance_name = args[0]
455
  force = opts.force
456

    
457
  if not force:
458
    usertext = ("Failover will happen to image %s."
459
                " This requires a shutdown of the instance. Continue?" %
460
                (instance_name,))
461
    if not AskUser(usertext):
462
      return 1
463

    
464
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
465
                                  ignore_consistency=opts.ignore_consistency)
466
  SubmitOpCode(op)
467
  return 0
468

    
469

    
470
def ConnectToInstanceConsole(opts, args):
471
  """Connect to the console of an instance.
472

    
473
  Args:
474
    opts - class with options as members
475
    args - list with a single element, the instance name
476

    
477
  """
478
  instance_name = args[0]
479

    
480
  op = opcodes.OpConnectConsole(instance_name=instance_name)
481
  cmd, argv = SubmitOpCode(op)
482
  # drop lock and exec so other commands can run while we have console
483
  utils.Unlock("cmd")
484
  try:
485
    os.execvp(cmd, argv)
486
  finally:
487
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
488
                     (cmd, " ".join(argv)))
489
    os._exit(1)
490

    
491

    
492
def _FormatBlockDevInfo(buf, dev, indent_level):
493
  """Show block device information.
494

    
495
  This is only used by ShowInstanceConfig(), but it's too big to be
496
  left for an inline definition.
497

    
498
  """
499
  def helper(buf, dtype, status):
500
    """Format one line for phsyical device status."""
501
    if not status:
502
      buf.write("not active\n")
503
    else:
504
      (path, major, minor, syncp, estt, degr) = status
505
      buf.write("%s (%d:%d)" % (path, major, minor))
506
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7):
507
        if syncp is not None:
508
          sync_text = "*RECOVERING* %5.2f%%," % syncp
509
          if estt:
510
            sync_text += " ETA %ds" % estt
511
          else:
512
            sync_text += " ETA unknown"
513
        else:
514
          sync_text = "in sync"
515
        if degr:
516
          degr_text = "*DEGRADED*"
517
        else:
518
          degr_text = "ok"
519
        buf.write(" %s, status %s" % (sync_text, degr_text))
520
      buf.write("\n")
521

    
522
  if dev["iv_name"] is not None:
523
    data = "  - %s, " % dev["iv_name"]
524
  else:
525
    data = "  - "
526
  data += "type: %s" % dev["dev_type"]
527
  if dev["logical_id"] is not None:
528
    data += ", logical_id: %s" % (dev["logical_id"],)
529
  elif dev["physical_id"] is not None:
530
    data += ", physical_id: %s" % (dev["physical_id"],)
531
  buf.write("%*s%s\n" % (2*indent_level, "", data))
532
  buf.write("%*s    primary:   " % (2*indent_level, ""))
533
  helper(buf, dev["dev_type"], dev["pstatus"])
534

    
535
  if dev["sstatus"]:
536
    buf.write("%*s    secondary: " % (2*indent_level, ""))
537
    helper(buf, dev["dev_type"], dev["sstatus"])
538

    
539
  if dev["children"]:
540
    for child in dev["children"]:
541
      _FormatBlockDevInfo(buf, child, indent_level+1)
542

    
543

    
544
def ShowInstanceConfig(opts, args):
545
  """Compute instance run-time status.
546

    
547
  """
548
  retcode = 0
549
  op = opcodes.OpQueryInstanceData(instances=args)
550
  result = SubmitOpCode(op)
551

    
552
  if not result:
553
    logger.ToStdout("No instances.")
554
    return 1
555

    
556
  buf = StringIO()
557
  retcode = 0
558
  for instance_name in result:
559
    instance = result[instance_name]
560
    buf.write("Instance name: %s\n" % instance["name"])
561
    buf.write("State: configured to be %s, actual state is %s\n" %
562
              (instance["config_state"], instance["run_state"]))
563
    buf.write("  Nodes:\n")
564
    buf.write("    - primary: %s\n" % instance["pnode"])
565
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
566
    buf.write("  Operating system: %s\n" % instance["os"])
567
    buf.write("  Hardware:\n")
568
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
569
    buf.write("    - memory: %dMiB\n" % instance["memory"])
570
    buf.write("    - NICs: %s\n" %
571
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
572
                   (mac, ip, bridge)
573
                     for mac, ip, bridge in instance["nics"]]))
574
    buf.write("  Block devices:\n")
575

    
576
    for device in instance["disks"]:
577
      _FormatBlockDevInfo(buf, device, 1)
578

    
579
  logger.ToStdout(buf.getvalue().rstrip('\n'))
580
  return retcode
581

    
582

    
583
def SetInstanceParms(opts, args):
584
  """Modifies an instance.
585

    
586
  All parameters take effect only at the next restart of the instance.
587

    
588
  Args:
589
    opts - class with options as members
590
    args - list with a single element, the instance name
591
  Opts used:
592
    memory - the new memory size
593
    vcpus - the new number of cpus
594

    
595
  """
596
  if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
597
    logger.ToStdout("Please give at least one of the parameters.")
598
    return 1
599

    
600
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
601
                                  vcpus=opts.vcpus, ip=opts.ip,
602
                                  bridge=opts.bridge)
603
  result = SubmitOpCode(op)
604

    
605
  if result:
606
    logger.ToStdout("Modified instance %s" % args[0])
607
    for param, data in result:
608
      logger.ToStdout(" - %-5s -> %s" % (param, data))
609
    logger.ToStdout("Please don't forget that these parameters take effect"
610
                    " only at the next start of the instance.")
611
  return 0
612

    
613

    
614
# options used in more than one cmd
615
node_opt = make_option("-n", "--node", dest="node", help="Target node",
616
                       metavar="<node>")
617

    
618
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
619
                    metavar="<os>")
620

    
621
# multi-instance selection options
622
m_force_multi = make_option("--force-multiple", dest="force_multi",
623
                            help="Do not ask for confirmation when more than"
624
                            " one instance is affected",
625
                            action="store_true", default=False)
626

    
627
m_pri_node_opt = make_option("--primary", dest="multi_mode",
628
                             help="Filter by nodes (primary only)",
629
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
630

    
631
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
632
                             help="Filter by nodes (secondary only)",
633
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
634

    
635
m_node_opt = make_option("--node", dest="multi_mode",
636
                         help="Filter by nodes (primary and secondary)",
637
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
638

    
639
m_clust_opt = make_option("--all", dest="multi_mode",
640
                          help="Select all instances in the cluster",
641
                          const=_SHUTDOWN_CLUSTER, action="store_const")
642

    
643
m_inst_opt = make_option("--instance", dest="multi_mode",
644
                         help="Filter by instance name [default]",
645
                         const=_SHUTDOWN_INSTANCES, action="store_const")
646

    
647

    
648
# this is defined separately due to readability only
649
add_opts = [
650
  DEBUG_OPT,
651
  node_opt,
652
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
653
             " a suffix is used",
654
             default=20 * 1024, type="unit", metavar="<size>"),
655
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
656
             " suffix is used",
657
             default=4 * 1024, type="unit", metavar="<size>"),
658
  os_opt,
659
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
660
              default=128, type="unit", metavar="<mem>"),
661
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
662
              default=1, type="int", metavar="<PROC>"),
663
  make_option("-t", "--disk-template", dest="disk_template",
664
              help="Custom disk setup (diskless, plain, local_raid1 or"
665
              " remote_raid1)", default=None, metavar="TEMPL"),
666
  make_option("-i", "--ip", dest="ip",
667
              help="IP address ('none' [default], 'auto', or specify address)",
668
              default='none', type="string", metavar="<ADDRESS>"),
669
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
670
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
671
  make_option("--secondary-node", dest="snode",
672
              help="Secondary node for remote_raid1 disk layout",
673
              metavar="<node>"),
674
  make_option("-b", "--bridge", dest="bridge",
675
              help="Bridge to connect this instance to",
676
              default=None, metavar="<bridge>"),
677
  make_option("--no-start", dest="start", default=True,
678
              action="store_false", help="Don't start the instance after"
679
              " creation"),
680
  make_option("--no-ip-check", dest="ip_check", default=True,
681
              action="store_false", help="Don't check that the instance's IP"
682
              " is alive (only valid with --no-start)"),
683
  ]
684

    
685
commands = {
686
  'add': (AddInstance, ARGS_ONE, add_opts,
687
          "[opts...] <name>",
688
          "Creates and adds a new instance to the cluster"),
689
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
690
                [DEBUG_OPT, node_opt,
691
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
692
                             help=("The name of the instance disk for which to"
693
                                   " add the mirror"))],
694
                "-n node -b disk <instance>",
695
                "Creates a new mirror for the instance"),
696
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
697
              "<instance>",
698
              "Opens a console on the specified instance"),
699
  'failover': (FailoverInstance, ARGS_ONE,
700
               [DEBUG_OPT, FORCE_OPT,
701
                make_option("--ignore-consistency", dest="ignore_consistency",
702
                            action="store_true", default=False,
703
                            help="Ignore the consistency of the disks on"
704
                            " the secondary"),
705
                ],
706
               "[-f] <instance>",
707
               "Stops the instance and starts it on the backup node, using"
708
               " the remote mirror (only for instances of type remote_raid1)"),
709
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
710
           "Show information on the specified instance"),
711
  'list': (ListInstances, ARGS_NONE,
712
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
713
           "", "Lists the instances and their status"),
714
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
715
                "[-f] <instance>", "Reinstall the instance"),
716
  'remove': (RemoveInstance, ARGS_ONE,
717
             [DEBUG_OPT, FORCE_OPT,
718
              make_option("--ignore-failures", dest="ignore_failures",
719
                          action="store_true", default=False,
720
                          help=("Remove the instance from the cluster even"
721
                                " if there are failures during the removal"
722
                                " process (shutdown, disk removal, etc.)")),
723
              ],
724
             "[-f] <instance>", "Shuts down the instance and removes it"),
725
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
726
                   [DEBUG_OPT, node_opt,
727
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
728
                                help=("The name of the instance disk"
729
                                      " for which to add the mirror")),
730
                    make_option("-p", "--port", dest="port", metavar="PORT",
731
                                help=("The port of the drbd device"
732
                                      " which to remove from the mirror"),
733
                                type="int"),
734
                    ],
735
                   "-b disk -p port <instance>",
736
                   "Removes a mirror from the instance"),
737
  'rename': (RenameInstance, ARGS_FIXED(2),
738
             [DEBUG_OPT,
739
              make_option("--no-ip-check", dest="ignore_ip",
740
                          help="Do not check that the IP of the new name"
741
                          " is alive",
742
                          default=False, action="store_true"),
743
              ],
744
             "<instance> <new_name>", "Rename the instance"),
745
  'replace-disks': (ReplaceDisks, ARGS_ONE,
746
                    [DEBUG_OPT,
747
                     make_option("-n", "--new-secondary", dest="new_secondary",
748
                                 metavar="NODE",
749
                                 help=("New secondary node (if you want to"
750
                                       " change the secondary)"))],
751
                    "[-n NODE] <instance>",
752
                    "Replaces all disks for the instance"),
753
  'modify': (SetInstanceParms, ARGS_ONE,
754
             [DEBUG_OPT, FORCE_OPT,
755
              cli_option("-m", "--memory", dest="mem",
756
                         help="Memory size",
757
                         default=None, type="unit", metavar="<mem>"),
758
              make_option("-p", "--cpu", dest="vcpus",
759
                          help="Number of virtual CPUs",
760
                          default=None, type="int", metavar="<PROC>"),
761
              make_option("-i", "--ip", dest="ip",
762
                          help="IP address ('none' or numeric IP)",
763
                          default=None, type="string", metavar="<ADDRESS>"),
764
              make_option("-b", "--bridge", dest="bridge",
765
                          help="Bridge to connect this instance to",
766
                          default=None, type="string", metavar="<bridge>"),
767
              ],
768
             "<instance>", "Alters the parameters of an instance"),
769
  'shutdown': (ShutdownInstance, ARGS_ANY,
770
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
771
                m_clust_opt, m_inst_opt, m_force_multi],
772
               "<instance>", "Stops an instance"),
773
  'startup': (StartupInstance, ARGS_ANY,
774
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
775
               make_option("-e", "--extra", dest="extra_args",
776
                           help="Extra arguments for the instance's kernel",
777
                           default=None, type="string", metavar="<PARAMS>"),
778
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
779
               m_clust_opt, m_inst_opt,
780
               ],
781
            "<instance>", "Starts an instance"),
782

    
783
  'reboot': (RebootInstance, ARGS_ANY,
784
              [DEBUG_OPT, m_force_multi,
785
               make_option("-e", "--extra", dest="extra_args",
786
                           help="Extra arguments for the instance's kernel",
787
                           default=None, type="string", metavar="<PARAMS>"),
788
               make_option("-t", "--type", dest="reboot_type",
789
                           help="Type of reboot: soft/hard/full",
790
                           default=constants.INSTANCE_REBOOT_SOFT,
791
                           type="string", metavar="<REBOOT>"),
792
               make_option("--ignore-secondaries", dest="ignore_secondaries",
793
                           default=False, action="store_true",
794
                           help="Ignore errors from secondaries"),
795
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
796
               m_clust_opt, m_inst_opt,
797
               ],
798
            "<instance>", "Reboots an instance"),
799
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
800
                     "<instance>",
801
                     "Activate an instance's disks"),
802
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
803
                       "<instance>",
804
                       "Deactivate an instance's disks"),
805
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
806
                "<node_name>", "List the tags of the given instance"),
807
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
808
               "<node_name> tag...", "Add tags to the given instance"),
809
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
810
                  "<node_name> tag...", "Remove tags from given instance"),
811
  }
812

    
813
if __name__ == '__main__':
814
  sys.exit(GenericMain(commands,
815
                       override={"tag_type": constants.TAG_INSTANCE}))