Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 6785674e

History | View | Annotate | Download (42.2 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", "hypervisor", "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
    client = GetClient()
73
    idata = client.QueryInstances([], ["name"])
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
    client = GetClient()
82
    ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
83
    ipri = [row[1] for row in ndata]
84
    pri_names = list(itertools.chain(*ipri))
85
    isec = [row[2] for row in ndata]
86
    sec_names = list(itertools.chain(*isec))
87
    if mode == _SHUTDOWN_NODES_BOTH:
88
      inames = pri_names + sec_names
89
    elif mode == _SHUTDOWN_NODES_PRI:
90
      inames = pri_names
91
    elif mode == _SHUTDOWN_NODES_SEC:
92
      inames = sec_names
93
    else:
94
      raise errors.ProgrammerError("Unhandled shutdown type")
95

    
96
  elif mode == _SHUTDOWN_INSTANCES:
97
    if not names:
98
      raise errors.OpPrereqError("No instance names passed")
99
    client = GetClient()
100
    idata = client.QueryInstances(names, ["name"])
101
    inames = [row[0] for row in idata]
102

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

    
106
  return inames
107

    
108

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

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

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

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

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

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

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

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

    
143

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

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

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

    
164
  return result_path
165

    
166

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

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

    
178
  output = GetClient().QueryInstances([], selected_fields)
179

    
180
  if not opts.no_headers:
181
    headers = {
182
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
183
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
184
      "oper_state": "Running", "admin_ram": "Configured_memory",
185
      "oper_ram": "Memory", "disk_template": "Disk_template",
186
      "ip": "IP_address", "mac": "MAC_address",
187
      "bridge": "Bridge", "vcpus": "VCPUs",
188
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
189
      "status": "Status", "tags": "Tags",
190
      "network_port": "Network_port",
191
      "kernel_path": "Kernel_path",
192
      "initrd_path": "Initrd_path",
193
      "hvm_boot_order": "HVM_boot_order",
194
      "hvm_acpi": "HVM_ACPI",
195
      "hvm_pae": "HVM_PAE",
196
      "hvm_cdrom_image_path": "HVM_CDROM_image_path",
197
      "hvm_nic_type": "HVM_NIC_type",
198
      "hvm_disk_type": "HVM_disk_type",
199
      "vnc_bind_address": "VNC_bind_address",
200
      "serial_no": "SerialNo", "hypervisor": "Hypervisor",
201
      }
202
  else:
203
    headers = None
204

    
205
  if opts.human_readable:
206
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
207
  else:
208
    unitfields = None
209

    
210
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus",
211
               "serial_no"]
212

    
213
  list_type_fields = ("tags",)
214
  # change raw values to nicer strings
215
  for row in output:
216
    for idx, field in enumerate(selected_fields):
217
      val = row[idx]
218
      if field == "snodes":
219
        val = ",".join(val) or "-"
220
      elif field == "admin_state":
221
        if val:
222
          val = "yes"
223
        else:
224
          val = "no"
225
      elif field == "oper_state":
226
        if val is None:
227
          val = "(node down)"
228
        elif val: # True
229
          val = "running"
230
        else:
231
          val = "stopped"
232
      elif field == "oper_ram":
233
        if val is None:
234
          val = "(node down)"
235
      elif field == "sda_size" or field == "sdb_size":
236
        if val is None:
237
          val = "N/A"
238
      elif field in list_type_fields:
239
        val = ",".join(val)
240
      row[idx] = str(val)
241

    
242
  data = GenerateTable(separator=opts.separator, headers=headers,
243
                       fields=selected_fields, unitfields=unitfields,
244
                       numfields=numfields, data=output)
245

    
246
  for line in data:
247
    logger.ToStdout(line)
248

    
249
  return 0
250

    
251

    
252
def AddInstance(opts, args):
253
  """Add an instance to the cluster.
254

    
255
  Args:
256
    opts - class with options as members
257
    args - list with a single element, the instance name
258
  Opts used:
259
    mem - amount of memory to allocate to instance (MiB)
260
    size - amount of disk space to allocate to instance (MiB)
261
    os - which OS to run on instance
262
    node - node to run new instance on
263

    
264
  """
265
  instance = args[0]
266

    
267
  (pnode, snode) = SplitNodeOption(opts.node)
268

    
269
  hypervisor = None
