Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 6340bb0a

History | View | Annotate | Download (43.2 kB)

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

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

    
21

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

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

    
35

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

    
42

    
43
_VALUE_TRUE = "true"
44

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

    
49

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

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

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

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

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

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

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

    
107
  return inames
108

    
109

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

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

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

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

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

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

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

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

    
144

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

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

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

    
165
  return result_path
166

    
167

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

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

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

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

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

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

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

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

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

    
248
  return 0
249

    
250

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

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

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

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

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

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

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

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

    
302
  SubmitOrSend(op, opts)
303
  return 0
304

    
305

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

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

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

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

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

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

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

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

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

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

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

    
354
  return 0
355

    
356

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

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

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

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

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

    
380

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

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

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

    
395

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

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

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

    
412

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

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

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

    
425

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

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

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

    
440

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

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

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

    
467

    
468
def RebootInstance(opts, args):
469
  """Reboot an instance
470

    
471
  Args:
472
    opts - class with options as members
473
    args - list containing a single element, the instance name
474

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

    
490
    SubmitOrSend(op, opts)
491
  return 0
492

    
493

    
494
def ShutdownInstance(opts, args):
495
  """Shutdown an instance.
496

    
497
  Args:
498
    opts - class with options as members
499
    args - list containing a single element, the instance name
500

    
501
  """
502
  if opts.multi_mode is None:
503
    opts.multi_mode = _SHUTDOWN_INSTANCES
504
  inames = _ExpandMultiNames(opts.multi_mode, args)
505
  if not inames:
506
    raise errors.OpPrereqError("Selection filter does not match any instances")
507
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
508
  if not (opts.force_multi or not multi_on
509
          or _ConfirmOperation(inames, "shutdown")):
510
    return 1
511
  for name in inames:
512
    op = opcodes.OpShutdownInstance(instance_name=name)
513
    if multi_on:
514
      logger.ToStdout("Shutting down %s" % name)
515
    SubmitOpCode(op)
516
  return 0
517

    
518

    
519
def ReplaceDisks(opts, args):
520
  """Replace the disks of an instance
521

    
522
  Args:
523
    opts - class with options as members
524
    args - list with a single element, the instance name
525

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

    
545
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
546
                              remote_node=new_2ndary, mode=mode,
547
                              iallocator=iallocator)
548
  SubmitOrSend(op, opts)
549
  return 0
550

    
551

    
552
def FailoverInstance(opts, args):
553
  """Failover an instance.
554

    
555
  The failover is done by shutting it down on its present node and
556
  starting it on the secondary.
557

    
558
  Args:
559
    opts - class with options as members
560
    args - list with a single element, the instance name
561
  Opts used:
562
    force - whether to failover without asking questions.
563

    
564
  """
565
  instance_name = args[0]
566
  force = opts.force
567

    
568
  if not force:
569
    usertext = ("Failover will happen to image %s."
570
                " This requires a shutdown of the instance. Continue?" %
571
                (instance_name,))
572
    if not AskUser(usertext):
573
      return 1
574

    
575
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
576
                                  ignore_consistency=opts.ignore_consistency)
577
  SubmitOrSend(op, opts)
578
  return 0
579

    
580

    
581
def ConnectToInstanceConsole(opts, args):
582
  """Connect to the console of an instance.
583

    
584
  Args:
585
    opts - class with options as members
586
    args - list with a single element, the instance name
587

    
588
  """
589
  instance_name = args[0]
590

    
591
  op = opcodes.OpConnectConsole(instance_name=instance_name)
592
  cmd = SubmitOpCode(op)
593

    
594
  if opts.show_command:
595
    print utils.ShellQuoteArgs(cmd)
596
  else:
597
    try:
598
      os.execvp(cmd[0], cmd)
599
    finally:
600
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
601
                       (cmd, " ".join(argv)))
602
      os._exit(1)
603

    
604

    
605
def _FormatBlockDevInfo(buf, dev, indent_level):
606
  """Show block device information.
607

    
608
  This is only used by ShowInstanceConfig(), but it's too big to be
609
  left for an inline definition.
610

    
611
  """
612
  def helper(buf, dtype, status):
613
    """Format one line for physical device status."""
614
    if not status:
615
      buf.write("not active\n")
616
    else:
617
      (path, major, minor, syncp, estt, degr, ldisk) = status
618
      if major is None:
619
        major_string = "N/A"
620
      else:
621
        major_string = str(major)
622

    
623
      if minor is None:
624
        minor_string = "N/A"
625
      else:
626
        minor_string = str(minor)
627

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

    
655
  if dev["iv_name"] is not None:
656
    data = "  - %s, " % dev["iv_name"]
657
  else:
658
    data = "  - "
659
  data += "type: %s" % dev["dev_type"]
660
  if dev["logical_id"] is not None:
661
    data += ", logical_id: %s" % (dev["logical_id"],)
662
  elif dev["physical_id"] is not None:
663
    data += ", physical_id: %s" % (dev["physical_id"],)
664
  buf.write("%*s%s\n" % (2*indent_level, "", data))
665
  buf.write("%*s    primary:   " % (2*indent_level, ""))
666
  helper(buf, dev["dev_type"], dev["pstatus"])
667

    
668
  if dev["sstatus"]:
669
    buf.write("%*s    secondary: " % (2*indent_level, ""))
670
    helper(buf, dev["dev_type"], dev["sstatus"])
