Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 5018a335

History | View | Annotate | Download (42.3 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
      "hv/kernel_path": "Kernel_path",
192
      "hv/initrd_path": "Initrd_path",
193
      "hv/boot_order": "HVM_boot_order",
194
      "hv/acpi": "HVM_ACPI",
195
      "hv/pae": "HVM_PAE",
196
      "hv/cdrom_image_path": "HVM_CDROM_image_path",
197
      "hv/nic_type": "HVM_NIC_type",
198
      "hv/disk_type": "HVM_Disk_type",
199
      "vnc_bind_address": "VNC_bind_address",
200
      "serial_no": "SerialNo", "hypervisor": "Hypervisor",
201
      "hvparams": "Hypervisor_parameters",
202
      }
203
  else:
204
    headers = None
205

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

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

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

    
245
  data = GenerateTable(separator=opts.separator, headers=headers,
246
                       fields=selected_fields, unitfields=unitfields,
247
                       numfields=numfields, data=output)
248

    
249
  for line in data:
250
    logger.ToStdout(line)
251

    
252
  return 0
253

    
254

    
255
def AddInstance(opts, args):
256
  """Add an instance to the cluster.
257

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

    
267
  """
268
  instance = args[0]
269

    
270
  (pnode, snode) = SplitNodeOption(opts.node)
271

    
272
  hypervisor = None
273
  hvparams = {}
274
  if opts.hypervisor:
275
    hypervisor, hvparams = opts.hypervisor
276

    
277
##  kernel_path = _TransformPath(opts.kernel_path)
278
##  initrd_path = _TransformPath(opts.initrd_path)
279

    
280
##  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
281
##  hvm_pae = opts.hvm_pae == _VALUE_TRUE
282

    
283
##  if ((opts.hvm_cdrom_image_path is not None) and
284
##      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
285
##    hvm_cdrom_image_path = None
286
##  else:
287
##    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
288

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

    
306
  SubmitOrSend(op, opts)
307
  return 0
308

    
309

    
310
def ReinstallInstance(opts, args):
311
  """Reinstall an instance.
312

    
313
  Args:
314
    opts - class with options as members
315
    args - list containing a single element, the instance name
316

    
317
  """
318
  instance_name = args[0]
319

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

    
324
    if not result:
325
      logger.ToStdout("Can't get the OS list")
326
      return 1
327

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

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

    
340
    if selected == 'exit':
341
      logger.ToStdout("User aborted reinstall, exiting")
342
      return 1
343

    
344
    os = selected
345
  else:
346
    os = opts.os
347

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

    
354
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
355
                                   os_type=os)
356
  SubmitOrSend(op, opts)
357

    
358
  return 0
359

    
360

    
361
def RemoveInstance(opts, args):
362
  """Remove an instance.
363

    
364
  Args:
365
    opts - class with options as members
366
    args - list containing a single element, the instance name
367

    
368
  """
369
  instance_name = args[0]
370
  force = opts.force
371

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

    
379
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
380
                                ignore_failures=opts.ignore_failures)
381
  SubmitOrSend(op, opts)
382
  return 0
383

    
384

    
385
def RenameInstance(opts, args):
386
  """Rename an instance.
387

    
388
  Args:
389
    opts - class with options as members
390
    args - list containing two elements, the instance name and the new name
391

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

    
399

    
400
def ActivateDisks(opts, args):
401
  """Activate an instance's disks.
402

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

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

    
416

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

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

    
423
  """
424
  instance_name = args[0]
425
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
426
  SubmitOrSend(op, opts)
427
  return 0
428

    
429

    
430
def GrowDisk(opts, args):
431
  """Command-line interface for _ShutdownInstanceBlockDevices.
432

    
433
  This function takes the instance name, looks for its primary node
434
  and the tries to shutdown its block devices on that node.
435

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

    
444

    
445
def StartupInstance(opts, args):
446
  """Startup an instance.
447

    
448
  Args:
449
    opts - class with options as members
450
    args - list containing a single element, the instance name
451

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

    
475

    
476
def RebootInstance(opts, args):
477
  """Reboot an instance
478

    
479
  Args:
480
    opts - class with options as members
481
    args - list containing a single element, the instance name
482

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

    
498
    SubmitOrSend(op, opts)
499
  return 0
500

    
501

    
502
def ShutdownInstance(opts, args):
503
  """Shutdown an instance.
504

    
505
  Args:
506
    opts - class with options as members
507
    args - list containing a single element, the instance name
508

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

    
530

    
531
def ReplaceDisks(opts, args):
532
  """Replace the disks of an instance
533

    
534
  Args:
535
    opts - class with options as members
