Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 3fc175f0

History | View | Annotate | Download (38.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
_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
  output = GetClient().QueryInstances([], selected_fields)
180

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

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

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

    
202
  list_type_fields = ("tags",)
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
      elif field in list_type_fields:
228
        val = ",".join(val)
229
      row[idx] = str(val)
230

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

    
235
  for line in data:
236
    logger.ToStdout(line)
237

    
238
  return 0
239

    
240

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

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

    
253
  """
254
  instance = args[0]
255

    
256
  (pnode, snode) = SplitNodeOption(opts.node)
257

    
258
  kernel_path = _TransformPath(opts.kernel_path)
259
  initrd_path = _TransformPath(opts.initrd_path)
260

    
261
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
262
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
263

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

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

    
290
  SubmitOpCode(op)
291
  return 0
292

    
293

    
294
def ReinstallInstance(opts, args):
295
  """Reinstall an instance.
296

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

    
301
  """
302
  instance_name = args[0]
303

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

    
310
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
311
                                   os_type=opts.os)
312
  SubmitOpCode(op)
313

    
314
  return 0
315

    
316

    
317
def RemoveInstance(opts, args):
318
  """Remove an instance.
319

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

    
324
  """
325
  instance_name = args[0]
326
  force = opts.force
327

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

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

    
340

    
341
def RenameInstance(opts, args):
342
  """Rename an instance.
343

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

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

    
354
  return 0
355

    
356

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

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

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

    
373

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

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

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

    
386

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

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

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

    
401

    
402
def StartupInstance(opts, args):
403
  """Startup an instance.
404

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

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

    
428

    
429
def RebootInstance(opts, args):
430
  """Reboot an instance
431

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

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

    
451
    SubmitOpCode(op)
452
  return 0
453

    
454

    
455
def ShutdownInstance(opts, args):
456
  """Shutdown an instance.
457

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

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

    
479

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

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

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

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

    
512

    
513
def FailoverInstance(opts, args):
514
  """Failover an instance.
515

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

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

    
525
  """
526
  instance_name = args[0]
527
  force = opts.force
528

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

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

    
541

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

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

    
549
  """
550
  instance_name = args[0]
551

    
552
  op = opcodes.OpConnectConsole(instance_name=instance_name)
553
  cmd = SubmitOpCode(op)
554

    
555
  if opts.show_command:
556
    print utils.ShellQuoteArgs(cmd)
557
  else:
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_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
  if opts.hvm_acpi is None:
733
    hvm_acpi = opts.hvm_acpi
734
  else:
735
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
736

    
737
  if opts.hvm_pae is None:
738
    hvm_pae = opts.hvm_pae
739
  else:
740
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
741

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

    
753
  result = SubmitOpCode(op)
754

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

    
763

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

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

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

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

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

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

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

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

    
797

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

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

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

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

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