Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ b6e82a65

History | View | Annotate | Download (34.6 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
def _ExpandMultiNames(mode, names):
44
  """Expand the given names using the passed mode.
45

    
46
  Args:
47
    - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
48
      _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
49
    - names, which is a list of names; for cluster, it must be empty,
50
      and for node and instance it must be a list of valid item
51
      names (short names are valid as usual, e.g. node1 instead of
52
      node1.example.com)
53

    
54
  For _SHUTDOWN_CLUSTER, all instances will be returned. For
55
  _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
56
  primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
57
  instances having those nodes as either primary or secondary will be
58
  returned. For _SHUTDOWN_INSTANCES, the given instances will be
59
  returned.
60

    
61
  """
62
  if mode == _SHUTDOWN_CLUSTER:
63
    if names:
64
      raise errors.OpPrereqError("Cluster filter mode takes no arguments")
65
    op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
66
    idata = SubmitOpCode(op)
67
    inames = [row[0] for row in idata]
68

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

    
90
  elif mode == _SHUTDOWN_INSTANCES:
91
    if not names:
92
      raise errors.OpPrereqError("No instance names passed")
93
    op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
94
    idata = SubmitOpCode(op)
95
    inames = [row[0] for row in idata]
96

    
97
  else:
98
    raise errors.OpPrereqError("Unknown mode '%s'" % mode)
99

    
100
  return inames
101

    
102

    
103
def _ConfirmOperation(inames, text):
104
  """Ask the user to confirm an operation on a list of instances.
105

    
106
  This function is used to request confirmation for doing an operation
107
  on a given list of instances.
108

    
109
  The inames argument is what the selection algorithm computed, and
110
  the text argument is the operation we should tell the user to
111
  confirm (e.g. 'shutdown' or 'startup').
112

    
113
  Returns: boolean depending on user's confirmation.
114

    
115
  """
116
  count = len(inames)
117
  msg = ("The %s will operate on %d instances.\n"
118
         "Do you want to continue?" % (text, count))
119
  affected = ("\nAffected instances:\n" +
120
              "\n".join(["  %s" % name for name in inames]))
121

    
122
  choices = [('y', True, 'Yes, execute the %s' % text),
123
             ('n', False, 'No, abort the %s' % text)]
124

    
125
  if count > 20:
126
    choices.insert(1, ('v', 'v', 'View the list of affected instances'))
127
    ask = msg
128
  else:
129
    ask = msg + affected
130

    
131
  choice = AskUser(ask, choices)
132
  if choice == 'v':
133
    choices.pop(1)
134
    choice = AskUser(msg + affected, choices)
135
  return choice
136

    
137

    
138
def _TransformPath(user_input):
139
  """Transform a user path into a canonical value.
140

    
141
  This function transforms the a path passed as textual information
142
  into the constants that the LU code expects.
143

    
144
  """
145
  if user_input:
146
    if user_input.lower() == "default":
147
      result_path = constants.VALUE_DEFAULT
148
    elif user_input.lower() == "none":
149
      result_path = constants.VALUE_NONE
150
    else:
151
      if not os.path.isabs(user_input):
152
        raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
153
                                   user_input)
154
      result_path = user_input
155
  else:
156
    result_path = constants.VALUE_DEFAULT
157

    
158
  return result_path
159

    
160

    
161
def ListInstances(opts, args):
162
  """List instances and their properties.
163

    
164
  """
165
  if opts.output is None:
166
    selected_fields = ["name", "os", "pnode", "status", "oper_ram"]
167
  else:
168
    selected_fields = opts.output.split(",")
169

    
170
  op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
171
  output = SubmitOpCode(op)
172

    
173
  if not opts.no_headers:
174
    headers = {
175
      "name": "Instance", "os": "OS", "pnode": "Primary_node",
176
      "snodes": "Secondary_Nodes", "admin_state": "Autostart",
177
      "oper_state": "Running", "admin_ram": "Configured_memory",
178
      "oper_ram": "Memory", "disk_template": "Disk_template",
179
      "ip": "IP Address", "mac": "MAC Address",
180
      "bridge": "Bridge", "vcpus": "VCPUs",
181
      "sda_size": "Disk/0", "sdb_size": "Disk/1",
182
      "status": "Status",
183
      }
