Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ c6e911bc

History | View | Annotate | Download (38.9 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
_VALUE_TRUE = "true"
44

    
45
_LIST_DEF_FIELDS = [
46
  "name", "os", "pnode", "status", "oper_ram",
47
  ]
48

    
49

    
50
def _ExpandMultiNames(mode, names):
51
  """Expand the given names using the passed mode.
52

    
53
  Args:
54
    - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
55
      _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
56
    - names, which is a list of names; for cluster, it must be empty,
57
      and for node and instance it must be a list of valid item
58
      names (short names are valid as usual, e.g. node1 instead of
59
      node1.example.com)
60

    
61
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
62
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
63
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
64
  instances having those nodes as either primary or secondary will be
65
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
66
  returned.
67

    
68
  """
69
  if mode == _SHUTDOWN_CLUSTER:
70
    if names:
71
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
72
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
73
    idata = SubmitOpCode(op)
74
    inames = [row[0] for row in idata]
75

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

    
97
  elif mode == _SHUTDOWN_INSTANCES:
98
    if not names:
99
      raise errors.OpPrereqError("No instance names passed")
100
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
101
    idata = SubmitOpCode(op)
102
    inames = [row[0] for row in idata]
103

    
104
  else:
105
    raise errors.OpPrereqError("Unknown mode '%s'" % mode)
106

    
107
  return inames
108

    
109

    
110
def _ConfirmOperation(inames, text):
111
  """Ask the user to confirm an operation on a list of instances.
112

    
113
  This function is used to request confirmation for doing an operation
114
  on a given list of instances.
115

    
116
  The inames argument is what the selection algorithm computed, and
117
  the text argument is the operation we should tell the user to
118
  confirm (e.g. 'shutdown' or 'startup').
119

    
120
  Returns: boolean depending on user's confirmation.
121

    
122
  """
123
  count = len(inames)
124
  msg = ("The %s will operate on %d instances.\n"
125
         "Do you want to continue?" % (text, count))
126
  affected = ("\nAffected instances:\n" +
127
              "\n".join(["  %s" % name for name in inames]))
128

    
129
  choices = [('y', True, 'Yes, execute the %s' % text),
130
             ('n', False, 'No, abort the %s' % text)]
131

    
132
  if count > 20:
133
    choices.insert(1, ('v', 'v', 'View the list of affected instances'))
134
    ask = msg
135
  else:
136
    ask = msg + affected
137

    
138
  choice = AskUser(ask, choices)
139
  if choice == 'v':
140
    choices.pop(1)
141
    choice = AskUser(msg + affected, choices)
142
  return choice
143

    
144

    
145
def _TransformPath(user_input):
146
  """Transform a user path into a canonical value.
147

    
148
  This function transforms the a path passed as textual information
149
  into the constants that the LU code expects.
150

    
151
  """
152
  if user_input:
153
    if user_input.lower() == "default":
154
      result_path = constants.VALUE_DEFAULT
155
    elif user_input.lower() == "none":
156
      result_path = constants.VALUE_NONE
157
    else:
158
      if not os.path.isabs(user_input):
159
        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
160
                                   user_input)
161
      result_path = user_input
162
  else:
163
    result_path = constants.VALUE_DEFAULT
164

    
165
  return result_path
166

    
167

    
168
def ListInstances(opts, args):
169
  """List instances and their properties.
170

    
171
  """
172
  if opts.output is None:
173
    selected_fields = _LIST_DEF_FIELDS
174
  elif opts.output.startswith("+"):
175
    selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
176
  else:
177
    selected_fields = opts.output.split(",")
178

    
179
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
180
  output = SubmitOpCode(op)
181

    
182
  if not opts.no_headers:
183
    headers = {
184
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
185
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
186
      "oper_state": "Running", "admin_ram": "Configured_memory",
187
      "oper_ram": "Memory", "disk_template": "Disk_template",
188
      "ip": "IP Address", "mac": "MAC Address",
189
      "bridge": "Bridge", "vcpus": "VCPUs",
190
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
191
      "status": "Status",
192
      }
193
  else:
194
    headers = None
195

    
196
  if opts.human_readable:
197
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
198
  else:
199
    unitfields = None
200

    
201
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
202

    
203
  # change raw values to nicer strings
204
  for row in output:
205
    for idx, field in enumerate(selected_fields):
206
      val = row[idx]
207
      if field == "snodes":