671

    
672
  if dev["children"]:
673
    for child in dev["children"]:
674
      _FormatBlockDevInfo(buf, child, indent_level+1)
675

    
676

    
677
def ShowInstanceConfig(opts, args):
678
  """Compute instance run-time status.
679

    
680
  """
681
  retcode = 0
682
  op = opcodes.OpQueryInstanceData(instances=args)
683
  result = SubmitOpCode(op)
684
  hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
685
                    "hvm_boot_order", "hvm_nic_type", "hvm_disk_type")
686

    
687
  pvm_parameters = ("kernel_path", "initrd_path")
688

    
689
  if not result:
690
    logger.ToStdout("No instances.")
691
    return 1
692

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

    
739
    for device in instance["disks"]:
740
      _FormatBlockDevInfo(buf, device, 1)
741

    
742
  logger.ToStdout(buf.getvalue().rstrip('\n'))
743
  return retcode
744

    
745

    
746
def SetInstanceParams(opts, args):
747
  """Modifies an instance.
748

    
749
  All parameters take effect only at the next restart of the instance.
750

    
751
  Args:
752
    opts - class with options as members
753
    args - list with a single element, the instance name
754
  Opts used:
755
    memory - the new memory size
756
    vcpus - the new number of cpus
757
    mac - the new MAC address of the instance
758

    
759
  """
760
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
761
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
762
          opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
763
          opts.vnc_bind_address or opts.hvm_nic_type or opts.hvm_disk_type):
764
    logger.ToStdout("Please give at least one of the parameters.")
765
    return 1
766

    
767
  kernel_path = _TransformPath(opts.kernel_path)
768
  initrd_path = _TransformPath(opts.initrd_path)
769
  if opts.hvm_boot_order == 'default':
770
    hvm_boot_order = constants.VALUE_DEFAULT
771
  else:
772
    hvm_boot_order = opts.hvm_boot_order
773

    
774
  if opts.hvm_acpi is None:
775
    hvm_acpi = opts.hvm_acpi
776
  else:
777
    hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
778

    
779
  if opts.hvm_pae is None:
780
    hvm_pae = opts.hvm_pae
781
  else:
782
    hvm_pae = opts.hvm_pae == _VALUE_TRUE
783

    
784
  if opts.hvm_nic_type == constants.VALUE_NONE:
785
    hvm_nic_type = None
786
  else:
787
    hvm_nic_type = opts.hvm_nic_type
788

    
789
  if opts.hvm_disk_type == constants.VALUE_NONE:
790
    hvm_disk_type = None
791
  else:
792
    hvm_disk_type = opts.hvm_disk_type
793

    
794
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
795
                                   vcpus=opts.vcpus, ip=opts.ip,
796
                                   bridge=opts.bridge, mac=opts.mac,
797
                                   kernel_path=opts.kernel_path,
798
                                   initrd_path=opts.initrd_path,
799
                                   hvm_boot_order=hvm_boot_order,
800
                                   hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
801
                                   hvm_cdrom_image_path=
802
                                   opts.hvm_cdrom_image_path,
803
                                   vnc_bind_address=opts.vnc_bind_address,
804
                                   hvm_nic_type=hvm_nic_type,
805
                                   hvm_disk_type=hvm_disk_type,
806
                                   force=opts.force)
807

    
808
  # even if here we process the result, we allow submit only
809
  result = SubmitOrSend(op, opts)
810

    
811
  if result:
812
    logger.ToStdout("Modified instance %s" % args[0])
813
    for param, data in result:
814
      logger.ToStdout(" - %-5s -> %s" % (param, data))
815
    logger.ToStdout("Please don't forget that these parameters take effect"
816
                    " only at the next start of the instance.")
817
  return 0
818

    
819

    
820
# options used in more than one cmd
821
node_opt = make_option("-n", "--node", dest="node", help="Target node",
822
                       metavar="<node>")
823

    
824
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
825
                    metavar="<os>")
826

    
827
# multi-instance selection options
828
m_force_multi = make_option("--force-multiple", dest="force_multi",
829
                            help="Do not ask for confirmation when more than"
830
                            " one instance is affected",
831
                            action="store_true", default=False)
832

    
833
m_pri_node_opt = make_option("--primary", dest="multi_mode",
834
                             help="Filter by nodes (primary only)",
835
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
836

    
837
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
838
                             help="Filter by nodes (secondary only)",
839
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
840

    
841
m_node_opt = make_option("--node", dest="multi_mode",
842
                         help="Filter by nodes (primary and secondary)",
843
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
844

    
845
m_clust_opt = make_option("--all", dest="multi_mode",
846
                          help="Select all instances in the cluster",
847
                          const=_SHUTDOWN_CLUSTER, action="store_const")
848

    
849
m_inst_opt = make_option("--instance", dest="multi_mode",
850
                         help="Filter by instance name [default]",
851
                         const=_SHUTDOWN_INSTANCES, action="store_const")
852

    
853

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

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

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

    
1138
aliases = {
1139
  'activate_block_devs': 'activate-disks',
1140
  'replace_disks': 'replace-disks',
1141
  'start': 'startup',
1142
  'stop': 'shutdown',
1143
  }
1144

    
1145
if __name__ == '__main__':
1146
  sys.exit(GenericMain(commands, aliases=aliases,
1147
                       override={"tag_type": constants.TAG_INSTANCE}))