Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ fd38ef95

History | View | Annotate | Download (34.8 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
  iallocator = opts.iallocator
452
  if opts.disks is None:
453
    disks = ["sda", "sdb"]
454
  else:
455
    disks = opts.disks.split(",")
456
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
457
    mode = constants.REPLACE_DISK_ALL
458
  elif opts.on_primary: # only on primary:
459
    mode = constants.REPLACE_DISK_PRI
460
    if new_2ndary is not None or iallocator is not None:
461
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
462
                                 " replacement")
463
  elif opts.on_secondary is not None or iallocator is not None:
464
    # only on secondary
465
    mode = constants.REPLACE_DISK_SEC
466

    
467
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
468
                              remote_node=new_2ndary, mode=mode,
469
                              iallocator=iallocator)
470
  SubmitOpCode(op)
471
  return 0
472

    
473

    
474
def FailoverInstance(opts, args):
475
  """Failover an instance.
476

    
477
  The failover is done by shutting it down on its present node and
478
  starting it on the secondary.
479

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

    
486
  """
487
  instance_name = args[0]
488
  force = opts.force
489

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

    
497
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
498
                                  ignore_consistency=opts.ignore_consistency)
499
  SubmitOpCode(op)
500
  return 0
501

    
502

    
503
def ConnectToInstanceConsole(opts, args):
504
  """Connect to the console of an instance.
505

    
506
  Args:
507
    opts - class with options as members
508
    args - list with a single element, the instance name
509

    
510
  """
511
  instance_name = args[0]
512

    
513
  op = opcodes.OpConnectConsole(instance_name=instance_name)
514
  cmd = SubmitOpCode(op)
515

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

    
528

    
529
def _FormatBlockDevInfo(buf, dev, indent_level):
530
  """Show block device information.
531

    
532
  This is only used by ShowInstanceConfig(), but it's too big to be
533
  left for an inline definition.
534

    
535
  """
536
  def helper(buf, dtype, status):
537
    """Format one line for physical device status."""
538
    if not status:
539
      buf.write("not active\n")
540
    else:
541
      (path, major, minor, syncp, estt, degr, ldisk) = status
542
      if major is None:
543
        major_string = "N/A"
544
      else:
545
        major_string = str(major)
546

    
547
      if minor is None:
548
        minor_string = "N/A"
549
      else:
550
        minor_string = str(minor)
551

    
552
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
553
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
554
        if syncp is not None:
555
          sync_text = "*RECOVERING* %5.2f%%," % syncp
556
          if estt:
557
            sync_text += " ETA %ds" % estt
558
          else:
559
            sync_text += " ETA unknown"
560
        else:
561
          sync_text = "in sync"
562
        if degr:
563
          degr_text = "*DEGRADED*"
564
        else:
565
          degr_text = "ok"
566
        if ldisk:
567
          ldisk_text = " *MISSING DISK*"
568
        else:
569
          ldisk_text = ""
570
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
571
      elif dtype == constants.LD_LV:
572
        if ldisk:
573
          ldisk_text = " *FAILED* (failed drive?)"
574
        else:
575
          ldisk_text = ""
576
        buf.write(ldisk_text)
577
      buf.write("\n")
578

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

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

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

    
600

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

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

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

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

    
647
    for device in instance["disks"]:
648
      _FormatBlockDevInfo(buf, device, 1)
649

    
650
  logger.ToStdout(buf.getvalue().rstrip('\n'))
651
  return retcode
652

    
653

    
654
def SetInstanceParams(opts, args):
655
  """Modifies an instance.
656

    
657
  All parameters take effect only at the next restart of the instance.
658

    
659
  Args:
660
    opts - class with options as members
661
    args - list with a single element, the instance name
662
  Opts used:
663
    memory - the new memory size
664
    vcpus - the new number of cpus
665
    mac - the new MAC address of the instance
666

    
667
  """
668
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
669
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
670
    logger.ToStdout("Please give at least one of the parameters.")
