Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ f4bc1f2c

History | View | Annotate | Download (30.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
  (pnode, snode) = SplitNodeOption(opts.node)
220

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

    
233

    
234
def ReinstallInstance(opts, args):
235
  """Reinstall an instance.
236

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

    
241
  """
242
  instance_name = args[0]
243

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

    
250
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
251
                                   os_type=opts.os)
252
  SubmitOpCode(op)
253

    
254
  return 0
255

    
256

    
257
def RemoveInstance(opts, args):
258
  """Remove an instance.
259

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

    
264
  """
265
  instance_name = args[0]
266
  force = opts.force
267

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

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

    
280

    
281
def RenameInstance(opts, args):
282
  """Rename an instance.
283

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

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

    
294
  return 0
295

    
296

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

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

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

    
313

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

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

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

    
326

    
327
def StartupInstance(opts, args):
328
  """Startup an instance.
329

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

    
334
  """
335
  if opts.multi_mode is None:
336
    opts.multi_mode = _SHUTDOWN_INSTANCES
337
  inames = _ExpandMultiNames(opts.multi_mode, args)
338
  if not inames:
339
    raise errors.OpPrereqError("Selection filter does not match any instances")
340
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
341
  if not (opts.force_multi or not multi_on
342
          or _ConfirmOperation(inames, "startup")):
343
    return 1
344
  for name in inames:
345
    op = opcodes.OpStartupInstance(instance_name=name,
346
                                   force=opts.force,
347
                                   extra_args=opts.extra_args)
348
    if multi_on:
349
      logger.ToStdout("Starting up %s" % name)
350
    SubmitOpCode(op)
351
  return 0
352

    
353
def RebootInstance(opts, args):
354
  """Reboot an instance
355

    
356
  Args:
357
    opts - class with options as members
358
    args - list containing a single element, the instance name
359

    
360
  """
361
  if opts.multi_mode is None:
362
    opts.multi_mode = _SHUTDOWN_INSTANCES
363
  inames = _ExpandMultiNames(opts.multi_mode, args)
364
  if not inames:
365
    raise errors.OpPrereqError("Selection filter does not match any instances")
366
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
367
  if not (opts.force_multi or not multi_on
368
          or _ConfirmOperation(inames, "reboot")):
369
    return 1
370
  for name in inames:
371
    op = opcodes.OpRebootInstance(instance_name=name,
372
                                  reboot_type=opts.reboot_type,
373
                                  ignore_secondaries=opts.ignore_secondaries)
374

    
375
    SubmitOpCode(op)
376
  return 0
377

    
378
def ShutdownInstance(opts, args):
379
  """Shutdown an instance.
380

    
381
  Args:
382
    opts - class with options as members
383
    args - list containing a single element, the instance name
384

    
385
  """
386
  if opts.multi_mode is None:
387
    opts.multi_mode = _SHUTDOWN_INSTANCES
388
  inames = _ExpandMultiNames(opts.multi_mode, args)
389
  if not inames:
390
    raise errors.OpPrereqError("Selection filter does not match any instances")
391
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
392
  if not (opts.force_multi or not multi_on
393
          or _ConfirmOperation(inames, "shutdown")):
394
    return 1
395
  for name in inames:
396
    op = opcodes.OpShutdownInstance(instance_name=name)
397
    if multi_on:
398
      logger.ToStdout("Shutting down %s" % name)
399
    SubmitOpCode(op)
400
  return 0
401

    
402

    
403
def AddMDDRBDComponent(opts, args):
404
  """Add a new component to a remote_raid1 disk.
405

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

    
410
  """
411
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
412
                                    disk_name=opts.disk,
413
                                    remote_node=opts.node)
414
  SubmitOpCode(op)
415
  return 0
416

    
417

    
418
def RemoveMDDRBDComponent(opts, args):
419
  """Remove a component from a remote_raid1 disk.
420

    
421
  Args:
422
    opts - class with options as members
423
    args - list with a single element, the instance name
424

    
425
  """
426
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
427
                                       disk_name=opts.disk,
428
                                       disk_id=opts.port)
429
  SubmitOpCode(op)
430
  return 0
431

    
432

    
433
def ReplaceDisks(opts, args):
434
  """Replace the disks of an instance
435

    
436
  Args:
437
    opts - class with options as members
438
    args - list with a single element, the instance name
439

    
440
  """
441
  instance_name = args[0]
442
  new_2ndary = opts.new_secondary
443
  if opts.disks is None:
444
    disks = ["sda", "sdb"]
445
  else:
446
    disks = opts.disks.split(",")
447
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
448
    mode = constants.REPLACE_DISK_ALL
449
  elif opts.on_primary: # only on primary:
450
    mode = constants.REPLACE_DISK_PRI
451
    if new_2ndary is not None:
452
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
453
                                 " replacement")
454
  elif opts.on_secondary is not None: # only on secondary
455
    mode = constants.REPLACE_DISK_SEC
456

    
457
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
458
                              remote_node=new_2ndary, mode=mode)
459
  SubmitOpCode(op)
460
  return 0
461

    
462

    
463
def FailoverInstance(opts, args):
464
  """Failover an instance.
465

    
466
  The failover is done by shutting it down on its present node and
467
  starting it on the secondary.
468

    
469
  Args:
470
    opts - class with options as members
471
    args - list with a single element, the instance name
472
  Opts used:
473
    force - whether to failover without asking questions.
474

    
475
  """
476
  instance_name = args[0]
477
  force = opts.force
478

    
479
  if not force:
480
    usertext = ("Failover will happen to image %s."
481
                " This requires a shutdown of the instance. Continue?" %
482
                (instance_name,))
483
    if not AskUser(usertext):
484
      return 1
485

    
486
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
487
                                  ignore_consistency=opts.ignore_consistency)
488
  SubmitOpCode(op)
489
  return 0
490

    
491

    
492
def ConnectToInstanceConsole(opts, args):
493
  """Connect to the console of an instance.