270
  hvparams = {}
271
  if opts.hypervisor:
272
    hypervisor, hvparams = opts.hypervisor
273

    
274
##  kernel_path = _TransformPath(opts.kernel_path)
275
##  initrd_path = _TransformPath(opts.initrd_path)
276

    
277
##  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
278
##  hvm_pae = opts.hvm_pae == _VALUE_TRUE
279

    
280
##  if ((opts.hvm_cdrom_image_path is not None) and
281
##      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
282
##    hvm_cdrom_image_path = None
283
##  else:
284
##    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
285

    
286
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
287
                                disk_size=opts.size, swap_size=opts.swap,
288
                                disk_template=opts.disk_template,
289
                                mode=constants.INSTANCE_CREATE,
290
                                os_type=opts.os, pnode=pnode,
291
                                snode=snode, vcpus=opts.vcpus,
292
                                ip=opts.ip, bridge=opts.bridge,
293
                                start=opts.start, ip_check=opts.ip_check,
294
                                wait_for_sync=opts.wait_for_sync,
295
                                mac=opts.mac,
296
                                hypervisor=hypervisor,
297
                                hvparams=hvparams,
298
                                iallocator=opts.iallocator,
299
                                file_storage_dir=opts.file_storage_dir,
300
                                file_driver=opts.file_driver,
301
                                )
302

    
303
  SubmitOrSend(op, opts)
304
  return 0
305

    
306

    
307
def ReinstallInstance(opts, args):
308
  """Reinstall an instance.
309

    
310
  Args:
311
    opts - class with options as members
312
    args - list containing a single element, the instance name
313

    
314
  """
315
  instance_name = args[0]
316

    
317
  if opts.select_os is True:
318
    op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
319
    result = SubmitOpCode(op)
320

    
321
    if not result:
322
      logger.ToStdout("Can't get the OS list")
323
      return 1
324

    
325
    logger.ToStdout("Available OS templates:")
326
    number = 0
327
    choices = []
328
    for entry in result:
329
      logger.ToStdout("%3s: %s" % (number, entry[0]))
330
      choices.append(("%s" % number, entry[0], entry[0]))
331
      number = number + 1
332

    
333
    choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
334
    selected = AskUser("Enter OS template name or number (or x to abort):",
335
                       choices)
336

    
337
    if selected == 'exit':
338
      logger.ToStdout("User aborted reinstall, exiting")
339
      return 1
340

    
341
    os = selected
342
  else:
343
    os = opts.os
344

    
345
  if not opts.force:
346
    usertext = ("This will reinstall the instance %s and remove"
347
                " all data. Continue?") % instance_name
348
    if not AskUser(usertext):
349
      return 1
350

    
351
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
352
                                   os_type=os)
353
  SubmitOrSend(op, opts)
354

    
355
  return 0
356

    
357

    
358
def RemoveInstance(opts, args):
359
  """Remove an instance.
360

    
361
  Args:
362
    opts - class with options as members
363
    args - list containing a single element, the instance name
364

    
365
  """
366
  instance_name = args[0]
367
  force = opts.force
368

    
369
  if not force:
370
    usertext = ("This will remove the volumes of the instance %s"
371
                " (including mirrors), thus removing all the data"
372
                " of the instance. Continue?") % instance_name
373
    if not AskUser(usertext):
374
      return 1
375

    
376
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
377
                                ignore_failures=opts.ignore_failures)
378
  SubmitOrSend(op, opts)
379
  return 0
380

    
381

    
382
def RenameInstance(opts, args):
383
  """Rename an instance.
384

    
385
  Args:
386
    opts - class with options as members
387
    args - list containing two elements, the instance name and the new name
388

    
389
  """
390
  op = opcodes.OpRenameInstance(instance_name=args[0],
391
                                new_name=args[1],
392
                                ignore_ip=opts.ignore_ip)
393
  SubmitOrSend(op, opts)
394
  return 0
395

    
396

    
397
def ActivateDisks(opts, args):
398
  """Activate an instance's disks.
399

    
400
  This serves two purposes:
401
    - it allows one (as long as the instance is not running) to mount
402
    the disks and modify them from the node
403
    - it repairs inactive secondary drbds
404

    
405
  """
406
  instance_name = args[0]
407
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
408
  disks_info = SubmitOrSend(op, opts)
409
  for host, iname, nname in disks_info:
410
    print "%s:%s:%s" % (host, iname, nname)
411
  return 0
412

    
413

    
414
def DeactivateDisks(opts, args):
415
  """Command-line interface for _ShutdownInstanceBlockDevices.
416

    
417
  This function takes the instance name, looks for its primary node
418
  and the tries to shutdown its block devices on that node.
419

    
420
  """
421
  instance_name = args[0]
422
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
423
  SubmitOrSend(op, opts)
424
  return 0
425

    
426

    
427
def GrowDisk(opts, args):
428
  """Command-line interface for _ShutdownInstanceBlockDevices.
429

    
430
  This function takes the instance name, looks for its primary node
431
  and the tries to shutdown its block devices on that node.
432

    
433
  """
434
  instance = args[0]
435
  disk = args[1]
436
  amount = utils.ParseUnit(args[2])
437
  op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount)
438
  SubmitOrSend(op, opts)
439
  return 0
440

    
441

    
442
def StartupInstance(opts, args):
443
  """Startup an instance.
444

    
445
  Args:
446
    opts - class with options as members
447
    args - list containing a single element, the instance name
448

    
449
  """
450
  if opts.multi_mode is None:
451
    opts.multi_mode = _SHUTDOWN_INSTANCES
452
  inames = _ExpandMultiNames(opts.multi_mode, args)
453
  if not inames:
454
    raise errors.OpPrereqError("Selection filter does not match any instances")
455
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
456
  if not (opts.force_multi or not multi_on
457
          or _ConfirmOperation(inames, "startup")):
458
    return 1
459
  for name in inames:
460
    op = opcodes.OpStartupInstance(instance_name=name,
461
                                   force=opts.force,
462
                                   extra_args=opts.extra_args)
463
    if multi_on:
464
      logger.ToStdout("Starting up %s" % name)
465
    try:
466
      SubmitOrSend(op, opts)
467
    except JobSubmittedException, err:
468
      _, txt = FormatError(err)
469
      logger.ToStdout("%s" % txt)
470
  return 0
471

    
472

    
473
def RebootInstance(opts, args):
474
  """Reboot an instance
475

    
476
  Args:
477
    opts - class with options as members
478
    args - list containing a single element, the instance name
479

    
480
  """
481
  if opts.multi_mode is None:
482
    opts.multi_mode = _SHUTDOWN_INSTANCES
483
  inames = _ExpandMultiNames(opts.multi_mode, args)
484
  if not inames:
485
    raise errors.OpPrereqError("Selection filter does not match any instances")
486
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
487
  if not (opts.force_multi or not multi_on
488
          or _ConfirmOperation(inames, "reboot")):
489
    return 1
490
  for name in inames:
491
    op = opcodes.OpRebootInstance(instance_name=name,
492
                                  reboot_type=opts.reboot_type,
493
                                  ignore_secondaries=opts.ignore_secondaries)
494

    
495
    SubmitOrSend(op, opts)
496
  return 0
497

    
498

    
499
def ShutdownInstance(opts, args):
500
  """Shutdown an instance.
501

    
502
  Args:
503
    opts - class with options as members
504
    args - list containing a single element, the instance name
505

    
506
  """
507
  if opts.multi_mode is None:
508
    opts.multi_mode = _SHUTDOWN_INSTANCES
509
  inames = _ExpandMultiNames(opts.multi_mode, args)
510
  if not inames:
511
    raise errors.OpPrereqError("Selection filter does not match any instances")
512
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
513
  if not (opts.force_multi or not multi_on
514
          or _ConfirmOperation(inames, "shutdown")):
515
    return 1
516
  for name in inames:
517
    op = opcodes.OpShutdownInstance(instance_name=name)
518
    if multi_on:
519
      logger.ToStdout("Shutting down %s" % name)
520
    try:
521
      SubmitOrSend(op, opts)
522
    except JobSubmittedException, err:
523
      _, txt = FormatError(err)
524
      logger.ToStdout("%s" % txt)
525
  return 0
526

    
527

    
528
def ReplaceDisks(opts, args):
529
  """Replace the disks of an instance
530

    
531
  Args:
532
    opts - class with options as members
533
    args - list with a single element, the instance name
534

    
535
  """
536
  instance_name = args[0]
537
  new_2ndary = opts.new_secondary
538
  iallocator = opts.iallocator
