Statistics
| Branch: | Tag: | Revision:

root / scripts / gnt-instance @ 71aa8f73

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

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

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

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

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

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

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

    
99
  return inames
100

    
101

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

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

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

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

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

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

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

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

    
136

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

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

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

    
157
  return result_path
158

    
159

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

    
163
  """
164
  if opts.output is None:
165
    selected_fields = ["name", "os", "pnode", "admin_state",
166
                       "oper_state", "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 = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
175
               "snodes": "Secondary_Nodes", "admin_state": "Autostart",
176
               "oper_state": "Status", "admin_ram": "Configured_memory",
177
               "oper_ram": "Memory", "disk_template": "Disk_template",
178
               "ip": "IP Address", "mac": "MAC Address",
179
               "bridge": "Bridge",
180
               "sda_size": "Disk/0", "sdb_size": "Disk/1"}
181
  else:
182
    headers = None
183

    
184
  if opts.human_readable:
185
    unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
186
  else:
187
    unitfields = None
188

    
189
  numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
190

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

    
217
  data = GenerateTable(separator=opts.separator, headers=headers,
218
                       fields=selected_fields, unitfields=unitfields,
219
                       numfields=numfields, data=output)
220

    
221
  for line in data:
222
    logger.ToStdout(line)
223

    
224
  return 0
225

    
226

    
227
def AddInstance(opts, args):
228
  """Add an instance to the cluster.
229

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

    
239
  """
240
  instance = args[0]
241

    
242
  (pnode, snode) = SplitNodeOption(opts.node)
243

    
244
  kernel_path = _TransformPath(opts.kernel_path)
245
  initrd_path = _TransformPath(opts.initrd_path)
246

    
247
  op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
248
                                disk_size=opts.size, swap_size=opts.swap,
249
                                disk_template=opts.disk_template,
250
                                mode=constants.INSTANCE_CREATE,
251
                                os_type=opts.os, pnode=pnode,
252
                                snode=snode, vcpus=opts.vcpus,
253
                                ip=opts.ip, bridge=opts.bridge,
254
                                start=opts.start, ip_check=opts.ip_check,
255
                                wait_for_sync=opts.wait_for_sync,
256
                                mac=opts.mac,
257
                                kernel_path=kernel_path,
258
                                initrd_path=initrd_path)
259
  SubmitOpCode(op)
260
  return 0
261

    
262

    
263
def ReinstallInstance(opts, args):
264
  """Reinstall an instance.
265

    
266
  Args:
267
    opts - class with options as members
268
    args - list containing a single element, the instance name
269

    
270
  """
271
  instance_name = args[0]
272

    
273
  if not opts.force:
274
    usertext = ("This will reinstall the instance %s and remove"
275
                " all data. Continue?") % instance_name
276
    if not AskUser(usertext):
277
      return 1
278

    
279
  op = opcodes.OpReinstallInstance(instance_name=instance_name,
280
                                   os_type=opts.os)
281
  SubmitOpCode(op)
282

    
283
  return 0
284

    
285

    
286
def RemoveInstance(opts, args):
287
  """Remove an instance.
288

    
289
  Args:
290
    opts - class with options as members
291
    args - list containing a single element, the instance name
292

    
293
  """
294
  instance_name = args[0]
295
  force = opts.force
296

    
297
  if not force:
298
    usertext = ("This will remove the volumes of the instance %s"
299
                " (including mirrors), thus removing all the data"
300
                " of the instance. Continue?") % instance_name
301
    if not AskUser(usertext):
302
      return 1
303

    
304
  op = opcodes.OpRemoveInstance(instance_name=instance_name,
305
                                ignore_failures=opts.ignore_failures)
306
  SubmitOpCode(op)
307
  return 0
308

    
309

    
310
def RenameInstance(opts, args):
311
  """Rename an instance.
312

    
313
  Args:
314
    opts - class with options as members
315
    args - list containing two elements, the instance name and the new name
316

    
317
  """
318
  op = opcodes.OpRenameInstance(instance_name=args[0],
319
                                new_name=args[1],
320
                                ignore_ip=opts.ignore_ip)
321
  SubmitOpCode(op)
322

    
323
  return 0
324

    
325

    
326
def ActivateDisks(opts, args):
327
  """Activate an instance's disks.
