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