Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 20e23543

History | View | Annotate | Download (42.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 opts.select_os is True:
307
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
308
    result = SubmitOpCode(op)
309

    
310
    if not result:
311
      logger.ToStdout("Can't get the OS list")
312
      return 1
313

    
314
    logger.ToStdout("Available OS templates:")
315
    number = 0
316
    choices = []
317
    for entry in result:
318
      logger.ToStdout("%3s: %s" % (number, entry[0]))
319
      choices.append(("%s" % number, entry[0], entry[0]))
320
      number = number + 1
321

    
322
    choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
323
    selected = AskUser("Enter OS template name or number (or x to abort):",
324
                       choices)
325

    
326
    if selected == 'exit':
327
      logger.ToStdout("User aborted reinstall, exiting")
328
      return 1
329

    
330
    os = selected
331
  else:
332
    os = opts.os
333

    
334
  if not opts.force:
335
    usertext = ("This will reinstall the instance %s and remove"
336
                " all data. Continue?") % instance_name
337
    if not AskUser(usertext):
338
      return 1
339

    
340
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
341
                                   os_type=os)
342
  SubmitOpCode(op)
343

    
344
  return 0
345

    
346

    
347
def RemoveInstance(opts, args):
348
  """Remove an instance.
349

    
350
  Args:
351
    opts - class with options as members
352
    args - list containing a single element, the instance name
353

    
354
  """
355
  instance_name = args[0]
356
  force = opts.force
357

    
358
  if not force:
359
    usertext = ("This will remove the volumes of the instance %s"
360
                " (including mirrors), thus removing all the data"
361
                " of the instance. Continue?") % instance_name
362
    if not AskUser(usertext):
363
      return 1
364

    
365
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
366
                                ignore_failures=opts.ignore_failures)
367
  SubmitOpCode(op)
368
  return 0
369

    
370

    
371
def RenameInstance(opts, args):
372
  """Rename an instance.
373

    
374
  Args:
375
    opts - class with options as members
376
    args - list containing two elements, the instance name and the new name
377

    
378
  """
379
  op = opcodes.OpRenameInstance(instance_name=args[0],
380
                                new_name=args[1],
381
                                ignore_ip=opts.ignore_ip)
382
  SubmitOpCode(op)
383

    
384
  return 0
385

    
386

    
387
def ActivateDisks(opts, args):
388
  """Activate an instance's disks.
389

    
390
  This serves two purposes:
391
    - it allows one (as long as the instance is not running) to mount
392
    the disks and modify them from the node
393
    - it repairs inactive secondary drbds
394

    
395
  """
396
  instance_name = args[0]
397
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
398
  disks_info = SubmitOpCode(op)
399
  for host, iname, nname in disks_info:
400
    print "%s:%s:%s" % (host, iname, nname)
401
  return 0
402

    
403

    
404
def DeactivateDisks(opts, args):
405
  """Command-line interface for _ShutdownInstanceBlockDevices.
406

    
407
  This function takes the instance name, looks for its primary node
408
  and the tries to shutdown its block devices on that node.
409

    
410
  """
411
  instance_name = args[0]
412
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
413
  SubmitOpCode(op)
414
  return 0
415

    
416

    
417
def GrowDisk(opts, args):
418
  """Command-line interface for _ShutdownInstanceBlockDevices.
419

    
420
  This function takes the instance name, looks for its primary node
421
  and the tries to shutdown its block devices on that node.
422

    
423
  """
424
  instance = args[0]
425
  disk = args[1]
426
  amount = utils.ParseUnit(args[2])
427
  op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount)
428
  SubmitOpCode(op)
429
  return 0
430

    
431

    
432
def StartupInstance(opts, args):
433
  """Startup an instance.
434

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

    
439
  """
440
  if opts.multi_mode is None:
441
    opts.multi_mode = _SHUTDOWN_INSTANCES
442
  inames = _ExpandMultiNames(opts.multi_mode, args)
443
  if not inames:
444
    raise errors.OpPrereqError("Selection filter does not match any instances")
