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 (pnode, snode) = SplitNodeOption(opts.node)
221 op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
222 disk_size=opts.size, swap_size=opts.swap,
223 disk_template=opts.disk_template,
224 mode=constants.INSTANCE_CREATE,
225 os_type=opts.os, pnode=pnode,
226 snode=snode, vcpus=opts.vcpus,
227 ip=opts.ip, bridge=opts.bridge,
228 start=opts.start, ip_check=opts.ip_check,
229 wait_for_sync=opts.wait_for_sync)
234 def ReinstallInstance(opts, args):
235 """Reinstall an instance.
238 opts - class with options as members
239 args - list containing a single element, the instance name
242 instance_name = args[0]
245 usertext = ("This will reinstall the instance %s and remove"
246 " all data. Continue?") % instance_name
247 if not AskUser(usertext):
250 op = opcodes.OpReinstallInstance(instance_name=instance_name,
257 def RemoveInstance(opts, args):
258 """Remove an instance.
261 opts - class with options as members
262 args - list containing a single element, the instance name
265 instance_name = args[0]
269 usertext = ("This will remove the volumes of the instance %s"
270 " (including mirrors), thus removing all the data"
271 " of the instance. Continue?") % instance_name
272 if not AskUser(usertext):
275 op = opcodes.OpRemoveInstance(instance_name=instance_name,
276 ignore_failures=opts.ignore_failures)
281 def RenameInstance(opts, args):
282 """Rename an instance.
285 opts - class with options as members
286 args - list containing two elements, the instance name and the new name
289 op = opcodes.OpRenameInstance(instance_name=args[0],
291 ignore_ip=opts.ignore_ip)
297 def ActivateDisks(opts, args):
298 """Activate an instance's disks.
300 This serves two purposes:
301 - it allows one (as long as the instance is not running) to mount
302 the disks and modify them from the node
303 - it repairs inactive secondary drbds
306 instance_name = args[0]
307 op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
308 disks_info = SubmitOpCode(op)
309 for host, iname, nname in disks_info:
310 print "%s:%s:%s" % (host, iname, nname)
314 def DeactivateDisks(opts, args):
315 """Command-line interface for _ShutdownInstanceBlockDevices.
317 This function takes the instance name, looks for its primary node
318 and the tries to shutdown its block devices on that node.
321 instance_name = args[0]
322 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
327 def StartupInstance(opts, args):
328 """Startup an instance.
331 opts - class with options as members
332 args - list containing a single element, the instance name
335 if opts.multi_mode is None:
336 opts.multi_mode = _SHUTDOWN_INSTANCES
337 inames = _ExpandMultiNames(opts.multi_mode, args)
339 raise errors.OpPrereqError("Selection filter does not match any instances")
340 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
341 if not (opts.force_multi or not multi_on
342 or _ConfirmOperation(inames, "startup")):
345 op = opcodes.OpStartupInstance(instance_name=name,
347 extra_args=opts.extra_args)
349 logger.ToStdout("Starting up %s" % name)
353 def RebootInstance(opts, args):
354 """Reboot an instance
357 opts - class with options as members
358 args - list containing a single element, the instance name
361 if opts.multi_mode is None:
362 opts.multi_mode = _SHUTDOWN_INSTANCES
363 inames = _ExpandMultiNames(opts.multi_mode, args)
365 raise errors.OpPrereqError("Selection filter does not match any instances")
366 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
367 if not (opts.force_multi or not multi_on
368 or _ConfirmOperation(inames, "reboot")):
371 op = opcodes.OpRebootInstance(instance_name=name,
372 reboot_type=opts.reboot_type,
373 ignore_secondaries=opts.ignore_secondaries)
378 def ShutdownInstance(opts, args):
379 """Shutdown an instance.
382 opts - class with options as members
383 args - list containing a single element, the instance name
386 if opts.multi_mode is None:
387 opts.multi_mode = _SHUTDOWN_INSTANCES
388 inames = _ExpandMultiNames(opts.multi_mode, args)
390 raise errors.OpPrereqError("Selection filter does not match any instances")
391 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
392 if not (opts.force_multi or not multi_on
393 or _ConfirmOperation(inames, "shutdown")):
396 op = opcodes.OpShutdownInstance(instance_name=name)
398 logger.ToStdout("Shutting down %s" % name)
403 def AddMDDRBDComponent(opts, args):
404 """Add a new component to a remote_raid1 disk.
407 opts - class with options as members
408 args - list with a single element, the instance name
411 op = opcodes.OpAddMDDRBDComponent(instance_name=args[0],
413 remote_node=opts.node)
418 def RemoveMDDRBDComponent(opts, args):
419 """Remove a component from a remote_raid1 disk.
422 opts - class with options as members
423 args - list with a single element, the instance name
426 op = opcodes.OpRemoveMDDRBDComponent(instance_name=args[0],
433 def ReplaceDisks(opts, args):
434 """Replace the disks of an instance
437 opts - class with options as members
438 args - list with a single element, the instance name
441 instance_name = args[0]
442 new_2ndary = opts.new_secondary
443 if opts.disks is None:
444 disks = ["sda", "sdb"]
446 disks = opts.disks.split(",")
447 if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
448 mode = constants.REPLACE_DISK_ALL
449 elif opts.on_primary: # only on primary:
450 mode = constants.REPLACE_DISK_PRI
451 if new_2ndary is not None:
452 raise errors.OpPrereqError("Can't change secondary node on primary disk"
454 elif opts.on_secondary is not None: # only on secondary
455 mode = constants.REPLACE_DISK_SEC
457 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
458 remote_node=new_2ndary, mode=mode)
463 def FailoverInstance(opts, args):
464 """Failover an instance.
466 The failover is done by shutting it down on its present node and
467 starting it on the secondary.
470 opts - class with options as members
471 args - list with a single element, the instance name
473 force - whether to failover without asking questions.
476 instance_name = args[0]
480 usertext = ("Failover will happen to image %s."
481 " This requires a shutdown of the instance. Continue?" %
483 if not AskUser(usertext):
486 op = opcodes.OpFailoverInstance(instance_name=instance_name,
487 ignore_consistency=opts.ignore_consistency)
492 def ConnectToInstanceConsole(opts, args):
493 """Connect to the console of an instance.
496 opts - class with options as members
497 args - list with a single element, the instance name
500 instance_name = args[0]
502 op = opcodes.OpConnectConsole(instance_name=instance_name)
503 cmd, argv = SubmitOpCode(op)
504 # drop lock and exec so other commands can run while we have console
509 sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
510 (cmd, " ".join(argv)))
514 def _FormatBlockDevInfo(buf, dev, indent_level):
515 """Show block device information.
517 This is only used by ShowInstanceConfig(), but it's too big to be
518 left for an inline definition.
521 def helper(buf, dtype, status):
522 """Format one line for phsyical device status."""
524 buf.write("not active\n")
526 (path, major, minor, syncp, estt, degr, ldisk) = status
527 buf.write("%s (%d:%d)" % (path, major, minor))
528 if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
529 if syncp is not None:
530 sync_text = "*RECOVERING* %5.2f%%," % syncp
532 sync_text += " ETA %ds" % estt
534 sync_text += " ETA unknown"
536 sync_text = "in sync"
538 degr_text = "*DEGRADED*"
542 ldisk_text = " *MISSING DISK*"
545 buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
546 elif dtype == constants.LD_LV:
548 ldisk_text = " *FAILED* (failed drive?)"
551 buf.write(ldisk_text)
554 if dev["iv_name"] is not None:
555 data = " - %s, " % dev["iv_name"]
558 data += "type: %s" % dev["dev_type"]
559 if dev["logical_id"] is not None:
560 data += ", logical_id: %s" % (dev["logical_id"],)
561 elif dev["physical_id"] is not None:
562 data += ", physical_id: %s" % (dev["physical_id"],)
563 buf.write("%*s%s\n" % (2*indent_level, "", data))
564 buf.write("%*s primary: " % (2*indent_level, ""))
565 helper(buf, dev["dev_type"], dev["pstatus"])
568 buf.write("%*s secondary: " % (2*indent_level, ""))
569 helper(buf, dev["dev_type"], dev["sstatus"])
572 for child in dev["children"]:
573 _FormatBlockDevInfo(buf, child, indent_level+1)
576 def ShowInstanceConfig(opts, args):
577 """Compute instance run-time status.
581 op = opcodes.OpQueryInstanceData(instances=args)
582 result = SubmitOpCode(op)
585 logger.ToStdout("No instances.")
590 for instance_name in result:
591 instance = result[instance_name]
592 buf.write("Instance name: %s\n" % instance["name"])
593 buf.write("State: configured to be %s, actual state is %s\n" %
594 (instance["config_state"], instance["run_state"]))
595 buf.write(" Nodes:\n")
596 buf.write(" - primary: %s\n" % instance["pnode"])
597 buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"]))
598 buf.write(" Operating system: %s\n" % instance["os"])
599 buf.write(" Hardware:\n")
600 buf.write(" - VCPUs: %d\n" % instance["vcpus"])
601 buf.write(" - memory: %dMiB\n" % instance["memory"])
602 buf.write(" - NICs: %s\n" %
603 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
605 for mac, ip, bridge in instance["nics"]]))
606 buf.write(" Block devices:\n")
608 for device in instance["disks"]:
609 _FormatBlockDevInfo(buf, device, 1)
611 logger.ToStdout(buf.getvalue().rstrip('\n'))
615 def SetInstanceParms(opts, args):
616 """Modifies an instance.
618 All parameters take effect only at the next restart of the instance.
621 opts - class with options as members
622 args - list with a single element, the instance name
624 memory - the new memory size
625 vcpus - the new number of cpus
628 if not opts.mem and not opts.vcpus and not opts.ip and not opts.bridge:
629 logger.ToStdout("Please give at least one of the parameters.")
632 op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
633 vcpus=opts.vcpus, ip=opts.ip,
635 result = SubmitOpCode(op)
638 logger.ToStdout("Modified instance %s" % args[0])
639 for param, data in result:
640 logger.ToStdout(" - %-5s -> %s" % (param, data))
641 logger.ToStdout("Please don't forget that these parameters take effect"
642 " only at the next start of the instance.")
646 # options used in more than one cmd
647 node_opt = make_option("-n", "--node", dest="node", help="Target node",
650 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
653 # multi-instance selection options
654 m_force_multi = make_option("--force-multiple", dest="force_multi",
655 help="Do not ask for confirmation when more than"
656 " one instance is affected",
657 action="store_true", default=False)
659 m_pri_node_opt = make_option("--primary", dest="multi_mode",
660 help="Filter by nodes (primary only)",
661 const=_SHUTDOWN_NODES_PRI, action="store_const")
663 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
664 help="Filter by nodes (secondary only)",
665 const=_SHUTDOWN_NODES_SEC, action="store_const")
667 m_node_opt = make_option("--node", dest="multi_mode",
668 help="Filter by nodes (primary and secondary)",
669 const=_SHUTDOWN_NODES_BOTH, action="store_const")
671 m_clust_opt = make_option("--all", dest="multi_mode",
672 help="Select all instances in the cluster",
673 const=_SHUTDOWN_CLUSTER, action="store_const")
675 m_inst_opt = make_option("--instance", dest="multi_mode",
676 help="Filter by instance name [default]",
677 const=_SHUTDOWN_INSTANCES, action="store_const")
680 # this is defined separately due to readability only
683 make_option("-n", "--node", dest="node",
684 help="Target node and optional secondary node",
685 metavar="<pnode>[:<snode>]"),
686 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
688 default=20 * 1024, type="unit", metavar="<size>"),
689 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
691 default=4 * 1024, type="unit", metavar="<size>"),
693 cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
694 default=128, type="unit", metavar="<mem>"),
695 make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
696 default=1, type="int", metavar="<PROC>"),
697 make_option("-t", "--disk-template", dest="disk_template",
698 help="Custom disk setup (diskless, plain, local_raid1,"
699 " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
700 make_option("-i", "--ip", dest="ip",
701 help="IP address ('none' [default], 'auto', or specify address)",
702 default='none', type="string", metavar="<ADDRESS>"),
703 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
704 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
705 make_option("-b", "--bridge", dest="bridge",
706 help="Bridge to connect this instance to",
707 default=None, metavar="<bridge>"),
708 make_option("--no-start", dest="start", default=True,
709 action="store_false", help="Don't start the instance after"
711 make_option("--no-ip-check", dest="ip_check", default=True,
712 action="store_false", help="Don't check that the instance's IP"
713 " is alive (only valid with --no-start)"),
717 'add': (AddInstance, ARGS_ONE, add_opts,
719 "Creates and adds a new instance to the cluster"),
720 'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
721 [DEBUG_OPT, node_opt,
722 make_option("-b", "--disk", dest="disk", metavar="sdX",
723 help=("The name of the instance disk for which to"
724 " add the mirror"))],
725 "-n node -b disk <instance>",
726 "Creates a new mirror for the instance"),
727 'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
729 "Opens a console on the specified instance"),
730 'failover': (FailoverInstance, ARGS_ONE,
731 [DEBUG_OPT, FORCE_OPT,
732 make_option("--ignore-consistency", dest="ignore_consistency",
733 action="store_true", default=False,
734 help="Ignore the consistency of the disks on"
738 "Stops the instance and starts it on the backup node, using"
739 " the remote mirror (only for instances of type remote_raid1)"),
740 'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
741 "Show information on the specified instance"),
742 'list': (ListInstances, ARGS_NONE,
743 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
744 "", "Lists the instances and their status"),
745 'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
746 "[-f] <instance>", "Reinstall the instance"),
747 'remove': (RemoveInstance, ARGS_ONE,
748 [DEBUG_OPT, FORCE_OPT,
749 make_option("--ignore-failures", dest="ignore_failures",
750 action="store_true", default=False,
751 help=("Remove the instance from the cluster even"
752 " if there are failures during the removal"
753 " process (shutdown, disk removal, etc.)")),
755 "[-f] <instance>", "Shuts down the instance and removes it"),
756 'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
757 [DEBUG_OPT, node_opt,
758 make_option("-b", "--disk", dest="disk", metavar="sdX",
759 help=("The name of the instance disk"
760 " for which to add the mirror")),
761 make_option("-p", "--port", dest="port", metavar="PORT",
762 help=("The port of the drbd device"
763 " which to remove from the mirror"),
766 "-b disk -p port <instance>",
767 "Removes a mirror from the instance"),
768 'rename': (RenameInstance, ARGS_FIXED(2),
770 make_option("--no-ip-check", dest="ignore_ip",
771 help="Do not check that the IP of the new name"
773 default=False, action="store_true"),
775 "<instance> <new_name>", "Rename the instance"),
776 'replace-disks': (ReplaceDisks, ARGS_ONE,
778 make_option("-n", "--new-secondary", dest="new_secondary",
779 help=("New secondary node (for secondary"
780 " node change)"), metavar="NODE"),
781 make_option("-p", "--on-primary", dest="on_primary",
782 default=False, action="store_true",
783 help=("Replace the disk(s) on the primary"
784 " node (only for the drbd template)")),
785 make_option("-s", "--on-secondary", dest="on_secondary",
786 default=False, action="store_true",
787 help=("Replace the disk(s) on the secondary"
788 " node (only for the drbd template)")),
789 make_option("--disks", dest="disks", default=None,
790 help=("Comma-separated list of disks"
791 " to replace (e.g. sda) (optional,"
792 " defaults to all disks")),
794 "[-n NODE] <instance>",
795 "Replaces all disks for the instance"),
796 'modify': (SetInstanceParms, ARGS_ONE,
797 [DEBUG_OPT, FORCE_OPT,
798 cli_option("-m", "--memory", dest="mem",
800 default=None, type="unit", metavar="<mem>"),
801 make_option("-p", "--cpu", dest="vcpus",
802 help="Number of virtual CPUs",
803 default=None, type="int", metavar="<PROC>"),
804 make_option("-i", "--ip", dest="ip",
805 help="IP address ('none' or numeric IP)",
806 default=None, type="string", metavar="<ADDRESS>"),
807 make_option("-b", "--bridge", dest="bridge",
808 help="Bridge to connect this instance to",
809 default=None, type="string", metavar="<bridge>"),
811 "<instance>", "Alters the parameters of an instance"),
812 'shutdown': (ShutdownInstance, ARGS_ANY,
813 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
814 m_clust_opt, m_inst_opt, m_force_multi],
815 "<instance>", "Stops an instance"),
816 'startup': (StartupInstance, ARGS_ANY,
817 [DEBUG_OPT, FORCE_OPT, m_force_multi,
818 make_option("-e", "--extra", dest="extra_args",
819 help="Extra arguments for the instance's kernel",
820 default=None, type="string", metavar="<PARAMS>"),
821 m_node_opt, m_pri_node_opt, m_sec_node_opt,
822 m_clust_opt, m_inst_opt,
824 "<instance>", "Starts an instance"),
826 'reboot': (RebootInstance, ARGS_ANY,
827 [DEBUG_OPT, m_force_multi,
828 make_option("-e", "--extra", dest="extra_args",
829 help="Extra arguments for the instance's kernel",
830 default=None, type="string", metavar="<PARAMS>"),
831 make_option("-t", "--type", dest="reboot_type",
832 help="Type of reboot: soft/hard/full",
833 default=constants.INSTANCE_REBOOT_SOFT,
834 type="string", metavar="<REBOOT>"),
835 make_option("--ignore-secondaries", dest="ignore_secondaries",
836 default=False, action="store_true",
837 help="Ignore errors from secondaries"),
838 m_node_opt, m_pri_node_opt, m_sec_node_opt,
839 m_clust_opt, m_inst_opt,
841 "<instance>", "Reboots an instance"),
842 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
844 "Activate an instance's disks"),
845 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
847 "Deactivate an instance's disks"),
848 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
849 "<node_name>", "List the tags of the given instance"),
850 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
851 "<node_name> tag...", "Add tags to the given instance"),
852 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
853 "<node_name> tag...", "Remove tags from given instance"),
856 if __name__ == '__main__':
857 sys.exit(GenericMain(commands,
858 override={"tag_type": constants.TAG_INSTANCE}))