671
    return 1
672

    
673
  kernel_path = _TransformPath(opts.kernel_path)
674
  initrd_path = _TransformPath(opts.initrd_path)
675
  if opts.hvm_boot_order == 'default':
676
    hvm_boot_order = constants.VALUE_DEFAULT
677
  else:
678
    hvm_boot_order = opts.hvm_boot_order
679

    
680
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
681
                                   vcpus=opts.vcpus, ip=opts.ip,
682
                                   bridge=opts.bridge, mac=opts.mac,
683
                                   kernel_path=opts.kernel_path,
684
                                   initrd_path=opts.initrd_path,
685
                                   hvm_boot_order=hvm_boot_order)
686
  result = SubmitOpCode(op)
687

    
688
  if result:
689
    logger.ToStdout("Modified instance %s" % args[0])
690
    for param, data in result:
691
      logger.ToStdout(" - %-5s -> %s" % (param, data))
692
    logger.ToStdout("Please don't forget that these parameters take effect"
693
                    " only at the next start of the instance.")
694
  return 0
695

    
696

    
697
# options used in more than one cmd
698
node_opt = make_option("-n", "--node", dest="node", help="Target node",
699
                       metavar="<node>")
700

    
701
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
702
                    metavar="<os>")
703

    
704
# multi-instance selection options
705
m_force_multi = make_option("--force-multiple", dest="force_multi",
706
                            help="Do not ask for confirmation when more than"
707
                            " one instance is affected",
708
                            action="store_true", default=False)
709

    
710
m_pri_node_opt = make_option("--primary", dest="multi_mode",
711
                             help="Filter by nodes (primary only)",
712
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
713

    
714
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
715
                             help="Filter by nodes (secondary only)",
716
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
717

    
718
m_node_opt = make_option("--node", dest="multi_mode",
719
                         help="Filter by nodes (primary and secondary)",
720
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
721

    
722
m_clust_opt = make_option("--all", dest="multi_mode",
723
                          help="Select all instances in the cluster",
724
                          const=_SHUTDOWN_CLUSTER, action="store_const")
725

    
726
m_inst_opt = make_option("--instance", dest="multi_mode",
727
                         help="Filter by instance name [default]",
728
                         const=_SHUTDOWN_INSTANCES, action="store_const")
729

    
730

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

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

    
911
  'reboot': (RebootInstance, ARGS_ANY,
912
              [DEBUG_OPT, m_force_multi,
913
               make_option("-e", "--extra", dest="extra_args",
914
                           help="Extra arguments for the instance's kernel",
915
                           default=None, type="string", metavar="<PARAMS>"),
916
               make_option("-t", "--type", dest="reboot_type",
917
                           help="Type of reboot: soft/hard/full",
918
                           default=constants.INSTANCE_REBOOT_SOFT,
919
                           type="string", metavar="<REBOOT>"),
920
               make_option("--ignore-secondaries", dest="ignore_secondaries",
921
                           default=False, action="store_true",
922
                           help="Ignore errors from secondaries"),
923
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
924
               m_clust_opt, m_inst_opt,
925
               ],
926
            "<instance>", "Reboots an instance"),
927
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
928
                     "<instance>",
929
                     "Activate an instance's disks"),
930
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
931
                       "<instance>",
932
                       "Deactivate an instance's disks"),
933
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
934
                "<node_name>", "List the tags of the given instance"),
935
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
936
               "<node_name> tag...", "Add tags to the given instance"),
937
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
938
                  "<node_name> tag...", "Remove tags from given instance"),
939
  }
940

    
941
aliases = {
942
  'activate_block_devs': 'activate-disks',
943
  'replace_disks': 'replace-disks',
944
  'start': 'startup',
945
  'stop': 'shutdown',
946
  }
947

    
948
if __name__ == '__main__':
949
  sys.exit(GenericMain(commands, aliases=aliases,
950
                       override={"tag_type": constants.TAG_INSTANCE}))