Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ b7329c9c

History | View | Annotate | Download (43.1 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
    SubmitOpCode(op)
464
  return 0
465

    
466

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

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

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

    
489
    SubmitOrSend(op, opts)
490
  return 0
491

    
492

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

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

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

    
517

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

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

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

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

    
550

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

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

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

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

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

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

    
579

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

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

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

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

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

    
603

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

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

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

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

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

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

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

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

    
675

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

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

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

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

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

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

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

    
744

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

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

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

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

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

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

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

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

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

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

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

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

    
818

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

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

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

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

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

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

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

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

    
852

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

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

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

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

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