208
        val = ",".join(val) or "-"
209
      elif field == "admin_state":
210
        if val:
211
          val = "yes"
212
        else:
213
          val = "no"
214
      elif field == "oper_state":
215
        if val is None:
216
          val = "(node down)"
217
        elif val: # True
218
          val = "running"
219
        else:
220
          val = "stopped"
221
      elif field == "oper_ram":
222
        if val is None:
223
          val = "(node down)"
224
      elif field == "sda_size" or field == "sdb_size":
225
        if val is None:
226
          val = "N/A"
227
      row[idx] = str(val)
228

    
229
  data = GenerateTable(separator=opts.separator, headers=headers,
230
                       fields=selected_fields, unitfields=unitfields,
231
                       numfields=numfields, data=output)
232

    
233
  for line in data:
234
    logger.ToStdout(line)
235

    
236
  return 0
237

    
238

    
239
def AddInstance(opts, args):
240
  """Add an instance to the cluster.
241

    
242
  Args:
243
    opts - class with options as members
244
    args - list with a single element, the instance name
245
  Opts used:
246
    mem - amount of memory to allocate to instance (MiB)
247
    size - amount of disk space to allocate to instance (MiB)
248
    os - which OS to run on instance
249
    node - node to run new instance on
250

    
251
  """
252
  instance = args[0]
253

    
254
  (pnode, snode) = SplitNodeOption(opts.node)
255

    
256
  kernel_path = _TransformPath(opts.kernel_path)
257
  initrd_path = _TransformPath(opts.initrd_path)
258

    
259
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
260
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
261

    
262
  if ((opts.hvm_cdrom_image_path is not None) and
263
      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
264
    hvm_cdrom_image_path = None
265
  else:
266
    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
267

    
268
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
269
                                disk_size=opts.size, swap_size=opts.swap,
270
                                disk_template=opts.disk_template,
271
                                mode=constants.INSTANCE_CREATE,
272
                                os_type=opts.os, pnode=pnode,
273
                                snode=snode, vcpus=opts.vcpus,
274
                                ip=opts.ip, bridge=opts.bridge,
275
                                start=opts.start, ip_check=opts.ip_check,
276
                                wait_for_sync=opts.wait_for_sync,
277
                                mac=opts.mac,
278
                                kernel_path=kernel_path,
279
                                initrd_path=initrd_path,
280
                                iallocator=opts.iallocator,
281
                                hvm_boot_order=opts.hvm_boot_order,
282
                                file_storage_dir=opts.file_storage_dir,
283
                                file_driver=opts.file_driver,
284
                                hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
285
                                hvm_cdrom_image_path=hvm_cdrom_image_path,
286
                                vnc_bind_address=opts.vnc_bind_address)
287

    
288
  SubmitOpCode(op)
289
  return 0
290

    
291

    
292
def ReinstallInstance(opts, args):
293
  """Reinstall an instance.
294

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

    
299
  """
300
  instance_name = args[0]
301

    
302
  if not opts.force:
303
    usertext = ("This will reinstall the instance %s and remove"
304
                " all data. Continue?") % instance_name
305
    if not AskUser(usertext):
306
      return 1
307

    
308
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
309
                                   os_type=opts.os)
310
  SubmitOpCode(op)
311

    
312
  return 0
313

    
314

    
315
def RemoveInstance(opts, args):
316
  """Remove an instance.
317

    
318
  Args:
319
    opts - class with options as members
320
    args - list containing a single element, the instance name
321

    
322
  """
323
  instance_name = args[0]
324
  force = opts.force
325

    
326
  if not force:
327
    usertext = ("This will remove the volumes of the instance %s"
328
                " (including mirrors), thus removing all the data"
329
                " of the instance. Continue?") % instance_name
330
    if not AskUser(usertext):
331
      return 1
332

    
333
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
334
                                ignore_failures=opts.ignore_failures)
335
  SubmitOpCode(op)
336
  return 0
337

    
338

    
339
def RenameInstance(opts, args):
340
  """Rename an instance.
341

    
342
  Args:
343
    opts - class with options as members
344
    args - list containing two elements, the instance name and the new name
345

    
346
  """
347
  op = opcodes.OpRenameInstance(instance_name=args[0],
348
                                new_name=args[1],
349
                                ignore_ip=opts.ignore_ip)
350
  SubmitOpCode(op)
351

    
352
  return 0