445
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
446
  if not (opts.force_multi or not multi_on
447
          or _ConfirmOperation(inames, "startup")):
448
    return 1
449
  for name in inames:
450
    op = opcodes.OpStartupInstance(instance_name=name,
451
                                   force=opts.force,
452
                                   extra_args=opts.extra_args)
453
    if multi_on:
454
      logger.ToStdout("Starting up %s" % name)
455
    SubmitOpCode(op)
456
  return 0
457

    
458

    
459
def RebootInstance(opts, args):
460
  """Reboot an instance
461

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

    
466
  """
467
  if opts.multi_mode is None:
468
    opts.multi_mode = _SHUTDOWN_INSTANCES
469
  inames = _ExpandMultiNames(opts.multi_mode, args)
470
  if not inames:
471
    raise errors.OpPrereqError("Selection filter does not match any instances")
472
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
473
  if not (opts.force_multi or not multi_on
474
          or _ConfirmOperation(inames, "reboot")):
475
    return 1
476
  for name in inames:
477
    op = opcodes.OpRebootInstance(instance_name=name,
478
                                  reboot_type=opts.reboot_type,
479
                                  ignore_secondaries=opts.ignore_secondaries)
480

    
481
    SubmitOpCode(op)
482
  return 0
483

    
484

    
485
def ShutdownInstance(opts, args):
486
  """Shutdown an instance.
487

    
488
  Args:
489
    opts - class with options as members
490
    args - list containing a single element, the instance name
491

    
492
  """
493
  if opts.multi_mode is None:
494
    opts.multi_mode = _SHUTDOWN_INSTANCES
495
  inames = _ExpandMultiNames(opts.multi_mode, args)
496
  if not inames:
497
    raise errors.OpPrereqError("Selection filter does not match any instances")
498
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
499
  if not (opts.force_multi or not multi_on
500
          or _ConfirmOperation(inames, "shutdown")):
501
    return 1
502
  for name in inames:
503
    op = opcodes.OpShutdownInstance(instance_name=name)
504
    if multi_on:
505
      logger.ToStdout("Shutting down %s" % name)
506
    SubmitOpCode(op)
507
  return 0
508

    
509

    
510
def ReplaceDisks(opts, args):
511
  """Replace the disks of an instance
512

    
513
  Args:
514
    opts - class with options as members
515
    args - list with a single element, the instance name
516

    
517
  """
518
  instance_name = args[0]
519
  new_2ndary = opts.new_secondary
520
  iallocator = opts.iallocator
521
  if opts.disks is None:
522
    disks = ["sda", "sdb"]
523
  else:
524
    disks = opts.disks.split(",")
525
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
526
    mode = constants.REPLACE_DISK_ALL
527
  elif opts.on_primary: # only on primary:
528
    mode = constants.REPLACE_DISK_PRI
529
    if new_2ndary is not None or iallocator is not None:
530
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
531
                                 " replacement")
532
  elif opts.on_secondary is not None or iallocator is not None:
533
    # only on secondary
534
    mode = constants.REPLACE_DISK_SEC
535

    
536
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
537
                              remote_node=new_2ndary, mode=mode,
538
                              iallocator=iallocator)
539
  SubmitOpCode(op)
540
  return 0
541

    
542

    
543
def FailoverInstance(opts, args):
544
  """Failover an instance.
545

    
546
  The failover is done by shutting it down on its present node and
547
  starting it on the secondary.
548

    
549
  Args:
550
    opts - class with options as members
551
    args - list with a single element, the instance name
552
  Opts used:
553
    force - whether to failover without asking questions.
554

    
555
  """
556
  instance_name = args[0]
557
  force = opts.force
558

    
559
  if not force:
560
    usertext = ("Failover will happen to image %s."
561
                " This requires a shutdown of the instance. Continue?" %
562
                (instance_name,))
563
    if not AskUser(usertext):
564
      return 1
565

    
566
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
567
                                  ignore_consistency=opts.ignore_consistency)
568
  SubmitOpCode(op)
569
  return 0
