Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 1862d460

History | View | Annotate | Download (31.1 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, mac=opts.mac)
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("  Allocated network port: %s\n" % instance["network_port"])
600
    buf.write("  Hardware:\n")
601
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
602
    buf.write("    - memory: %dMiB\n" % instance["memory"])
603
    buf.write("    - NICs: %s\n" %
604
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
605
                   (mac, ip, bridge)
606
                     for mac, ip, bridge in instance["nics"]]))
607
    buf.write("  Block devices:\n")
608

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

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

    
615

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

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

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

    
629
  """
630
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac):
631
    logger.ToStdout("Please give at least one of the parameters.")
632
    return 1
633

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

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

    
647

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

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

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

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

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

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

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

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

    
681

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

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

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

    
864
if __name__ == '__main__':
865
  sys.exit(GenericMain(commands,
866
                       override={"tag_type": constants.TAG_INSTANCE}))