184
  else:
185
    headers = None
186

    
187
  if opts.human_readable:
188
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
189
  else:
190
    unitfields = None
191

    
192
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
193

    
194
  # change raw values to nicer strings
195
  for row in output:
196
    for idx, field in enumerate(selected_fields):
197
      val = row[idx]
198
      if field == "snodes":
199
        val = ",".join(val) or "-"
200
      elif field == "admin_state":
201
        if val:
202
          val = "yes"
203
        else:
204
          val = "no"
205
      elif field == "oper_state":
206
        if val is None:
207
          val = "(node down)"
208
        elif val: # True
209
          val = "running"
210
        else:
211
          val = "stopped"
212
      elif field == "oper_ram":
213
        if val is None:
214
          val = "(node down)"
215
      elif field == "sda_size" or field == "sdb_size":
216
        if val is None:
217
          val = "N/A"
218
      row[idx] = str(val)
219

    
220
  data = GenerateTable(separator=opts.separator, headers=headers,
221
                       fields=selected_fields, unitfields=unitfields,
222
                       numfields=numfields, data=output)
223

    
224
  for line in data:
225
    logger.ToStdout(line)
226

    
227
  return 0
228

    
229

    
230
def AddInstance(opts, args):
231
  """Add an instance to the cluster.
232

    
233
  Args:
234
    opts - class with options as members
235
    args - list with a single element, the instance name
236
  Opts used:
237
    mem - amount of memory to allocate to instance (MiB)
238
    size - amount of disk space to allocate to instance (MiB)
239
    os - which OS to run on instance
240
    node - node to run new instance on
241

    
242
  """
243
  instance = args[0]
244

    
245
  (pnode, snode) = SplitNodeOption(opts.node)
246

    
247
  kernel_path = _TransformPath(opts.kernel_path)
248
  initrd_path = _TransformPath(opts.initrd_path)
249

    
250
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
251
                                disk_size=opts.size, swap_size=opts.swap,
252
                                disk_template=opts.disk_template,
253
                                mode=constants.INSTANCE_CREATE,
254
                                os_type=opts.os, pnode=pnode,
255
                                snode=snode, vcpus=opts.vcpus,
256
                                ip=opts.ip, bridge=opts.bridge,
257
                                start=opts.start, ip_check=opts.ip_check,
258
                                wait_for_sync=opts.wait_for_sync,
259
                                mac=opts.mac,
260
                                kernel_path=kernel_path,
261
                                initrd_path=initrd_path,
262
                                hvm_boot_order=opts.hvm_boot_order,
263
                                file_storage_dir=opts.file_storage_dir,
264
                                file_driver=opts.file_driver,
265
                                iallocator=opts.iallocator)
266
  SubmitOpCode(op)
267
  return 0
268

    
269

    
270
def ReinstallInstance(opts, args):
271
  """Reinstall an instance.
272

    
273
  Args:
274
    opts - class with options as members
275
    args - list containing a single element, the instance name
276

    
277
  """
278
  instance_name = args[0]
279

    
280
  if not opts.force:
281
    usertext = ("This will reinstall the instance %s and remove"
282
                " all data. Continue?") % instance_name
283
    if not AskUser(usertext):
284
      return 1
285

    
286
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
287
                                   os_type=opts.os)
288
  SubmitOpCode(op)
289

    
290
  return 0
291

    
292

    
293
def RemoveInstance(opts, args):
294
  """Remove an instance.
295

    
296
  Args:
297
    opts - class with options as members
298
    args - list containing a single element, the instance name
299

    
300
  """
301
  instance_name = args[0]
302
  force = opts.force
303

    
304
  if not force:
305
    usertext = ("This will remove the volumes of the instance %s"
306
                " (including mirrors), thus removing all the data"
307
                " of the instance. Continue?") % instance_name
308
    if not AskUser(usertext):