353

    
354

    
355
def ActivateDisks(opts, args):
356
  """Activate an instance's disks.
357

    
358
  This serves two purposes:
359
    - it allows one (as long as the instance is not running) to mount
360
    the disks and modify them from the node
361
    - it repairs inactive secondary drbds
362

    
363
  """
364
  instance_name = args[0]
365
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
366
  disks_info = SubmitOpCode(op)
367
  for host, iname, nname in disks_info:
368
    print "%s:%s:%s" % (host, iname, nname)
369
  return 0
370

    
371

    
372
def DeactivateDisks(opts, args):
373
  """Command-line interface for _ShutdownInstanceBlockDevices.
374

    
375
  This function takes the instance name, looks for its primary node
376
  and the tries to shutdown its block devices on that node.
377

    
378
  """
379
  instance_name = args[0]
380
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
381
  SubmitOpCode(op)
382
  return 0
383

    
384

    
385
def GrowDisk(opts, args):
386
  """Command-line interface for _ShutdownInstanceBlockDevices.
387

    
388
  This function takes the instance name, looks for its primary node
389
  and the tries to shutdown its block devices on that node.
390

    
391
  """
392
  instance = args[0]
393
  disk = args[1]
394
  amount = utils.ParseUnit(args[2])
395
  op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount)
396
  SubmitOpCode(op)
397
  return 0
398

    
399

    
400
def StartupInstance(opts, args):
401
  """Startup an instance.
402

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

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

    
426

    
427
def RebootInstance(opts, args):
428
  """Reboot an instance
429

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

    
434
  """
435
  if opts.multi_mode is None:
436
    opts.multi_mode = _SHUTDOWN_INSTANCES
437
  inames = _ExpandMultiNames(opts.multi_mode, args)
438
  if not inames:
439
    raise errors.OpPrereqError("Selection filter does not match any instances")
440
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
441
  if not (opts.force_multi or not multi_on
442
          or _ConfirmOperation(inames, "reboot")):
443
    return 1
444
  for name in inames:
445
    op = opcodes.OpRebootInstance(instance_name=name,
446
                                  reboot_type=opts.reboot_type,
447
                                  ignore_secondaries=opts.ignore_secondaries)
448

    
449
    SubmitOpCode(op)
450
  return 0
451

    
452

    
453
def ShutdownInstance(opts, args):
454
  """Shutdown an instance.
455

    
456
  Args:
457
    opts - class with options as members
458
    args - list containing a single element, the instance name
459

    
460
  """
461
  if opts.multi_mode is None:
462
    opts.multi_mode = _SHUTDOWN_INSTANCES
463
  inames = _ExpandMultiNames(opts.multi_mode, args)
464
  if not inames:
465
    raise errors.OpPrereqError("Selection filter does not match any instances")
466
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
467
  if not (opts.force_multi or not multi_on
468
          or _ConfirmOperation(inames, "shutdown")):
469
    return 1
470
  for name in inames:
471
    op = opcodes.OpShutdownInstance(instance_name=name)
472
    if multi_on:
473
      logger.ToStdout("Shutting down %s" % name)
474
    SubmitOpCode(op)
475
  return 0
476

    
477

    
478
def ReplaceDisks(opts, args):
479
  """Replace the disks of an instance
480

    
481
  Args:
482
    opts - class with options as members
483
    args - list with a single element, the instance name
484

    
485
  """
486
  instance_name = args[0]
487
  new_2ndary = opts.new_secondary
488
  iallocator = opts.iallocator
489
  if opts.disks is None:
490
    disks = ["sda", "sdb"]
491
  else:
492
    disks = opts.disks.split(",")
493
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
494
    mode = constants.REPLACE_DISK_ALL
495
  elif opts.on_primary: # only on primary:
496
    mode = constants.REPLACE_DISK_PRI
497
    if new_2ndary is not None or iallocator is not None:
498
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
499
                                 " replacement")
500
  elif opts.on_secondary is not None or iallocator is not None:
501
    # only on secondary
502
    mode = constants.REPLACE_DISK_SEC
503

    
504
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
505
                              remote_node=new_2ndary, mode=mode,
506
                              iallocator=iallocator)
507
  SubmitOpCode(op)
508
  return 0
509

    
510

    
511
def FailoverInstance(opts, args):
512
  """Failover an instance.
513

    
514
  The failover is done by shutting it down on its present node and
515
  starting it on the secondary.
516

    
517
  Args:
518
    opts - class with options as members
