Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 973d7867

History | View | Annotate | Download (32.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 _TransformPath(user_input):
138
  """Transform a user path into a canonical value.
139

    
140
  This function transforms the a path passed as textual information
141
  into the constants that the LU code expects.
142

    
143
  """
144
  if user_input:
145
    if user_input.lower() == "default":
146
      result_path = constants.VALUE_DEFAULT
147
    elif user_input.lower() == "none":
148
      result_path = constants.VALUE_NONE
149
    else:
150
      if not os.path.isabs(user_input):
151
        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
152
                                   user_input)
153
      result_path = user_input
154
  else:
155
    result_path = constants.VALUE_DEFAULT
156

    
157
  return result_path
158

    
159

    
160
def ListInstances(opts, args):
161
  """List instances and their properties.
162

    
163
  """
164
  if opts.output is None:
165
    selected_fields = ["name", "os", "pnode", "admin_state",
166
                       "oper_state", "oper_ram"]
167
  else:
168
    selected_fields = opts.output.split(",")
169

    
170
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
171
  output = SubmitOpCode(op)
172

    
173
  if not opts.no_headers:
174
    headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
175
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
176
               "oper_state": "Status", "admin_ram": "Configured_memory",
177
               "oper_ram": "Memory", "disk_template": "Disk_template",
178
               "ip": "IP Address", "mac": "MAC Address",
179
               "bridge": "Bridge",
180
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
181
  else:
182
    headers = None
183

    
184
  if opts.human_readable:
185
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
186
  else:
187
    unitfields = None
188

    
189
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
190

    
191
  # change raw values to nicer strings
192
  for row in output:
193
    for idx, field in enumerate(selected_fields):
194
      val = row[idx]
195
      if field == "snodes":
196
        val = ",".join(val) or "-"
197
      elif field == "admin_state":
198
        if val:
199
          val = "yes"
200
        else:
201
          val = "no"
202
      elif field == "oper_state":
203
        if val is None:
204
          val = "(node down)"
205
        elif val: # True
206
          val = "running"
207
        else:
208
          val = "stopped"
209
      elif field == "oper_ram":
210
        if val is None:
211
          val = "(node down)"
212
      elif field == "sda_size" or field == "sdb_size":
213
        if val is None:
214
          val = "N/A"
215
      row[idx] = str(val)
216

    
217
  data = GenerateTable(separator=opts.separator, headers=headers,
218
                       fields=selected_fields, unitfields=unitfields,
219
                       numfields=numfields, data=output)
220

    
221
  for line in data:
222
    logger.ToStdout(line)
223

    
224
  return 0
225

    
226

    
227
def AddInstance(opts, args):
228
  """Add an instance to the cluster.
229

    
230
  Args:
231
    opts - class with options as members
232
    args - list with a single element, the instance name
233
  Opts used:
234
    mem - amount of memory to allocate to instance (MiB)
235
    size - amount of disk space to allocate to instance (MiB)
236
    os - which OS to run on instance
237
    node - node to run new instance on
238

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

    
242
  (pnode, snode) = SplitNodeOption(opts.node)
243

    
244
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
245
                                disk_size=opts.size, swap_size=opts.swap,
246
                                disk_template=opts.disk_template,
247
                                mode=constants.INSTANCE_CREATE,
248
                                os_type=opts.os, pnode=pnode,
249
                                snode=snode, vcpus=opts.vcpus,
250
                                ip=opts.ip, bridge=opts.bridge,
251
                                start=opts.start, ip_check=opts.ip_check,
252
                                wait_for_sync=opts.wait_for_sync, mac=opts.mac)
253
  SubmitOpCode(op)
254
  return 0
255

    
256

    
257
def ReinstallInstance(opts, args):
258
  """Reinstall 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

    
267
  if not opts.force:
268
    usertext = ("This will reinstall the instance %s and remove"
269
                " all data. Continue?") % instance_name
270
    if not AskUser(usertext):
271
      return 1
272

    
273
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
274
                                   os_type=opts.os)
275
  SubmitOpCode(op)
276

    
277
  return 0
278

    
279

    
280
def RemoveInstance(opts, args):
281
  """Remove an instance.
282

    
283
  Args:
284
    opts - class with options as members
285
    args - list containing a single element, the instance name
286

    
287
  """
