Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 00fb8246

History | View | Annotate | Download (39 kB)

1
#!/usr/bin/python
2
#
3

    
4
# Copyright (C) 2006, 2007 Google Inc.
5
#
6
# This program is free software; you can redistribute it and/or modify
7
# it under the terms of the GNU General Public License as published by
8
# the Free Software Foundation; either version 2 of the License, or
9
# (at your option) any later version.
10
#
11
# This program is distributed in the hope that it will be useful, but
12
# WITHOUT ANY WARRANTY; without even the implied warranty of
13
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14
# General Public License for more details.
15
#
16
# You should have received a copy of the GNU General Public License
17
# along with this program; if not, write to the Free Software
18
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA
19
# 02110-1301, USA.
20

    
21

    
22
import sys
23
import os
24
import itertools
25
from optparse import make_option
26
from cStringIO import StringIO
27

    
28
from ganeti.cli import *
29
from ganeti import opcodes
30
from ganeti import logger
31
from ganeti import constants
32
from ganeti import utils
33
from ganeti import errors
34

    
35

    
36
_SHUTDOWN_CLUSTER = "cluster"
37
_SHUTDOWN_NODES_BOTH = "nodes"
38
_SHUTDOWN_NODES_PRI = "nodes-pri"
39
_SHUTDOWN_NODES_SEC = "nodes-sec"
40
_SHUTDOWN_INSTANCES = "instances"
41

    
42

    
43
_VALUE_TRUE = "true"
44

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

    
49

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

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

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

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

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

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

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

    
107
  return inames
108

    
109

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

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

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

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

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

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

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

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

    
144

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

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

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

    
165
  return result_path
166

    
167

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

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

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

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

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

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

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

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

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

    
239
  return 0
240

    
241

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

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

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

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

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

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

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

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

    
291
  SubmitOpCode(op)
292
  return 0
293

    
294

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

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

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

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

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

    
315
  return 0
316

    
317

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

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

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

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

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

    
341

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

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

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

    
355
  return 0
356

    
357

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

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

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

    
374

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

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

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

    
387

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

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

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

    
402

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

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

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

    
429

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

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

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

    
452
    SubmitOpCode(op)
453
  return 0
454

    
455

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

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

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

    
480

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

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

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

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

    
513

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

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

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

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

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

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

    
542

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

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

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

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

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

    
568

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

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

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

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

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

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

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

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

    
640

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

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

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

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

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

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

    
703
  logger.ToStdout(buf.getvalue().rstrip('\n'))
704
  return retcode
705

    
706

    
707
def SetInstanceParams(opts, args):
708
  """Modifies an instance.
709

    
710
  All parameters take effect only at the next restart of the instance.
711

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

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

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

    
735
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
736
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
737

    
738
  if ((opts.hvm_cdrom_image_path is not None) and
739
      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
740
    hvm_cdrom_image_path = None
741
  else:
742
    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
743

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

    
754
  result = SubmitOpCode(op)
755

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

    
764

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

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

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

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

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

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

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

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

    
798

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

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

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

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

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