519
    args - list with a single element, the instance name
520
  Opts used:
521
    force - whether to failover without asking questions.
522

    
523
  """
524
  instance_name = args[0]
525
  force = opts.force
526

    
527
  if not force:
528
    usertext = ("Failover will happen to image %s."
529
                " This requires a shutdown of the instance. Continue?" %
530
                (instance_name,))
531
    if not AskUser(usertext):
532
      return 1
533

    
534
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
535
                                  ignore_consistency=opts.ignore_consistency)
536
  SubmitOpCode(op)
537
  return 0
538

    
539

    
540
def ConnectToInstanceConsole(opts, args):
541
  """Connect to the console of an instance.
542

    
543
  Args:
544
    opts - class with options as members
545
    args - list with a single element, the instance name
546

    
547
  """
548
  instance_name = args[0]
549

    
550
  op = opcodes.OpConnectConsole(instance_name=instance_name)
551
  cmd = SubmitOpCode(op)
552

    
553
  if opts.show_command:
554
    print utils.ShellQuoteArgs(cmd)
555
  else:
556
    # drop lock and exec so other commands can run while we have console
557
    utils.Unlock("cmd")
558
    try:
559
      os.execvp(cmd[0], cmd)
560
    finally:
561
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
562
                       (cmd, " ".join(argv)))
563
      os._exit(1)
564

    
565

    
566
def _FormatBlockDevInfo(buf, dev, indent_level):
567
  """Show block device information.
568

    
569
  This is only used by ShowInstanceConfig(), but it's too big to be
570
  left for an inline definition.
571

    
572
  """
573
  def helper(buf, dtype, status):
574
    """Format one line for physical device status."""
575
    if not status:
576
      buf.write("not active\n")
577
    else:
578
      (path, major, minor, syncp, estt, degr, ldisk) = status
579
      if major is None:
580
        major_string = "N/A"
581
      else:
582
        major_string = str(major)
583

    
584
      if minor is None:
585
        minor_string = "N/A"
586
      else:
587
        minor_string = str(minor)
588

    
589
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
590
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
591
        if syncp is not None:
592
          sync_text = "*RECOVERING* %5.2f%%," % syncp
593
          if estt:
594
            sync_text += " ETA %ds" % estt
595
          else:
596
            sync_text += " ETA unknown"
597
        else:
598
          sync_text = "in sync"
599
        if degr:
600
          degr_text = "*DEGRADED*"
601
        else:
602
          degr_text = "ok"
603
        if ldisk:
604
          ldisk_text = " *MISSING DISK*"
605
        else:
606
          ldisk_text = ""
607
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
608
      elif dtype == constants.LD_LV:
609
        if ldisk:
610
          ldisk_text = " *FAILED* (failed drive?)"
611
        else:
612
          ldisk_text = ""
613
        buf.write(ldisk_text)
614
      buf.write("\n")
615

    
616
  if dev["iv_name"] is not None:
617
    data = "  - %s, " % dev["iv_name"]
618
  else:
619
    data = "  - "
620
  data += "type: %s" % dev["dev_type"]
621
  if dev["logical_id"] is not None:
622
    data += ", logical_id: %s" % (dev["logical_id"],)
623
  elif dev["physical_id"] is not None:
624
    data += ", physical_id: %s" % (dev["physical_id"],)
625
  buf.write("%*s%s\n" % (2*indent_level, "", data))
626
  buf.write("%*s    primary:   " % (2*indent_level, ""))
627
  helper(buf, dev["dev_type"], dev["pstatus"])
628

    
629
  if dev["sstatus"]:
630
    buf.write("%*s    secondary: " % (2*indent_level, ""))
631
    helper(buf, dev["dev_type"], dev["sstatus"])
632

    
633
  if dev["children"]:
634
    for child in dev["children"]:
635
      _FormatBlockDevInfo(buf, child, indent_level+1)
636

    
637

    
638
def ShowInstanceConfig(opts, args):
639
  """Compute instance run-time status.