309
      return 1
310

    
311
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
312
                                ignore_failures=opts.ignore_failures)
313
  SubmitOpCode(op)
314
  return 0
315

    
316

    
317
def RenameInstance(opts, args):
318
  """Rename an instance.
319

    
320
  Args:
321
    opts - class with options as members
322
    args - list containing two elements, the instance name and the new name
323

    
324
  """
325
  op = opcodes.OpRenameInstance(instance_name=args[0],
326
                                new_name=args[1],
327
                                ignore_ip=opts.ignore_ip)
328
  SubmitOpCode(op)
329

    
330
  return 0
331

    
332

    
333
def ActivateDisks(opts, args):
334
  """Activate an instance's disks.
335

    
336
  This serves two purposes:
337
    - it allows one (as long as the instance is not running) to mount
338
    the disks and modify them from the node
339
    - it repairs inactive secondary drbds
340

    
341
  """
342
  instance_name = args[0]
343
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
344
  disks_info = SubmitOpCode(op)
345
  for host, iname, nname in disks_info:
346
    print "%s:%s:%s" % (host, iname, nname)
347
  return 0
348

    
349

    
350
def DeactivateDisks(opts, args):
351
  """Command-line interface for _ShutdownInstanceBlockDevices.
352

    
353
  This function takes the instance name, looks for its primary node
354
  and the tries to shutdown its block devices on that node.
355

    
356
  """
357
  instance_name = args[0]
358
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
359
  SubmitOpCode(op)
360
  return 0
361

    
362

    
363
def StartupInstance(opts, args):
364
  """Startup an instance.
365

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

    
370
  """
371
  if opts.multi_mode is None:
372
    opts.multi_mode = _SHUTDOWN_INSTANCES
373
  inames = _ExpandMultiNames(opts.multi_mode, args)
374
  if not inames:
375
    raise errors.OpPrereqError("Selection filter does not match any instances")
376
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
377
  if not (opts.force_multi or not multi_on
378
          or _ConfirmOperation(inames, "startup")):
379
    return 1
380
  for name in inames:
381
    op = opcodes.OpStartupInstance(instance_name=name,
382
                                   force=opts.force,
383
                                   extra_args=opts.extra_args)
384
    if multi_on:
385
      logger.ToStdout("Starting up %s" % name)
386
    SubmitOpCode(op)
387
  return 0
388

    
389

    
390
def RebootInstance(opts, args):
391
  """Reboot an instance
392

    
393
  Args:
394
    opts - class with options as members
395
    args - list containing a single element, the instance name
396

    
397
  """
398
  if opts.multi_mode is None:
399
    opts.multi_mode = _SHUTDOWN_INSTANCES
400
  inames = _ExpandMultiNames(opts.multi_mode, args)
401
  if not inames:
402
    raise errors.OpPrereqError("Selection filter does not match any instances")
403
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
404
  if not (opts.force_multi or not multi_on
405
          or _ConfirmOperation(inames, "reboot")):
406
    return 1
407
  for name in inames:
408
    op = opcodes.OpRebootInstance(instance_name=name,
409
                                  reboot_type=opts.reboot_type,
410
                                  ignore_secondaries=opts.ignore_secondaries)
411

    
412
    SubmitOpCode(op)
413
  return 0
414

    
415

    
416
def ShutdownInstance(opts, args):
417
  """Shutdown an instance.
418

    
419
  Args:
420
    opts - class with options as members
421
    args - list containing a single element, the instance name
422

    
423
  """
424
  if opts.multi_mode is None:
425
    opts.multi_mode = _SHUTDOWN_INSTANCES
426
  inames = _ExpandMultiNames(opts.multi_mode, args)
427
  if not inames:
428
    raise errors.OpPrereqError("Selection filter does not match any instances")
429
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
430
  if not (opts.force_multi or not multi_on
431
          or _ConfirmOperation(inames, "shutdown")):
432
    return 1
433
  for name in inames:
434
    op = opcodes.OpShutdownInstance(instance_name=name)
435
    if multi_on:
436
      logger.ToStdout("Shutting down %s" % name)
437
    SubmitOpCode(op)
438
  return 0
439

    
440

    
441
def ReplaceDisks(opts, args):
442
  """Replace the disks of an instance
443

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

    
448
  """
449
  instance_name = args[0]
450
  new_2ndary = opts.new_secondary
451
  iallocator = opts.iallocator
452
  if opts.disks is None:
453
    disks = ["sda", "sdb"]
454
  else:
455
    disks = opts.disks.split(",")
456
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
457
    mode = constants.REPLACE_DISK_ALL
458
  elif opts.on_primary: # only on primary:
459
    mode = constants.REPLACE_DISK_PRI
460
    if new_2ndary is not None or iallocator is not None:
461
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
462
                                 " replacement")
463
  elif opts.on_secondary is not None or iallocator is not None:
464
    # only on secondary
465
    mode = constants.REPLACE_DISK_SEC
466

    
467
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
468
                              remote_node=new_2ndary, mode=mode,
469
                              iallocator=iallocator)
470
  SubmitOpCode(op)
471
  return 0
472

    
473

    
474
def FailoverInstance(opts, args):
475
  """Failover an instance.
476

    
477
  The failover is done by shutting it down on its present node and
478
  starting it on the secondary.
479

    
480
  Args:
481
    opts - class with options as members
482
    args - list with a single element, the instance name
483
  Opts used:
484
    force - whether to failover without asking questions.
485

    
486
  """
487
  instance_name = args[0]
488
  force = opts.force
489

    
490
  if not force:
491
    usertext = ("Failover will happen to image %s."
492
                " This requires a shutdown of the instance. Continue?" %
493
                (instance_name,))
494
    if not AskUser(usertext):
495
      return 1
496

    
497
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
498
                                  ignore_consistency=opts.ignore_consistency)
499
  SubmitOpCode(op)
500
  return 0
501

    
502

    
503
def ConnectToInstanceConsole(opts, args):
504
  """Connect to the console of an instance.
505

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

    
510
  """
511
  instance_name = args[0]
512

    
513
  op = opcodes.OpConnectConsole(instance_name=instance_name)
514
  cmd = SubmitOpCode(op)
515

    
516
  if opts.show_command:
517
    print utils.ShellQuoteArgs(cmd)
518
  else:
519
    # drop lock and exec so other commands can run while we have console
520
    utils.Unlock("cmd")
521
    try:
522
      os.execvp(cmd[0], cmd)
523
    finally:
524
      sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
525
                       (cmd, " ".join(argv)))
526
      os._exit(1)
527

    
528

    
529
def _FormatBlockDevInfo(buf, dev, indent_level):
530
  """Show block device information.
531

    
532
  This is only used by ShowInstanceConfig(), but it's too big to be
533
  left for an inline definition.
534

    
535
  """
536
  def helper(buf, dtype, status):
537
    """Format one line for physical device status."""
538
    if not status:
539
      buf.write("not active\n")
540
    else:
541
      (path, major, minor, syncp, estt, degr, ldisk) = status
542
      buf.write("%s (%d:%d)" % (path, major, minor))
543
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
544
        if syncp is not None:
545
          sync_text = "*RECOVERING* %5.2f%%," % syncp
546
          if estt:
547
            sync_text += " ETA %ds" % estt
548
          else:
549
            sync_text += " ETA unknown"
550
        else:
551
          sync_text = "in sync"
552
        if degr:
553
          degr_text = "*DEGRADED*"
554
        else:
555
          degr_text = "ok"
556
        if ldisk:
557
          ldisk_text = " *MISSING DISK*"
558
        else:
559
          ldisk_text = ""
560
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
561
      elif dtype == constants.LD_LV:
562
        if ldisk:
563
          ldisk_text = " *FAILED* (failed drive?)"
564
        else:
565
          ldisk_text = ""
566
        buf.write(ldisk_text)
567
      buf.write("\n")
568

    
569
  if dev["iv_name"] is not None:
570
    data = "  - %s, " % dev["iv_name"]