539
  if opts.disks is None:
540
    disks = ["sda", "sdb"]
541
  else:
542
    disks = opts.disks.split(",")
543
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
544
    mode = constants.REPLACE_DISK_ALL
545
  elif opts.on_primary: # only on primary:
546
    mode = constants.REPLACE_DISK_PRI
547
    if new_2ndary is not None or iallocator is not None:
548
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
549
                                 " replacement")
550
  elif opts.on_secondary is not None or iallocator is not None:
551
    # only on secondary
552
    mode = constants.REPLACE_DISK_SEC
553

    
554
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
555
                              remote_node=new_2ndary, mode=mode,
556
                              iallocator=iallocator)
557
  SubmitOrSend(op, opts)
558
  return 0
559

    
560

    
561
def FailoverInstance(opts, args):
562
  """Failover an instance.
563

    
564
  The failover is done by shutting it down on its present node and
565
  starting it on the secondary.
566

    
567
  Args:
568
    opts - class with options as members
569
    args - list with a single element, the instance name
570
  Opts used:
571
    force - whether to failover without asking questions.
572

    
573
  """
574
  instance_name = args[0]
575
  force = opts.force
576

    
577
  if not force:
578
    usertext = ("Failover will happen to image %s."
579
                " This requires a shutdown of the instance. Continue?" %
580
                (instance_name,))
581
    if not AskUser(usertext):
582
      return 1
583

    
584
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
585
                                  ignore_consistency=opts.ignore_consistency)
586
  SubmitOrSend(op, opts)
587
  return 0
588

    
589

    
590
def ConnectToInstanceConsole(opts, args):
591
  """Connect to the console of an instance.
592

    
593
  Args:
594
    opts - class with options as members
595
    args - list with a single element, the instance name
596

    
597
  """
598
  instance_name = args[0]
599

    
600
  op = opcodes.OpConnectConsole(instance_name=instance_name)
601
  cmd = SubmitOpCode(op)
602

    
603
  if opts.show_command:
604
    print utils.ShellQuoteArgs(cmd)
605
  else:
606
    try:
607
      os.execvp(cmd[0], cmd)
608
    finally:
609
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
610
                       (cmd, " ".join(argv)))
611
      os._exit(1)
612

    
613

    
614
def _FormatBlockDevInfo(buf, dev, indent_level):
615
  """Show block device information.
616

    
617
  This is only used by ShowInstanceConfig(), but it's too big to be
618
  left for an inline definition.
619

    
620
  """
621
  def helper(buf, dtype, status):
622
    """Format one line for physical device status."""
623
    if not status:
624
      buf.write("not active\n")
625
    else:
626
      (path, major, minor, syncp, estt, degr, ldisk) = status
627
      if major is None:
628
        major_string = "N/A"
629
      else:
630
        major_string = str(major)
631

    
632
      if minor is None:
633
        minor_string = "N/A"
634
      else:
635
        minor_string = str(minor)
636

    
637
      buf.write("%s (%s:%s)" % (path, major_string, minor_string))
638
      if dtype in (constants.LD_DRBD8, ):
639
        if syncp is not None:
640
          sync_text = "*RECOVERING* %5.2f%%," % syncp
641
          if estt:
642
            sync_text += " ETA %ds" % estt
643
          else:
644
            sync_text += " ETA unknown"
645
        else:
646
          sync_text = "in sync"
647
        if degr:
648
          degr_text = "*DEGRADED*"
649
        else:
650
          degr_text = "ok"
651
        if ldisk:
652
          ldisk_text = " *MISSING DISK*"
653
        else:
654
          ldisk_text = ""
655
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
656
      elif dtype == constants.LD_LV:
657
        if ldisk:
658
          ldisk_text = " *FAILED* (failed drive?)"
659
        else:
660
          ldisk_text = ""
661
        buf.write(ldisk_text)
662
      buf.write("\n")
663

    
664
  if dev["iv_name"] is not None:
665
    data = "  - %s, " % dev["iv_name"]
666
  else:
667
    data = "  - "
668
  data += "type: %s" % dev["dev_type"]
669
  if dev["logical_id"] is not None:
670
    data += ", logical_id: %s" % (dev["logical_id"],)
671
  elif dev["physical_id"] is not None:
672
    data += ", physical_id: %s" % (dev["physical_id"],)