570

    
571

    
572
def ConnectToInstanceConsole(opts, args):
573
  """Connect to the console of an instance.
574

    
575
  Args:
576
    opts - class with options as members
577
    args - list with a single element, the instance name
578

    
579
  """
580
  instance_name = args[0]
581

    
582
  op = opcodes.OpConnectConsole(instance_name=instance_name)
583
  cmd = SubmitOpCode(op)
584

    
585
  if opts.show_command:
586
    print utils.ShellQuoteArgs(cmd)
587
  else:
588
    try:
589
      os.execvp(cmd[0], cmd)
590
    finally:
591
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
592
                       (cmd, " ".join(argv)))
593
      os._exit(1)
594

    
595

    
596
def _FormatBlockDevInfo(buf, dev, indent_level):
597
  """Show block device information.
598

    
599
  This is only used by ShowInstanceConfig(), but it's too big to be
600
  left for an inline definition.
601

    
602
  """
603
  def helper(buf, dtype, status):
604
    """Format one line for physical device status."""
605
    if not status:
606
      buf.write("not active\n")
607
    else:
608
      (path, major, minor, syncp, estt, degr, ldisk) = status
609
      if major is None:
610
        major_string = "N/A"
611
      else:
612
        major_string = str(major)
613

    
614
      if minor is None:
615
        minor_string = "N/A"
616
      else:
617
        minor_string = str(minor)
618

    
619
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
620
      if dtype in (constants.LD_DRBD8, ):
621
        if syncp is not None:
622
          sync_text = "*RECOVERING* %5.2f%%," % syncp
623
          if estt:
624
            sync_text += " ETA %ds" % estt
625
          else:
626
            sync_text += " ETA unknown"
627
        else:
628
          sync_text = "in sync"
629
        if degr:
630
          degr_text = "*DEGRADED*"
631
        else:
632
          degr_text = "ok"
633
        if ldisk:
634
          ldisk_text = " *MISSING DISK*"
635
        else:
636
          ldisk_text = ""
637
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
638
      elif dtype == constants.LD_LV:
639
        if ldisk:
640
          ldisk_text = " *FAILED* (failed drive?)"
641
        else:
642
          ldisk_text = ""
643
        buf.write(ldisk_text)
644
      buf.write("\n")
645

    
646
  if dev["iv_name"] is not None:
647
    data = "  - %s, " % dev["iv_name"]
648
  else:
649
    data = "  - "
650
  data += "type: %s" % dev["dev_type"]
651
  if dev["logical_id"] is not None:
652
    data += ", logical_id: %s" % (dev["logical_id"],)
653
  elif dev["physical_id"] is not None:
654
    data += ", physical_id: %s" % (dev["physical_id"],)
655
  buf.write("%*s%s\n" % (2*indent_level, "", data))
656
  buf.write("%*s    primary:   " % (2*indent_level, ""))
657
  helper(buf, dev["dev_type"], dev["pstatus"])
658

    
659
  if dev["sstatus"]:
660
    buf.write("%*s    secondary: " % (2*indent_level, ""))
661
    helper(buf, dev["dev_type"], dev["sstatus"])
662

    
663
  if dev["children"]:
664
    for child in dev["children"]:
665
      _FormatBlockDevInfo(buf, child, indent_level+1)
666

    
667

    
668
def ShowInstanceConfig(opts, args):
669
  """Compute instance run-time status.
670

    
671
  """
672
  retcode = 0
673
  op = opcodes.OpQueryInstanceData(instances=args)
674
  result = SubmitOpCode(op)
675
  hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
676
                    "hvm_boot_order", "hvm_nic_type", "hvm_disk_type")
677

    
678
  pvm_parameters = ("kernel_path", "initrd_path")
679

    
680
  if not result:
681
    logger.ToStdout("No instances.")
682
    return 1
683

    
684
  buf = StringIO()
685
  retcode = 0
686
  for instance_name in result:
687
    instance = result[instance_name]
688
    buf.write("Instance name: %s\n" % instance["name"])
689
    buf.write("State: configured to be %s, actual state is %s\n" %
690
              (instance["config_state"], instance["run_state"]))
