Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 746ea1da

History | View | Annotate | Download (43.4 kB)

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

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

    
21

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

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

    
35

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

    
42

    
43
_VALUE_TRUE = "true"
44

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

    
49

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

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

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

    
68
  """
69
  if mode == _SHUTDOWN_CLUSTER:
70
    if names:
71
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
72
    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
      }
201
  else:
202
    headers = None
203

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

    
209
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
210

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

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

    
244
  for line in data:
245
    logger.ToStdout(line)
246

    
247
  return 0
248

    
249

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

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

    
262
  """
263
  instance = args[0]
264

    
265
  (pnode, snode) = SplitNodeOption(opts.node)
266

    
267
  kernel_path = _TransformPath(opts.kernel_path)
268
  initrd_path = _TransformPath(opts.initrd_path)
269

    
270
  hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
271
  hvm_pae = opts.hvm_pae == _VALUE_TRUE
272

    
273
  if ((opts.hvm_cdrom_image_path is not None) and
274
      (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
275
    hvm_cdrom_image_path = None
276
  else:
277
    hvm_cdrom_image_path = opts.hvm_cdrom_image_path
278

    
279
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
280
                                disk_size=opts.size, swap_size=opts.swap,
281
                                disk_template=opts.disk_template,
282
                                mode=constants.INSTANCE_CREATE,
283
                                os_type=opts.os, pnode=pnode,
284
                                snode=snode, vcpus=opts.vcpus,
285
                                ip=opts.ip, bridge=opts.bridge,
286
                                start=opts.start, ip_check=opts.ip_check,
287
                                wait_for_sync=opts.wait_for_sync,
288
                                mac=opts.mac,
289
                                kernel_path=kernel_path,
290
                                initrd_path=initrd_path,
291
                                iallocator=opts.iallocator,
292
                                hvm_boot_order=opts.hvm_boot_order,
293
                                file_storage_dir=opts.file_storage_dir,
294
                                file_driver=opts.file_driver,
295
                                hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
296
                                hvm_cdrom_image_path=hvm_cdrom_image_path,
297
                                vnc_bind_address=opts.vnc_bind_address,
298
                                hvm_nic_type=opts.hvm_nic_type,
299
                                hvm_disk_type=opts.hvm_disk_type)
300

    
301
  SubmitOrSend(op, opts)
302
  return 0
303

    
304

    
305
def ReinstallInstance(opts, args):
306
  """Reinstall an instance.
307

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

    
312
  """
313
  instance_name = args[0]
314

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

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

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

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

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

    
339
    os = selected
340
  else:
341
    os = opts.os
342

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

    
349
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
350
                                   os_type=os)
351
  SubmitOrSend(op, opts)
352

    
353
  return 0
354

    
355

    
356
def RemoveInstance(opts, args):
357
  """Remove an instance.
358

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

    
363
  """
364
  instance_name = args[0]
365
  force = opts.force
366

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

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

    
379

    
380
def RenameInstance(opts, args):
381
  """Rename an instance.
382

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

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

    
394

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

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

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

    
411

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

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

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

    
424

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

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

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

    
439

    
440
def StartupInstance(opts, args):
441
  """Startup an instance.
442

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

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

    
470

    
471
def RebootInstance(opts, args):
472
  """Reboot an instance
473

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

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

    
493
    SubmitOrSend(op, opts)
494
  return 0
495

    
496

    
497
def ShutdownInstance(opts, args):
498
  """Shutdown an instance.
499

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

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

    
525

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

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

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

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

    
558

    
559
def FailoverInstance(opts, args):
560
  """Failover an instance.
561

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

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

    
571
  """
572
  instance_name = args[0]
573
  force = opts.force
574

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

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

    
587

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

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

    
595
  """
596
  instance_name = args[0]
597

    
598
  op = opcodes.OpConnectConsole(instance_name=instance_name)
599
  cmd = SubmitOpCode(op)
600

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

    
611

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

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

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

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

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

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

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

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

    
683

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

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

    
694
  pvm_parameters = ("kernel_path", "initrd_path")
695

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

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

    
746
    for device in instance["disks"]:
747
      _FormatBlockDevInfo(buf, device, 1)
748

    
749
  logger.ToStdout(buf.getvalue().rstrip('\n'))
750
  return retcode
751

    
752

    
753
def SetInstanceParams(opts, args):
754
  """Modifies an instance.
755

    
756
  All parameters take effect only at the next restart of the instance.
757

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

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

    
774
  kernel_path = _TransformPath(opts.kernel_path)
775
  initrd_path = _TransformPath(opts.initrd_path)
776
  if opts.hvm_boot_order == 'default':
777
    hvm_boot_order = constants.VALUE_DEFAULT
778
  else:
779
    hvm_boot_order = opts.hvm_boot_order
780

    
781
  if opts.hvm_acpi is None:
782
    hvm_acpi = opts.hvm_acpi
783
  else:
784
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
785

    
786
  if opts.hvm_pae is None:
787
    hvm_pae = opts.hvm_pae
788
  else:
789
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
790

    
791
  if opts.hvm_nic_type == constants.VALUE_NONE:
792
    hvm_nic_type = None
793
  else:
794
    hvm_nic_type = opts.hvm_nic_type
795

    
796
  if opts.hvm_disk_type == constants.VALUE_NONE:
797
    hvm_disk_type = None
798
  else:
799
    hvm_disk_type = opts.hvm_disk_type
800

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

    
815
  # even if here we process the result, we allow submit only
816
  result = SubmitOrSend(op, opts)
817

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

    
826

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

    
831
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
832
                    metavar="<os>")
833

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

    
840
m_pri_node_opt = make_option("--primary", dest="multi_mode",
841
                             help="Filter by nodes (primary only)",
842
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
843

    
844
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
845
                             help="Filter by nodes (secondary only)",
846
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
847

    
848
m_node_opt = make_option("--node", dest="multi_mode",
849
                         help="Filter by nodes (primary and secondary)",
850
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
851

    
852
m_clust_opt = make_option("--all", dest="multi_mode",
853
                          help="Select all instances in the cluster",
854
                          const=_SHUTDOWN_CLUSTER, action="store_const")
855

    
856
m_inst_opt = make_option("--instance", dest="multi_mode",
857
                         help="Filter by instance name [default]",
858
                         const=_SHUTDOWN_INSTANCES, action="store_const")
859

    
860

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

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

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

    
1145
aliases = {
1146
  'activate_block_devs': 'activate-disks',
1147
  'replace_disks': 'replace-disks',
1148
  'start': 'startup',
1149
  'stop': 'shutdown',
1150
  }
1151

    
1152
if __name__ == '__main__':
1153
  sys.exit(GenericMain(commands, aliases=aliases,
1154
                       override={"tag_type": constants.TAG_INSTANCE}))