Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 34b6ab97

History | View | Annotate | Download (41.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
  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
                                hvm_nic_type=opts.hvm_nic_type,
290
                                hvm_disk_type=opts.hvm_disk_type)
291

    
292
  SubmitOpCode(op)
293
  return 0
294

    
295

    
296
def ReinstallInstance(opts, args):
297
  """Reinstall an instance.
298

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

    
303
  """
304
  instance_name = args[0]
305

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

    
312
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
313
                                   os_type=opts.os)
314
  SubmitOpCode(op)
315

    
316
  return 0
317

    
318

    
319
def RemoveInstance(opts, args):
320
  """Remove an instance.
321

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

    
326
  """
327
  instance_name = args[0]
328
  force = opts.force
329

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

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

    
342

    
343
def RenameInstance(opts, args):
344
  """Rename an instance.
345

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

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

    
356
  return 0
357

    
358

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

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

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

    
375

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

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

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

    
388

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

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

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

    
403

    
404
def StartupInstance(opts, args):
405
  """Startup an instance.
406

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

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

    
430

    
431
def RebootInstance(opts, args):
432
  """Reboot an instance
433

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

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

    
453
    SubmitOpCode(op)
454
  return 0
455

    
456

    
457
def ShutdownInstance(opts, args):
458
  """Shutdown an instance.
459

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

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

    
481

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

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

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

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

    
514

    
515
def FailoverInstance(opts, args):
516
  """Failover an instance.
517

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

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

    
527
  """
528
  instance_name = args[0]
529
  force = opts.force
530

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

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

    
543

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

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

    
551
  """
552
  instance_name = args[0]
553

    
554
  op = opcodes.OpConnectConsole(instance_name=instance_name)
555
  cmd = SubmitOpCode(op)
556

    
557
  if opts.show_command:
558
    print utils.ShellQuoteArgs(cmd)
559
  else:
560
    try:
561
      os.execvp(cmd[0], cmd)
562
    finally:
563
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
564
                       (cmd, " ".join(argv)))
565
      os._exit(1)
566

    
567

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

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

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

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

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

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

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

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

    
639

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

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

    
650
  pvm_parameters = ("kernel_path", "initrd_path")
651

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

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

    
702
    for device in instance["disks"]:
703
      _FormatBlockDevInfo(buf, device, 1)
704

    
705
  logger.ToStdout(buf.getvalue().rstrip('\n'))
706
  return retcode
707

    
708

    
709
def SetInstanceParams(opts, args):
710
  """Modifies an instance.
711

    
712
  All parameters take effect only at the next restart of the instance.
713

    
714
  Args:
715
    opts - class with options as members
716
    args - list with a single element, the instance name
717
  Opts used:
718
    memory - the new memory size
719
    vcpus - the new number of cpus
720
    mac - the new MAC address of the instance