288
  instance_name = args[0]
289
  force = opts.force
290

    
291
  if not force:
292
    usertext = ("This will remove the volumes of the instance %s"
293
                " (including mirrors), thus removing all the data"
294
                " of the instance. Continue?") % instance_name
295
    if not AskUser(usertext):
296
      return 1
297

    
298
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
299
                                ignore_failures=opts.ignore_failures)
300
  SubmitOpCode(op)
301
  return 0
302

    
303

    
304
def RenameInstance(opts, args):
305
  """Rename an instance.
306

    
307
  Args:
308
    opts - class with options as members
309
    args - list containing two elements, the instance name and the new name
310

    
311
  """
312
  op = opcodes.OpRenameInstance(instance_name=args[0],
313
                                new_name=args[1],
314
                                ignore_ip=opts.ignore_ip)
315
  SubmitOpCode(op)
316

    
317
  return 0
318

    
319

    
320
def ActivateDisks(opts, args):
321
  """Activate an instance's disks.
322

    
323
  This serves two purposes:
324
    - it allows one (as long as the instance is not running) to mount
325
    the disks and modify them from the node
326
    - it repairs inactive secondary drbds
327

    
328
  """
329
  instance_name = args[0]
330
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
331
  disks_info = SubmitOpCode(op)
332
  for host, iname, nname in disks_info:
333
    print "%s:%s:%s" % (host, iname, nname)
334
  return 0
335

    
336

    
337
def DeactivateDisks(opts, args):
338
  """Command-line interface for _ShutdownInstanceBlockDevices.
339

    
340
  This function takes the instance name, looks for its primary node
341
  and the tries to shutdown its block devices on that node.
342

    
343
  """
344
  instance_name = args[0]
345
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
346
  SubmitOpCode(op)
347
  return 0
348

    
349

    
350
def StartupInstance(opts, args):
351
  """Startup an instance.
352

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

    
357
  """
358
  if opts.multi_mode is None:
359
    opts.multi_mode = _SHUTDOWN_INSTANCES
360
  inames = _ExpandMultiNames(opts.multi_mode, args)
361
  if not inames:
362
    raise errors.OpPrereqError("Selection filter does not match any instances")
363
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
364
  if not (opts.force_multi or not multi_on
365
          or _ConfirmOperation(inames, "startup")):
366
    return 1
367
  for name in inames:
368
    op = opcodes.OpStartupInstance(instance_name=name,
369
                                   force=opts.force,
370
                                   extra_args=opts.extra_args)
371
    if multi_on:
372
      logger.ToStdout("Starting up %s" % name)
373
    SubmitOpCode(op)
374
  return 0
375

    
376
def RebootInstance(opts, args):
377
  """Reboot an instance
378

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

    
383
  """
384
  if opts.multi_mode is None:
385
    opts.multi_mode = _SHUTDOWN_INSTANCES
386
  inames = _ExpandMultiNames(opts.multi_mode, args)
387
  if not inames:
388
    raise errors.OpPrereqError("Selection filter does not match any instances")
389
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
390
  if not (opts.force_multi or not multi_on
391
          or _ConfirmOperation(inames, "reboot")):
392
    return 1
393
  for name in inames:
394
    op = opcodes.OpRebootInstance(instance_name=name,
395
                                  reboot_type=opts.reboot_type,
396
                                  ignore_secondaries=opts.ignore_secondaries)
397

    
398
    SubmitOpCode(op)
399
  return 0
400

    
401
def ShutdownInstance(opts, args):
402
  """Shutdown an instance.
403

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

    
408
  """
409
  if opts.multi_mode is None:
410
    opts.multi_mode = _SHUTDOWN_INSTANCES
411
  inames = _ExpandMultiNames(opts.multi_mode, args)
412
  if not inames:
413
    raise errors.OpPrereqError("Selection filter does not match any instances")
414
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
415
  if not (opts.force_multi or not multi_on
416
          or _ConfirmOperation(inames, "shutdown")):
417
    return 1
418
  for name in inames:
419
    op = opcodes.OpShutdownInstance(instance_name=name)
420
    if multi_on:
421
      logger.ToStdout("Shutting down %s" % name)
