4 # Copyright (C) 2006, 2007 Google Inc.
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.
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.
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
25 from optparse import make_option
26 from cStringIO import StringIO
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
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"
42 def _ExpandMultiNames(mode, names):
43 """Expand the given names using the passed mode.
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
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
61 if mode == _SHUTDOWN_CLUSTER:
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]
68 elif mode in (_SHUTDOWN_NODES_BOTH,
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:
84 elif mode == _SHUTDOWN_NODES_SEC:
87 raise errors.ProgrammerError("Unhandled shutdown type")
89 elif mode == _SHUTDOWN_INSTANCES:
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]
97 raise errors.OpPrereqError("Unknown mode '%s'" % mode)
102 def _ConfirmOperation(inames, text):
103 """Ask the user to confirm an operation on a list of instances.
105 This function is used to request confirmation for doing an operation
106 on a given list of instances.
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').
112 Returns: boolean depending on user's confirmation.
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]))
121 choices = [('y', True, 'Yes, execute the %s' % text),
122 ('n', False, 'No, abort the %s' % text)]
125 choices.insert(1, ('v', 'v', 'View the list of affected instances'))
130 choice = AskUser(ask, choices)
133 choice = AskUser(choices, msg + affected)
137 def ListInstances(opts, args):
138 """List instances and their properties.
141 if opts.output is None:
142 selected_fields = ["name", "os", "pnode", "admin_state",
143 "oper_state", "oper_ram"]
145 selected_fields = opts.output.split(",")
147 op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
148 output = SubmitOpCode(op)
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",
157 "sda_size": "Disk/0", "sdb_size": "Disk/1"}
161 if opts.human_readable:
162 unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
166 numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
168 # change raw values to nicer strings
170 for idx, field in enumerate(selected_fields):
172 if field == "snodes":
173 val = ",".join(val) or "-"
174 elif field == "admin_state":
179 elif field == "oper_state":
186 elif field == "oper_ram":
189 elif field == "sda_size" or field == "sdb_size":
194 data = GenerateTable(separator=opts.separator, headers=headers,
195 fields=selected_fields, unitfields=unitfields,
196 numfields=numfields, data=output)
199 logger.ToStdout(line)
204 def AddInstance(opts, args):
205 """Add an instance to the cluster.
208 opts - class with options as members
209 args - list with a single element, the instance name
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
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)
232 def ReinstallInstance(opts, args):
233 """Reinstall an instance.
236 opts - class with options as members
237 args - list containing a single element, the instance name
240 instance_name = args[0]
243 usertext = ("This will reinstall the instance %s and remove "
244 "all data. Continue?") % instance_name
245 if not AskUser(usertext):
248 op = opcodes.OpReinstallInstance(instance_name=instance_name,
255 def RemoveInstance(opts, args):
256 """Remove an instance.
259 opts - class with options as members
260 args - list containing a single element, the instance name
263 instance_name = args[0]
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):
273 op = opcodes.OpRemoveInstance(instance_name=instance_name,
274 ignore_failures=opts.ignore_failures)
279 def RenameInstance(opts, args):
280 """Rename an instance.
283 opts - class with options as members
284 args - list containing two elements, the instance name and the new name
287 op = opcodes.OpRenameInstance(instance_name=args[0],
289 ignore_ip=opts.ignore_ip)
295 def ActivateDisks(opts, args):
296 """Activate an instance's disks.
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
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)
312 def DeactivateDisks(opts, args):
313 """Command-line interface for _ShutdownInstanceBlockDevices.
315 This function takes the instance name, looks for its primary node
316 and the tries to shutdown its block devices on that node.
319 instance_name = args[0]
320 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
325 def StartupInstance(opts, args):
326 """Startup an instance.
329 opts - class with options as members
330 args - list containing a single element, the instance name
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")):
341 op = opcodes.OpStartupInstance(instance_name=name,
343 extra_args=opts.extra_args)
345 logger.ToStdout("Starting up %s" % name)
349 def RebootInstance(opts, args):
350 """Reboot an instance
353 opts - class with options as members
354 args - list containing a single element, the instance name
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")):
365 op = opcodes.OpRebootInstance(instance_name=name,
366 reboot_type=opts.reboot_type,
367 ignore_secondaries=opts.ignore_secondaries)
372 def ShutdownInstance(opts, args):
373 """Shutdown an instance.
376 opts - class with options as members
377 args - list containing a single element, the instance name
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")):
388 op = opcodes.OpShutdownInstance(instance_name=name)
390 logger.ToStdout("Shutting down %s" % name)
395 def AddMDDRBDComponent(opts, args):
396 """Add a new component to a remote_raid1 disk.
399 opts - class with options as members
400 args - list with a single element, the instance name
403 op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
405 remote_node=opts.node)
410 def RemoveMDDRBDComponent(opts, args):
411 """Remove a component from a remote_raid1 disk.
414 opts - class with options as members
415 args - list with a single element, the instance name
418 op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
425 def ReplaceDisks(opts, args):
426 """Replace the disks of an instance
429 opts - class with options as members
430 args - list with a single element, the instance name
433 instance_name = args[0]
434 new_2ndary = opts.new_secondary
435 if opts.disks is None:
436 disks = ["sda", "sdb"]
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"
446 elif opts.on_secondary is not None: # only on secondary
447 mode = constants.REPLACE_DISK_SEC
449 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
450 remote_node=new_2ndary, mode=mode)
455 def FailoverInstance(opts, args):
456 """Failover an instance.
458 The failover is done by shutting it down on its present node and
459 starting it on the secondary.
462 opts - class with options as members
463 args - list with a single element, the instance name
465 force - whether to failover without asking questions.
468 if opts.multi_mode is None:
469 opts.multi_mode = _SHUTDOWN_INSTANCES
470 inames = _ExpandMultiNames(opts.multi_mode, args)
472 logger.ToStderr("No instances match the selected options")
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)")):
481 op = opcodes.OpFailoverInstance(instance_name=name,
482 ignore_consistency=opts.ignore_consistency)
484 logger.ToStdout("Failing over instance %s" % name)
487 except errors.OpExecError, err:
489 _, err_msg = FormatError(err)
490 logger.ToStderr(err_msg)
495 def ConnectToInstanceConsole(opts, args):
496 """Connect to the console of an instance.
499 opts - class with options as members
500 args - list with a single element, the instance name
503 instance_name = args[0]
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
512 sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
513 (cmd, " ".join(argv)))
517 def _FormatBlockDevInfo(buf, dev, indent_level):
518 """Show block device information.
520 This is only used by ShowInstanceConfig(), but it's too big to be
521 left for an inline definition.
524 def helper(buf, dtype, status):
525 """Format one line for phsyical device status."""
527 buf.write("not active\n")
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
535 sync_text += " ETA %ds" % estt
537 sync_text += " ETA unknown"
539 sync_text = "in sync"
541 degr_text = "*DEGRADED*"
544 buf.write(" %s, status %s" % (sync_text, degr_text))
547 if dev["iv_name"] is not None:
548 data = " - %s, " % dev["iv_name"]
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"])
561 buf.write("%*s secondary: " % (2*indent_level, ""))
562 helper(buf, dev["dev_type"], dev["sstatus"])
565 for child in dev["children"]:
566 _FormatBlockDevInfo(buf, child, indent_level+1)
569 def ShowInstanceConfig(opts, args):
570 """Compute instance run-time status.
574 op = opcodes.OpQueryInstanceData(instances=args)
575 result = SubmitOpCode(op)
578 logger.ToStdout("No instances.")
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}" %
598 for mac, ip, bridge in instance["nics"]]))
599 buf.write(" Block devices:\n")
601 for device in instance["disks"]:
602 _FormatBlockDevInfo(buf, device, 1)
604 logger.ToStdout(buf.getvalue().rstrip('\n'))
608 def SetInstanceParms(opts, args):
609 """Modifies an instance.
611 All parameters take effect only at the next restart of the instance.
614 opts - class with options as members
615 args - list with a single element, the instance name
617 memory - the new memory size
618 vcpus - the new number of cpus
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.")
625 op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
626 vcpus=opts.vcpus, ip=opts.ip,
628 result = SubmitOpCode(op)
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.")
639 # options used in more than one cmd
640 node_opt = make_option("-n", "--node", dest="node", help="Target node",
643 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
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)
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")
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")
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")
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")
668 m_inst_opt = make_option("--instance", dest="multi_mode",
669 help="Filter by instance name [default]",
670 const=_SHUTDOWN_INSTANCES, action="store_const")
673 # this is defined separately due to readability only
677 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
679 default=20 * 1024, type="unit", metavar="<size>"),
680 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
682 default=4 * 1024, type="unit", metavar="<size>"),
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",
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"
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)"),
711 'add': (AddInstance, ARGS_ONE, add_opts,
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],
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"
730 m_pri_node_opt, m_sec_node_opt, m_inst_opt, m_force_multi,
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.)")),
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"),
761 "-b disk -p port <instance>",
762 "Removes a mirror from the instance"),
763 'rename': (RenameInstance, ARGS_FIXED(2),
765 make_option("--no-ip-check", dest="ignore_ip",
766 help="Do not check that the IP of the new name"
768 default=False, action="store_true"),
770 "<instance> <new_name>", "Rename the instance"),
771 'replace-disks': (ReplaceDisks, ARGS_ONE,
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")),
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",
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>"),
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,
819 "<instance>", "Starts an instance"),
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,
836 "<instance>", "Reboots an instance"),
837 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
839 "Activate an instance's disks"),
840 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
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"),
851 if __name__ == '__main__':
852 sys.exit(GenericMain(commands,
853 override={"tag_type": constants.TAG_INSTANCE}))