Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 35a0d128

History | View | Annotate | Download (34.1 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
import sys
23
import os
24
import itertools
25
from optparse import make_option
26
from cStringIO import StringIO
27

    
28
from ganeti.cli import *
29
from ganeti import opcodes
30
from ganeti import logger
31
from ganeti import constants
32
from ganeti import utils
33
from ganeti import errors
34

    
35

    
36
_SHUTDOWN_CLUSTER = "cluster"
37
_SHUTDOWN_NODES_BOTH = "nodes"
38
_SHUTDOWN_NODES_PRI = "nodes-pri"
39
_SHUTDOWN_NODES_SEC = "nodes-sec"
40
_SHUTDOWN_INSTANCES = "instances"
41

    
42

    
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
                                file_storage_dir=opts.file_storage_dir,
264
                                file_driver=opts.file_driver,
265
                                iallocator=opts.iallocator)
266
  SubmitOpCode(op)
267
  return 0
268

    
269

    
270
def ReinstallInstance(opts, args):
271
  """Reinstall an instance.
272

    
273
  Args:
274
    opts - class with options as members
275
    args - list containing a single element, the instance name
276

    
277
  """
278
  instance_name = args[0]
279

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

    
286
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
287
                                   os_type=opts.os)
288
  SubmitOpCode(op)
289

    
290
  return 0
291

    
292

    
293
def RemoveInstance(opts, args):
294
  """Remove an instance.
295

    
296
  Args:
297
    opts - class with options as members
298
    args - list containing a single element, the instance name
299

    
300
  """
301
  instance_name = args[0]
302
  force = opts.force
303

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

    
311
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
312
                                ignore_failures=opts.ignore_failures)
313
  SubmitOpCode(op)
314
  return 0
315

    
316

    
317
def RenameInstance(opts, args):
318
  """Rename an instance.
319

    
320
  Args:
321
    opts - class with options as members
322
    args - list containing two elements, the instance name and the new name
323

    
324
  """
325
  op = opcodes.OpRenameInstance(instance_name=args[0],
326
                                new_name=args[1],
327
                                ignore_ip=opts.ignore_ip)
328
  SubmitOpCode(op)
329

    
330
  return 0
331

    
332

    
333
def ActivateDisks(opts, args):
334
  """Activate an instance's disks.
335

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

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

    
349

    
350
def DeactivateDisks(opts, args):
351
  """Command-line interface for _ShutdownInstanceBlockDevices.
352

    
353
  This function takes the instance name, looks for its primary node
354
  and the tries to shutdown its block devices on that node.
355

    
356
  """
357
  instance_name = args[0]
358
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
359
  SubmitOpCode(op)
360
  return 0
361

    
362

    
363
def StartupInstance(opts, args):
364
  """Startup an instance.
365

    
366
  Args:
367
    opts - class with options as members
368
    args - list containing a single element, the instance name
369

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

    
389

    
390
def RebootInstance(opts, args):
391
  """Reboot an instance
392

    
393
  Args:
394
    opts - class with options as members
395
    args - list containing a single element, the instance name
396

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

    
412
    SubmitOpCode(op)
413
  return 0
414

    
415

    
416
def ShutdownInstance(opts, args):
417
  """Shutdown an instance.
418

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

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

    
440

    
441
def ReplaceDisks(opts, args):
442
  """Replace the disks of an instance
443

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

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

    
465
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
466
                              remote_node=new_2ndary, mode=mode)
467
  SubmitOpCode(op)
468
  return 0
469

    
470

    
471
def FailoverInstance(opts, args):
472
  """Failover an instance.
473

    
474
  The failover is done by shutting it down on its present node and
475
  starting it on the secondary.
476

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

    
483
  """
484
  instance_name = args[0]
485
  force = opts.force
486

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

    
494
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
495
                                  ignore_consistency=opts.ignore_consistency)
496
  SubmitOpCode(op)
497
  return 0
498

    
499

    
500
def ConnectToInstanceConsole(opts, args):
501
  """Connect to the console of an instance.
502

    
503
  Args:
504
    opts - class with options as members
505
    args - list with a single element, the instance name
506

    
507
  """
508
  instance_name = args[0]
509

    
510
  op = opcodes.OpConnectConsole(instance_name=instance_name)
511
  cmd = SubmitOpCode(op)
512

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

    
525

    
526
def _FormatBlockDevInfo(buf, dev, indent_level):
527
  """Show block device information.
528

    
529
  This is only used by ShowInstanceConfig(), but it's too big to be
530
  left for an inline definition.
531

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

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

    
579
  if dev["sstatus"]:
580
    buf.write("%*s    secondary: " % (2*indent_level, ""))
581
    helper(buf, dev["dev_type"], dev["sstatus"])
582

    
583
  if dev["children"]:
584
    for child in dev["children"]:
585
      _FormatBlockDevInfo(buf, child, indent_level+1)
586

    
587

    
588
def ShowInstanceConfig(opts, args):
589
  """Compute instance run-time status.
590

    
591
  """
592
  retcode = 0
593
  op = opcodes.OpQueryInstanceData(instances=args)
594
  result = SubmitOpCode(op)
595

    
596
  if not result:
597
    logger.ToStdout("No instances.")
598
    return 1
599

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

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

    
637
  logger.ToStdout(buf.getvalue().rstrip('\n'))
638
  return retcode
639

    
640

    
641
def SetInstanceParams(opts, args):
642
  """Modifies an instance.
643

    
644
  All parameters take effect only at the next restart of the instance.
645

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

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

    
660
  kernel_path = _TransformPath(opts.kernel_path)
661
  initrd_path = _TransformPath(opts.initrd_path)
662
  if opts.hvm_boot_order == 'default':
663
    hvm_boot_order = constants.VALUE_DEFAULT
664
  else:
665
    hvm_boot_order = opts.hvm_boot_order
666

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

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

    
683

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

    
688
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
689
                    metavar="<os>")
690

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

    
697
m_pri_node_opt = make_option("--primary", dest="multi_mode",
698
                             help="Filter by nodes (primary only)",
699
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
700

    
701
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
702
                             help="Filter by nodes (secondary only)",
703
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
704

    
705
m_node_opt = make_option("--node", dest="multi_mode",
706
                         help="Filter by nodes (primary and secondary)",
707
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
708

    
709
m_clust_opt = make_option("--all", dest="multi_mode",
710
                          help="Select all instances in the cluster",
711
                          const=_SHUTDOWN_CLUSTER, action="store_const")
712

    
713
m_inst_opt = make_option("--instance", dest="multi_mode",
714
                         help="Filter by instance name [default]",
715
                         const=_SHUTDOWN_INSTANCES, action="store_const")
716

    
717

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

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

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

    
922
aliases = {
923
  'activate_block_devs': 'activate-disks',
924
  'replace_disks': 'replace-disks',
925
  'start': 'startup',
926
  'stop': 'shutdown',
927
  }
928

    
929
if __name__ == '__main__':
930
  sys.exit(GenericMain(commands, aliases=aliases,
931
                       override={"tag_type": constants.TAG_INSTANCE}))