Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 7767bbf5

History | View | Annotate | Download (33.3 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

    
43
def _ExpandMultiNames(mode, names):
44
  """Expand the given names using the passed mode.
45

    
46
  Args:
47
    - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
48
      _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
49
    - names, which is a list of names; for cluster, it must be empty,
50
      and for node and instance it must be a list of valid item
51
      names (short names are valid as usual, e.g. node1 instead of
52
      node1.example.com)
53

    
54
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
55
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
56
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
57
  instances having those nodes as either primary or secondary will be
58
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
59
  returned.
60

    
61
  """
62
  if mode == _SHUTDOWN_CLUSTER:
63
    if names:
64
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
65
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
66
    idata = SubmitOpCode(op)
67
    inames = [row[0] for row in idata]
68

    
69
  elif mode in (_SHUTDOWN_NODES_BOTH,
70
                _SHUTDOWN_NODES_PRI,
71
                _SHUTDOWN_NODES_SEC):
72
    if not names:
73
      raise errors.OpPrereqError("No node names passed")
74
    op = opcodes.OpQueryNodes(output_fields=["name", "pinst_list",
75
                                             "sinst_list"], names=names)
76
    ndata = SubmitOpCode(op)
77
    ipri = [row[1] for row in ndata]
78
    pri_names = list(itertools.chain(*ipri))
79
    isec = [row[2] for row in ndata]
80
    sec_names = list(itertools.chain(*isec))
81
    if mode == _SHUTDOWN_NODES_BOTH:
82
      inames = pri_names + sec_names
83
    elif mode == _SHUTDOWN_NODES_PRI:
84
      inames = pri_names
85
    elif mode == _SHUTDOWN_NODES_SEC:
86
      inames = sec_names
87
    else:
88
      raise errors.ProgrammerError("Unhandled shutdown type")
89

    
90
  elif mode == _SHUTDOWN_INSTANCES:
91
    if not names:
92
      raise errors.OpPrereqError("No instance names passed")
93
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
94
    idata = SubmitOpCode(op)
95
    inames = [row[0] for row in idata]
96

    
97
  else:
98
    raise errors.OpPrereqError("Unknown mode '%s'" % mode)
99

    
100
  return inames
101

    
102

    
103
def _ConfirmOperation(inames, text):
104
  """Ask the user to confirm an operation on a list of instances.
105

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

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

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

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

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

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

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

    
137

    
138
def _TransformPath(user_input):
139
  """Transform a user path into a canonical value.
140

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

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

    
158
  return result_path
159

    
160

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

    
164
  """
165
  if opts.output is None:
166
    selected_fields = ["name", "os", "pnode", "status", "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 = {
175
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
176
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
177
      "oper_state": "Running", "admin_ram": "Configured_memory",
178
      "oper_ram": "Memory", "disk_template": "Disk_template",
179
      "ip": "IP Address", "mac": "MAC Address",
180
      "bridge": "Bridge", "vcpus": "VCPUs",
181
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
182
      "status": "Status",
183
      }
184
  else:
185
    headers = None
186

    
187
  if opts.human_readable:
188
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
189
  else:
190
    unitfields = None
191

    
192
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
193

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

    
220
  data = GenerateTable(separator=opts.separator, headers=headers,
221
                       fields=selected_fields, unitfields=unitfields,
222
                       numfields=numfields, data=output)
223

    
224
  for line in data:
225
    logger.ToStdout(line)
226

    
227
  return 0
228

    
229

    
230
def AddInstance(opts, args):
231
  """Add an instance to the cluster.
232

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

    
242
  """
243
  instance = args[0]
244

    
245
  (pnode, snode) = SplitNodeOption(opts.node)
246

    
247
  kernel_path = _TransformPath(opts.kernel_path)
248
  initrd_path = _TransformPath(opts.initrd_path)
249

    
250
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
251
                                disk_size=opts.size, swap_size=opts.swap,
252
                                disk_template=opts.disk_template,
253
                                mode=constants.INSTANCE_CREATE,
254
                                os_type=opts.os, pnode=pnode,
255
                                snode=snode, vcpus=opts.vcpus,
256
                                ip=opts.ip, bridge=opts.bridge,
257
                                start=opts.start, ip_check=opts.ip_check,
258
                                wait_for_sync=opts.wait_for_sync,
259
                                mac=opts.mac,
260
                                kernel_path=kernel_path,
261
                                initrd_path=initrd_path,
262
                                hvm_boot_order=opts.hvm_boot_order)
263
  SubmitOpCode(op)
264
  return 0
265

    
266

    
267
def ReinstallInstance(opts, args):
268
  """Reinstall an instance.
269

    
270
  Args:
271
    opts - class with options as members
272
    args - list containing a single element, the instance name
273

    
274
  """
275
  instance_name = args[0]
276

    
277
  if not opts.force:
278
    usertext = ("This will reinstall the instance %s and remove"
279
                " all data. Continue?") % instance_name
280
    if not AskUser(usertext):
281
      return 1
282

    
283
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
284
                                   os_type=opts.os)
285
  SubmitOpCode(op)
286

    
287
  return 0
288

    
289

    
290
def RemoveInstance(opts, args):
291
  """Remove an instance.
292

    
293
  Args:
294
    opts - class with options as members
295
    args - list containing a single element, the instance name
296

    
297
  """
298
  instance_name = args[0]
299
  force = opts.force
300

    
301
  if not force:
302
    usertext = ("This will remove the volumes of the instance %s"
303
                " (including mirrors), thus removing all the data"
304
                " of the instance. Continue?") % instance_name
305
    if not AskUser(usertext):
306
      return 1
307

    
308
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
309
                                ignore_failures=opts.ignore_failures)
310
  SubmitOpCode(op)
311
  return 0
312

    
313

    
314
def RenameInstance(opts, args):
315
  """Rename an instance.
316

    
317
  Args:
318
    opts - class with options as members
319
    args - list containing two elements, the instance name and the new name
320

    
321
  """
322
  op = opcodes.OpRenameInstance(instance_name=args[0],
323
                                new_name=args[1],
324
                                ignore_ip=opts.ignore_ip)
325
  SubmitOpCode(op)
326

    
327
  return 0
328

    
329

    
330
def ActivateDisks(opts, args):
331
  """Activate an instance's disks.
332

    
333
  This serves two purposes:
334
    - it allows one (as long as the instance is not running) to mount
335
    the disks and modify them from the node
336
    - it repairs inactive secondary drbds
337

    
338
  """
339
  instance_name = args[0]
340
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
341
  disks_info = SubmitOpCode(op)
342
  for host, iname, nname in disks_info:
343
    print "%s:%s:%s" % (host, iname, nname)
344
  return 0
345

    
346

    
347
def DeactivateDisks(opts, args):
348
  """Command-line interface for _ShutdownInstanceBlockDevices.
349

    
350
  This function takes the instance name, looks for its primary node
351
  and the tries to shutdown its block devices on that node.
352

    
353
  """
354
  instance_name = args[0]
355
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
356
  SubmitOpCode(op)
357
  return 0
358

    
359

    
360
def StartupInstance(opts, args):
361
  """Startup an instance.
362

    
363
  Args:
364
    opts - class with options as members
365
    args - list containing a single element, the instance name
366

    
367
  """
368
  if opts.multi_mode is None:
369
    opts.multi_mode = _SHUTDOWN_INSTANCES
370
  inames = _ExpandMultiNames(opts.multi_mode, args)
371
  if not inames:
372
    raise errors.OpPrereqError("Selection filter does not match any instances")
373
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
374
  if not (opts.force_multi or not multi_on
375
          or _ConfirmOperation(inames, "startup")):
376
    return 1
377
  for name in inames:
378
    op = opcodes.OpStartupInstance(instance_name=name,
379
                                   force=opts.force,
380
                                   extra_args=opts.extra_args)
381
    if multi_on:
382
      logger.ToStdout("Starting up %s" % name)
383
    SubmitOpCode(op)
384
  return 0
385

    
386

    
387
def RebootInstance(opts, args):
388
  """Reboot an instance
389

    
390
  Args:
391
    opts - class with options as members
392
    args - list containing a single element, the instance name
393

    
394
  """
395
  if opts.multi_mode is None:
396
    opts.multi_mode = _SHUTDOWN_INSTANCES
397
  inames = _ExpandMultiNames(opts.multi_mode, args)
398
  if not inames:
399
    raise errors.OpPrereqError("Selection filter does not match any instances")
400
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
401
  if not (opts.force_multi or not multi_on
402
          or _ConfirmOperation(inames, "reboot")):
403
    return 1
404
  for name in inames:
405
    op = opcodes.OpRebootInstance(instance_name=name,
406
                                  reboot_type=opts.reboot_type,
407
                                  ignore_secondaries=opts.ignore_secondaries)
408

    
409
    SubmitOpCode(op)
410
  return 0
411

    
412

    
413
def ShutdownInstance(opts, args):
414
  """Shutdown an instance.
415

    
416
  Args:
417
    opts - class with options as members
418
    args - list containing a single element, the instance name
419

    
420
  """
421
  if opts.multi_mode is None:
422
    opts.multi_mode = _SHUTDOWN_INSTANCES
423
  inames = _ExpandMultiNames(opts.multi_mode, args)
424
  if not inames:
425
    raise errors.OpPrereqError("Selection filter does not match any instances")
426
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
427
  if not (opts.force_multi or not multi_on
428
          or _ConfirmOperation(inames, "shutdown")):
429
    return 1
430
  for name in inames:
431
    op = opcodes.OpShutdownInstance(instance_name=name)
432
    if multi_on:
433
      logger.ToStdout("Shutting down %s" % name)
434
    SubmitOpCode(op)
435
  return 0
436

    
437

    
438
def ReplaceDisks(opts, args):
439
  """Replace the disks of an instance
440

    
441
  Args:
442
    opts - class with options as members
443
    args - list with a single element, the instance name
444

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

    
462
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
463
                              remote_node=new_2ndary, mode=mode)