571
  else:
572
    data = "  - "
573
  data += "type: %s" % dev["dev_type"]
574
  if dev["logical_id"] is not None:
575
    data += ", logical_id: %s" % (dev["logical_id"],)
576
  elif dev["physical_id"] is not None:
577
    data += ", physical_id: %s" % (dev["physical_id"],)
578
  buf.write("%*s%s\n" % (2*indent_level, "", data))
579
  buf.write("%*s    primary:   " % (2*indent_level, ""))
580
  helper(buf, dev["dev_type"], dev["pstatus"])
581

    
582
  if dev["sstatus"]:
583
    buf.write("%*s    secondary: " % (2*indent_level, ""))
584
    helper(buf, dev["dev_type"], dev["sstatus"])
585

    
586
  if dev["children"]:
587
    for child in dev["children"]:
588
      _FormatBlockDevInfo(buf, child, indent_level+1)
589

    
590

    
591
def ShowInstanceConfig(opts, args):
592
  """Compute instance run-time status.
593

    
594
  """
595
  retcode = 0
596
  op = opcodes.OpQueryInstanceData(instances=args)
597
  result = SubmitOpCode(op)
598

    
599
  if not result:
600
    logger.ToStdout("No instances.")
601
    return 1
602

    
603
  buf = StringIO()
604
  retcode = 0
605
  for instance_name in result:
606
    instance = result[instance_name]
607
    buf.write("Instance name: %s\n" % instance["name"])
608
    buf.write("State: configured to be %s, actual state is %s\n" %
609
              (instance["config_state"], instance["run_state"]))
610
    buf.write("  Nodes:\n")
611
    buf.write("    - primary: %s\n" % instance["pnode"])
612
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
613
    buf.write("  Operating system: %s\n" % instance["os"])
614
    buf.write("  Allocated network port: %s\n" % instance["network_port"])
615
    if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
616
      kpath = "(default: %s)" % constants.XEN_KERNEL
617
    else:
618
      kpath = instance["kernel_path"]
619
    buf.write("  Kernel path: %s\n" % kpath)
620
    if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
621
      initrd = "(default: %s)" % constants.XEN_INITRD
622
    elif instance["initrd_path"] == constants.VALUE_NONE:
623
      initrd = "(none)"
624
    else:
625
      initrd = instance["initrd_path"]
626
    buf.write("       initrd: %s\n" % initrd)
627
    buf.write("  HVM boot order: %s\n" % instance["hvm_boot_order"])
628
    buf.write("  Hardware:\n")
629
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
630
    buf.write("    - memory: %dMiB\n" % instance["memory"])
631
    buf.write("    - NICs: %s\n" %
632
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
633
                   (mac, ip, bridge)
634
                     for mac, ip, bridge in instance["nics"]]))
635
    buf.write("  Block devices:\n")
636

    
637
    for device in instance["disks"]:
638
      _FormatBlockDevInfo(buf, device, 1)
639

    
640
  logger.ToStdout(buf.getvalue().rstrip('\n'))
641
  return retcode
642

    
643

    
644
def SetInstanceParams(opts, args):
645
  """Modifies an instance.
646

    
647
  All parameters take effect only at the next restart of the instance.
648

    
649
  Args:
650
    opts - class with options as members
651
    args - list with a single element, the instance name
652
  Opts used:
653
    memory - the new memory size
654
    vcpus - the new number of cpus
655
    mac - the new MAC address of the instance
656

    
657
  """
658
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
659
          opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
660
    logger.ToStdout("Please give at least one of the parameters.")
661
    return 1
662

    
663
  kernel_path = _TransformPath(opts.kernel_path)
664
  initrd_path = _TransformPath(opts.initrd_path)
665
  if opts.hvm_boot_order == 'default':
666
    hvm_boot_order = constants.VALUE_DEFAULT
667
  else:
668
    hvm_boot_order = opts.hvm_boot_order
669

    
670
  op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
671
                                   vcpus=opts.vcpus, ip=opts.ip,
672
                                   bridge=opts.bridge, mac=opts.mac,
