Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 36c87212

History | View | Annotate | Download (30.5 kB)

1
#!/usr/bin/python
2
#
3

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

    
21

    
22
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_2ndary = opts.new_secondary
435
  if opts.disks is None:
436
    disks = ["sda", "sdb"]
437
  else:
438
    disks = opts.disks.split(",")
439
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
440
    mode = constants.REPLACE_DISK_ALL
441
  elif opts.on_primary: # only on primary:
442
    mode = constants.REPLACE_DISK_PRI
443
    if new_2ndary is not None:
444
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
445
                                 " replacement")
446
  elif opts.on_secondary is not None: # only on secondary
447
    mode = constants.REPLACE_DISK_SEC
448

    
449
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
450
                              remote_node=new_2ndary, mode=mode)
451
  SubmitOpCode(op)
452
  return 0
453

    
454

    
455
def FailoverInstance(opts, args):
456
  """Failover an instance.
457

    
458
  The failover is done by shutting it down on its present node and
459
  starting it on the secondary.
460

    
461
  Args:
462
    opts - class with options as members
463
    args - list with a single element, the instance name
464
  Opts used:
465
    force - whether to failover without asking questions.
466

    
467
  """
468
  if opts.multi_mode is None:
469
    opts.multi_mode = _SHUTDOWN_INSTANCES
470
  inames = _ExpandMultiNames(opts.multi_mode, args)
471
  if not inames:
472
    logger.ToStderr("No instances match the selected options")
473
    return 1
474
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
475
  if not (opts.force_multi or not multi_on
476
          or _ConfirmOperation(inames, "failover (including shutdown)")):
477
    return 1
478

    
479
  result = 0
480
  for name in inames:
481
    op = opcodes.OpFailoverInstance(instance_name=name,
482
                                    ignore_consistency=opts.ignore_consistency)
483
    if multi_on:
484
      logger.ToStdout("Failing over instance %s" % name)
485
    try:
486
      SubmitOpCode(op)
487
    except errors.OpExecError, err:
488
      result = 1
489
      _, err_msg = FormatError(err)
490
      logger.ToStderr(err_msg)
491

    
492
  return result
493

    
494

    
495
def ConnectToInstanceConsole(opts, args):
496
  """Connect to the console of an instance.
497

    
498
  Args:
499
    opts - class with options as members
500
    args - list with a single element, the instance name
501

    
502
  """
503
  instance_name = args[0]
504

    
505
  op = opcodes.OpConnectConsole(instance_name=instance_name)
506
  cmd, argv = SubmitOpCode(op)
507
  # drop lock and exec so other commands can run while we have console
508
  utils.Unlock("cmd")
509
  try:
510
    os.execvp(cmd, argv)
511
  finally:
512
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
513
                     (cmd, " ".join(argv)))
514
    os._exit(1)
515

    
516

    
517
def _FormatBlockDevInfo(buf, dev, indent_level):
518
  """Show block device information.
519

    
520
  This is only used by ShowInstanceConfig(), but it's too big to be
521
  left for an inline definition.
522

    
523
  """
524
  def helper(buf, dtype, status):
525
    """Format one line for phsyical device status."""
526
    if not status:
527
      buf.write("not active\n")
528
    else:
529
      (path, major, minor, syncp, estt, degr) = status
530
      buf.write("%s (%d:%d)" % (path, major, minor))
531
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
532
        if syncp is not None:
533
          sync_text = "*RECOVERING* %5.2f%%," % syncp
534
          if estt:
535
            sync_text += " ETA %ds" % estt
536
          else:
537
            sync_text += " ETA unknown"
538
        else:
539
          sync_text = "in sync"
540
        if degr:
541
          degr_text = "*DEGRADED*"
542
        else:
543
          degr_text = "ok"
544
        buf.write(" %s, status %s" % (sync_text, degr_text))
545
      buf.write("\n")
546

    
547
  if dev["iv_name"] is not None:
548
    data = "  - %s, " % dev["iv_name"]
549
  else:
550
    data = "  - "
551
  data += "type: %s" % dev["dev_type"]
552
  if dev["logical_id"] is not None:
553
    data += ", logical_id: %s" % (dev["logical_id"],)
554
  elif dev["physical_id"] is not None:
555
    data += ", physical_id: %s" % (dev["physical_id"],)
556
  buf.write("%*s%s\n" % (2*indent_level, "", data))
557
  buf.write("%*s    primary:   " % (2*indent_level, ""))
558
  helper(buf, dev["dev_type"], dev["pstatus"])
559

    
560
  if dev["sstatus"]:
561
    buf.write("%*s    secondary: " % (2*indent_level, ""))
562
    helper(buf, dev["dev_type"], dev["sstatus"])
563

    
564
  if dev["children"]:
565
    for child in dev["children"]:
566
      _FormatBlockDevInfo(buf, child, indent_level+1)
567

    
568

    
569
def ShowInstanceConfig(opts, args):
570
  """Compute instance run-time status.
571

    
572
  """
573
  retcode = 0
574
  op = opcodes.OpQueryInstanceData(instances=args)
575
  result = SubmitOpCode(op)
576

    
577
  if not result:
578
    logger.ToStdout("No instances.")
579
    return 1
580

    
581
  buf = StringIO()
582
  retcode = 0
583
  for instance_name in result:
584
    instance = result[instance_name]
585
    buf.write("Instance name: %s\n" % instance["name"])
586
    buf.write("State: configured to be %s, actual state is %s\n" %
587
              (instance["config_state"], instance["run_state"]))
588
    buf.write("  Nodes:\n")
589
    buf.write("    - primary: %s\n" % instance["pnode"])
590
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
591
    buf.write("  Operating system: %s\n" % instance["os"])
592
    buf.write("  Hardware:\n")
593
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
594
    buf.write("    - memory: %dMiB\n" % instance["memory"])
595
    buf.write("    - NICs: %s\n" %
596
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
597
                   (mac, ip, bridge)
598
                     for mac, ip, bridge in instance["nics"]]))
599
    buf.write("  Block devices:\n")
600

    
601
    for device in instance["disks"]:
602
      _FormatBlockDevInfo(buf, device, 1)
603

    
604
  logger.ToStdout(buf.getvalue().rstrip('\n'))
605
  return retcode
606

    
607

    
608
def SetInstanceParms(opts, args):
609
  """Modifies an instance.
610

    
611
  All parameters take effect only at the next restart of the instance.
612

    
613
  Args:
614
    opts - class with options as members
615
    args - list with a single element, the instance name
616
  Opts used:
617
    memory - the new memory size
618
    vcpus - the new number of cpus
619

    
620
  """
621
  if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
622
    logger.ToStdout("Please give at least one of the parameters.")
623
    return 1
624

    
625
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
626
                                  vcpus=opts.vcpus, ip=opts.ip,
627
                                  bridge=opts.bridge)
628
  result = SubmitOpCode(op)
629

    
630
  if result:
631
    logger.ToStdout("Modified instance %s" % args[0])
632
    for param, data in result:
633
      logger.ToStdout(" - %-5s -> %s" % (param, data))
634
    logger.ToStdout("Please don't forget that these parameters take effect"
635
                    " only at the next start of the instance.")
636
  return 0
637

    
638

    
639
# options used in more than one cmd
640
node_opt = make_option("-n", "--node", dest="node", help="Target node",
641
                       metavar="<node>")
642

    
643
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
644
                    metavar="<os>")
645

    
646
# multi-instance selection options
647
m_force_multi = make_option("--force-multiple", dest="force_multi",
648
                            help="Do not ask for confirmation when more than"
649
                            " one instance is affected",
650
                            action="store_true", default=False)