494

    
495
  Args:
496
    opts - class with options as members
497
    args - list with a single element, the instance name
498

    
499
  """
500
  instance_name = args[0]
501

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

    
513

    
514
def _FormatBlockDevInfo(buf, dev, indent_level):
515
  """Show block device information.
516

    
517
  This is only used by ShowInstanceConfig(), but it's too big to be
518
  left for an inline definition.
519

    
520
  """
521
  def helper(buf, dtype, status):
522
    """Format one line for phsyical device status."""
523
    if not status:
524
      buf.write("not active\n")
525
    else:
526
      (path, major, minor, syncp, estt, degr, ldisk) = status
527
      buf.write("%s (%d:%d)" % (path, major, minor))
528
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
529
        if syncp is not None:
530
          sync_text = "*RECOVERING* %5.2f%%," % syncp
531
          if estt:
532
            sync_text += " ETA %ds" % estt
533
          else:
534
            sync_text += " ETA unknown"
535
        else:
536
          sync_text = "in sync"
537
        if degr:
538
          degr_text = "*DEGRADED*"
539
        else:
540
          degr_text = "ok"
541
        if ldisk:
542
          ldisk_text = " *MISSING DISK*"
543
        else:
544
          ldisk_text = ""
545
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
546
      elif dtype == constants.LD_LV:
547
        if ldisk:
548
          ldisk_text = " *FAILED* (failed drive?)"
549
        else:
550
          ldisk_text = ""
551
        buf.write(ldisk_text)
552
      buf.write("\n")
553

    
554
  if dev["iv_name"] is not None:
555
    data = "  - %s, " % dev["iv_name"]
556
  else:
557
    data = "  - "
558
  data += "type: %s" % dev["dev_type"]
559
  if dev["logical_id"] is not None:
560
    data += ", logical_id: %s" % (dev["logical_id"],)
561
  elif dev["physical_id"] is not None:
562
    data += ", physical_id: %s" % (dev["physical_id"],)
563
  buf.write("%*s%s\n" % (2*indent_level, "", data))
564
  buf.write("%*s    primary:   " % (2*indent_level, ""))
565
  helper(buf, dev["dev_type"], dev["pstatus"])
566

    
567
  if dev["sstatus"]:
568
    buf.write("%*s    secondary: " % (2*indent_level, ""))
569
    helper(buf, dev["dev_type"], dev["sstatus"])
570

    
571
  if dev["children"]:
572
    for child in dev["children"]:
573
      _FormatBlockDevInfo(buf, child, indent_level+1)
574

    
575

    
576
def ShowInstanceConfig(opts, args):
577
  """Compute instance run-time status.
578

    
579
  """
580
  retcode = 0
581
  op = opcodes.OpQueryInstanceData(instances=args)
582
  result = SubmitOpCode(op)
583

    
584
  if not result:
585
    logger.ToStdout("No instances.")
586
    return 1
587

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

    
608
    for device in instance["disks"]:
609
      _FormatBlockDevInfo(buf, device, 1)
610

    
611
  logger.ToStdout(buf.getvalue().rstrip('\n'))
612
  return retcode
613

    
614

    
615
def SetInstanceParms(opts, args):
616
  """Modifies an instance.
617

    
618
  All parameters take effect only at the next restart of the instance.
619

    
620
  Args:
621
    opts - class with options as members
622
    args - list with a single element, the instance name
623
  Opts used:
624
    memory - the new memory size
625
    vcpus - the new number of cpus
626

    
627
  """
628
  if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
629
    logger.ToStdout("Please give at least one of the parameters.")
630
    return 1
631

    
632
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
633
                                  vcpus=opts.vcpus, ip=opts.ip,
634
                                  bridge=opts.bridge)
635
  result = SubmitOpCode(op)
636

    
637
  if result:
638
    logger.ToStdout("Modified instance %s" % args[0])
639
    for param, data in result:
640
      logger.ToStdout(" - %-5s -> %s" % (param, data))
641
    logger.ToStdout("Please don't forget that these parameters take effect"
642
                    " only at the next start of the instance.")
643
  return 0
644

    
645

    
646
# options used in more than one cmd
647
node_opt = make_option("-n", "--node", dest="node", help="Target node",
648
                       metavar="<node>")
649

    
650
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
651
                    metavar="<os>")
652

    
653
# multi-instance selection options
654
m_force_multi = make_option("--force-multiple", dest="force_multi",
655
                            help="Do not ask for confirmation when more than"
656
                            " one instance is affected",
657
                            action="store_true", default=False)
658

    
659
m_pri_node_opt = make_option("--primary", dest="multi_mode",
660
                             help="Filter by nodes (primary only)",
661
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
662

    
663
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
664
                             help="Filter by nodes (secondary only)",
665
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
666

    
667
m_node_opt = make_option("--node", dest="multi_mode",
668
                         help="Filter by nodes (primary and secondary)",
669
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
670

    
671
m_clust_opt = make_option("--all", dest="multi_mode",
672
                          help="Select all instances in the cluster",
673
                          const=_SHUTDOWN_CLUSTER, action="store_const")
674

    
675
m_inst_opt = make_option("--instance", dest="multi_mode",
676
                         help="Filter by instance name [default]",
677
                         const=_SHUTDOWN_INSTANCES, action="store_const")
678

    
679

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

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

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

    
856
if __name__ == '__main__':
857
  sys.exit(GenericMain(commands,
858
                       override={"tag_type": constants.TAG_INSTANCE}))