673
  buf.write("%*s%s\n" % (2*indent_level, "", data))
674
  buf.write("%*s    primary:   " % (2*indent_level, ""))
675
  helper(buf, dev["dev_type"], dev["pstatus"])
676

    
677
  if dev["sstatus"]:
678
    buf.write("%*s    secondary: " % (2*indent_level, ""))
679
    helper(buf, dev["dev_type"], dev["sstatus"])
680

    
681
  if dev["children"]:
682
    for child in dev["children"]:
683
      _FormatBlockDevInfo(buf, child, indent_level+1)
684

    
685

    
686
def ShowInstanceConfig(opts, args):
687
  """Compute instance run-time status.
688

    
689
  """
690
  retcode = 0
691
  op = opcodes.OpQueryInstanceData(instances=args)
692
  result = SubmitOpCode(op)
693
  hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
694
                    "hvm_boot_order", "hvm_nic_type", "hvm_disk_type")
695

    
696
  pvm_parameters = ("kernel_path", "initrd_path")
697

    
698
  if not result:
699
    logger.ToStdout("No instances.")
700
    return 1
701

    
702
  buf = StringIO()
703
  retcode = 0
704
  for instance_name in result:
705
    instance = result[instance_name]
706
    buf.write("Instance name: %s\n" % instance["name"])
707
    buf.write("State: configured to be %s, actual state is %s\n" %
708
              (instance["config_state"], instance["run_state"]))
709
    buf.write("  Nodes:\n")
710
    buf.write("    - primary: %s\n" % instance["pnode"])
711
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
712
    buf.write("  Hypervisor: %s\n" % instance["hypervisor"])
713
    buf.write("  Operating system: %s\n" % instance["os"])
714
    if instance.has_key("network_port"):
715
      buf.write("  Allocated network port: %s\n" % instance["network_port"])
716
    if False not in map(instance.has_key, pvm_parameters):
717
      if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
718
        kpath = "(default: %s)" % constants.XEN_KERNEL
719
      else:
720
        kpath = instance["kernel_path"]
721
      buf.write("  Kernel path: %s\n" % kpath)
722
      if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
723
        initrd = "(default: %s)" % constants.XEN_INITRD
724
      elif instance["initrd_path"] == constants.VALUE_NONE:
725
        initrd = "(none)"
726
      else:
727
        initrd = instance["initrd_path"]
728
      buf.write("       initrd: %s\n" % initrd)
729
    if False not in map(instance.has_key, hvm_parameters):
730
      buf.write("  HVM:\n")
731
      buf.write("    - boot order: %s\n" % instance["hvm_boot_order"])
732
      buf.write("    - ACPI support: %s\n" % instance["hvm_acpi"])
733
      buf.write("    - PAE support: %s\n" % instance["hvm_pae"])
734
      buf.write("    - virtual CDROM: %s\n" % instance["hvm_cdrom_image_path"])
735
      buf.write("    - virtual NIC type: %s\n" %  instance["hvm_nic_type"])
736
      buf.write("    - virtual disk type: %s\n" %  instance["hvm_disk_type"])
737
    if instance.has_key("vnc_bind_address"):
738
      buf.write("  VNC bind address: %s\n" % instance["vnc_bind_address"])
739
      buf.write("  VNC console port: %s\n" % instance["vnc_console_port"])
740
    buf.write("  Hardware:\n")
741
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
742
    buf.write("    - memory: %dMiB\n" % instance["memory"])
743
    buf.write("    - NICs: %s\n" %
744
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
745
                   (mac, ip, bridge)
746
                     for mac, ip, bridge in instance["nics"]]))
747
    buf.write("  Block devices:\n")
748

    
749
    for device in instance["disks"]:
750
      _FormatBlockDevInfo(buf, device, 1)
751

    
752
  logger.ToStdout(buf.getvalue().rstrip('\n'))
753
  return retcode
754

    
755

    
756
def SetInstanceParams(opts, args):
757
  """Modifies an instance.
758

    
759
  All parameters take effect only at the next restart of the instance.
760

    
761
  Args:
762
    opts - class with options as members
763
    args - list with a single element, the instance name
764
  Opts used:
765
    memory - the new memory size
766
    vcpus - the new number of cpus
767
    mac - the new MAC address of the instance
768

    
769
  """