651

    
652
m_pri_node_opt = make_option("--primary", dest="multi_mode",
653
                             help="Filter by nodes (primary only)",
654
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
655

    
656
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
657
                             help="Filter by nodes (secondary only)",
658
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
659

    
660
m_node_opt = make_option("--node", dest="multi_mode",
661
                         help="Filter by nodes (primary and secondary)",
662
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
663

    
664
m_clust_opt = make_option("--all", dest="multi_mode",
665
                          help="Select all instances in the cluster",
666
                          const=_SHUTDOWN_CLUSTER, action="store_const")
667

    
668
m_inst_opt = make_option("--instance", dest="multi_mode",
669
                         help="Filter by instance name [default]",
670
                         const=_SHUTDOWN_INSTANCES, action="store_const")
671

    
672

    
673
# this is defined separately due to readability only
674
add_opts = [
675
  DEBUG_OPT,
676
  node_opt,
677
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
678
             " a suffix is used",
679
             default=20 * 1024, type="unit", metavar="<size>"),
680
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
681
             " suffix is used",
682
             default=4 * 1024, type="unit", metavar="<size>"),
683
  os_opt,
684
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
685
              default=128, type="unit", metavar="<mem>"),
686
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
687
              default=1, type="int", metavar="<PROC>"),
688
  make_option("-t", "--disk-template", dest="disk_template",
689
              help="Custom disk setup (diskless, plain, local_raid1,"
690
              " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
691
  make_option("-i", "--ip", dest="ip",
692
              help="IP address ('none' [default], 'auto', or specify address)",
693
              default='none', type="string", metavar="<ADDRESS>"),
694
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
695
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
696
  make_option("--secondary-node", dest="snode",
697
              help="Secondary node for remote_raid1 disk layout",
698
              metavar="<node>"),
699
  make_option("-b", "--bridge", dest="bridge",
700
              help="Bridge to connect this instance to",
701
              default=None, metavar="<bridge>"),
702
  make_option("--no-start", dest="start", default=True,
703
              action="store_false", help="Don't start the instance after"
704
              " creation"),
705
  make_option("--no-ip-check", dest="ip_check", default=True,
706
              action="store_false", help="Don't check that the instance's IP"
707
              " is alive (only valid with --no-start)"),
708
  ]
709

    
710
commands = {
711
  'add': (AddInstance, ARGS_ONE, add_opts,
712
          "[opts...] <name>",
713
          "Creates and adds a new instance to the cluster"),
714
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
715
                [DEBUG_OPT, node_opt,
716
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
717
                             help=("The name of the instance disk for which to"
718
                                   " add the mirror"))],
719
                "-n node -b disk <instance>",
720
                "Creates a new mirror for the instance"),
721
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
722
              "<instance>",
723
              "Opens a console on the specified instance"),
724
  'failover': (FailoverInstance, ARGS_ATLEAST(1),
725
               [DEBUG_OPT, FORCE_OPT,
726
                make_option("--ignore-consistency", dest="ignore_consistency",
727
                            action="store_true", default=False,
728
                            help="Ignore the consistency of the disks on"
729
                            " the secondary"),
730
                m_pri_node_opt, m_sec_node_opt, m_inst_opt, m_force_multi,
731
                ],
732
               "[-f] <instance>",
733
               "Stops the instance and starts it on the backup node, using"
734
               " the remote mirror (only for instances of type remote_raid1)"),
735
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
736
           "Show information on the specified instance"),
737
  'list': (ListInstances, ARGS_NONE,
738
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
739
           "", "Lists the instances and their status"),
740
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
741
                "[-f] <instance>", "Reinstall the instance"),
742
  'remove': (RemoveInstance, ARGS_ONE,
743
             [DEBUG_OPT, FORCE_OPT,
744
              make_option("--ignore-failures", dest="ignore_failures",
745
                          action="store_true", default=False,
746
                          help=("Remove the instance from the cluster even"
747
                                " if there are failures during the removal"
748
                                " process (shutdown, disk removal, etc.)")),
749
              ],
750
             "[-f] <instance>", "Shuts down the instance and removes it"),
751
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
752
                   [DEBUG_OPT, node_opt,
753
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
754
                                help=("The name of the instance disk"
755
                                      " for which to add the mirror")),
756
                    make_option("-p", "--port", dest="port", metavar="PORT",
757
                                help=("The port of the drbd device"
758
                                      " which to remove from the mirror"),
759
                                type="int"),
760
                    ],
761
                   "-b disk -p port <instance>",
762
                   "Removes a mirror from the instance"),
