Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 917f91d5

History | View | Annotate | Download (38.4 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 StartupInstance(opts, args):
386
  """Startup an instance.
387

    
388
  Args:
389
    opts - class with options as members
390
    args - list containing a single element, the instance name
391

    
392
  """
393
  if opts.multi_mode is None:
394
    opts.multi_mode = _SHUTDOWN_INSTANCES
395
  inames = _ExpandMultiNames(opts.multi_mode, args)
396
  if not inames:
397
    raise errors.OpPrereqError("Selection filter does not match any instances")
398
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
399
  if not (opts.force_multi or not multi_on
400
          or _ConfirmOperation(inames, "startup")):
401
    return 1
402
  for name in inames:
403
    op = opcodes.OpStartupInstance(instance_name=name,
404
                                   force=opts.force,
405
                                   extra_args=opts.extra_args)
406
    if multi_on:
407
      logger.ToStdout("Starting up %s" % name)
408
    SubmitOpCode(op)
409
  return 0
410

    
411

    
412
def RebootInstance(opts, args):
413
  """Reboot an instance
414

    
415
  Args:
416
    opts - class with options as members
417
    args - list containing a single element, the instance name
418

    
419
  """
420
  if opts.multi_mode is None:
421
    opts.multi_mode = _SHUTDOWN_INSTANCES
422
  inames = _ExpandMultiNames(opts.multi_mode, args)
423
  if not inames:
424
    raise errors.OpPrereqError("Selection filter does not match any instances")
425
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
426
  if not (opts.force_multi or not multi_on
427
          or _ConfirmOperation(inames, "reboot")):
428
    return 1
429
  for name in inames:
430
    op = opcodes.OpRebootInstance(instance_name=name,
431
                                  reboot_type=opts.reboot_type,
432
                                  ignore_secondaries=opts.ignore_secondaries)
433

    
434
    SubmitOpCode(op)
435
  return 0
436

    
437

    
438
def ShutdownInstance(opts, args):
439
  """Shutdown an instance.
440

    
441
  Args:
442
    opts - class with options as members
443
    args - list containing a single element, the instance name
444

    
445
  """
446
  if opts.multi_mode is None:
447
    opts.multi_mode = _SHUTDOWN_INSTANCES
448
  inames = _ExpandMultiNames(opts.multi_mode, args)
449
  if not inames:
450
    raise errors.OpPrereqError("Selection filter does not match any instances")
451
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
452
  if not (opts.force_multi or not multi_on
453
          or _ConfirmOperation(inames, "shutdown")):
454
    return 1
455
  for name in inames:
456
    op = opcodes.OpShutdownInstance(instance_name=name)
457
    if multi_on:
458
      logger.ToStdout("Shutting down %s" % name)
459
    SubmitOpCode(op)
460
  return 0
461

    
462

    
463
def ReplaceDisks(opts, args):
464
  """Replace the disks of an instance
465

    
466
  Args:
467
    opts - class with options as members
468
    args - list with a single element, the instance name
469

    
470
  """
471
  instance_name = args[0]
472
  new_2ndary = opts.new_secondary
473
  iallocator = opts.iallocator
474
  if opts.disks is None:
475
    disks = ["sda", "sdb"]
476
  else:
477
    disks = opts.disks.split(",")
478
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
479
    mode = constants.REPLACE_DISK_ALL
480
  elif opts.on_primary: # only on primary:
481
    mode = constants.REPLACE_DISK_PRI
482
    if new_2ndary is not None or iallocator is not None:
483
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
484
                                 " replacement")
485
  elif opts.on_secondary is not None or iallocator is not None:
486
    # only on secondary
487
    mode = constants.REPLACE_DISK_SEC
488

    
489
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
490
                              remote_node=new_2ndary, mode=mode,
491
                              iallocator=iallocator)
492
  SubmitOpCode(op)
493
  return 0
494

    
495

    
496
def FailoverInstance(opts, args):
497
  """Failover an instance.
498

    
499
  The failover is done by shutting it down on its present node and
500
  starting it on the secondary.
501

    
502
  Args:
503
    opts - class with options as members
504
    args - list with a single element, the instance name
505
  Opts used:
506
    force - whether to failover without asking questions.
507

    
508
  """
509
  instance_name = args[0]
510
  force = opts.force
511

    
512
  if not force:
513
    usertext = ("Failover will happen to image %s."
514
                " This requires a shutdown of the instance. Continue?" %
515
                (instance_name,))
516
    if not AskUser(usertext):
517
      return 1