464
  SubmitOpCode(op)
465
  return 0
466

    
467

    
468
def FailoverInstance(opts, args):
469
  """Failover an instance.
470

    
471
  The failover is done by shutting it down on its present node and
472
  starting it on the secondary.
473

    
474
  Args:
475
    opts - class with options as members
476
    args - list with a single element, the instance name
477
  Opts used:
478
    force - whether to failover without asking questions.
479

    
480
  """
481
  instance_name = args[0]
482
  force = opts.force
483

    
484
  if not force:
485
    usertext = ("Failover will happen to image %s."
486
                " This requires a shutdown of the instance. Continue?" %
487
                (instance_name,))
488
    if not AskUser(usertext):
489
      return 1
490

    
491
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
492
                                  ignore_consistency=opts.ignore_consistency)
493
  SubmitOpCode(op)
494
  return 0
495

    
496

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

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

    
504
  """
505
  instance_name = args[0]
506

    
507
  op = opcodes.OpConnectConsole(instance_name=instance_name)
508
  cmd = SubmitOpCode(op)
509

    
510
  if opts.show_command:
511
    print utils.ShellQuoteArgs(cmd)
512
  else:
513
    # drop lock and exec so other commands can run while we have console
514
    utils.Unlock("cmd")
515
    try:
516
      os.execvp(cmd[0], cmd)
517
    finally:
518
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
519
                       (cmd, " ".join(argv)))
520
      os._exit(1)
521

    
522

    
523
def _FormatBlockDevInfo(buf, dev, indent_level):
524
  """Show block device information.