691
    buf.write("  Nodes:\n")
692
    buf.write("    - primary: %s\n" % instance["pnode"])
693
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
694
    buf.write("  Operating system: %s\n" % instance["os"])
695
    if instance.has_key("network_port"):
696
      buf.write("  Allocated network port: %s\n" % instance["network_port"])
697
    if False not in map(instance.has_key, pvm_parameters):
698
      if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
699
        kpath = "(default: %s)" % constants.XEN_KERNEL
700
      else:
701
        kpath = instance["kernel_path"]
702
      buf.write("  Kernel path: %s\n" % kpath)
703
      if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
704
        initrd = "(default: %s)" % constants.XEN_INITRD
705
      elif instance["initrd_path"] == constants.VALUE_NONE:
706
        initrd = "(none)"
707
      else:
708
        initrd = instance["initrd_path"]
709
      buf.write("       initrd: %s\n" % initrd)
710
    if False not in map(instance.has_key, hvm_parameters):
711
      buf.write("  HVM:\n")
712
      buf.write("    - boot order: %s\n" % instance["hvm_boot_order"])
713
      buf.write("    - ACPI support: %s\n" % instance["hvm_acpi"])
714
      buf.write("    - PAE support: %s\n" % instance["hvm_pae"])
715
      buf.write("    - virtual CDROM: %s\n" % instance["hvm_cdrom_image_path"])
716
      buf.write("    - virtual NIC type: %s\n" %  instance["hvm_nic_type"])
717
      buf.write("    - virtual disk type: %s\n" %  instance["hvm_disk_type"])
718
    if instance.has_key("vnc_bind_address"):
719
      buf.write("  VNC bind address: %s\n" % instance["vnc_bind_address"])
720
      buf.write("  VNC console port: %s\n" % instance["vnc_console_port"])
721
    buf.write("  Hardware:\n")
722
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
723
    buf.write("    - memory: %dMiB\n" % instance["memory"])
724
    buf.write("    - NICs: %s\n" %
725
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
726
                   (mac, ip, bridge)
727
                     for mac, ip, bridge in instance["nics"]]))
728
    buf.write("  Block devices:\n")
729

    
730
    for device in instance["disks"]:
731
      _FormatBlockDevInfo(buf, device, 1)
732

    
733
  logger.ToStdout(buf.getvalue().rstrip('\n'))
734
  return retcode
735

    
736

    
737
def SetInstanceParams(opts, args):
738
  """Modifies an instance.
739

    
740
  All parameters take effect only at the next restart of the instance.
741

    
742
  Args:
743
    opts - class with options as members
744
    args - list with a single element, the instance name
745
  Opts used:
746
    memory - the new memory size
747
    vcpus - the new number of cpus
748
    mac - the new MAC address of the instance
749

    
750
  """
751
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
752
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
753
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
754
          opts.vnc_bind_address or opts.hvm_nic_type or opts.hvm_disk_type):
755
    logger.ToStdout("Please give at least one of the parameters.")
756
    return 1
757

    
758
  kernel_path = _TransformPath(opts.kernel_path)
759
  initrd_path = _TransformPath(opts.initrd_path)
760
  if opts.hvm_boot_order == 'default':
761
    hvm_boot_order = constants.VALUE_DEFAULT
762
  else:
763
    hvm_boot_order = opts.hvm_boot_order
764

    
765
  if opts.hvm_acpi is None:
766
    hvm_acpi = opts.hvm_acpi
767
  else:
768
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
769

    
770
  if opts.hvm_pae is None:
771
    hvm_pae = opts.hvm_pae
772
  else:
773
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
774

    
775
  if opts.hvm_nic_type == constants.VALUE_NONE:
776
    hvm_nic_type = None
777
  else:
778
    hvm_nic_type = opts.hvm_nic_type
779

    
780
  if opts.hvm_disk_type == constants.VALUE_NONE:
781
    hvm_disk_type = None
782
  else:
783
    hvm_disk_type = opts.hvm_disk_type
784

    
785
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
786
                                   vcpus=opts.vcpus, ip=opts.ip,