422
    SubmitOpCode(op)
423
  return 0
424

    
425

    
426
def AddMDDRBDComponent(opts, args):
427
  """Add a new component to a remote_raid1 disk.
428

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

    
433
  """
434
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
435
                                    disk_name=opts.disk,
436
                                    remote_node=opts.node)
437
  SubmitOpCode(op)
438
  return 0
439

    
440

    
441
def RemoveMDDRBDComponent(opts, args):
442
  """Remove a component from a remote_raid1 disk.
443

    
444
  Args:
445
    opts - class with options as members
446
    args - list with a single element, the instance name
447

    
448
  """
449
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
450
                                       disk_name=opts.disk,
451
                                       disk_id=opts.port)
452
  SubmitOpCode(op)
453
  return 0
454

    
455

    
456
def ReplaceDisks(opts, args):
457
  """Replace the disks of an instance
458

    
459
  Args:
460
    opts - class with options as members
461
    args - list with a single element, the instance name
462

    
463
  """
464
  instance_name = args[0]
465
  new_2ndary = opts.new_secondary
466
  if opts.disks is None:
467
    disks = ["sda", "sdb"]
468
  else:
469
    disks = opts.disks.split(",")
470
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
471
    mode = constants.REPLACE_DISK_ALL
472
  elif opts.on_primary: # only on primary:
473
    mode = constants.REPLACE_DISK_PRI
474
    if new_2ndary is not None:
475
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
476
                                 " replacement")
477
  elif opts.on_secondary is not None: # only on secondary
478
    mode = constants.REPLACE_DISK_SEC
479

    
480
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
481
                              remote_node=new_2ndary, mode=mode)
482
  SubmitOpCode(op)
483
  return 0
484

    
485

    
486
def FailoverInstance(opts, args):
487
  """Failover an instance.
488

    
489
  The failover is done by shutting it down on its present node and
490
  starting it on the secondary.
491

    
492
  Args:
493
    opts - class with options as members
494
    args - list with a single element, the instance name
495
  Opts used:
496
    force - whether to failover without asking questions.
497

    
498
  """
499
  instance_name = args[0]
500
  force = opts.force
501

    
502
  if not force:
503
    usertext = ("Failover will happen to image %s."
504
                " This requires a shutdown of the instance. Continue?" %
505
                (instance_name,))
506
    if not AskUser(usertext):
507
      return 1
508

    
509
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
510
                                  ignore_consistency=opts.ignore_consistency)
511
  SubmitOpCode(op)
512
  return 0
513

    
514

    
515
def ConnectToInstanceConsole(opts, args):
516
  """Connect to the console of an instance.
517

    
518
  Args:
519
    opts - class with options as members
520
    args - list with a single element, the instance name
521

    
522
  """
523
  instance_name = args[0]
524

    
525
  op = opcodes.OpConnectConsole(instance_name=instance_name)
526
  cmd, argv = SubmitOpCode(op)
527
  # drop lock and exec so other commands can run while we have console
528
  utils.Unlock("cmd")
529
  try:
530
    os.execvp(cmd, argv)
531
  finally:
532
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
533
                     (cmd, " ".join(argv)))
534
    os._exit(1)
535

    
536

    
537
def _FormatBlockDevInfo(buf, dev, indent_level):
538
  """Show block device information.
539

    
540
  This is only used by ShowInstanceConfig(), but it's too big to be
541
  left for an inline definition.
542

    
543
  """
544
  def helper(buf, dtype, status):
545
    """Format one line for phsyical device status."""
546
    if not status:
547
      buf.write("not active\n")
548
    else:
549
      (path, major, minor, syncp, estt, degr, ldisk) = status
550
      buf.write("%s (%d:%d)" % (path, major, minor))
551
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
552
        if syncp is not None:
553
          sync_text = "*RECOVERING* %5.2f%%," % syncp
554
          if estt:
555
            sync_text += " ETA %ds" % estt
556
          else:
557
            sync_text += " ETA unknown"
558
        else:
559
          sync_text = "in sync"
560
        if degr:
561
          degr_text = "*DEGRADED*"
562
        else:
563
          degr_text = "ok"
564
        if ldisk:
565
          ldisk_text = " *MISSING DISK*"
566
        else:
567
          ldisk_text = ""
568
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
569
      elif dtype == constants.LD_LV:
570
        if ldisk:
571
          ldisk_text = " *FAILED* (failed drive?)"
572
        else:
573
          ldisk_text = ""
574
        buf.write(ldisk_text)
575
      buf.write("\n")
576

    
577
  if dev["iv_name"] is not None:
578
    data = "  - %s, " % dev["iv_name"]
579
  else:
580
    data = "  - "
581
  data += "type: %s" % dev["dev_type"]
582
  if dev["logical_id"] is not None:
583
    data += ", logical_id: %s" % (dev["logical_id"],)
584
  elif dev["physical_id"] is not None:
585
    data += ", physical_id: %s" % (dev["physical_id"],)
586
  buf.write("%*s%s\n" % (2*indent_level, "", data))
587
  buf.write("%*s    primary:   " % (2*indent_level, ""))
588
  helper(buf, dev["dev_type"], dev["pstatus"])
589

    
590
  if dev["sstatus"]:
591
    buf.write("%*s    secondary: " % (2*indent_level, ""))
592
    helper(buf, dev["dev_type"], dev["sstatus"])
593

    
594
  if dev["children"]:
595
    for child in dev["children"]:
596
      _FormatBlockDevInfo(buf, child, indent_level+1)
597

    
598

    
599
def ShowInstanceConfig(opts, args):
600
  """Compute instance run-time status.
601

    
602
  """
603
  retcode = 0
604
  op = opcodes.OpQueryInstanceData(instances=args)
605
  result = SubmitOpCode(op)
606

    
607
  if not result:
608
    logger.ToStdout("No instances.")
609
    return 1
610

    
611
  buf = StringIO()
612
  retcode = 0
613
  for instance_name in result:
614
    instance = result[instance_name]
615
    buf.write("Instance name: %s\n" % instance["name"])
616
    buf.write("State: configured to be %s, actual state is %s\n" %
617
              (instance["config_state"], instance["run_state"]))
618
    buf.write("  Nodes:\n")
619
    buf.write("    - primary: %s\n" % instance["pnode"])
620
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
621
    buf.write("  Operating system: %s\n" % instance["os"])
622
    buf.write("  Allocated network port: %s\n" % instance["network_port"])
623
    buf.write("  Hardware:\n")
624
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
625
    buf.write("    - memory: %dMiB\n" % instance["memory"])
626
    buf.write("    - NICs: %s\n" %
627
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
628
                   (mac, ip, bridge)
629
                     for mac, ip, bridge in instance["nics"]]))
630
    buf.write("  Block devices:\n")
631

    
632
    for device in instance["disks"]:
633
      _FormatBlockDevInfo(buf, device, 1)
634

    
635
  logger.ToStdout(buf.getvalue().rstrip('\n'))
636
  return retcode
637

    
638

    
639
def SetInstanceParms(opts, args):
640
  """Modifies an instance.
641

    
642
  All parameters take effect only at the next restart of the instance.
643

    
644
  Args:
645
    opts - class with options as members
646
    args - list with a single element, the instance name
647
  Opts used:
648
    memory - the new memory size
649
    vcpus - the new number of cpus
650
    mac - the new MAC address of the instance
651

    
652
  """
653
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
654
          opts.kernel_path or opts.initrd_path):
655
    logger.ToStdout("Please give at least one of the parameters.")
656
    return 1
657

    
658
  kernel_path = _TransformPath(opts.kernel_path)
659
  initrd_path = _TransformPath(opts.initrd_path)
660

    
661
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
662
                                  vcpus=opts.vcpus, ip=opts.ip,
663
                                  bridge=opts.bridge, mac=opts.mac,
664
                                  kernel_path=opts.kernel_path,
665
                                  initrd_path=opts.initrd_path)
666
  result = SubmitOpCode(op)
667

    
668
  if result:
669
    logger.ToStdout("Modified instance %s" % args[0])
670
    for param, data in result:
671
      logger.ToStdout(" - %-5s -> %s" % (param, data))
672
    logger.ToStdout("Please don't forget that these parameters take effect"
673
                    " only at the next start of the instance.")
674
  return 0