763
  'rename': (RenameInstance, ARGS_FIXED(2),
764
             [DEBUG_OPT,
765
              make_option("--no-ip-check", dest="ignore_ip",
766
                          help="Do not check that the IP of the new name"
767
                          " is alive",
768
                          default=False, action="store_true"),
769
              ],
770
             "<instance> <new_name>", "Rename the instance"),
771
  'replace-disks': (ReplaceDisks, ARGS_ONE,
772
                    [DEBUG_OPT,
773
                     make_option("-n", "--new-secondary", dest="new_secondary",
774
                                 help=("New secondary node (for secondary"
775
                                       " node change)"), metavar="NODE"),
776
                     make_option("-p", "--on-primary", dest="on_primary",
777
                                 default=False, action="store_true",
778
                                 help=("Replace the disk(s) on the primary"
779
                                       " node (only for the drbd8 template)")),
780
                     make_option("-s", "--on-secondary", dest="on_secondary",
781
                                 default=False, action="store_true",
782
                                 help=("Replace the disk(s) on the secondary"
783
                                       " node (only for the drbd8 template)")),
784
                     make_option("--disks", dest="disks", default=None,
785
                                 help=("Comma-separated list of disks"
786
                                       " to replace (e.g. sda) (optional,"
787
                                       " defaults to all disks")),
788
                     ],
789
                    "[-n NODE] <instance>",
790
                    "Replaces all disks for the instance"),
791
  'modify': (SetInstanceParms, ARGS_ONE,
792
             [DEBUG_OPT, FORCE_OPT,
793
              cli_option("-m", "--memory", dest="mem",
794
                         help="Memory size",
795
                         default=None, type="unit", metavar="<mem>"),
796
              make_option("-p", "--cpu", dest="vcpus",
797
                          help="Number of virtual CPUs",
798
                          default=None, type="int", metavar="<PROC>"),
799
              make_option("-i", "--ip", dest="ip",
800
                          help="IP address ('none' or numeric IP)",
801
                          default=None, type="string", metavar="<ADDRESS>"),
802
              make_option("-b", "--bridge", dest="bridge",
803
                          help="Bridge to connect this instance to",
804
                          default=None, type="string", metavar="<bridge>"),
805
              ],
806
             "<instance>", "Alters the parameters of an instance"),
807
  'shutdown': (ShutdownInstance, ARGS_ANY,
808
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
809
                m_clust_opt, m_inst_opt, m_force_multi],
810
               "<instance>", "Stops an instance"),
811
  'startup': (StartupInstance, ARGS_ANY,
812
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
813
               make_option("-e", "--extra", dest="extra_args",
814
                           help="Extra arguments for the instance's kernel",
815
                           default=None, type="string", metavar="<PARAMS>"),
816
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
817
               m_clust_opt, m_inst_opt,
818
               ],
819
            "<instance>", "Starts an instance"),
820

    
821
  'reboot': (RebootInstance, ARGS_ANY,
822
              [DEBUG_OPT, m_force_multi,
823
               make_option("-e", "--extra", dest="extra_args",
824
                           help="Extra arguments for the instance's kernel",
825
                           default=None, type="string", metavar="<PARAMS>"),
826
               make_option("-t", "--type", dest="reboot_type",
827
                           help="Type of reboot: soft/hard/full",
828
                           default=constants.INSTANCE_REBOOT_SOFT,
829
                           type="string", metavar="<REBOOT>"),
830
               make_option("--ignore-secondaries", dest="ignore_secondaries",
831
                           default=False, action="store_true",
832
                           help="Ignore errors from secondaries"),
833
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
834
               m_clust_opt, m_inst_opt,
835
               ],
836
            "<instance>", "Reboots an instance"),
837
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
838
                     "<instance>",
839
                     "Activate an instance's disks"),
840
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
841
                       "<instance>",
842
                       "Deactivate an instance's disks"),
843
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
844
                "<node_name>", "List the tags of the given instance"),
845
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
846
               "<node_name> tag...", "Add tags to the given instance"),
847
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
848
                  "<node_name> tag...", "Remove tags from given instance"),
849
  }
850

    
851
if __name__ == '__main__':
852
  sys.exit(GenericMain(commands,
853
                       override={"tag_type": constants.TAG_INSTANCE}))