673
                                   kernel_path=opts.kernel_path,
674
                                   initrd_path=opts.initrd_path,
675
                                   hvm_boot_order=hvm_boot_order)
676
  result = SubmitOpCode(op)
677

    
678
  if result:
679
    logger.ToStdout("Modified instance %s" % args[0])
680
    for param, data in result:
681
      logger.ToStdout(" - %-5s -> %s" % (param, data))
682
    logger.ToStdout("Please don't forget that these parameters take effect"
683
                    " only at the next start of the instance.")
684
  return 0
685

    
686

    
687
# options used in more than one cmd
688
node_opt = make_option("-n", "--node", dest="node", help="Target node",
689
                       metavar="<node>")
690

    
691
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
692
                    metavar="<os>")
693

    
694
# multi-instance selection options
695
m_force_multi = make_option("--force-multiple", dest="force_multi",
696
                            help="Do not ask for confirmation when more than"
697
                            " one instance is affected",
698
                            action="store_true", default=False)
699

    
700
m_pri_node_opt = make_option("--primary", dest="multi_mode",
701
                             help="Filter by nodes (primary only)",
702
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
703

    
704
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
705
                             help="Filter by nodes (secondary only)",
706
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
707

    
708
m_node_opt = make_option("--node", dest="multi_mode",
709
                         help="Filter by nodes (primary and secondary)",
710
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
711

    
712
m_clust_opt = make_option("--all", dest="multi_mode",
713
                          help="Select all instances in the cluster",
714
                          const=_SHUTDOWN_CLUSTER, action="store_const")
715

    
716
m_inst_opt = make_option("--instance", dest="multi_mode",
717
                         help="Filter by instance name [default]",
718
                         const=_SHUTDOWN_INSTANCES, action="store_const")
719

    
720

    
721
# this is defined separately due to readability only
722
add_opts = [
723
  DEBUG_OPT,
724
  make_option("-n", "--node", dest="node",
725
              help="Target node and optional secondary node",
726
              metavar="<pnode>[:<snode>]"),
727
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
728
             " a suffix is used",
729
             default=20 * 1024, type="unit", metavar="<size>"),
730
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
731
             " suffix is used",
732
             default=4 * 1024, type="unit", metavar="<size>"),
733
  os_opt,
734
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
735
              default=128, type="unit", metavar="<mem>"),
736
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
737
              default=1, type="int", metavar="<PROC>"),
738
  make_option("-t", "--disk-template", dest="disk_template",
739
              help="Custom disk setup (diskless, file, plain or drbd)",
740
              default=None, metavar="TEMPL"),
741
  make_option("-i", "--ip", dest="ip",
742
              help="IP address ('none' [default], 'auto', or specify address)",
743
              default='none', type="string", metavar="<ADDRESS>"),
744
  make_option("--mac", dest="mac",
745
              help="MAC address ('auto' [default], or specify address)",
746
              default='auto', type="string", metavar="<MACADDRESS>"),
747
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
748
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
749
  make_option("-b", "--bridge", dest="bridge",
750
              help="Bridge to connect this instance to",
751
              default=None, metavar="<bridge>"),
752
  make_option("--no-start", dest="start", default=True,
753
              action="store_false", help="Don't start the instance after"
754
              " creation"),
755
  make_option("--no-ip-check", dest="ip_check", default=True,
756
              action="store_false", help="Don't check that the instance's IP"
757
              " is alive (only valid with --no-start)"),
758
  make_option("--kernel", dest="kernel_path",
759
              help="Path to the instances' kernel (or 'default')",
760
              default=None,
761
              type="string", metavar="<FILENAME>"),
762
  make_option("--initrd", dest="initrd_path",
763
              help="Path to the instances' initrd (or 'none', or 'default')",
764
              default=None,
765
              type="string", metavar="<FILENAME>"),
766
  make_option("--hvm-boot-order", dest="hvm_boot_order",
767
              help="Boot device order for HVM (one or more of [acdn])",
768
              default=None, type="string", metavar="<BOOTORDER>"),