675

    
676

    
677
# options used in more than one cmd
678
node_opt = make_option("-n", "--node", dest="node", help="Target node",
679
                       metavar="<node>")
680

    
681
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
682
                    metavar="<os>")
683

    
684
# multi-instance selection options
685
m_force_multi = make_option("--force-multiple", dest="force_multi",
686
                            help="Do not ask for confirmation when more than"
687
                            " one instance is affected",
688
                            action="store_true", default=False)
689

    
690
m_pri_node_opt = make_option("--primary", dest="multi_mode",
691
                             help="Filter by nodes (primary only)",
692
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
693

    
694
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
695
                             help="Filter by nodes (secondary only)",
696
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
697

    
698
m_node_opt = make_option("--node", dest="multi_mode",
699
                         help="Filter by nodes (primary and secondary)",
700
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
701

    
702
m_clust_opt = make_option("--all", dest="multi_mode",
703
                          help="Select all instances in the cluster",
704
                          const=_SHUTDOWN_CLUSTER, action="store_const")
705

    
706
m_inst_opt = make_option("--instance", dest="multi_mode",
707
                         help="Filter by instance name [default]",
708
                         const=_SHUTDOWN_INSTANCES, action="store_const")
709

    
710

    
711
# this is defined separately due to readability only
712
add_opts = [
713
  DEBUG_OPT,
714
  make_option("-n", "--node", dest="node",
715
              help="Target node and optional secondary node",
716
              metavar="<pnode>[:<snode>]"),
717
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
718
             " a suffix is used",
719
             default=20 * 1024, type="unit", metavar="<size>"),
720
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
721
             " suffix is used",
722
             default=4 * 1024, type="unit", metavar="<size>"),
723
  os_opt,
724
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
725
              default=128, type="unit", metavar="<mem>"),
726
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
727
              default=1, type="int", metavar="<PROC>"),
728
  make_option("-t", "--disk-template", dest="disk_template",
729
              help="Custom disk setup (diskless, plain, local_raid1,"
730
              " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
731
  make_option("-i", "--ip", dest="ip",
732
              help="IP address ('none' [default], 'auto', or specify address)",
733
              default='none', type="string", metavar="<ADDRESS>"),
734
  make_option("--mac", dest="mac",
735
              help="MAC address ('auto' [default], or specify address)",
736
              default='auto', type="string", metavar="<MACADDRESS>"),
737
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
738
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
739
  make_option("-b", "--bridge", dest="bridge",
740
              help="Bridge to connect this instance to",
741
              default=None, metavar="<bridge>"),
742
  make_option("--no-start", dest="start", default=True,
743
              action="store_false", help="Don't start the instance after"
744
              " creation"),
745
  make_option("--no-ip-check", dest="ip_check", default=True,
746
              action="store_false", help="Don't check that the instance's IP"
747
              " is alive (only valid with --no-start)"),
748
  ]
749

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

    
871
  'reboot': (RebootInstance, ARGS_ANY,
872
              [DEBUG_OPT, m_force_multi,
873
               make_option("-e", "--extra", dest="extra_args",
874
                           help="Extra arguments for the instance's kernel",
875
                           default=None, type="string", metavar="<PARAMS>"),
876
               make_option("-t", "--type", dest="reboot_type",
877
                           help="Type of reboot: soft/hard/full",
878
                           default=constants.INSTANCE_REBOOT_SOFT,
879
                           type="string", metavar="<REBOOT>"),
880
               make_option("--ignore-secondaries", dest="ignore_secondaries",
881
                           default=False, action="store_true",
882
                           help="Ignore errors from secondaries"),
883
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
884
               m_clust_opt, m_inst_opt,
885
               ],
886
            "<instance>", "Reboots an instance"),
887
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
888
                     "<instance>",
889
                     "Activate an instance's disks"),
890
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
891
                       "<instance>",
892
                       "Deactivate an instance's disks"),
893
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
894
                "<node_name>", "List the tags of the given instance"),
895
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
896
               "<node_name> tag...", "Add tags to the given instance"),
897
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
898
                  "<node_name> tag...", "Remove tags from given instance"),
899
  }
900

    
901
if __name__ == '__main__':
902
  sys.exit(GenericMain(commands,
903
                       override={"tag_type": constants.TAG_INSTANCE}))