328

    
329
  This serves two purposes:
330
    - it allows one (as long as the instance is not running) to mount
331
    the disks and modify them from the node
332
    - it repairs inactive secondary drbds
333

    
334
  """
335
  instance_name = args[0]
336
  op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
337
  disks_info = SubmitOpCode(op)
338
  for host, iname, nname in disks_info:
339
    print "%s:%s:%s" % (host, iname, nname)
340
  return 0
341

    
342

    
343
def DeactivateDisks(opts, args):
344
  """Command-line interface for _ShutdownInstanceBlockDevices.
345

    
346
  This function takes the instance name, looks for its primary node
347
  and the tries to shutdown its block devices on that node.
348

    
349
  """
350
  instance_name = args[0]
351
  op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
352
  SubmitOpCode(op)
353
  return 0
354

    
355

    
356
def StartupInstance(opts, args):
357
  """Startup 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
  if opts.multi_mode is None:
365
    opts.multi_mode = _SHUTDOWN_INSTANCES
366
  inames = _ExpandMultiNames(opts.multi_mode, args)
367
  if not inames:
368
    raise errors.OpPrereqError("Selection filter does not match any instances")
369
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
370
  if not (opts.force_multi or not multi_on
371
          or _ConfirmOperation(inames, "startup")):
372
    return 1
373
  for name in inames:
374
    op = opcodes.OpStartupInstance(instance_name=name,
375
                                   force=opts.force,
376
                                   extra_args=opts.extra_args)
377
    if multi_on:
378
      logger.ToStdout("Starting up %s" % name)
379
    SubmitOpCode(op)
380
  return 0
381

    
382
def RebootInstance(opts, args):
383
  """Reboot an instance
384

    
385
  Args:
386
    opts - class with options as members
387
    args - list containing a single element, the instance name
388

    
389
  """
390
  if opts.multi_mode is None:
391
    opts.multi_mode = _SHUTDOWN_INSTANCES
392
  inames = _ExpandMultiNames(opts.multi_mode, args)
393
  if not inames:
394
    raise errors.OpPrereqError("Selection filter does not match any instances")
395
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
396
  if not (opts.force_multi or not multi_on
397
          or _ConfirmOperation(inames, "reboot")):
398
    return 1
399
  for name in inames:
400
    op = opcodes.OpRebootInstance(instance_name=name,
401
                                  reboot_type=opts.reboot_type,
402
                                  ignore_secondaries=opts.ignore_secondaries)
403

    
404
    SubmitOpCode(op)
405
  return 0
406

    
407
def ShutdownInstance(opts, args):
408
  """Shutdown an instance.
409

    
410
  Args:
411
    opts - class with options as members
412
    args - list containing a single element, the instance name
413

    
414
  """
415
  if opts.multi_mode is None:
416
    opts.multi_mode = _SHUTDOWN_INSTANCES
417
  inames = _ExpandMultiNames(opts.multi_mode, args)
418
  if not inames:
419
    raise errors.OpPrereqError("Selection filter does not match any instances")
420
  multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
421
  if not (opts.force_multi or not multi_on
422
          or _ConfirmOperation(inames, "shutdown")):
423
    return 1
424
  for name in inames:
425
    op = opcodes.OpShutdownInstance(instance_name=name)
426
    if multi_on:
427
      logger.ToStdout("Shutting down %s" % name)
428
    SubmitOpCode(op)
429
  return 0
430

    
431

    
432
def AddMDDRBDComponent(opts, args):
433
  """Add a new component to a remote_raid1 disk.
434

    
435
  Args:
436
    opts - class with options as members
437
    args - list with a single element, the instance name
438

    
439
  """
440
  op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
441
                                    disk_name=opts.disk,
442
                                    remote_node=opts.node)
443
  SubmitOpCode(op)
444
  return 0
445

    
446

    
447
def RemoveMDDRBDComponent(opts, args):
448
  """Remove a component from a remote_raid1 disk.
449

    
450
  Args:
451
    opts - class with options as members
452
    args - list with a single element, the instance name
453

    
454
  """
455
  op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
456
                                       disk_name=opts.disk,