525

    
526
  This is only used by ShowInstanceConfig(), but it's too big to be
527
  left for an inline definition.
528

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

    
563
  if dev["iv_name"] is not None:
564
    data = "  - %s, " % dev["iv_name"]
565
  else:
566
    data = "  - "
567
  data += "type: %s" % dev["dev_type"]
568
  if dev["logical_id"] is not None:
569
    data += ", logical_id: %s" % (dev["logical_id"],)
570
  elif dev["physical_id"] is not None:
571
    data += ", physical_id: %s" % (dev["physical_id"],)
572
  buf.write("%*s%s\n" % (2*indent_level, "", data))
573
  buf.write("%*s    primary:   " % (2*indent_level, ""))
574
  helper(buf, dev["dev_type"], dev["pstatus"])
575

    
576
  if dev["sstatus"]:
577
    buf.write("%*s    secondary: " % (2*indent_level, ""))
578
    helper(buf, dev["dev_type"], dev["sstatus"])
579

    
580
  if dev["children"]:
581
    for child in dev["children"]:
582
      _FormatBlockDevInfo(buf, child, indent_level+1)
583

    
584

    
585
def ShowInstanceConfig(opts, args):
586
  """Compute instance run-time status.
587

    
588
  """
589
  retcode = 0
590
  op = opcodes.OpQueryInstanceData(instances=args)