640

    
641
  """
642
  retcode = 0
643
  op = opcodes.OpQueryInstanceData(instances=args)
644
  result = SubmitOpCode(op)
645
  hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
646
                    "hvm_boot_order")
647

    
648
  pvm_parameters = ("kernel_path", "initrd_path")
649

    
650
  if not result:
651
    logger.ToStdout("No instances.")
652
    return 1
653

    
654
  buf = StringIO()
655
  retcode = 0
656
  for instance_name in result:
657
    instance = result[instance_name]
658
    buf.write("Instance name: %s\n" % instance["name"])
659
    buf.write("State: configured to be %s, actual state is %s\n" %
660
              (instance["config_state"], instance["run_state"]))
661
    buf.write("  Nodes:\n")
662
    buf.write("    - primary: %s\n" % instance["pnode"])
663
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
664
    buf.write("  Operating system: %s\n" % instance["os"])
665
    if instance.has_key("network_port"):
666
      buf.write("  Allocated network port: %s\n" % instance["network_port"])
667
    if False not in map(instance.has_key, pvm_parameters):
668
      if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
669
        kpath = "(default: %s)" % constants.XEN_KERNEL
670
      else:
671
        kpath = instance["kernel_path"]
672
      buf.write("  Kernel path: %s\n" % kpath)
673
      if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
674
        initrd = "(default: %s)" % constants.XEN_INITRD
675
      elif instance["initrd_path"] == constants.VALUE_NONE:
676
        initrd = "(none)"
677
      else:
678
        initrd = instance["initrd_path"]
679
      buf.write("       initrd: %s\n" % initrd)
680
    if False not in map(instance.has_key, hvm_parameters):
681
      buf.write("  HVM:\n")
682
      buf.write("    - boot order: %s\n" % instance["hvm_boot_order"])
683
      buf.write("    - ACPI support: %s\n" % instance["hvm_acpi"])
684
      buf.write("    - PAE support: %s\n" % instance["hvm_pae"])
685
      buf.write("    - virtual CDROM: %s\n" % instance["hvm_cdrom_image_path"])
686
    if instance.has_key("vnc_bind_address"):
687
      buf.write("  VNC bind address: %s\n" % instance["vnc_bind_address"])
688
    buf.write("  Hardware:\n")
689
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
690
    buf.write("    - memory: %dMiB\n" % instance["memory"])
691
    buf.write("    - NICs: %s\n" %
692
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
693
                   (mac, ip, bridge)
694
                     for mac, ip, bridge in instance["nics"]]))
695
    buf.write("  Block devices:\n")
696

    
697
    for device in instance["disks"]:
698
      _FormatBlockDevInfo(buf, device, 1)
699

    
700
  logger.ToStdout(buf.getvalue().rstrip('\n'))
701
  return retcode
702

    
703

    
704
def SetInstanceParams(opts, args):
705
  """Modifies an instance.
706

    
707
  All parameters take effect only at the next restart of the instance.
708

    
709
  Args:
710
    opts - class with options as members
711
    args - list with a single element, the instance name
712
  Opts used:
713
    memory - the new memory size
714
    vcpus - the new number of cpus
715
    mac - the new MAC address of the instance