518

    
519
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
520
                                  ignore_consistency=opts.ignore_consistency)
521
  SubmitOpCode(op)
522
  return 0
523

    
524

    
525
def ConnectToInstanceConsole(opts, args):
526
  """Connect to the console of an instance.
527

    
528
  Args:
529
    opts - class with options as members
530
    args - list with a single element, the instance name
531

    
532
  """
533
  instance_name = args[0]
534

    
535
  op = opcodes.OpConnectConsole(instance_name=instance_name)
536
  cmd = SubmitOpCode(op)
537

    
538
  if opts.show_command:
539
    print utils.ShellQuoteArgs(cmd)
540
  else:
541
    # drop lock and exec so other commands can run while we have console
542
    utils.Unlock("cmd")
543
    try:
544
      os.execvp(cmd[0], cmd)
545
    finally:
546
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
547
                       (cmd, " ".join(argv)))
548
      os._exit(1)
549

    
550

    
551
def _FormatBlockDevInfo(buf, dev, indent_level):
552
  """Show block device information.
553

    
554
  This is only used by ShowInstanceConfig(), but it's too big to be
555
  left for an inline definition.
556

    
557
  """
558
  def helper(buf, dtype, status):
559
    """Format one line for physical device status."""
560
    if not status:
561
      buf.write("not active\n")
562
    else:
563
      (path, major, minor, syncp, estt, degr, ldisk) = status
564
      if major is None:
565
        major_string = "N/A"
566
      else:
567
        major_string = str(major)
568

    
569
      if minor is None:
570
        minor_string = "N/A"
571
      else:
572
        minor_string = str(minor)
573

    
574
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
575
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
576
        if syncp is not None:
577
          sync_text = "*RECOVERING* %5.2f%%," % syncp
578
          if estt:
579
            sync_text += " ETA %ds" % estt
580
          else:
581
            sync_text += " ETA unknown"
582
        else:
583
          sync_text = "in sync"
584
        if degr:
585
          degr_text = "*DEGRADED*"
586
        else:
587
          degr_text = "ok"
588
        if ldisk:
589
          ldisk_text = " *MISSING DISK*"
590
        else:
591
          ldisk_text = ""
592
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
593
      elif dtype == constants.LD_LV:
594
        if ldisk:
595
          ldisk_text = " *FAILED* (failed drive?)"
596
        else:
597
          ldisk_text = ""
598
        buf.write(ldisk_text)
599
      buf.write("\n")
600

    
601
  if dev["iv_name"] is not None:
602
    data = "  - %s, " % dev["iv_name"]
603
  else:
604
    data = "  - "
605
  data += "type: %s" % dev["dev_type"]
606
  if dev["logical_id"] is not None:
607
    data += ", logical_id: %s" % (dev["logical_id"],)
608
  elif dev["physical_id"] is not None:
609
    data += ", physical_id: %s" % (dev["physical_id"],)
610
  buf.write("%*s%s\n" % (2*indent_level, "", data))
611
  buf.write("%*s    primary:   " % (2*indent_level, ""))
612
  helper(buf, dev["dev_type"], dev["pstatus"])
613

    
614
  if dev["sstatus"]:
615
    buf.write("%*s    secondary: " % (2*indent_level, ""))
616
    helper(buf, dev["dev_type"], dev["sstatus"])
617

    
618
  if dev["children"]:
619
    for child in dev["children"]:
620
      _FormatBlockDevInfo(buf, child, indent_level+1)
621

    
622

    
623
def ShowInstanceConfig(opts, args):
624
  """Compute instance run-time status.
625

    
626
  """
627
  retcode = 0
628
  op = opcodes.OpQueryInstanceData(instances=args)
629
  result = SubmitOpCode(op)
630
  hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
631
                    "hvm_boot_order")
632

    
633
  pvm_parameters = ("kernel_path", "initrd_path")
634

    
635
  if not result:
636
    logger.ToStdout("No instances.")
637
    return 1
638

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

    
682
    for device in instance["disks"]:
683
      _FormatBlockDevInfo(buf, device, 1)
684

    
685
  logger.ToStdout(buf.getvalue().rstrip('\n'))
686
  return retcode
687

    
688

    
689
def SetInstanceParams(opts, args):
690
  """Modifies an instance.
691

    
692
  All parameters take effect only at the next restart of the instance.
693

    
694
  Args:
695
    opts - class with options as members
696
    args - list with a single element, the instance name
697
  Opts used:
698
    memory - the new memory size
699
    vcpus - the new number of cpus
700
    mac - the new MAC address of the instance
701

    
702
  """
