Make DiagnoseOS use the modified OS objects
[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 instances 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                                 ignore_failures=opts.ignore_failures)
275   SubmitOpCode(op)
276   return 0
277
278
279 def RenameInstance(opts, args):
280   """Rename an instance.
281
282   Args:
283     opts - class with options as members
284     args - list containing two elements, the instance name and the new name
285
286   """
287   op = opcodes.OpRenameInstance(instance_name=args[0],
288                                 new_name=args[1],
289                                 ignore_ip=opts.ignore_ip)
290   SubmitOpCode(op)
291
292   return 0
293
294
295 def ActivateDisks(opts, args):
296   """Activate an instance's disks.
297
298   This serves two purposes:
299     - it allows one (as long as the instance is not running) to mount
300     the disks and modify them from the node
301     - it repairs inactive secondary drbds
302
303   """
304   instance_name = args[0]
305   op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
306   disks_info = SubmitOpCode(op)
307   for host, iname, nname in disks_info:
308     print "%s:%s:%s" % (host, iname, nname)
309   return 0
310
311
312 def DeactivateDisks(opts, args):
313   """Command-line interface for _ShutdownInstanceBlockDevices.
314
315   This function takes the instance name, looks for its primary node
316   and the tries to shutdown its block devices on that node.
317
318   """
319   instance_name = args[0]
320   op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
321   SubmitOpCode(op)
322   return 0
323
324
325 def StartupInstance(opts, args):
326   """Startup an instance.
327
328   Args:
329     opts - class with options as members
330     args - list containing a single element, the instance name
331
332   """
333   if opts.multi_mode is None:
334     opts.multi_mode = _SHUTDOWN_INSTANCES
335   inames = _ExpandMultiNames(opts.multi_mode, args)
336   if not inames:
337     raise errors.OpPrereqError("Selection filter does not match any instances")
338   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
339   if not (opts.force_multi or not multi_on
340           or _ConfirmOperation(inames, "startup")):
341     return 1
342   for name in inames:
343     op = opcodes.OpStartupInstance(instance_name=name,
344                                    force=opts.force,
345                                    extra_args=opts.extra_args)
346     if multi_on:
347       logger.ToStdout("Starting up %s" % name)
348     SubmitOpCode(op)
349   return 0
350
351 def RebootInstance(opts, args):
352   """Reboot an instance
353
354   Args:
355     opts - class with options as members
356     args - list containing a single element, the instance name
357
358   """
359   if opts.multi_mode is None:
360     opts.multi_mode = _SHUTDOWN_INSTANCES
361   inames = _ExpandMultiNames(opts.multi_mode, args)
362   if not inames:
363     raise errors.OpPrereqError("Selection filter does not match any instances")
364   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
365   if not (opts.force_multi or not multi_on
366           or _ConfirmOperation(inames, "reboot")):
367     return 1
368   for name in inames:
369     op = opcodes.OpRebootInstance(instance_name=name,
370                                   reboot_type=opts.reboot_type,
371                                   ignore_secondaries=opts.ignore_secondaries)
372
373     SubmitOpCode(op)
374   return 0
375
376 def ShutdownInstance(opts, args):
377   """Shutdown an instance.
378
379   Args:
380     opts - class with options as members
381     args - list containing a single element, the instance name
382
383   """
384   if opts.multi_mode is None:
385     opts.multi_mode = _SHUTDOWN_INSTANCES
386   inames = _ExpandMultiNames(opts.multi_mode, args)
387   if not inames:
388     raise errors.OpPrereqError("Selection filter does not match any instances")
389   multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
390   if not (opts.force_multi or not multi_on
391           or _ConfirmOperation(inames, "shutdown")):
392     return 1
393   for name in inames:
394     op = opcodes.OpShutdownInstance(instance_name=name)
395     if multi_on:
396       logger.ToStdout("Shutting down %s" % name)
397     SubmitOpCode(op)
398   return 0
399
400
401 def AddMDDRBDComponent(opts, args):
402   """Add a new component to a remote_raid1 disk.
403
404   Args:
405     opts - class with options as members
406     args - list with a single element, the instance name
407
408   """
409   op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
410                                     disk_name=opts.disk,
411                                     remote_node=opts.node)
412   SubmitOpCode(op)
413   return 0
414
415
416 def RemoveMDDRBDComponent(opts, args):
417   """Remove a component from a remote_raid1 disk.
418
419   Args:
420     opts - class with options as members
421     args - list with a single element, the instance name
422
423   """
424   op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
425                                        disk_name=opts.disk,
426                                        disk_id=opts.port)
427   SubmitOpCode(op)
428   return 0
429
430
431 def ReplaceDisks(opts, args):
432   """Replace the disks of an instance
433
434   Args:
435     opts - class with options as members
436     args - list with a single element, the instance name
437
438   """
439   instance_name = args[0]
440   new_2ndary = opts.new_secondary
441   if opts.disks is None:
442     disks = ["sda", "sdb"]
443   else:
444     disks = opts.disks.split(",")
445   if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
446     mode = constants.REPLACE_DISK_ALL
447   elif opts.on_primary: # only on primary:
448     mode = constants.REPLACE_DISK_PRI
449     if new_2ndary is not None:
450       raise errors.OpPrereqError("Can't change secondary node on primary disk"
451                                  " replacement")
452   elif opts.on_secondary is not None: # only on secondary
453     mode = constants.REPLACE_DISK_SEC
454
455   op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
456                               remote_node=new_2ndary, mode=mode)
457   SubmitOpCode(op)
458   return 0
459
460
461 def FailoverInstance(opts, args):
462   """Failover an instance.
463
464   The failover is done by shutting it down on its present node and
465   starting it on the secondary.
466
467   Args:
468     opts - class with options as members
469     args - list with a single element, the instance name
470   Opts used:
471     force - whether to failover without asking questions.
472
473   """
474   instance_name = args[0]
475   force = opts.force
476
477   if not force:
478     usertext = ("Failover will happen to image %s."
479                 " This requires a shutdown of the instance. Continue?" %
480                 (instance_name,))
481     if not AskUser(usertext):
482       return 1
483
484   op = opcodes.OpFailoverInstance(instance_name=instance_name,
485                                   ignore_consistency=opts.ignore_consistency)
486   SubmitOpCode(op)
487   return 0
488
489
490 def ConnectToInstanceConsole(opts, args):
491   """Connect to the console of an instance.
492
493   Args:
494     opts - class with options as members
495     args - list with a single element, the instance name
496
497   """
498   instance_name = args[0]
499
500   op = opcodes.OpConnectConsole(instance_name=instance_name)
501   cmd, argv = SubmitOpCode(op)
502   # drop lock and exec so other commands can run while we have console
503   utils.Unlock("cmd")
504   try:
505     os.execvp(cmd, argv)
506   finally:
507     sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
508                      (cmd, " ".join(argv)))
509     os._exit(1)
510
511
512 def _FormatBlockDevInfo(buf, dev, indent_level):
513   """Show block device information.
514
515   This is only used by ShowInstanceConfig(), but it's too big to be
516   left for an inline definition.
517
518   """
519   def helper(buf, dtype, status):
520     """Format one line for phsyical device status."""
521     if not status:
522       buf.write("not active\n")
523     else:
524       (path, major, minor, syncp, estt, degr) = status
525       buf.write("%s (%d:%d)" % (path, major, minor))
526       if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
527         if syncp is not None:
528           sync_text = "*RECOVERING* %5.2f%%," % syncp
529           if estt:
530             sync_text += " ETA %ds" % estt
531           else:
532             sync_text += " ETA unknown"
533         else:
534           sync_text = "in sync"
535         if degr:
536           degr_text = "*DEGRADED*"
537         else:
538           degr_text = "ok"
539         buf.write(" %s, status %s" % (sync_text, degr_text))
540       buf.write("\n")
541
542   if dev["iv_name"] is not None:
543     data = "  - %s, " % dev["iv_name"]
544   else:
545     data = "  - "
546   data += "type: %s" % dev["dev_type"]
547   if dev["logical_id"] is not None:
548     data += ", logical_id: %s" % (dev["logical_id"],)
549   elif dev["physical_id"] is not None:
550     data += ", physical_id: %s" % (dev["physical_id"],)
551   buf.write("%*s%s\n" % (2*indent_level, "", data))
552   buf.write("%*s    primary:   " % (2*indent_level, ""))
553   helper(buf, dev["dev_type"], dev["pstatus"])
554
555   if dev["sstatus"]:
556     buf.write("%*s    secondary: " % (2*indent_level, ""))
557     helper(buf, dev["dev_type"], dev["sstatus"])
558
559   if dev["children"]:
560     for child in dev["children"]:
561       _FormatBlockDevInfo(buf, child, indent_level+1)
562
563
564 def ShowInstanceConfig(opts, args):
565   """Compute instance run-time status.
566
567   """
568   retcode = 0
569   op = opcodes.OpQueryInstanceData(instances=args)
570   result = SubmitOpCode(op)
571
572   if not result:
573     logger.ToStdout("No instances.")
574     return 1
575
576   buf = StringIO()
577   retcode = 0
578   for instance_name in result:
579     instance = result[instance_name]
580     buf.write("Instance name: %s\n" % instance["name"])
581     buf.write("State: configured to be %s, actual state is %s\n" %
582               (instance["config_state"], instance["run_state"]))
583     buf.write("  Nodes:\n")
584     buf.write("    - primary: %s\n" % instance["pnode"])
585     buf.write("    - secondaries: %s\n" % ", ".join(instance["snodes"]))
586     buf.write("  Operating system: %s\n" % instance["os"])
587     buf.write("  Hardware:\n")
588     buf.write("    - VCPUs: %d\n" % instance["vcpus"])
589     buf.write("    - memory: %dMiB\n" % instance["memory"])
590     buf.write("    - NICs: %s\n" %
591         ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
592                    (mac, ip, bridge)
593                      for mac, ip, bridge in instance["nics"]]))
594     buf.write("  Block devices:\n")
595
596     for device in instance["disks"]:
597       _FormatBlockDevInfo(buf, device, 1)
598
599   logger.ToStdout(buf.getvalue().rstrip('\n'))
600   return retcode
601
602
603 def SetInstanceParms(opts, args):
604   """Modifies an instance.
605
606   All parameters take effect only at the next restart of the instance.
607
608   Args:
609     opts - class with options as members
610     args - list with a single element, the instance name
611   Opts used:
612     memory - the new memory size
613     vcpus - the new number of cpus
614
615   """
616   if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
617     logger.ToStdout("Please give at least one of the parameters.")
618     return 1
619
620   op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
621                                   vcpus=opts.vcpus, ip=opts.ip,
622                                   bridge=opts.bridge)
623   result = SubmitOpCode(op)
624
625   if result:
626     logger.ToStdout("Modified instance %s" % args[0])
627     for param, data in result:
628       logger.ToStdout(" - %-5s -> %s" % (param, data))
629     logger.ToStdout("Please don't forget that these parameters take effect"
630                     " only at the next start of the instance.")
631   return 0
632
633
634 # options used in more than one cmd
635 node_opt = make_option("-n", "--node", dest="node", help="Target node",
636                        metavar="<node>")
637
638 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
639                     metavar="<os>")
640
641 # multi-instance selection options
642 m_force_multi = make_option("--force-multiple", dest="force_multi",
643                             help="Do not ask for confirmation when more than"
644                             " one instance is affected",
645                             action="store_true", default=False)
646
647 m_pri_node_opt = make_option("--primary", dest="multi_mode",
648                              help="Filter by nodes (primary only)",
649                              const=_SHUTDOWN_NODES_PRI, action="store_const")
650
651 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
652                              help="Filter by nodes (secondary only)",
653                              const=_SHUTDOWN_NODES_SEC, action="store_const")
654
655 m_node_opt = make_option("--node", dest="multi_mode",
656                          help="Filter by nodes (primary and secondary)",
657                          const=_SHUTDOWN_NODES_BOTH, action="store_const")
658
659 m_clust_opt = make_option("--all", dest="multi_mode",
660                           help="Select all instances in the cluster",
661                           const=_SHUTDOWN_CLUSTER, action="store_const")
662
663 m_inst_opt = make_option("--instance", dest="multi_mode",
664                          help="Filter by instance name [default]",
665                          const=_SHUTDOWN_INSTANCES, action="store_const")
666
667
668 # this is defined separately due to readability only
669 add_opts = [
670   DEBUG_OPT,
671   node_opt,
672   cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
673              " a suffix is used",
674              default=20 * 1024, type="unit", metavar="<size>"),
675   cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
676              " suffix is used",
677              default=4 * 1024, type="unit", metavar="<size>"),
678   os_opt,
679   cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
680               default=128, type="unit", metavar="<mem>"),
681   make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
682               default=1, type="int", metavar="<PROC>"),
683   make_option("-t", "--disk-template", dest="disk_template",
684               help="Custom disk setup (diskless, plain, local_raid1,"
685               " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
686   make_option("-i", "--ip", dest="ip",
687               help="IP address ('none' [default], 'auto', or specify address)",
688               default='none', type="string", metavar="<ADDRESS>"),
689   make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
690               action="store_false", help="Don't wait for sync (DANGEROUS!)"),
691   make_option("--secondary-node", dest="snode",
692               help="Secondary node for remote_raid1 disk layout",
693               metavar="<node>"),
694   make_option("-b", "--bridge", dest="bridge",
695               help="Bridge to connect this instance to",
696               default=None, metavar="<bridge>"),
697   make_option("--no-start", dest="start", default=True,
698               action="store_false", help="Don't start the instance after"
699               " creation"),
700   make_option("--no-ip-check", dest="ip_check", default=True,
701               action="store_false", help="Don't check that the instance's IP"
702               " is alive (only valid with --no-start)"),
703   ]
704
705 commands = {
706   'add': (AddInstance, ARGS_ONE, add_opts,
707           "[opts...] <name>",
708           "Creates and adds a new instance to the cluster"),
709   'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
710                 [DEBUG_OPT, node_opt,
711                  make_option("-b", "--disk", dest="disk", metavar="sdX",
712                              help=("The name of the instance disk for which to"
713                                    " add the mirror"))],
714                 "-n node -b disk <instance>",
715                 "Creates a new mirror for the instance"),
716   'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
717               "<instance>",
718               "Opens a console on the specified instance"),
719   'failover': (FailoverInstance, ARGS_ONE,
720                [DEBUG_OPT, FORCE_OPT,
721                 make_option("--ignore-consistency", dest="ignore_consistency",
722                             action="store_true", default=False,
723                             help="Ignore the consistency of the disks on"
724                             " the secondary"),
725                 ],
726                "[-f] <instance>",
727                "Stops the instance and starts it on the backup node, using"
728                " the remote mirror (only for instances of type remote_raid1)"),
729   'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
730            "Show information on the specified instance"),
731   'list': (ListInstances, ARGS_NONE,
732            [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
733            "", "Lists the instances and their status"),
734   'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
735                 "[-f] <instance>", "Reinstall the instance"),
736   'remove': (RemoveInstance, ARGS_ONE,
737              [DEBUG_OPT, FORCE_OPT,
738               make_option("--ignore-failures", dest="ignore_failures",
739                           action="store_true", default=False,
740                           help=("Remove the instance from the cluster even"
741                                 " if there are failures during the removal"
742                                 " process (shutdown, disk removal, etc.)")),
743               ],
744              "[-f] <instance>", "Shuts down the instance and removes it"),
745   'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
746                    [DEBUG_OPT, node_opt,
747                     make_option("-b", "--disk", dest="disk", metavar="sdX",
748                                 help=("The name of the instance disk"
749                                       " for which to add the mirror")),
750                     make_option("-p", "--port", dest="port", metavar="PORT",
751                                 help=("The port of the drbd device"
752                                       " which to remove from the mirror"),
753                                 type="int"),
754                     ],
755                    "-b disk -p port <instance>",
756                    "Removes a mirror from the instance"),
757   'rename': (RenameInstance, ARGS_FIXED(2),
758              [DEBUG_OPT,
759               make_option("--no-ip-check", dest="ignore_ip",
760                           help="Do not check that the IP of the new name"
761                           " is alive",
762                           default=False, action="store_true"),
763               ],
764              "<instance> <new_name>", "Rename the instance"),
765   'replace-disks': (ReplaceDisks, ARGS_ONE,
766                     [DEBUG_OPT,
767                      make_option("-n", "--new-secondary", dest="new_secondary",
768                                  help=("New secondary node (for secondary"
769                                        " node change)"), metavar="NODE"),
770                      make_option("-p", "--on-primary", dest="on_primary",
771                                  default=False, action="store_true",
772                                  help=("Replace the disk(s) on the primary"
773                                        " node (only for the drbd8 template)")),
774                      make_option("-s", "--on-secondary", dest="on_secondary",
775                                  default=False, action="store_true",
776                                  help=("Replace the disk(s) on the secondary"
777                                        " node (only for the drbd8 template)")),
778                      make_option("--disks", dest="disks", default=None,
779                                  help=("Comma-separated list of disks"
780                                        " to replace (e.g. sda) (optional,"
781                                        " defaults to all disks")),
782                      ],
783                     "[-n NODE] <instance>",
784                     "Replaces all disks for the instance"),
785   'modify': (SetInstanceParms, ARGS_ONE,
786              [DEBUG_OPT, FORCE_OPT,
787               cli_option("-m", "--memory", dest="mem",
788                          help="Memory size",
789                          default=None, type="unit", metavar="<mem>"),
790               make_option("-p", "--cpu", dest="vcpus",
791                           help="Number of virtual CPUs",
792                           default=None, type="int", metavar="<PROC>"),
793               make_option("-i", "--ip", dest="ip",
794                           help="IP address ('none' or numeric IP)",
795                           default=None, type="string", metavar="<ADDRESS>"),
796               make_option("-b", "--bridge", dest="bridge",
797                           help="Bridge to connect this instance to",
798                           default=None, type="string", metavar="<bridge>"),
799               ],
800              "<instance>", "Alters the parameters of an instance"),
801   'shutdown': (ShutdownInstance, ARGS_ANY,
802                [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
803                 m_clust_opt, m_inst_opt, m_force_multi],
804                "<instance>", "Stops an instance"),
805   'startup': (StartupInstance, ARGS_ANY,
806               [DEBUG_OPT, FORCE_OPT, m_force_multi,
807                make_option("-e", "--extra", dest="extra_args",
808                            help="Extra arguments for the instance's kernel",
809                            default=None, type="string", metavar="<PARAMS>"),
810                m_node_opt, m_pri_node_opt, m_sec_node_opt,
811                m_clust_opt, m_inst_opt,
812                ],
813             "<instance>", "Starts an instance"),
814
815   'reboot': (RebootInstance, ARGS_ANY,
816               [DEBUG_OPT, m_force_multi,
817                make_option("-e", "--extra", dest="extra_args",
818                            help="Extra arguments for the instance's kernel",
819                            default=None, type="string", metavar="<PARAMS>"),
820                make_option("-t", "--type", dest="reboot_type",
821                            help="Type of reboot: soft/hard/full",
822                            default=constants.INSTANCE_REBOOT_SOFT,
823                            type="string", metavar="<REBOOT>"),
824                make_option("--ignore-secondaries", dest="ignore_secondaries",
825                            default=False, action="store_true",
826                            help="Ignore errors from secondaries"),
827                m_node_opt, m_pri_node_opt, m_sec_node_opt,
828                m_clust_opt, m_inst_opt,
829                ],
830             "<instance>", "Reboots an instance"),
831   'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
832                      "<instance>",
833                      "Activate an instance's disks"),
834   'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
835                        "<instance>",
836                        "Deactivate an instance's disks"),
837   'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
838                 "<node_name>", "List the tags of the given instance"),
839   'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
840                "<node_name> tag...", "Add tags to the given instance"),
841   'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
842                   "<node_name> tag...", "Remove tags from given instance"),
843   }
844
845 if __name__ == '__main__':
846   sys.exit(GenericMain(commands,
847                        override={"tag_type": constants.TAG_INSTANCE}))