721

    
722
  """
723
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
724
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
725
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
726
          opts.vnc_bind_address or opts.hvm_nic_type or opts.hvm_disk_type):
727
    logger.ToStdout("Please give at least one of the parameters.")
728
    return 1
729

    
730
  kernel_path = _TransformPath(opts.kernel_path)
731
  initrd_path = _TransformPath(opts.initrd_path)
732
  if opts.hvm_boot_order == 'default':
733
    hvm_boot_order = constants.VALUE_DEFAULT
734
  else:
735
    hvm_boot_order = opts.hvm_boot_order
736

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

    
742
  if opts.hvm_pae is None:
743
    hvm_pae = opts.hvm_pae
744
  else:
745
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
746

    
747
  if opts.hvm_nic_type == constants.VALUE_NONE:
748
    hvm_nic_type = None
749
  else:
750
    hvm_nic_type = opts.hvm_nic_type
751

    
752
  if opts.hvm_disk_type == constants.VALUE_NONE:
753
    hvm_disk_type = None
754
  else:
755
    hvm_disk_type = opts.hvm_disk_type
756

    
757
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
758
                                   vcpus=opts.vcpus, ip=opts.ip,
759
                                   bridge=opts.bridge, mac=opts.mac,
760
                                   kernel_path=opts.kernel_path,
761
                                   initrd_path=opts.initrd_path,
762
                                   hvm_boot_order=hvm_boot_order,
763
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
764
                                   hvm_cdrom_image_path=
765
                                   opts.hvm_cdrom_image_path,
766
                                   vnc_bind_address=opts.vnc_bind_address,
767
                                   hvm_nic_type=hvm_nic_type,
768
                                   hvm_disk_type=hvm_disk_type,
769
                                   force=opts.force)
770

    
771
  result = SubmitOpCode(op)
772

    
773
  if result:
774
    logger.ToStdout("Modified instance %s" % args[0])
775
    for param, data in result:
776
      logger.ToStdout(" - %-5s -> %s" % (param, data))
777
    logger.ToStdout("Please don't forget that these parameters take effect"
778
                    " only at the next start of the instance.")
779
  return 0
780

    
781

    
782
# options used in more than one cmd
783
node_opt = make_option("-n", "--node", dest="node", help="Target node",
784
                       metavar="<node>")
785

    
786
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
787
                    metavar="<os>")
788

    
789
# multi-instance selection options
790
m_force_multi = make_option("--force-multiple", dest="force_multi",
791
                            help="Do not ask for confirmation when more than"
792
                            " one instance is affected",
793
                            action="store_true", default=False)
794

    
795
m_pri_node_opt = make_option("--primary", dest="multi_mode",
796
                             help="Filter by nodes (primary only)",
797
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
798

    
799
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
800
                             help="Filter by nodes (secondary only)",
801
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
802

    
803
m_node_opt = make_option("--node", dest="multi_mode",
804
                         help="Filter by nodes (primary and secondary)",
805
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
806

    
807
m_clust_opt = make_option("--all", dest="multi_mode",
808
                          help="Select all instances in the cluster",
809
                          const=_SHUTDOWN_CLUSTER, action="store_const")
810

    
811
m_inst_opt = make_option("--instance", dest="multi_mode",
812
                         help="Filter by instance name [default]",
813
                         const=_SHUTDOWN_INSTANCES, action="store_const")
814

    
815

    
816
# this is defined separately due to readability only
817
add_opts = [
818
  DEBUG_OPT,
819
  make_option("-n", "--node", dest="node",
820
              help="Target node and optional secondary node",
821
              metavar="<pnode>[:<snode>]"),
822
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
823
             " a suffix is used",
824
             default=20 * 1024, type="unit", metavar="<size>"),
825
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
826
             " suffix is used",
827
             default=4 * 1024, type="unit", metavar="<size>"),
828
  os_opt,
829
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
830
              default=128, type="unit", metavar="<mem>"),
831
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
832
              default=1, type="int", metavar="<PROC>"),
833
  make_option("-t", "--disk-template", dest="disk_template",
834
              help="Custom disk setup (diskless, file, plain or drbd)",
835
              default=None, metavar="TEMPL"),
836
  make_option("-i", "--ip", dest="ip",
837
              help="IP address ('none' [default], 'auto', or specify address)",
838
              default='none', type="string", metavar="<ADDRESS>"),
839
  make_option("--mac", dest="mac",
840
              help="MAC address ('auto' [default], or specify address)",
841
              default='auto', type="string", metavar="<MACADDRESS>"),
842
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
843
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
844
  make_option("-b", "--bridge", dest="bridge",
845
              help="Bridge to connect this instance to",
846
              default=None, metavar="<bridge>"),
847
  make_option("--no-start", dest="start", default=True,
848
              action="store_false", help="Don't start the instance after"
849
              " creation"),
850
  make_option("--no-ip-check", dest="ip_check", default=True,
851
              action="store_false", help="Don't check that the instance's IP"
852
              " is alive (only valid with --no-start)"),
853
  make_option("--kernel", dest="kernel_path",
854
              help="Path to the instances' kernel (or 'default')",
855
              default=None,
856
              type="string", metavar="<FILENAME>"),
857
  make_option("--initrd", dest="initrd_path",
858
              help="Path to the instances' initrd (or 'none', or 'default')",
859
              default=None,
860
              type="string", metavar="<FILENAME>"),
861
  make_option("--hvm-boot-order", dest="hvm_boot_order",
862
              help="Boot device order for HVM (one or more of [acdn])",
863
              default=None, type="string", metavar="<BOOTORDER>"),
864
  make_option("--file-storage-dir", dest="file_storage_dir",
865
              help="Relative path under default cluster-wide file storage dir"
866
              " to store file-based disks", default=None,
867
              metavar="<DIR>"),
868
  make_option("--file-driver", dest="file_driver", help="Driver to use"
869
              " for image files", default="loop", metavar="<DRIVER>"),
870
  make_option("--iallocator", metavar="<NAME>",
871
              help="Select nodes for the instance automatically using the"
872
              " <NAME> iallocator plugin", default=None, type="string"),
873
  make_option("--hvm-acpi", dest="hvm_acpi",
874
              help="ACPI support for HVM (true|false)",
875
              metavar="<BOOL>", choices=["true", "false"]),
876
  make_option("--hvm-nic-type", dest="hvm_nic_type",
877
              help="Type of virtual NIC for HVM "
878
              "(rtl8139,ne2k_pci,ne2k_isa,paravirtual)",
879
              metavar="NICTYPE", choices=[constants.HT_HVM_NIC_RTL8139,
880
                                          constants.HT_HVM_NIC_NE2K_PCI,
881
                                          constants.HT_HVM_NIC_NE2K_ISA,
882
                                          constants.HT_HVM_DEV_PARAVIRTUAL],
883
              default=constants.HT_HVM_NIC_RTL8139),
884
  make_option("--hvm-disk-type", dest="hvm_disk_type",
885
              help="Type of virtual disks for HVM (ioemu,paravirtual)",
886
              metavar="DISKTYPE", choices=[constants.HT_HVM_DEV_IOEMU,
887
                                           constants.HT_HVM_DEV_PARAVIRTUAL],
888
              default=constants.HT_HVM_DEV_IOEMU,),
889
  make_option("--hvm-pae", dest="hvm_pae",
890
              help="PAE support for HVM (true|false)",
891
              metavar="<BOOL>", choices=["true", "false"]),
892
  make_option("--hvm-cdrom-image-path", dest="hvm_cdrom_image_path",
893
              help="CDROM image path for HVM (absolute path or None)",
894
              default=None, type="string", metavar="<CDROMIMAGE>"),
895
  make_option("--vnc-bind-address", dest="vnc_bind_address",
896
              help="bind address for VNC (IP address)",
897
              default=None, type="string", metavar="<VNCADDRESS>"),
898
  ]
899

    
900
commands = {
901
  'add': (AddInstance, ARGS_ONE, add_opts,
902
          "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
903
          "Creates and adds a new instance to the cluster"),
904
  'console': (ConnectToInstanceConsole, ARGS_ONE,
905
              [DEBUG_OPT,
906
               make_option("--show-cmd", dest="show_command",
907
                           action="store_true", default=False,
908
                           help=("Show command instead of executing it"))],
909
              "[--show-cmd] <instance>",
910
              "Opens a console on the specified instance"),
911
  'failover': (FailoverInstance, ARGS_ONE,
912
               [DEBUG_OPT, FORCE_OPT,
913
                make_option("--ignore-consistency", dest="ignore_consistency",
914
                            action="store_true", default=False,
915
                            help="Ignore the consistency of the disks on"
916
                            " the secondary"),
917
                ],
918
               "[-f] <instance>",
919
               "Stops the instance and starts it on the backup node, using"
920
               " the remote mirror (only for instances of type drbd)"),
921
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
922
           "Show information on the specified instance"),
923
  'list': (ListInstances, ARGS_NONE,
924
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
925
           "Lists the instances and their status. The available fields are"
926
           " (see the man page for details): status, oper_state, oper_ram,"
927
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
928
           " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
929
           " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
930
           ),
931
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
932
                "[-f] <instance>", "Reinstall a stopped instance"),
933
  'remove': (RemoveInstance, ARGS_ONE,
934
             [DEBUG_OPT, FORCE_OPT,
935
              make_option("--ignore-failures", dest="ignore_failures",
936
                          action="store_true", default=False,
937
                          help=("Remove the instance from the cluster even"
938
                                " if there are failures during the removal"
939
                                " process (shutdown, disk removal, etc.)")),
940
              ],
941
             "[-f] <instance>", "Shuts down the instance and removes it"),
942
  'rename': (RenameInstance, ARGS_FIXED(2),
943
             [DEBUG_OPT,
944
              make_option("--no-ip-check", dest="ignore_ip",
945
                          help="Do not check that the IP of the new name"
946
                          " is alive",
947
                          default=False, action="store_true"),
948
              ],
949
             "<instance> <new_name>", "Rename the instance"),
950
  'replace-disks': (ReplaceDisks, ARGS_ONE,
951
                    [DEBUG_OPT,
952
                     make_option("-n", "--new-secondary", dest="new_secondary",
953
                                 help=("New secondary node (for secondary"
954
                                       " node change)"), metavar="NODE"),
955
                     make_option("-p", "--on-primary", dest="on_primary",
956
                                 default=False, action="store_true",
957
                                 help=("Replace the disk(s) on the primary"
958
                                       " node (only for the drbd template)")),
959
                     make_option("-s", "--on-secondary", dest="on_secondary",
960
                                 default=False, action="store_true",
961
                                 help=("Replace the disk(s) on the secondary"
962
                                       " node (only for the drbd template)")),
963
                     make_option("--disks", dest="disks", default=None,
964
                                 help=("Comma-separated list of disks"
965
                                       " to replace (e.g. sda) (optional,"
966
                                       " defaults to all disks")),
967
                     make_option("--iallocator", metavar="<NAME>",
968
                                 help="Select new secondary for the instance"
969
                                 " automatically using the"
970
                                 " <NAME> iallocator plugin (enables"
971
                                 " secondary node replacement)",
972
                                 default=None, type="string"),
973
                     ],
974
                    "[-s|-p|-n NODE] <instance>",
975
                    "Replaces all disks for the instance"),
976
  'modify': (SetInstanceParams, ARGS_ONE,
977
             [DEBUG_OPT, FORCE_OPT,
978
              cli_option("-m", "--memory", dest="mem",
979
                         help="Memory size",
980
                         default=None, type="unit", metavar="<mem>"),
981
              make_option("-p", "--cpu", dest="vcpus",
982
                          help="Number of virtual CPUs",
983
                          default=None, type="int", metavar="<PROC>"),
984
              make_option("-i", "--ip", dest="ip",
985
                          help="IP address ('none' or numeric IP)",
986
                          default=None, type="string", metavar="<ADDRESS>"),
987
              make_option("-b", "--bridge", dest="bridge",
988
                          help="Bridge to connect this instance to",
989
                          default=None, type="string", metavar="<bridge>"),
990
              make_option("--mac", dest="mac",
991
                          help="MAC address", default=None,
992
                          type="string", metavar="<MACADDRESS>"),
993
              make_option("--kernel", dest="kernel_path",
994
                          help="Path to the instances' kernel (or"
995
                          " 'default')", default=None,
996
                          type="string", metavar="<FILENAME>"),
997
              make_option("--initrd", dest="initrd_path",
998
                          help="Path to the instances' initrd (or 'none', or"
999
                          " 'default')", default=None,
1000
                          type="string", metavar="<FILENAME>"),
1001
              make_option("--hvm-boot-order", dest="hvm_boot_order",
1002
                          help="boot device order for HVM"
1003
                          "(either one or more of [acdn] or 'default')",
1004
                          default=None, type="string", metavar="<BOOTORDER>"),
1005
              make_option("--hvm-acpi", dest="hvm_acpi",
1006
                          help="ACPI support for HVM (true|false)",
1007
                          metavar="<BOOL>", choices=["true", "false"]),
1008
              make_option("--hvm-pae", dest="hvm_pae",
1009
                          help="PAE support for HVM (true|false)",
1010
                          metavar="<BOOL>", choices=["true", "false"]),
1011
              make_option("--hvm-cdrom-image-path",
1012
                          dest="hvm_cdrom_image_path",
1013
                          help="CDROM image path for HVM"
1014
                          "(absolute path or None)",
1015
                          default=None, type="string", metavar="<CDROMIMAGE>"),
1016
              make_option("--hvm-nic-type", dest="hvm_nic_type",
1017
                          help="Type of virtual NIC for HVM "
1018
                          "(rtl8139,ne2k_pci,ne2k_isa,paravirtual)",
1019
                          metavar="NICTYPE",
1020
                          choices=[constants.HT_HVM_NIC_RTL8139,
1021
                                   constants.HT_HVM_NIC_NE2K_PCI,
1022
                                   constants.HT_HVM_NIC_NE2K_ISA,
1023
                                   constants.HT_HVM_DEV_PARAVIRTUAL],
1024
                          default=None),
1025
              make_option("--hvm-disk-type", dest="hvm_disk_type",
1026
                          help="Type of virtual disks for HVM "
1027
                          "(ioemu,paravirtual)",
1028
                          metavar="DISKTYPE",
1029
                          choices=[constants.HT_HVM_DEV_IOEMU,
1030
                                   constants.HT_HVM_DEV_PARAVIRTUAL],
1031
                          default=None),
1032
              make_option("--vnc-bind-address", dest="vnc_bind_address",
1033
                          help="bind address for VNC (IP address)",
1034
                          default=None, type="string", metavar="<VNCADDRESS>"),
1035
              ],
1036
             "<instance>", "Alters the parameters of an instance"),
1037
  'shutdown': (ShutdownInstance, ARGS_ANY,
1038
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1039
                m_clust_opt, m_inst_opt, m_force_multi],
1040
               "<instance>", "Stops an instance"),
1041
  'startup': (StartupInstance, ARGS_ANY,
1042
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
1043
               make_option("-e", "--extra", dest="extra_args",
1044
                           help="Extra arguments for the instance's kernel",
1045
                           default=None, type="string", metavar="<PARAMS>"),
1046
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1047
               m_clust_opt, m_inst_opt,
1048
               ],
1049
            "<instance>", "Starts an instance"),
1050

    
1051
  'reboot': (RebootInstance, ARGS_ANY,
1052
              [DEBUG_OPT, m_force_multi,
1053
               make_option("-e", "--extra", dest="extra_args",
1054
                           help="Extra arguments for the instance's kernel",
1055
                           default=None, type="string", metavar="<PARAMS>"),
1056
               make_option("-t", "--type", dest="reboot_type",
1057
                           help="Type of reboot: soft/hard/full",
1058
                           default=constants.INSTANCE_REBOOT_SOFT,
1059
                           type="string", metavar="<REBOOT>"),
1060
               make_option("--ignore-secondaries", dest="ignore_secondaries",
1061
                           default=False, action="store_true",
1062
                           help="Ignore errors from secondaries"),
1063
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1064
               m_clust_opt, m_inst_opt,
1065
               ],
1066
            "<instance>", "Reboots an instance"),
1067
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1068
                     "<instance>",
1069
                     "Activate an instance's disks"),
1070
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1071
                       "<instance>",
1072
                       "Deactivate an instance's disks"),
1073
  'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT],
1074
                "<instance> <disk> <size>", "Grow an instance's disk"),
1075
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1076
                "<node_name>", "List the tags of the given instance"),
1077
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1078
               "<node_name> tag...", "Add tags to the given instance"),
1079
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1080
                  "<node_name> tag...", "Remove tags from given instance"),
1081
  }
1082

    
1083
aliases = {
1084
  'activate_block_devs': 'activate-disks',
1085
  'replace_disks': 'replace-disks',
1086
  'start': 'startup',
1087
  'stop': 'shutdown',
1088
  }
1089

    
1090
if __name__ == '__main__':
1091
  sys.exit(GenericMain(commands, aliases=aliases,
1092
                       override={"tag_type": constants.TAG_INSTANCE}))