591
  result = SubmitOpCode(op)
592

    
593
  if not result:
594
    logger.ToStdout("No instances.")
595
    return 1
596

    
597
  buf = StringIO()
598
  retcode = 0
599
  for instance_name in result:
600
    instance = result[instance_name]
601
    buf.write("Instance name: %s\n" % instance["name"])
602
    buf.write("State: configured to be %s, actual state is %s\n" %
603
              (instance["config_state"], instance["run_state"]))
604
    buf.write("  Nodes:\n")
605
    buf.write("    - primary: %s\n" % instance["pnode"])
606
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
607
    buf.write("  Operating system: %s\n" % instance["os"])
608
    buf.write("  Allocated network port: %s\n" % instance["network_port"])
609
    if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
610
      kpath = "(default: %s)" % constants.XEN_KERNEL
611
    else:
612
      kpath = instance["kernel_path"]
613
    buf.write("  Kernel path: %s\n" % kpath)
614
    if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
615
      initrd = "(default: %s)" % constants.XEN_INITRD
616
    elif instance["initrd_path"] == constants.VALUE_NONE:
617
      initrd = "(none)"
618
    else:
619
      initrd = instance["initrd_path"]
620
    buf.write("       initrd: %s\n" % initrd)
621
    buf.write("  HVM boot order: %s\n" % instance["hvm_boot_order"])
622
    buf.write("  Hardware:\n")
623
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
624
    buf.write("    - memory: %dMiB\n" % instance["memory"])
625
    buf.write("    - NICs: %s\n" %
626
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
627
                   (mac, ip, bridge)
628
                     for mac, ip, bridge in instance["nics"]]))
629
    buf.write("  Block devices:\n")
630

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

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

    
637

    
638
def SetInstanceParams(opts, args):
639
  """Modifies an instance.
640

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

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

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

    
657
  kernel_path = _TransformPath(opts.kernel_path)
658
  initrd_path = _TransformPath(opts.initrd_path)
659
  if opts.hvm_boot_order == 'default':
660
    hvm_boot_order = constants.VALUE_DEFAULT
661
  else:
662
    hvm_boot_order = opts.hvm_boot_order
663

    
664
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
665
                                   vcpus=opts.vcpus, ip=opts.ip,
666
                                   bridge=opts.bridge, mac=opts.mac,
667
                                   kernel_path=opts.kernel_path,
668
                                   initrd_path=opts.initrd_path,
669
                                   hvm_boot_order=hvm_boot_order)
670
  result = SubmitOpCode(op)
671

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

    
680

    
681
# options used in more than one cmd
682
node_opt = make_option("-n", "--node", dest="node", help="Target node",
683
                       metavar="<node>")
684

    
685
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
686
                    metavar="<os>")
687

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

    
694
m_pri_node_opt = make_option("--primary", dest="multi_mode",
695
                             help="Filter by nodes (primary only)",
696
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
697

    
698
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
699
                             help="Filter by nodes (secondary only)",
700
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
701

    
702
m_node_opt = make_option("--node", dest="multi_mode",
703
                         help="Filter by nodes (primary and secondary)",
704
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
705

    
706
m_clust_opt = make_option("--all", dest="multi_mode",
707
                          help="Select all instances in the cluster",
708
                          const=_SHUTDOWN_CLUSTER, action="store_const")
709

    
710
m_inst_opt = make_option("--instance", dest="multi_mode",
711
                         help="Filter by instance name [default]",
712
                         const=_SHUTDOWN_INSTANCES, action="store_const")
713

    
714

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

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

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

    
910
aliases = {
911
  'activate_block_devs': 'activate-disks',
912
  'replace_disks': 'replace-disks',
913
  'start': 'startup',
914
  'stop': 'shutdown',
915
  }
916

    
917
if __name__ == '__main__':
918
  sys.exit(GenericMain(commands, aliases=aliases,
919
                       override={"tag_type": constants.TAG_INSTANCE}))