Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 0a80a26f

History | View | Annotate | Download (33 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(choices, msg + affected)
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
  # drop lock and exec so other commands can run while we have console
510
  utils.Unlock("cmd")
511
  try:
512
    os.execvp(cmd[0], cmd)
513
  finally:
514
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
515
                     (cmd, " ".join(argv)))
516
    os._exit(1)
517

    
518

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

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

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

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

    
572
  if dev["sstatus"]:
573
    buf.write("%*s    secondary: " % (2*indent_level, ""))
574
    helper(buf, dev["dev_type"], dev["sstatus"])
575

    
576
  if dev["children"]:
577
    for child in dev["children"]:
578
      _FormatBlockDevInfo(buf, child, indent_level+1)
579

    
580

    
581
def ShowInstanceConfig(opts, args):
582
  """Compute instance run-time status.
583

    
584
  """
585
  retcode = 0
586
  op = opcodes.OpQueryInstanceData(instances=args)
587
  result = SubmitOpCode(op)
588

    
589
  if not result:
590
    logger.ToStdout("No instances.")
591
    return 1
592

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

    
627
    for device in instance["disks"]:
628
      _FormatBlockDevInfo(buf, device, 1)
629

    
630
  logger.ToStdout(buf.getvalue().rstrip('\n'))
631
  return retcode
632

    
633

    
634
def SetInstanceParms(opts, args):
635
  """Modifies an instance.
636

    
637
  All parameters take effect only at the next restart of the instance.
638

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

    
647
  """
648
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
649
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
650
    logger.ToStdout("Please give at least one of the parameters.")
651
    return 1
652

    
653
  kernel_path = _TransformPath(opts.kernel_path)
654
  initrd_path = _TransformPath(opts.initrd_path)
655
  if opts.hvm_boot_order == 'default':
656
    hvm_boot_order = constants.VALUE_DEFAULT
657
  else:
658
    hvm_boot_order = opts.hvm_boot_order
659

    
660
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
661
                                  vcpus=opts.vcpus, ip=opts.ip,
662
                                  bridge=opts.bridge, mac=opts.mac,
663
                                  kernel_path=opts.kernel_path,
664
                                  initrd_path=opts.initrd_path,
665
                                  hvm_boot_order=hvm_boot_order)
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, or drbd)",
730
              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
  make_option("--kernel", dest="kernel_path",
749
              help="Path to the instances' kernel (or 'default')",
750
              default=None,
751
              type="string", metavar="<FILENAME>"),
752
  make_option("--initrd", dest="initrd_path",
753
              help="Path to the instances' initrd (or 'none', or 'default')",
754
              default=None,
755
              type="string", metavar="<FILENAME>"),
756
  make_option("--hvm-boot-order", dest="hvm_boot_order",
757
              help="boot device order for HVM (one or more of [acdn])",
758
              default=None, type="string", metavar="<BOOTORDER>"),
759
  ]
760

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

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

    
902
aliases = {
903
  'activate_block_devs': 'activate-disks',
904
  'replace_disks': 'replace-disks',
905
  'start': 'startup',
906
  'stop': 'shutdown',
907
  }
908

    
909
if __name__ == '__main__':
910
  sys.exit(GenericMain(commands, aliases=aliases,
911
                       override={"tag_type": constants.TAG_INSTANCE}))