769
  make_option("--file-storage-dir", dest="file_storage_dir",
770
              help="Relative path under default cluster-wide file storage dir"
771
              " to store file-based disks", default=None,
772
              metavar="<DIR>"),
773
  make_option("--file-driver", dest="file_driver", help="Driver to use"
774
              " for image files", default="loop", metavar="<DRIVER>"),
775
  make_option("--iallocator", metavar="<NAME>",
776
              help="Select nodes for the instance automatically using the"
777
              " <NAME> iallocator plugin", default=None, type="string"),
778
  ]
779

    
780
commands = {
781
  'add': (AddInstance, ARGS_ONE, add_opts,
782
          "[opts...] <name>",
783
          "Creates and adds a new instance to the cluster"),
784
  'console': (ConnectToInstanceConsole, ARGS_ONE,
785
              [DEBUG_OPT,
786
               make_option("--show-cmd", dest="show_command",
787
                           action="store_true", default=False,
788
                           help=("Show command instead of executing it"))],
789
              "[--show-cmd] <instance>",
790
              "Opens a console on the specified instance"),
791
  'failover': (FailoverInstance, ARGS_ONE,
792
               [DEBUG_OPT, FORCE_OPT,
793
                make_option("--ignore-consistency", dest="ignore_consistency",
794
                            action="store_true", default=False,
795
                            help="Ignore the consistency of the disks on"
796
                            " the secondary"),
797
                ],
798
               "[-f] <instance>",
799
               "Stops the instance and starts it on the backup node, using"
800
               " the remote mirror (only for instances of type drbd)"),
801
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
802
           "Show information on the specified instance"),
803
  'list': (ListInstances, ARGS_NONE,
804
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
805
           "Lists the instances and their status. The available fields are"
806
           " (see the man page for details): status, oper_state, oper_ram,"
807
           " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
808
           " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
809
           " list is (in order): name, os, pnode, status,"
810
           " oper_ram."),
811
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
812
                "[-f] <instance>", "Reinstall a stopped instance"),
813
  'remove': (RemoveInstance, ARGS_ONE,
814
             [DEBUG_OPT, FORCE_OPT,
815
              make_option("--ignore-failures", dest="ignore_failures",
816
                          action="store_true", default=False,
817
                          help=("Remove the instance from the cluster even"
818
                                " if there are failures during the removal"
819
                                " process (shutdown, disk removal, etc.)")),
820
              ],
821
             "[-f] <instance>", "Shuts down the instance and removes it"),
822
  'rename': (RenameInstance, ARGS_FIXED(2),
823
             [DEBUG_OPT,
824
              make_option("--no-ip-check", dest="ignore_ip",
825
                          help="Do not check that the IP of the new name"
826
                          " is alive",
827
                          default=False, action="store_true"),
828
              ],
829
             "<instance> <new_name>", "Rename the instance"),
830
  'replace-disks': (ReplaceDisks, ARGS_ONE,
831
                    [DEBUG_OPT,
832
                     make_option("-n", "--new-secondary", dest="new_secondary",
833
                                 help=("New secondary node (for secondary"
834
                                       " node change)"), metavar="NODE"),
835
                     make_option("-p", "--on-primary", dest="on_primary",
836
                                 default=False, action="store_true",
837
                                 help=("Replace the disk(s) on the primary"
838
                                       " node (only for the drbd template)")),
839
                     make_option("-s", "--on-secondary", dest="on_secondary",
840
                                 default=False, action="store_true",
841
                                 help=("Replace the disk(s) on the secondary"
842
                                       " node (only for the drbd template)")),
843
                     make_option("--disks", dest="disks", default=None,
844
                                 help=("Comma-separated list of disks"
845
                                       " to replace (e.g. sda) (optional,"
846
                                       " defaults to all disks")),
847
                     make_option("--iallocator", metavar="<NAME>",
848
                                 help="Select new secondary for the instance"
849
                                 " automatically using the"
850
                                 " <NAME> iallocator plugin (enables"
851
                                 " secondary node replacement)",
852
                                 default=None, type="string"),
853
                     ],
854
                    "[-s|-p|-n NODE] <instance>",
855
                    "Replaces all disks for the instance"),