787
                                   bridge=opts.bridge, mac=opts.mac,
788
                                   kernel_path=opts.kernel_path,
789
                                   initrd_path=opts.initrd_path,
790
                                   hvm_boot_order=hvm_boot_order,
791
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
792
                                   hvm_cdrom_image_path=
793
                                   opts.hvm_cdrom_image_path,
794
                                   vnc_bind_address=opts.vnc_bind_address,
795
                                   hvm_nic_type=hvm_nic_type,
796
                                   hvm_disk_type=hvm_disk_type,
797
                                   force=opts.force)
798

    
799
  result = SubmitOpCode(op)
800

    
801
  if result:
802
    logger.ToStdout("Modified instance %s" % args[0])
803
    for param, data in result:
804
      logger.ToStdout(" - %-5s -> %s" % (param, data))
805
    logger.ToStdout("Please don't forget that these parameters take effect"
806
                    " only at the next start of the instance.")
807
  return 0
808

    
809

    
810
# options used in more than one cmd
811
node_opt = make_option("-n", "--node", dest="node", help="Target node",
812
                       metavar="<node>")
813

    
814
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
815
                    metavar="<os>")
816

    
817
# multi-instance selection options
818
m_force_multi = make_option("--force-multiple", dest="force_multi",
819
                            help="Do not ask for confirmation when more than"
820
                            " one instance is affected",
821
                            action="store_true", default=False)
822

    
823
m_pri_node_opt = make_option("--primary", dest="multi_mode",
824
                             help="Filter by nodes (primary only)",
825
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
826

    
827
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
828
                             help="Filter by nodes (secondary only)",
829
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
830

    
831
m_node_opt = make_option("--node", dest="multi_mode",
832
                         help="Filter by nodes (primary and secondary)",
833
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
834

    
835
m_clust_opt = make_option("--all", dest="multi_mode",
836
                          help="Select all instances in the cluster",
837
                          const=_SHUTDOWN_CLUSTER, action="store_const")
838

    
839
m_inst_opt = make_option("--instance", dest="multi_mode",
840
                         help="Filter by instance name [default]",
841
                         const=_SHUTDOWN_INSTANCES, action="store_const")
842

    
843

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

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

    
1084
  'reboot': (RebootInstance, ARGS_ANY,
1085
              [DEBUG_OPT, m_force_multi,
1086
               make_option("-e", "--extra", dest="extra_args",
1087
                           help="Extra arguments for the instance's kernel",
1088
                           default=None, type="string", metavar="<PARAMS>"),
1089
               make_option("-t", "--type", dest="reboot_type",
1090
                           help="Type of reboot: soft/hard/full",
1091
                           default=constants.INSTANCE_REBOOT_SOFT,
1092
                           type="string", metavar="<REBOOT>"),
1093
               make_option("--ignore-secondaries", dest="ignore_secondaries",
1094
                           default=False, action="store_true",
1095
                           help="Ignore errors from secondaries"),
1096
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1097
               m_clust_opt, m_inst_opt,
1098
               ],
1099
            "<instance>", "Reboots an instance"),
1100
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
1101
                     "<instance>",
1102
                     "Activate an instance's disks"),
1103
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
1104
                       "<instance>",
1105
                       "Deactivate an instance's disks"),
1106
  'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT],
1107
                "<instance> <disk> <size>", "Grow an instance's disk"),
1108
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1109
                "<node_name>", "List the tags of the given instance"),
1110
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1111
               "<node_name> tag...", "Add tags to the given instance"),
1112
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1113
                  "<node_name> tag...", "Remove tags from given instance"),
1114
  }
1115

    
1116
aliases = {
1117
  'activate_block_devs': 'activate-disks',
1118
  'replace_disks': 'replace-disks',
1119
  'start': 'startup',
1120
  'stop': 'shutdown',
1121
  }
1122

    
1123
if __name__ == '__main__':
1124
  sys.exit(GenericMain(commands, aliases=aliases,
1125
                       override={"tag_type": constants.TAG_INSTANCE}))