770
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
771
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
772
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
773
          opts.vnc_bind_address or opts.hvm_nic_type or opts.hvm_disk_type):
774
    logger.ToStdout("Please give at least one of the parameters.")
775
    return 1
776

    
777
  kernel_path = _TransformPath(opts.kernel_path)
778
  initrd_path = _TransformPath(opts.initrd_path)
779
  if opts.hvm_boot_order == 'default':
780
    hvm_boot_order = constants.VALUE_DEFAULT
781
  else:
782
    hvm_boot_order = opts.hvm_boot_order
783

    
784
  if opts.hvm_acpi is None:
785
    hvm_acpi = opts.hvm_acpi
786
  else:
787
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
788

    
789
  if opts.hvm_pae is None:
790
    hvm_pae = opts.hvm_pae
791
  else:
792
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
793

    
794
  if opts.hvm_nic_type == constants.VALUE_NONE:
795
    hvm_nic_type = None
796
  else:
797
    hvm_nic_type = opts.hvm_nic_type
798

    
799
  if opts.hvm_disk_type == constants.VALUE_NONE:
800
    hvm_disk_type = None
801
  else:
802
    hvm_disk_type = opts.hvm_disk_type
803

    
804
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
805
                                   vcpus=opts.vcpus, ip=opts.ip,
806
                                   bridge=opts.bridge, mac=opts.mac,
807
                                   kernel_path=opts.kernel_path,
808
                                   initrd_path=opts.initrd_path,
809
                                   hvm_boot_order=hvm_boot_order,
810
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
811
                                   hvm_cdrom_image_path=
812
                                   opts.hvm_cdrom_image_path,
813
                                   vnc_bind_address=opts.vnc_bind_address,
814
                                   hvm_nic_type=hvm_nic_type,
815
                                   hvm_disk_type=hvm_disk_type,
816
                                   force=opts.force)
817

    
818
  # even if here we process the result, we allow submit only
819
  result = SubmitOrSend(op, opts)
820

    
821
  if result:
822
    logger.ToStdout("Modified instance %s" % args[0])
823
    for param, data in result:
824
      logger.ToStdout(" - %-5s -> %s" % (param, data))
825
    logger.ToStdout("Please don't forget that these parameters take effect"
826
                    " only at the next start of the instance.")
827
  return 0
828

    
829

    
830
# options used in more than one cmd
831
node_opt = make_option("-n", "--node", dest="node", help="Target node",
832
                       metavar="<node>")
833

    
834
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
835
                    metavar="<os>")
836

    
837
# multi-instance selection options
838
m_force_multi = make_option("--force-multiple", dest="force_multi",
839
                            help="Do not ask for confirmation when more than"
840
                            " one instance is affected",
841
                            action="store_true", default=False)
842

    
843
m_pri_node_opt = make_option("--primary", dest="multi_mode",
844
                             help="Filter by nodes (primary only)",
845
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
846

    
847
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
848
                             help="Filter by nodes (secondary only)",
849
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
850

    
851
m_node_opt = make_option("--node", dest="multi_mode",
852
                         help="Filter by nodes (primary and secondary)",
853
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
854

    
855
m_clust_opt = make_option("--all", dest="multi_mode",
856
                          help="Select all instances in the cluster",
857
                          const=_SHUTDOWN_CLUSTER, action="store_const")
858

    
859
m_inst_opt = make_option("--instance", dest="multi_mode",
860
                         help="Filter by instance name [default]",
861
                         const=_SHUTDOWN_INSTANCES, action="store_const")
862

    
863

    
864
# this is defined separately due to readability only
865
add_opts = [
866
  DEBUG_OPT,
867
  make_option("-n", "--node", dest="node",
868
              help="Target node and optional secondary node",
869
              metavar="<pnode>[:<snode>]"),
870
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
871
             " a suffix is used",
872
             default=20 * 1024, type="unit", metavar="<size>"),
873
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
874
             " suffix is used",
875
             default=4 * 1024, type="unit", metavar="<size>"),
876
  os_opt,
877
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
878
              default=128, type="unit", metavar="<mem>"),
879
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
880
              default=1, type="int", metavar="<PROC>"),
881
  make_option("-t", "--disk-template", dest="disk_template",
882
              help="Custom disk setup (diskless, file, plain or drbd)",
883
              default=None, metavar="TEMPL"),
