Ask for confirmation when touching multiple instances
[ganeti-local] / scripts / gnt-instance
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 ListInstances(opts, args):
138   """List nodes and their properties.
139
140   """
141   if opts.output is None:
142     selected_fields = ["name", "os", "pnode", "admin_state",
143                        "oper_state", "oper_ram"]
144   else:
145     selected_fields = opts.output.split(",")
146
147   op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
148   output = SubmitOpCode(op)
149
150   if not opts.no_headers:
151     headers = {"name": "Instance", "os": "OS", "pnode": "Primary_node",
152                "snodes": "Secondary_Nodes", "admin_state": "Autostart",
153                "oper_state": "Status", "admin_ram": "Configured_memory",
154                "oper_ram": "Memory", "disk_template": "Disk_template",
155                "ip": "IP Address", "mac": "MAC Address",
156                "bridge": "Bridge",
157                "sda_size": "Disk/0", "sdb_size": "Disk/1"}
158   else:
159     headers = None
160
161   if opts.human_readable:
162     unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
163   else:
164     unitfields = None
165
166   numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
167
168   # change raw values to nicer strings
169   for row in output:
170     for idx, field in enumerate(selected_fields):
171       val = row[idx]
172       if field == "snodes":
173         val = ",".join(val) or "-"
174       elif field == "admin_state":
175         if val:
176           val = "yes"
177         else:
178           val = "no"
179       elif field == "oper_state":
180         if val is None:
181           val = "(node down)"
182         elif val: # True
183           val = "running"
184         else:
185           val = "stopped"
186       elif field == "oper_ram":
187         if val is None:
188           val = "(node down)"
189       elif field == "sda_size" or field == "sdb_size":
190         if val is None:
191           val = "N/A"
192       row[idx] = str(val)
193
194   data = GenerateTable(separator=opts.separator, headers=headers,
195                        fields=selected_fields, unitfields=unitfields,
196                        numfields=numfields, data=output)
197
198   for line in data:
199     logger.ToStdout(line)
200
201   return 0
202
203
204 def AddInstance(opts, args):
205   """Add an instance to the cluster.
206
207   Args:
208     opts - class with options as members
209     args - list with a single element, the instance name
210   Opts used:
211     mem - amount of memory to allocate to instance (MiB)
212     size - amount of disk space to allocate to instance (MiB)
213     os - which OS to run on instance
214     node - node to run new instance on
215
216   """
217   instance = args[0]
218
219   op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
220                                 disk_size=opts.size, swap_size=opts.swap,
221                                 disk_template=opts.disk_template,
222                                 mode=constants.INSTANCE_CREATE,
223                                 os_type=opts.os, pnode=opts.node,
224                                 snode=opts.snode, vcpus=opts.vcpus,
225                                 ip=opts.ip, bridge=opts.bridge,
226                                 start=opts.start, ip_check=opts.ip_check,
227                                 wait_for_sync=opts.wait_for_sync)
228   SubmitOpCode(op)
229   return 0
230
231
232 def ReinstallInstance(opts, args):
233   """Reinstall an instance.
234
235   Args:
236     opts - class with options as members
237     args - list containing a single element, the instance name
238
239   """
240   instance_name = args[0]
241
242   if not opts.force:
243     usertext = ("This will reinstall the instance %s and remove "
244                 "all data. Continue?") % instance_name
245     if not AskUser(usertext):
246       return 1
247
248   op = opcodes.OpReinstallInstance(instance_name=instance_name,
249                                    os_type=opts.os)
250   SubmitOpCode(op)
251
252   return 0
253
254
255 def RemoveInstance(opts, args):
256   """Remove an instance.
257
258   Args:
259     opts - class with options as members
260     args - list containing a single element, the instance name
261
262   """
263   instance_name = args[0]
264   force = opts.force
265
266   if not force:
267     usertext = ("This will remove the volumes of the instance %s"
268                 " (including mirrors), thus removing all the data"
269                 " of the instance. Continue?") % instance_name
270     if not AskUser(usertext):
271       return 1
272
273   op = opcodes.OpRemoveInstance(instance_name=instance_name)
274   SubmitOpCode(op)
275   return 0
276
277
278 def RenameInstance(opts, args):
279   """Rename an instance.
280
281   Args:
282     opts - class with options as members
283     args - list containing two elements, the instance name and the new name
284
285   """
286   op = opcodes.OpRenameInstance(instance_name=args[0],
287                                 new_name=args[1],
288                                 ignore_ip=opts.ignore_ip)
289   SubmitOpCode(op)
290
291   return 0
292
293
294 def ActivateDisks(opts, args):
295   """Activate an instance's disks.
296
297   This serves two purposes:
298     - it allows one (as long as the instance is not running) to mount
299     the disks and modify them from the node
300     - it repairs inactive secondary drbds
301
302   """
303   instance_name = args[0]
304   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
305   disks_info = SubmitOpCode(op)
306   for host, iname, nname in disks_info:
307     print "%s:%s:%s" % (host, iname, nname)
308   return 0
309
310
311 def DeactivateDisks(opts, args):
312   """Command-line interface for _ShutdownInstanceBlockDevices.
313
314   This function takes the instance name, looks for its primary node
315   and the tries to shutdown its block devices on that node.
316
317   """
318   instance_name = args[0]
319   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
320   SubmitOpCode(op)
321   return 0
322
323
324 def StartupInstance(opts, args):
325   """Startup an instance.
326
327   Args:
328     opts - class with options as members
329     args - list containing a single element, the instance name
330
331   """
332   if opts.multi_mode is None:
333     opts.multi_mode = _SHUTDOWN_INSTANCES
334   inames = _ExpandMultiNames(opts.multi_mode, args)
335   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
336   if not (opts.force_multi or not multi_on
337           or _ConfirmOperation(inames, "startup")):
338     return 1
339   for name in inames:
340     op = opcodes.OpStartupInstance(instance_name=name,
341                                    force=opts.force,
342                                    extra_args=opts.extra_args)
343     if multi_on:
344       logger.ToStdout("Starting up %s" % name)
345     SubmitOpCode(op)
346   return 0
347
348
349 def ShutdownInstance(opts, args):
350   """Shutdown an instance.
351
352   Args:
353     opts - class with options as members
354     args - list containing a single element, the instance name
355
356   """
357   if opts.multi_mode is None:
358     opts.multi_mode = _SHUTDOWN_INSTANCES
359   inames = _ExpandMultiNames(opts.multi_mode, args)
360   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
361   if not (opts.force_multi or not multi_on
362           or _ConfirmOperation(inames, "shutdown")):
363     return 1
364   for name in inames:
365     op = opcodes.OpShutdownInstance(instance_name=name)
366     if multi_on:
367       logger.ToStdout("Shutting down %s" % name)
368     SubmitOpCode(op)
369   return 0
370
371
372 def AddMDDRBDComponent(opts, args):
373   """Add a new component to a remote_raid1 disk.
374
375   Args:
376     opts - class with options as members
377     args - list with a single element, the instance name
378
379   """
380   op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
381                                     disk_name=opts.disk,
382                                     remote_node=opts.node)
383   SubmitOpCode(op)
384   return 0
385
386
387 def RemoveMDDRBDComponent(opts, args):
388   """Remove a component from a remote_raid1 disk.
389
390   Args:
391     opts - class with options as members
392     args - list with a single element, the instance name
393
394   """
395   op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
396                                        disk_name=opts.disk,
397                                        disk_id=opts.port)
398   SubmitOpCode(op)
399   return 0
400
401
402 def ReplaceDisks(opts, args):
403   """Replace the disks of an instance
404
405   Args:
406     opts - class with options as members
407     args - list with a single element, the instance name
408
409   """
410   instance_name = args[0]
411   new_secondary = opts.new_secondary
412   op = opcodes.OpReplaceDisks(instance_name=args[0],
413                               remote_node=opts.new_secondary)
414   SubmitOpCode(op)
415   return 0
416
417
418 def FailoverInstance(opts, args):
419   """Failover an instance.
420
421   The failover is done by shutting it down on its present node and
422   starting it on the secondary.
423
424   Args:
425     opts - class with options as members
426     args - list with a single element, the instance name
427   Opts used:
428     force - whether to failover without asking questions.
429
430   """
431   instance_name = args[0]
432   force = opts.force
433
434   if not force:
435     usertext = ("Failover will happen to image %s."
436                 " This requires a shutdown of the instance. Continue?" %
437                 (instance_name,))
438     if not AskUser(usertext):
439       return 1
440
441   op = opcodes.OpFailoverInstance(instance_name=instance_name,
442                                   ignore_consistency=opts.ignore_consistency)
443   SubmitOpCode(op)
444   return 0
445
446
447 def ConnectToInstanceConsole(opts, args):
448   """Connect to the console of an instance.
449
450   Args:
451     opts - class with options as members
452     args - list with a single element, the instance name
453
454   """
455   instance_name = args[0]
456
457   op = opcodes.OpConnectConsole(instance_name=instance_name)
458   cmd, argv = SubmitOpCode(op)
459   # drop lock and exec so other commands can run while we have console
460   utils.Unlock("cmd")
461   try:
462     os.execvp(cmd, argv)
463   finally:
464     sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
465                      (cmd, " ".join(argv)))
466     os._exit(1)
467
468
469 def _FormatBlockDevInfo(buf, dev, indent_level):
470   """Show block device information.
471
472   This is only used by ShowInstanceConfig(), but it's too big to be
473   left for an inline definition.
474
475   """
476   def helper(buf, dtype, status):
477     """Format one line for phsyical device status."""
478     if not status:
479       buf.write("not active\n")
480     else:
481       (path, major, minor, syncp, estt, degr) = status
482       buf.write("%s (%d:%d)" % (path, major, minor))
483       if dtype in ("md_raid1", "drbd"):
484         if syncp is not None:
485           sync_text = "*RECOVERING* %5.2f%%," % syncp
486           if estt:
487             sync_text += " ETA %ds" % estt
488           else:
489             sync_text += " ETA unknown"
490         else:
491           sync_text = "in sync"
492         if degr:
493           degr_text = "*DEGRADED*"
494         else:
495           degr_text = "ok"
496         buf.write(" %s, status %s" % (sync_text, degr_text))
497       buf.write("\n")
498
499   if dev["iv_name"] is not None:
500     data = "  - %s, " % dev["iv_name"]
501   else:
502     data = "  - "
503   data += "type: %s" % dev["dev_type"]
504   if dev["logical_id"] is not None:
505     data += ", logical_id: %s" % (dev["logical_id"],)
506   elif dev["physical_id"] is not None:
507     data += ", physical_id: %s" % (dev["physical_id"],)
508   buf.write("%*s%s\n" % (2*indent_level, "", data))
509   buf.write("%*s    primary:   " % (2*indent_level, ""))
510   helper(buf, dev["dev_type"], dev["pstatus"])
511
512   if dev["sstatus"]:
513     buf.write("%*s    secondary: " % (2*indent_level, ""))
514     helper(buf, dev["dev_type"], dev["sstatus"])
515
516   if dev["children"]:
517     for child in dev["children"]:
518       _FormatBlockDevInfo(buf, child, indent_level+1)
519
520
521 def ShowInstanceConfig(opts, args):
522   """Compute instance run-time status.
523
524   """
525   retcode = 0
526   op = opcodes.OpQueryInstanceData(instances=args)
527   result = SubmitOpCode(op)
528
529   if not result:
530     logger.ToStdout("No instances.")
531     return 1
532
533   buf = StringIO()
534   retcode = 0
535   for instance_name in result:
536     instance = result[instance_name]
537     buf.write("Instance name: %s\n" % instance["name"])
538     buf.write("State: configured to be %s, actual state is %s\n" %
539               (instance["config_state"], instance["run_state"]))
540     buf.write("  Nodes:\n")
541     buf.write("    - primary: %s\n" % instance["pnode"])
542     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
543     buf.write("  Operating system: %s\n" % instance["os"])
544     buf.write("  Hardware:\n")
545     buf.write("    - memory: %dMiB\n" % instance["memory"])
546     buf.write("    - NICs: %s\n" %
547         ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
548                    (mac, ip, bridge)
549                      for mac, ip, bridge in instance["nics"]]))
550     buf.write("  Block devices:\n")
551
552     for device in instance["disks"]:
553       _FormatBlockDevInfo(buf, device, 1)
554
555   logger.ToStdout(buf.getvalue().rstrip('\n'))
556   return retcode
557
558
559 def SetInstanceParms(opts, args):
560   """Modifies an instance.
561
562   All parameters take effect only at the next restart of the instance.
563
564   Args:
565     opts - class with options as members
566     args - list with a single element, the instance name
567   Opts used:
568     memory - the new memory size
569     vcpus - the new number of cpus
570
571   """
572   if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
573     logger.ToStdout("Please give at least one of the parameters.")
574     return 1
575
576   op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
577                                   vcpus=opts.vcpus, ip=opts.ip,
578                                   bridge=opts.bridge)
579   result = SubmitOpCode(op)
580
581   if result:
582     logger.ToStdout("Modified instance %s" % args[0])
583     for param, data in result:
584       logger.ToStdout(" - %-5s -> %s" % (param, data))
585     logger.ToStdout("Please don't forget that these parameters take effect"
586                     " only at the next start of the instance.")
587   return 0
588
589
590 # options used in more than one cmd
591 node_opt = make_option("-n", "--node", dest="node", help="Target node",
592                        metavar="<node>")
593
594 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
595                     metavar="<os>")
596
597 # multi-instance selection options
598 m_force_multi = make_option("--force-multiple", dest="force_multi",
599                             help="Do not ask for confirmation when more than"
600                             " one instance is affected",
601                             action="store_true", default=False)
602
603 m_pri_node_opt = make_option("--primary", dest="multi_mode",
604                              help="Filter by nodes (primary only)",
605                              const=_SHUTDOWN_NODES_PRI, action="store_const")
606
607 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
608                              help="Filter by nodes (secondary only)",
609                              const=_SHUTDOWN_NODES_SEC, action="store_const")
610
611 m_node_opt = make_option("--node", dest="multi_mode",
612                          help="Filter by nodes (primary and secondary)",
613                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
614
615 m_clust_opt = make_option("--all", dest="multi_mode",
616                           help="Select all instances in the cluster",
617                           const=_SHUTDOWN_CLUSTER, action="store_const")
618
619 m_inst_opt = make_option("--instance", dest="multi_mode",
620                          help="Filter by instance name [default]",
621                          const=_SHUTDOWN_INSTANCES, action="store_const")
622
623
624 # this is defined separately due to readability only
625 add_opts = [
626   DEBUG_OPT,
627   node_opt,
628   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
629              " a suffix is used",
630              default=20 * 1024, type="unit", metavar="<size>"),
631   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
632              " suffix is used",
633              default=4 * 1024, type="unit", metavar="<size>"),
634   os_opt,
635   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
636               default=128, type="unit", metavar="<mem>"),
637   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
638               default=1, type="int", metavar="<PROC>"),
639   make_option("-t", "--disk-template", dest="disk_template",
640               help="Custom disk setup (diskless, plain, local_raid1 or"
641               " remote_raid1)", default=None, metavar="TEMPL"),
642   make_option("-i", "--ip", dest="ip",
643               help="IP address ('none' [default], 'auto', or specify address)",
644               default='none', type="string", metavar="<ADDRESS>"),
645   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
646               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
647   make_option("--secondary-node", dest="snode",
648               help="Secondary node for remote_raid1 disk layout",
649               metavar="<node>"),
650   make_option("-b", "--bridge", dest="bridge",
651               help="Bridge to connect this instance to",
652               default=None, metavar="<bridge>"),
653   make_option("--no-start", dest="start", default=True,
654               action="store_false", help="Don't start the instance after"
655               " creation"),
656   make_option("--no-ip-check", dest="ip_check", default=True,
657               action="store_false", help="Don't check that the instance's IP"
658               " is alive (only valid with --no-start)"),
659   ]
660
661 commands = {
662   'add': (AddInstance, ARGS_ONE, add_opts,
663           "[opts...] <name>",
664           "Creates and adds a new instance to the cluster"),
665   'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
666                 [DEBUG_OPT, node_opt,
667                  make_option("-b", "--disk", dest="disk", metavar="sdX",
668                              help=("The name of the instance disk for which to"
669                                    " add the mirror"))],
670                 "-n node -b disk <instance>",
671                 "Creates a new mirror for the instance"),
672   'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
673               "<instance>",
674               "Opens a console on the specified instance"),
675   'failover': (FailoverInstance, ARGS_ONE,
676                [DEBUG_OPT, FORCE_OPT,
677                 make_option("--ignore-consistency", dest="ignore_consistency",
678                             action="store_true", default=False,
679                             help="Ignore the consistency of the disks on"
680                             " the secondary"),
681                 ],
682                "[-f] <instance>",
683                "Stops the instance and starts it on the backup node, using"
684                " the remote mirror (only for instances of type remote_raid1)"),
685   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
686            "Show information on the specified instance"),
687   'list': (ListInstances, ARGS_NONE,
688            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
689            "", "Lists the instances and their status"),
690   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
691                 "[-f] <instance>", "Reinstall the instance"),
692   'remove': (RemoveInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT],
693              "[-f] <instance>", "Shuts down the instance and removes it"),
694   'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
695                    [DEBUG_OPT, node_opt,
696                     make_option("-b", "--disk", dest="disk", metavar="sdX",
697                                 help=("The name of the instance disk"
698                                       " for which to add the mirror")),
699                     make_option("-p", "--port", dest="port", metavar="PORT",
700                                 help=("The port of the drbd device"
701                                       " which to remove from the mirror"),
702                                 type="int"),
703                     ],
704                    "-b disk -p port <instance>",
705                    "Removes a mirror from the instance"),
706   'rename': (RenameInstance, ARGS_FIXED(2),
707              [DEBUG_OPT,
708               make_option("--no-ip-check", dest="ignore_ip",
709                           help="Do not check that the IP of the new name"
710                           " is alive",
711                           default=False, action="store_true"),
712               ],
713              "<instance> <new_name>", "Rename the instance"),
714   'replace-disks': (ReplaceDisks, ARGS_ONE,
715                     [DEBUG_OPT,
716                      make_option("-n", "--new-secondary", dest="new_secondary",
717                                  metavar="NODE",
718                                  help=("New secondary node (if you want to"
719                                        " change the secondary)"))],
720                     "[-n NODE] <instance>",
721                     "Replaces all disks for the instance"),
722   'modify': (SetInstanceParms, ARGS_ONE,
723              [DEBUG_OPT, FORCE_OPT,
724               cli_option("-m", "--memory", dest="mem",
725                          help="Memory size",
726                          default=None, type="unit", metavar="<mem>"),
727               make_option("-p", "--cpu", dest="vcpus",
728                           help="Number of virtual CPUs",
729                           default=None, type="int", metavar="<PROC>"),
730               make_option("-i", "--ip", dest="ip",
731                           help="IP address ('none' or numeric IP)",
732                           default=None, type="string", metavar="<ADDRESS>"),
733               make_option("-b", "--bridge", dest="bridge",
734                           help="Bridge to connect this instance to",
735                           default=None, type="string", metavar="<bridge>"),
736               ],
737              "<instance>", "Alters the parameters of an instance"),
738   'shutdown': (ShutdownInstance, ARGS_ANY,
739                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
740                 m_clust_opt, m_inst_opt, m_force_multi],
741                "<instance>", "Stops an instance"),
742   'startup': (StartupInstance, ARGS_ANY,
743               [DEBUG_OPT, FORCE_OPT, m_force_multi,
744                make_option("-e", "--extra", dest="extra_args",
745                            help="Extra arguments for the instance's kernel",
746                            default=None, type="string", metavar="<PARAMS>"),
747                m_node_opt, m_pri_node_opt, m_sec_node_opt,
748                m_clust_opt, m_inst_opt,
749                ],
750             "<instance>", "Starts an instance"),
751   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
752                      "<instance>",
753                      "Activate an instance's disks"),
754   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
755                        "<instance>",
756                        "Deactivate an instance's disks"),
757   }
758
759 if __name__ == '__main__':
760   sys.exit(GenericMain(commands))