Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 38d7239a

History | View | Annotate | Download (43.5 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
      "serial_no": "SerialNo",
201
      }
202
  else:
203
    headers = None
204

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

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

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

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

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

    
249
  return 0
250

    
251

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

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

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

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

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

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

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

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

    
303
  SubmitOrSend(op, opts)
304
  return 0
305

    
306

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

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

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

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

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

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

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

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

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

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

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

    
355
  return 0
356

    
357

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

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

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

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

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

    
381

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

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

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

    
396

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

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

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

    
413

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

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

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

    
426

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

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

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

    
441

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

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

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

    
472

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

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

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

    
495
    SubmitOrSend(op, opts)
496
  return 0
497

    
498

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

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

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

    
527

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

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

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

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

    
560

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

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

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

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

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

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

    
589

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

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

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

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

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

    
613

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

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

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

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

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

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

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

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

    
685

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

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

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

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

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

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

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

    
754

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

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

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

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

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

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

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

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

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

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

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

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

    
828

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

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

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

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

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

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

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

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

    
862

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

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

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

    
1148
aliases = {
1149
  'activate_block_devs': 'activate-disks',
1150
  'replace_disks': 'replace-disks',
1151
  'start': 'startup',
1152
  'stop': 'shutdown',
1153
  }
1154

    
1155
if __name__ == '__main__':
1156
  sys.exit(GenericMain(commands, aliases=aliases,
1157
                       override={"tag_type": constants.TAG_INSTANCE}))