884
  make_option("-i", "--ip", dest="ip",
885
              help="IP address ('none' [default], 'auto', or specify address)",
886
              default='none', type="string", metavar="<ADDRESS>"),
887
  make_option("--mac", dest="mac",
888
              help="MAC address ('auto' [default], or specify address)",
889
              default='auto', type="string", metavar="<MACADDRESS>"),
890
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
891
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
892
  make_option("-b", "--bridge", dest="bridge",
893
              help="Bridge to connect this instance to",
894
              default=None, metavar="<bridge>"),
895
  make_option("--no-start", dest="start", default=True,
896
              action="store_false", help="Don't start the instance after"
897
              " creation"),
898
  make_option("--no-ip-check", dest="ip_check", default=True,
899
              action="store_false", help="Don't check that the instance's IP"
900
              " is alive (only valid with --no-start)"),
901
  make_option("--kernel", dest="kernel_path",
902
              help="Path to the instances' kernel (or 'default')",
903
              default=None,
904
              type="string", metavar="<FILENAME>"),
905
  make_option("--initrd", dest="initrd_path",
906
              help="Path to the instances' initrd (or 'none', or 'default')",
907
              default=None,
908
              type="string", metavar="<FILENAME>"),
909
  make_option("--hvm-boot-order", dest="hvm_boot_order",
910
              help="Boot device order for HVM (one or more of [acdn])",
911
              default=None, type="string", metavar="<BOOTORDER>"),
912
  make_option("--file-storage-dir", dest="file_storage_dir",
913
              help="Relative path under default cluster-wide file storage dir"
914
              " to store file-based disks", default=None,
915
              metavar="<DIR>"),
916
  make_option("--file-driver", dest="file_driver", help="Driver to use"
917
              " for image files", default="loop", metavar="<DRIVER>"),
918
  make_option("--iallocator", metavar="<NAME>",
919
              help="Select nodes for the instance automatically using the"
920
              " <NAME> iallocator plugin", default=None, type="string"),
921
  ikv_option("-H", "--hypervisor", dest="hypervisor",
922
              help="Hypervisor and hypervisor options, in the format"
923
              " hypervisor:option=value,option=value,...", default=None,
924
              type="identkeyval"),
925
  make_option("--vnc-bind-address", dest="vnc_bind_address",
926
              help="bind address for VNC (IP address)",
927
              default=None, type="string", metavar="<VNCADDRESS>"),
928
  SUBMIT_OPT,
929
  ]
930

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

    
1099
  'reboot': (RebootInstance, ARGS_ANY,
1100
              [DEBUG_OPT, m_force_multi,
1101
               make_option("-e", "--extra", dest="extra_args",
1102
                           help="Extra arguments for the instance's kernel",
1103
                           default=None, type="string", metavar="<PARAMS>"),
1104
               make_option("-t", "--type", dest="reboot_type",
1105
                           help="Type of reboot: soft/hard/full",
1106
                           default=constants.INSTANCE_REBOOT_HARD,
1107
                           type="string", metavar="<REBOOT>"),
1108
               make_option("--ignore-secondaries", dest="ignore_secondaries",
1109
                           default=False, action="store_true",
1110
                           help="Ignore errors from secondaries"),
1111
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
1112
               m_clust_opt, m_inst_opt,
1113
               SUBMIT_OPT,
1114
               ],
1115
            "<instance>", "Reboots an instance"),
1116
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1117
                     "<instance>",
1118
                     "Activate an instance's disks"),
1119
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1120
                       "<instance>",
1121
                       "Deactivate an instance's disks"),
1122
  'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT, SUBMIT_OPT],
1123
                "<instance> <disk> <size>", "Grow an instance's disk"),
1124
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1125
                "<instance_name>", "List the tags of the given instance"),
1126
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1127
               "<instance_name> tag...", "Add tags to the given instance"),
1128
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1129
                  "<instance_name> tag...", "Remove tags from given instance"),
1130
  }
1131

    
1132
aliases = {
1133
  'activate_block_devs': 'activate-disks',
1134
  'replace_disks': 'replace-disks',
1135
  'start': 'startup',
1136
  'stop': 'shutdown',
1137
  }
1138

    
1139
if __name__ == '__main__':
1140
  sys.exit(GenericMain(commands, aliases=aliases,
1141
                       override={"tag_type": constants.TAG_INSTANCE}))