457
                                       disk_id=opts.port)
458
  SubmitOpCode(op)
459
  return 0
460

    
461

    
462
def ReplaceDisks(opts, args):
463
  """Replace the disks of an instance
464

    
465
  Args:
466
    opts - class with options as members
467
    args - list with a single element, the instance name
468

    
469
  """
470
  instance_name = args[0]
471
  new_2ndary = opts.new_secondary
472
  if opts.disks is None:
473
    disks = ["sda", "sdb"]
474
  else:
475
    disks = opts.disks.split(",")
476
  if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
477
    mode = constants.REPLACE_DISK_ALL
478
  elif opts.on_primary: # only on primary:
479
    mode = constants.REPLACE_DISK_PRI
480
    if new_2ndary is not None:
481
      raise errors.OpPrereqError("Can't change secondary node on primary disk"
482
                                 " replacement")
483
  elif opts.on_secondary is not None: # only on secondary
484
    mode = constants.REPLACE_DISK_SEC
485

    
486
  op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
487
                              remote_node=new_2ndary, mode=mode)
488
  SubmitOpCode(op)
489
  return 0
490

    
491

    
492
def FailoverInstance(opts, args):
493
  """Failover an instance.
494

    
495
  The failover is done by shutting it down on its present node and
496
  starting it on the secondary.
497

    
498
  Args:
499
    opts - class with options as members
500
    args - list with a single element, the instance name
501
  Opts used:
502
    force - whether to failover without asking questions.
503

    
504
  """
505
  instance_name = args[0]
506
  force = opts.force
507

    
508
  if not force:
509
    usertext = ("Failover will happen to image %s."
510
                " This requires a shutdown of the instance. Continue?" %
511
                (instance_name,))
512
    if not AskUser(usertext):
513
      return 1
514

    
515
  op = opcodes.OpFailoverInstance(instance_name=instance_name,
516
                                  ignore_consistency=opts.ignore_consistency)
517
  SubmitOpCode(op)
518
  return 0
519

    
520

    
521
def ConnectToInstanceConsole(opts, args):
522
  """Connect to the console of an instance.
523

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

    
528
  """
529
  instance_name = args[0]
530

    
531
  op = opcodes.OpConnectConsole(instance_name=instance_name)
532
  cmd, argv = SubmitOpCode(op)
533
  # drop lock and exec so other commands can run while we have console
534
  utils.Unlock("cmd")
535
  try:
536
    os.execvp(cmd, argv)
537
  finally:
538
    sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
539
                     (cmd, " ".join(argv)))
540
    os._exit(1)
541

    
542

    
543
def _FormatBlockDevInfo(buf, dev, indent_level):
544
  """Show block device information.
545

    
546
  This is only used by ShowInstanceConfig(), but it's too big to be
547
  left for an inline definition.
548

    
549
  """
550
  def helper(buf, dtype, status):
551
    """Format one line for phsyical device status."""
552
    if not status:
553
      buf.write("not active\n")
554
    else:
555
      (path, major, minor, syncp, estt, degr, ldisk) = status
556
      buf.write("%s (%d:%d)" % (path, major, minor))
557
      if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
558
        if syncp is not None:
559
          sync_text = "*RECOVERING* %5.2f%%," % syncp
560
          if estt:
561
            sync_text += " ETA %ds" % estt
562
          else:
563
            sync_text += " ETA unknown"
564
        else:
565
          sync_text = "in sync"
566
        if degr:
567
          degr_text = "*DEGRADED*"
568
        else:
569
          degr_text = "ok"
570
        if ldisk:
571
          ldisk_text = " *MISSING DISK*"
572
        else:
573
          ldisk_text = ""
574
        buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
575
      elif dtype == constants.LD_LV:
576
        if ldisk:
577
          ldisk_text = " *FAILED* (failed drive?)"
578
        else:
579
          ldisk_text = ""
580
        buf.write(ldisk_text)
581
      buf.write("\n")
582

    
583
  if dev["iv_name"] is not None:
584
    data = "  - %s, " % dev["iv_name"]
585
  else:
586
    data = "  - "
587
  data += "type: %s" % dev["dev_type"]
588
  if dev["logical_id"] is not None:
589
    data += ", logical_id: %s" % (dev["logical_id"],)
590
  elif dev["physical_id"] is not None:
591
    data += ", physical_id: %s" % (dev["physical_id"],)
592
  buf.write("%*s%s\n" % (2*indent_level, "", data))
593
  buf.write("%*s    primary:   " % (2*indent_level, ""))
594
  helper(buf, dev["dev_type"], dev["pstatus"])
595

    
596
  if dev["sstatus"]:
597
    buf.write("%*s    secondary: " % (2*indent_level, ""))
598
    helper(buf, dev["dev_type"], dev["sstatus"])
599

    
600
  if dev["children"]:
601
    for child in dev["children"]:
602
      _FormatBlockDevInfo(buf, child, indent_level+1)
603

    
604

    
605
def ShowInstanceConfig(opts, args):
606
  """Compute instance run-time status.
607

    
608
  """
609
  retcode = 0
610
  op = opcodes.OpQueryInstanceData(instances=args)
611
  result = SubmitOpCode(op)
612

    
613
  if not result:
614
    logger.ToStdout("No instances.")
615
    return 1
616

    
617
  buf = StringIO()
618
  retcode = 0
619
  for instance_name in result:
620
    instance = result[instance_name]
621
    buf.write("Instance name: %s\n" % instance["name"])
622
    buf.write("State: configured to be %s, actual state is %s\n" %
623
              (instance["config_state"], instance["run_state"]))
624
    buf.write("  Nodes:\n")
625
    buf.write("    - primary: %s\n" % instance["pnode"])
626
    buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
627
    buf.write("  Operating system: %s\n" % instance["os"])
628
    buf.write("  Allocated network port: %s\n" % instance["network_port"])
629
    if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
630
      kpath = "(default: %s)" % constants.XEN_KERNEL
631
    else:
632
      kpath = instance["kernel_path"]
633
    buf.write("  Kernel path: %s\n" % kpath)
634
    if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
635
      initrd = "(default: %s)" % constants.XEN_INITRD
636
    elif instance["initrd_path"] == constants.VALUE_NONE:
637
      initrd = "(none)"
638
    else:
639
      initrd = instance["initrd_path"]
640
    buf.write("       initrd: %s\n" % initrd)
641
    buf.write("  Hardware:\n")
642
    buf.write("    - VCPUs: %d\n" % instance["vcpus"])
643
    buf.write("    - memory: %dMiB\n" % instance["memory"])
644
    buf.write("    - NICs: %s\n" %
645
        ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
646
                   (mac, ip, bridge)
647
                     for mac, ip, bridge in instance["nics"]]))
648
    buf.write("  Block devices:\n")
649

    
650
    for device in instance["disks"]:
651
      _FormatBlockDevInfo(buf, device, 1)
652

    
653
  logger.ToStdout(buf.getvalue().rstrip('\n'))
654
  return retcode
655

    
656

    
657
def SetInstanceParms(opts, args):
658
  """Modifies an instance.
659

    
660
  All parameters take effect only at the next restart of the instance.
661

    
662
  Args:
663
    opts - class with options as members
664
    args - list with a single element, the instance name
665
  Opts used:
666
    memory - the new memory size
667
    vcpus - the new number of cpus
668
    mac - the new MAC address of the instance
669

    
670
  """
671
  if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
672
          opts.kernel_path or opts.initrd_path):
673
    logger.ToStdout("Please give at least one of the parameters.")
674
    return 1
675

    
676
  kernel_path = _TransformPath(opts.kernel_path)
677
  initrd_path = _TransformPath(opts.initrd_path)
678

    
679
  op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
680
                                  vcpus=opts.vcpus, ip=opts.ip,
681
                                  bridge=opts.bridge, mac=opts.mac,
682
                                  kernel_path=opts.kernel_path,
683
                                  initrd_path=opts.initrd_path)
684
  result = SubmitOpCode(op)
685

    
686
  if result:
687
    logger.ToStdout("Modified instance %s" % args[0])
688
    for param, data in result:
689
      logger.ToStdout(" - %-5s -> %s" % (param, data))
690
    logger.ToStdout("Please don't forget that these parameters take effect"
691
                    " only at the next start of the instance.")
692
  return 0