703
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
704
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
705
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
706
          opts.vnc_bind_address):
707
    logger.ToStdout("Please give at least one of the parameters.")
708
    return 1
709

    
710
  kernel_path = _TransformPath(opts.kernel_path)
711
  initrd_path = _TransformPath(opts.initrd_path)
712
  if opts.hvm_boot_order == 'default':
713
    hvm_boot_order = constants.VALUE_DEFAULT
714
  else:
715
    hvm_boot_order = opts.hvm_boot_order
716

    
717
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
718
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
719

    
720
  if ((opts.hvm_cdrom_image_path is not None) and
721
      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
722
    hvm_cdrom_image_path = None
723
  else:
724
    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
725

    
726
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
727
                                   vcpus=opts.vcpus, ip=opts.ip,
728
                                   bridge=opts.bridge, mac=opts.mac,
729
                                   kernel_path=opts.kernel_path,
730
                                   initrd_path=opts.initrd_path,
731
                                   hvm_boot_order=hvm_boot_order,
732
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
733
                                   hvm_cdrom_image_path=hvm_cdrom_image_path,
734
                                   vnc_bind_address=opts.vnc_bind_address)
735

    
736
  result = SubmitOpCode(op)
737

    
738
  if result:
739
    logger.ToStdout("Modified instance %s" % args[0])
740
    for param, data in result:
741
      logger.ToStdout(" - %-5s -> %s" % (param, data))
742
    logger.ToStdout("Please don't forget that these parameters take effect"
743
                    " only at the next start of the instance.")
744
  return 0
745

    
746

    
747
# options used in more than one cmd
748
node_opt = make_option("-n", "--node", dest="node", help="Target node",
749
                       metavar="<node>")
750

    
751
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
752
                    metavar="<os>")
753

    
754
# multi-instance selection options
755
m_force_multi = make_option("--force-multiple", dest="force_multi",
756
                            help="Do not ask for confirmation when more than"
757
                            " one instance is affected",
758
                            action="store_true", default=False)
759

    
760
m_pri_node_opt = make_option("--primary", dest="multi_mode",
761
                             help="Filter by nodes (primary only)",
762
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
763

    
764
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
765
                             help="Filter by nodes (secondary only)",
766
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
767

    
768
m_node_opt = make_option("--node", dest="multi_mode",
769
                         help="Filter by nodes (primary and secondary)",
770
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
771

    
772
m_clust_opt = make_option("--all", dest="multi_mode",
773
                          help="Select all instances in the cluster",
774
                          const=_SHUTDOWN_CLUSTER, action="store_const")
775

    
776
m_inst_opt = make_option("--instance", dest="multi_mode",
777
                         help="Filter by instance name [default]",
778
                         const=_SHUTDOWN_INSTANCES, action="store_const")
779

    
780

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

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

    
987
  'reboot': (RebootInstance, ARGS_ANY,
988
              [DEBUG_OPT, m_force_multi,
989
               make_option("-e", "--extra", dest="extra_args",
990
                           help="Extra arguments for the instance's kernel",
991
                           default=None, type="string", metavar="<PARAMS>"),
992
               make_option("-t", "--type", dest="reboot_type",
993
                           help="Type of reboot: soft/hard/full",
994
                           default=constants.INSTANCE_REBOOT_SOFT,
995
                           type="string", metavar="<REBOOT>"),
996
               make_option("--ignore-secondaries", dest="ignore_secondaries",
997
                           default=False, action="store_true",
998
                           help="Ignore errors from secondaries"),
999
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1000
               m_clust_opt, m_inst_opt,
1001
               ],
1002
            "<instance>", "Reboots an instance"),
1003
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1004
                     "<instance>",
1005
                     "Activate an instance's disks"),
1006
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1007
                       "<instance>",
1008
                       "Deactivate an instance's disks"),
1009
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1010
                "<node_name>", "List the tags of the given instance"),
1011
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1012
               "<node_name> tag...", "Add tags to the given instance"),
1013
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1014
                  "<node_name> tag...", "Remove tags from given instance"),
1015
  }
1016

    
1017
aliases = {
1018
  'activate_block_devs': 'activate-disks',
1019
  'replace_disks': 'replace-disks',
1020
  'start': 'startup',
1021
  'stop': 'shutdown',
1022
  }
1023

    
1024
if __name__ == '__main__':
1025
  sys.exit(GenericMain(commands, aliases=aliases,
1026
                       override={"tag_type": constants.TAG_INSTANCE}))