716

    
717
  """
718
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
719
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
720
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
721
          opts.vnc_bind_address):
722
    logger.ToStdout("Please give at least one of the parameters.")
723
    return 1
724

    
725
  kernel_path = _TransformPath(opts.kernel_path)
726
  initrd_path = _TransformPath(opts.initrd_path)
727
  if opts.hvm_boot_order == 'default':
728
    hvm_boot_order = constants.VALUE_DEFAULT
729
  else:
730
    hvm_boot_order = opts.hvm_boot_order
731

    
732
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
733
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
734

    
735
  if ((opts.hvm_cdrom_image_path is not None) and
736
      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
737
    hvm_cdrom_image_path = None
738
  else:
739
    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
740

    
741
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
742
                                   vcpus=opts.vcpus, ip=opts.ip,
743
                                   bridge=opts.bridge, mac=opts.mac,
744
                                   kernel_path=opts.kernel_path,
745
                                   initrd_path=opts.initrd_path,
746
                                   hvm_boot_order=hvm_boot_order,
747
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
748
                                   hvm_cdrom_image_path=hvm_cdrom_image_path,
749
                                   vnc_bind_address=opts.vnc_bind_address)
750

    
751
  result = SubmitOpCode(op)
752

    
753
  if result:
754
    logger.ToStdout("Modified instance %s" % args[0])
755
    for param, data in result:
756
      logger.ToStdout(" - %-5s -> %s" % (param, data))
757
    logger.ToStdout("Please don't forget that these parameters take effect"
758
                    " only at the next start of the instance.")
759
  return 0
760

    
761

    
762
# options used in more than one cmd
763
node_opt = make_option("-n", "--node", dest="node", help="Target node",
764
                       metavar="<node>")
765

    
766
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
767
                    metavar="<os>")
768

    
769
# multi-instance selection options
770
m_force_multi = make_option("--force-multiple", dest="force_multi",
771
                            help="Do not ask for confirmation when more than"
772
                            " one instance is affected",
773
                            action="store_true", default=False)
774

    
775
m_pri_node_opt = make_option("--primary", dest="multi_mode",
776
                             help="Filter by nodes (primary only)",
777
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
778

    
779
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
780
                             help="Filter by nodes (secondary only)",
781
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
782

    
783
m_node_opt = make_option("--node", dest="multi_mode",
784
                         help="Filter by nodes (primary and secondary)",
785
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
786

    
787
m_clust_opt = make_option("--all", dest="multi_mode",
788
                          help="Select all instances in the cluster",
789
                          const=_SHUTDOWN_CLUSTER, action="store_const")
790

    
791
m_inst_opt = make_option("--instance", dest="multi_mode",
792
                         help="Filter by instance name [default]",
793
                         const=_SHUTDOWN_INSTANCES, action="store_const")
794

    
795

    
796
# this is defined separately due to readability only
797
add_opts = [
798
  DEBUG_OPT,
799
  make_option("-n", "--node", dest="node",
800
              help="Target node and optional secondary node",
801
              metavar="<pnode>[:<snode>]"),
802
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
803
             " a suffix is used",
804
             default=20 * 1024, type="unit", metavar="<size>"),
805
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
806
             " suffix is used",
807
             default=4 * 1024, type="unit", metavar="<size>"),
808
  os_opt,
809
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
810
              default=128, type="unit", metavar="<mem>"),
811
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
812
              default=1, type="int", metavar="<PROC>"),
813
  make_option("-t", "--disk-template", dest="disk_template",
814
              help="Custom disk setup (diskless, file, plain or drbd)",
815
              default=None, metavar="TEMPL"),
816
  make_option("-i", "--ip", dest="ip",
817
              help="IP address ('none' [default], 'auto', or specify address)",
818
              default='none', type="string", metavar="<ADDRESS>"),
819
  make_option("--mac", dest="mac",
820
              help="MAC address ('auto' [default], or specify address)",
821
              default='auto', type="string", metavar="<MACADDRESS>"),
822
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
823
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
824
  make_option("-b", "--bridge", dest="bridge",
825
              help="Bridge to connect this instance to",
826
              default=None, metavar="<bridge>"),
827
  make_option("--no-start", dest="start", default=True,
828
              action="store_false", help="Don't start the instance after"
829
              " creation"),
830
  make_option("--no-ip-check", dest="ip_check", default=True,
831
              action="store_false", help="Don't check that the instance's IP"
832
              " is alive (only valid with --no-start)"),
833
  make_option("--kernel", dest="kernel_path",
834
              help="Path to the instances' kernel (or 'default')",
835
              default=None,
836
              type="string", metavar="<FILENAME>"),
837
  make_option("--initrd", dest="initrd_path",
838
              help="Path to the instances' initrd (or 'none', or 'default')",
839
              default=None,
840
              type="string", metavar="<FILENAME>"),
841
  make_option("--hvm-boot-order", dest="hvm_boot_order",
842
              help="Boot device order for HVM (one or more of [acdn])",
843
              default=None, type="string", metavar="<BOOTORDER>"),
844
  make_option("--file-storage-dir", dest="file_storage_dir",
845
              help="Relative path under default cluster-wide file storage dir"
846
              " to store file-based disks", default=None,
847
              metavar="<DIR>"),
848
  make_option("--file-driver", dest="file_driver", help="Driver to use"
849
              " for image files", default="loop", metavar="<DRIVER>"),
850
  make_option("--iallocator", metavar="<NAME>",
851
              help="Select nodes for the instance automatically using the"
852
              " <NAME> iallocator plugin", default=None, type="string"),
853
  make_option("--hvm-acpi", dest="hvm_acpi",
854
              help="ACPI support for HVM (true|false)",
855
              metavar="<BOOL>", choices=["true", "false"]),
856
  make_option("--hvm-pae", dest="hvm_pae",
857
              help="PAE support for HVM (true|false)",
858
              metavar="<BOOL>", choices=["true", "false"]),
859
  make_option("--hvm-cdrom-image-path", dest="hvm_cdrom_image_path",
860
              help="CDROM image path for HVM (absolute path or None)",
861
              default=None, type="string", metavar="<CDROMIMAGE>"),
862
  make_option("--vnc-bind-address", dest="vnc_bind_address",
863
              help="bind address for VNC (IP address)",
864
              default=None, type="string", metavar="<VNCADDRESS>"),
865
  ]
866

    
867
commands = {
868
  'add': (AddInstance, ARGS_ONE, add_opts,
869
          "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
870
          "Creates and adds a new instance to the cluster"),
871
  'console': (ConnectToInstanceConsole, ARGS_ONE,
872
              [DEBUG_OPT,
873
               make_option("--show-cmd", dest="show_command",
874
                           action="store_true", default=False,
875
                           help=("Show command instead of executing it"))],
876
              "[--show-cmd] <instance>",
877
              "Opens a console on the specified instance"),
878
  'failover': (FailoverInstance, ARGS_ONE,
879
               [DEBUG_OPT, FORCE_OPT,
880
                make_option("--ignore-consistency", dest="ignore_consistency",
881
                            action="store_true", default=False,
882
                            help="Ignore the consistency of the disks on"
883
                            " the secondary"),
884
                ],
885
               "[-f] <instance>",
886
               "Stops the instance and starts it on the backup node, using"
887
               " the remote mirror (only for instances of type drbd)"),
888
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
889
           "Show information on the specified instance"),
890
  'list': (ListInstances, ARGS_NONE,
891
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
892
           "Lists the instances and their status. The available fields are"
893
           " (see the man page for details): status, oper_state, oper_ram,"
894
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
895
           " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
896
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
897
           ),
898
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
899
                "[-f] <instance>", "Reinstall a stopped instance"),
900
  'remove': (RemoveInstance, ARGS_ONE,
901
             [DEBUG_OPT, FORCE_OPT,
902
              make_option("--ignore-failures", dest="ignore_failures",
903
                          action="store_true", default=False,
904
                          help=("Remove the instance from the cluster even"
905
                                " if there are failures during the removal"
906
                                " process (shutdown, disk removal, etc.)")),
907
              ],
908
             "[-f] <instance>", "Shuts down the instance and removes it"),
909
  'rename': (RenameInstance, ARGS_FIXED(2),
910
             [DEBUG_OPT,
911
              make_option("--no-ip-check", dest="ignore_ip",
912
                          help="Do not check that the IP of the new name"
913
                          " is alive",
914
                          default=False, action="store_true"),
915
              ],
916
             "<instance> <new_name>", "Rename the instance"),
917
  'replace-disks': (ReplaceDisks, ARGS_ONE,
918
                    [DEBUG_OPT,
919
                     make_option("-n", "--new-secondary", dest="new_secondary",
920
                                 help=("New secondary node (for secondary"
921
                                       " node change)"), metavar="NODE"),
922
                     make_option("-p", "--on-primary", dest="on_primary",
923
                                 default=False, action="store_true",
924
                                 help=("Replace the disk(s) on the primary"
925
                                       " node (only for the drbd template)")),
926
                     make_option("-s", "--on-secondary", dest="on_secondary",
927
                                 default=False, action="store_true",
928
                                 help=("Replace the disk(s) on the secondary"
929
                                       " node (only for the drbd template)")),
930
                     make_option("--disks", dest="disks", default=None,
931
                                 help=("Comma-separated list of disks"
932
                                       " to replace (e.g. sda) (optional,"
933
                                       " defaults to all disks")),
934
                     make_option("--iallocator", metavar="<NAME>",
935
                                 help="Select new secondary for the instance"
936
                                 " automatically using the"
937
                                 " <NAME> iallocator plugin (enables"
938
                                 " secondary node replacement)",
939
                                 default=None, type="string"),
940
                     ],
941
                    "[-s|-p|-n NODE] <instance>",
942
                    "Replaces all disks for the instance"),
943
  'modify': (SetInstanceParams, ARGS_ONE,
944
             [DEBUG_OPT, FORCE_OPT,
945
              cli_option("-m", "--memory", dest="mem",
946
                         help="Memory size",
947
                         default=None, type="unit", metavar="<mem>"),
948
              make_option("-p", "--cpu", dest="vcpus",
949
                          help="Number of virtual CPUs",
950
                          default=None, type="int", metavar="<PROC>"),
951
              make_option("-i", "--ip", dest="ip",
952
                          help="IP address ('none' or numeric IP)",
953
                          default=None, type="string", metavar="<ADDRESS>"),
954
              make_option("-b", "--bridge", dest="bridge",
955
                          help="Bridge to connect this instance to",
956
                          default=None, type="string", metavar="<bridge>"),
957
              make_option("--mac", dest="mac",
958
                          help="MAC address", default=None,
959
                          type="string", metavar="<MACADDRESS>"),
960
              make_option("--kernel", dest="kernel_path",
961
                          help="Path to the instances' kernel (or"
962
                          " 'default')", default=None,
963
                          type="string", metavar="<FILENAME>"),
964
              make_option("--initrd", dest="initrd_path",
965
                          help="Path to the instances' initrd (or 'none', or"
966
                          " 'default')", default=None,
967
                          type="string", metavar="<FILENAME>"),
968
              make_option("--hvm-boot-order", dest="hvm_boot_order",
969
                          help="boot device order for HVM"
970
                          "(either one or more of [acdn] or 'default')",
971
                          default=None, type="string", metavar="<BOOTORDER>"),
972
              make_option("--hvm-acpi", dest="hvm_acpi",
973
                          help="ACPI support for HVM (true|false)",
974
                          metavar="<BOOL>", choices=["true", "false"]),
975
              make_option("--hvm-pae", dest="hvm_pae",
976
                          help="PAE support for HVM (true|false)",
977
                          metavar="<BOOL>", choices=["true", "false"]),
978
              make_option("--hvm-cdrom-image-path",
979
                          dest="hvm_cdrom_image_path",
980
                          help="CDROM image path for HVM"
981
                          "(absolute path or None)",
982
                          default=None, type="string", metavar="<CDROMIMAGE>"),
983
              make_option("--vnc-bind-address", dest="vnc_bind_address",
984
                          help="bind address for VNC (IP address)",
985
                          default=None, type="string", metavar="<VNCADDRESS>"),
986
              ],
987
             "<instance>", "Alters the parameters of an instance"),
988
  'shutdown': (ShutdownInstance, ARGS_ANY,
989
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
990
                m_clust_opt, m_inst_opt, m_force_multi],
991
               "<instance>", "Stops an instance"),
992
  'startup': (StartupInstance, ARGS_ANY,
993
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
994
               make_option("-e", "--extra", dest="extra_args",
995
                           help="Extra arguments for the instance's kernel",
996
                           default=None, type="string", metavar="<PARAMS>"),
997
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
998
               m_clust_opt, m_inst_opt,
999
               ],
1000
            "<instance>", "Starts an instance"),
1001

    
1002
  'reboot': (RebootInstance, ARGS_ANY,
1003
              [DEBUG_OPT, m_force_multi,
1004
               make_option("-e", "--extra", dest="extra_args",
1005
                           help="Extra arguments for the instance's kernel",
1006
                           default=None, type="string", metavar="<PARAMS>"),
1007
               make_option("-t", "--type", dest="reboot_type",
1008
                           help="Type of reboot: soft/hard/full",
1009
                           default=constants.INSTANCE_REBOOT_SOFT,
1010
                           type="string", metavar="<REBOOT>"),
1011
               make_option("--ignore-secondaries", dest="ignore_secondaries",
1012
                           default=False, action="store_true",
1013
                           help="Ignore errors from secondaries"),
1014
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1015
               m_clust_opt, m_inst_opt,
1016
               ],
1017
            "<instance>", "Reboots an instance"),
1018
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1019
                     "<instance>",
1020
                     "Activate an instance's disks"),
1021
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1022
                       "<instance>",
1023
                       "Deactivate an instance's disks"),
1024
  'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT],
1025
                "<instance> <disk> <size>", "Grow an instance's disk"),
1026
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1027
                "<node_name>", "List the tags of the given instance"),
1028
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1029
               "<node_name> tag...", "Add tags to the given instance"),
1030
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1031
                  "<node_name> tag...", "Remove tags from given instance"),
1032
  }
1033

    
1034
aliases = {
1035
  'activate_block_devs': 'activate-disks',
1036
  'replace_disks': 'replace-disks',
1037
  'start': 'startup',
1038
  'stop': 'shutdown',
1039
  }
1040

    
1041
if __name__ == '__main__':
1042
  sys.exit(GenericMain(commands, aliases=aliases,
1043
                       override={"tag_type": constants.TAG_INSTANCE}))