693

    
694

    
695
# options used in more than one cmd
696
node_opt = make_option("-n", "--node", dest="node", help="Target node",
697
                       metavar="<node>")
698

    
699
os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
700
                    metavar="<os>")
701

    
702
# multi-instance selection options
703
m_force_multi = make_option("--force-multiple", dest="force_multi",
704
                            help="Do not ask for confirmation when more than"
705
                            " one instance is affected",
706
                            action="store_true", default=False)
707

    
708
m_pri_node_opt = make_option("--primary", dest="multi_mode",
709
                             help="Filter by nodes (primary only)",
710
                             const=_SHUTDOWN_NODES_PRI, action="store_const")
711

    
712
m_sec_node_opt = make_option("--secondary", dest="multi_mode",
713
                             help="Filter by nodes (secondary only)",
714
                             const=_SHUTDOWN_NODES_SEC, action="store_const")
715

    
716
m_node_opt = make_option("--node", dest="multi_mode",
717
                         help="Filter by nodes (primary and secondary)",
718
                         const=_SHUTDOWN_NODES_BOTH, action="store_const")
719

    
720
m_clust_opt = make_option("--all", dest="multi_mode",
721
                          help="Select all instances in the cluster",
722
                          const=_SHUTDOWN_CLUSTER, action="store_const")
723

    
724
m_inst_opt = make_option("--instance", dest="multi_mode",
725
                         help="Filter by instance name [default]",
726
                         const=_SHUTDOWN_INSTANCES, action="store_const")
727

    
728

    
729
# this is defined separately due to readability only
730
add_opts = [
731
  DEBUG_OPT,
732
  make_option("-n", "--node", dest="node",
733
              help="Target node and optional secondary node",
734
              metavar="<pnode>[:<snode>]"),
735
  cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
736
             " a suffix is used",
737
             default=20 * 1024, type="unit", metavar="<size>"),
738
  cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
739
             " suffix is used",
740
             default=4 * 1024, type="unit", metavar="<size>"),
741
  os_opt,
742
  cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
743
              default=128, type="unit", metavar="<mem>"),
744
  make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
745
              default=1, type="int", metavar="<PROC>"),
746
  make_option("-t", "--disk-template", dest="disk_template",
747
              help="Custom disk setup (diskless, plain, local_raid1,"
748
              " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
749
  make_option("-i", "--ip", dest="ip",
750
              help="IP address ('none' [default], 'auto', or specify address)",
751
              default='none', type="string", metavar="<ADDRESS>"),
752
  make_option("--mac", dest="mac",
753
              help="MAC address ('auto' [default], or specify address)",
754
              default='auto', type="string", metavar="<MACADDRESS>"),
755
  make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
756
              action="store_false", help="Don't wait for sync (DANGEROUS!)"),
757
  make_option("-b", "--bridge", dest="bridge",
758
              help="Bridge to connect this instance to",
759
              default=None, metavar="<bridge>"),
760
  make_option("--no-start", dest="start", default=True,
761
              action="store_false", help="Don't start the instance after"
762
              " creation"),
763
  make_option("--no-ip-check", dest="ip_check", default=True,
764
              action="store_false", help="Don't check that the instance's IP"
765
              " is alive (only valid with --no-start)"),
766
  make_option("--kernel", dest="kernel_path",
767
              help="Path to the instances' kernel (or 'default')",
768
              default=None,
769
              type="string", metavar="<FILENAME>"),
770
  make_option("--initrd", dest="initrd_path",
771
              help="Path to the instances' initrd (or 'none', or 'default')",
772
              default=None,
773
              type="string", metavar="<FILENAME>"),
774
  ]
775

    
776
commands = {
777
  'add': (AddInstance, ARGS_ONE, add_opts,
778
          "[opts...] <name>",
779
          "Creates and adds a new instance to the cluster"),
780
  'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
781
                [DEBUG_OPT, node_opt,
782
                 make_option("-b", "--disk", dest="disk", metavar="sdX",
783
                             help=("The name of the instance disk for which to"
784
                                   " add the mirror"))],
785
                "-n node -b disk <instance>",
786
                "Creates a new mirror for the instance"),
787
  'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
788
              "<instance>",