856
  'modify': (SetInstanceParams, ARGS_ONE,
857
             [DEBUG_OPT, FORCE_OPT,
858
              cli_option("-m", "--memory", dest="mem",
859
                         help="Memory size",
860
                         default=None, type="unit", metavar="<mem>"),
861
              make_option("-p", "--cpu", dest="vcpus",
862
                          help="Number of virtual CPUs",
863
                          default=None, type="int", metavar="<PROC>"),
864
              make_option("-i", "--ip", dest="ip",
865
                          help="IP address ('none' or numeric IP)",
866
                          default=None, type="string", metavar="<ADDRESS>"),
867
              make_option("-b", "--bridge", dest="bridge",
868
                          help="Bridge to connect this instance to",
869
                          default=None, type="string", metavar="<bridge>"),
870
              make_option("--mac", dest="mac",
871
                          help="MAC address", default=None,
872
                          type="string", metavar="<MACADDRESS>"),
873
              make_option("--kernel", dest="kernel_path",
874
                          help="Path to the instances' kernel (or"
875
                          " 'default')", default=None,
876
                          type="string", metavar="<FILENAME>"),
877
              make_option("--initrd", dest="initrd_path",
878
                          help="Path to the instances' initrd (or 'none', or"
879
                          " 'default')", default=None,
880
                          type="string", metavar="<FILENAME>"),
881
              make_option("--hvm-boot-order", dest="hvm_boot_order",
882
                          help="boot device order for HVM"
883
                          "(either one or more of [acdn] or 'default')",
884
                          default=None, type="string", metavar="<BOOTORDER>"),
885
              ],
886
             "<instance>", "Alters the parameters of an instance"),
887
  'shutdown': (ShutdownInstance, ARGS_ANY,
888
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
889
                m_clust_opt, m_inst_opt, m_force_multi],
890
               "<instance>", "Stops an instance"),
891
  'startup': (StartupInstance, ARGS_ANY,
892
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
893
               make_option("-e", "--extra", dest="extra_args",
894
                           help="Extra arguments for the instance's kernel",
895
                           default=None, type="string", metavar="<PARAMS>"),
896
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
897
               m_clust_opt, m_inst_opt,
898
               ],
899
            "<instance>", "Starts an instance"),
900

    
901
  'reboot': (RebootInstance, ARGS_ANY,
902
              [DEBUG_OPT, m_force_multi,
903
               make_option("-e", "--extra", dest="extra_args",
904
                           help="Extra arguments for the instance's kernel",
905
                           default=None, type="string", metavar="<PARAMS>"),
906
               make_option("-t", "--type", dest="reboot_type",
907
                           help="Type of reboot: soft/hard/full",
908
                           default=constants.INSTANCE_REBOOT_SOFT,
909
                           type="string", metavar="<REBOOT>"),
910
               make_option("--ignore-secondaries", dest="ignore_secondaries",
911
                           default=False, action="store_true",
912
                           help="Ignore errors from secondaries"),
913
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
914
               m_clust_opt, m_inst_opt,
915
               ],
916
            "<instance>", "Reboots an instance"),
917
  'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
918
                     "<instance>",
919
                     "Activate an instance's disks"),
920
  'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
921
                       "<instance>",
922
                       "Deactivate an instance's disks"),
923
  'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
924
                "<node_name>", "List the tags of the given instance"),
925
  'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
926
               "<node_name> tag...", "Add tags to the given instance"),
927
  'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
928
                  "<node_name> tag...", "Remove tags from given instance"),
929
  }
930

    
931
aliases = {
932
  'activate_block_devs': 'activate-disks',
933
  'replace_disks': 'replace-disks',
934
  'start': 'startup',
935
  'stop': 'shutdown',
936
  }
937

    
938
if __name__ == '__main__':
939
  sys.exit(GenericMain(commands, aliases=aliases,
940
                       override={"tag_type": constants.TAG_INSTANCE}))