536
    args - list with a single element, the instance name
537

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

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

    
563

    
564
def FailoverInstance(opts, args):
565
  """Failover an instance.
566

    
567
  The failover is done by shutting it down on its present node and
568
  starting it on the secondary.
569

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

    
576
  """
577
  instance_name = args[0]
578
  force = opts.force
579

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

    
587
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
588
                                  ignore_consistency=opts.ignore_consistency)
589
  SubmitOrSend(op, opts)
590
  return 0
591

    
592

    
593
def ConnectToInstanceConsole(opts, args):
594
  """Connect to the console of an instance.
595

    
596
  Args:
597
    opts - class with options as members
598
    args - list with a single element, the instance name
599

    
600
  """
601
  instance_name = args[0]
602

    
603
  op = opcodes.OpConnectConsole(instance_name=instance_name)
604
  cmd = SubmitOpCode(op)
605

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

    
616

    
617
def _FormatBlockDevInfo(buf, dev, indent_level):
618
  """Show block device information.
619

    
620
  This is only used by ShowInstanceConfig(), but it's too big to be
621
  left for an inline definition.
622

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

    
635
      if minor is None:
636
        minor_string = "N/A"
637
      else:
638
        minor_string = str(minor)
639

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

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

    
680
  if dev["sstatus"]:
681
    buf.write("%*s    secondary: " % (2*indent_level, ""))
682
    helper(buf, dev["dev_type"], dev["sstatus"])
683

    
684
  if dev["children"]:
685
    for child in dev["children"]:
686
      _FormatBlockDevInfo(buf, child, indent_level+1)
687

    
688

    
689
def ShowInstanceConfig(opts, args):
690
  """Compute instance run-time status.
691

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

    
699
  pvm_parameters = ("kernel_path", "initrd_path")
700

    
701
  if not result:
702
    logger.ToStdout("No instances.")
703
    return 1
704

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

    
752
    for device in instance["disks"]:
753
      _FormatBlockDevInfo(buf, device, 1)
754

    
755
  logger.ToStdout(buf.getvalue().rstrip('\n'))
756
  return retcode
757

    
758

    
759
def SetInstanceParams(opts, args):
760
  """Modifies an instance.
761

    
762
  All parameters take effect only at the next restart of the instance.
763

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

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

    
780
  kernel_path = _TransformPath(opts.kernel_path)
781
  initrd_path = _TransformPath(opts.initrd_path)
782
  if opts.hvm_boot_order == 'default':
783
    hvm_boot_order = constants.VALUE_DEFAULT
784
  else:
785
    hvm_boot_order = opts.hvm_boot_order
786

    
787
  if opts.hvm_acpi is None:
788
    hvm_acpi = opts.hvm_acpi
789
  else:
790
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
791

    
792
  if opts.hvm_pae is None:
793
    hvm_pae = opts.hvm_pae
794
  else:
795
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
796

    
797
  if opts.hvm_nic_type == constants.VALUE_NONE:
798
    hvm_nic_type = None
799
  else:
800
    hvm_nic_type = opts.hvm_nic_type
801

    
802
  if opts.hvm_disk_type == constants.VALUE_NONE:
803
    hvm_disk_type = None
804
  else:
805
    hvm_disk_type = opts.hvm_disk_type
806

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

    
821
  # even if here we process the result, we allow submit only
822
  result = SubmitOrSend(op, opts)
823

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

    
832

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

    
837
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
838
                    metavar="<os>")
839

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

    
846
m_pri_node_opt = make_option("--primary", dest="multi_mode",
847
                             help="Filter by nodes (primary only)",
848
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
849

    
850
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
851
                             help="Filter by nodes (secondary only)",
852
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
853

    
854
m_node_opt = make_option("--node", dest="multi_mode",
855
                         help="Filter by nodes (primary and secondary)",
856
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
857

    
858
m_clust_opt = make_option("--all", dest="multi_mode",
859
                          help="Select all instances in the cluster",
860
                          const=_SHUTDOWN_CLUSTER, action="store_const")
861

    
862
m_inst_opt = make_option("--instance", dest="multi_mode",
863
                         help="Filter by instance name [default]",
864
                         const=_SHUTDOWN_INSTANCES, action="store_const")
865

    
866

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

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

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

    
1135
aliases = {
1136
  'activate_block_devs': 'activate-disks',
1137
  'replace_disks': 'replace-disks',
1138
  'start': 'startup',
1139
  'stop': 'shutdown',
1140
  }
1141

    
1142
if __name__ == '__main__':
1143
  sys.exit(GenericMain(commands, aliases=aliases,
1144
                       override={"tag_type": constants.TAG_INSTANCE}))