789
              "Opens a console on the specified instance"),
790
  'failover': (FailoverInstance, ARGS_ONE,
791
               [DEBUG_OPT, FORCE_OPT,
792
                make_option("--ignore-consistency", dest="ignore_consistency",
793
                            action="store_true", default=False,
794
                            help="Ignore the consistency of the disks on"
795
                            " the secondary"),
796
                ],
797
               "[-f] <instance>",
798
               "Stops the instance and starts it on the backup node, using"
799
               " the remote mirror (only for instances of type remote_raid1)"),
800
  'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
801
           "Show information on the specified instance"),
802
  'list': (ListInstances, ARGS_NONE,
803
           [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
804
           "", "Lists the instances and their status"),
805
  'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
806
                "[-f] <instance>", "Reinstall the instance"),
807
  'remove': (RemoveInstance, ARGS_ONE,
808
             [DEBUG_OPT, FORCE_OPT,
809
              make_option("--ignore-failures", dest="ignore_failures",
810
                          action="store_true", default=False,
811
                          help=("Remove the instance from the cluster even"
812
                                " if there are failures during the removal"
813
                                " process (shutdown, disk removal, etc.)")),
814
              ],
815
             "[-f] <instance>", "Shuts down the instance and removes it"),
816
  'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
817
                   [DEBUG_OPT, node_opt,
818
                    make_option("-b", "--disk", dest="disk", metavar="sdX",
819
                                help=("The name of the instance disk"
820
                                      " for which to add the mirror")),
821
                    make_option("-p", "--port", dest="port", metavar="PORT",
822
                                help=("The port of the drbd device"
823
                                      " which to remove from the mirror"),
824
                                type="int"),
825
                    ],
826
                   "-b disk -p port <instance>",
827
                   "Removes a mirror from the instance"),
828
  'rename': (RenameInstance, ARGS_FIXED(2),
829
             [DEBUG_OPT,
830
              make_option("--no-ip-check", dest="ignore_ip",
831
                          help="Do not check that the IP of the new name"
832
                          " is alive",
833
                          default=False, action="store_true"),
834
              ],
835
             "<instance> <new_name>", "Rename the instance"),
836
  'replace-disks': (ReplaceDisks, ARGS_ONE,
837
                    [DEBUG_OPT,
838
                     make_option("-n", "--new-secondary", dest="new_secondary",
839
                                 help=("New secondary node (for secondary"
840
                                       " node change)"), metavar="NODE"),
841
                     make_option("-p", "--on-primary", dest="on_primary",
842
                                 default=False, action="store_true",
843
                                 help=("Replace the disk(s) on the primary"
844
                                       " node (only for the drbd template)")),
845
                     make_option("-s", "--on-secondary", dest="on_secondary",
846
                                 default=False, action="store_true",
847
                                 help=("Replace the disk(s) on the secondary"
848
                                       " node (only for the drbd template)")),
849
                     make_option("--disks", dest="disks", default=None,
850
                                 help=("Comma-separated list of disks"
851
                                       " to replace (e.g. sda) (optional,"
852
                                       " defaults to all disks")),
853
                     ],
854
                    "[-n NODE] <instance>",
855
                    "Replaces all disks for the instance"),
856
  'modify': (SetInstanceParms, 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
              ],
882
             "<instance>", "Alters the parameters of an instance"),
883
  'shutdown': (ShutdownInstance, ARGS_ANY,
884
               [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
885
                m_clust_opt, m_inst_opt, m_force_multi],
886
               "<instance>", "Stops an instance"),
887
  'startup': (StartupInstance, ARGS_ANY,
888
              [DEBUG_OPT, FORCE_OPT, m_force_multi,
889
               make_option("-e", "--extra", dest="extra_args",
890
                           help="Extra arguments for the instance's kernel",
891
                           default=None, type="string", metavar="<PARAMS>"),
892
               m_node_opt, m_pri_node_opt, m_sec_node_opt,
893
               m_clust_opt, m_inst_opt,
894
               ],
895
            "<instance>", "Starts an instance"),
896

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

    
927
if __name__ == '__main__':
928
  sys.exit(GenericMain(commands,
929
                       override={"tag_type": constants.TAG_INSTANCE}))