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, mac=opts.mac)
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(" Allocated network port: %s\n" % instance["network_port"])
600 buf.write(" Hardware:\n")
601 buf.write(" - VCPUs: %d\n" % instance["vcpus"])
602 buf.write(" - memory: %dMiB\n" % instance["memory"])
603 buf.write(" - NICs: %s\n" %
604 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
606 for mac, ip, bridge in instance["nics"]]))
607 buf.write(" Block devices:\n")
609 for device in instance["disks"]:
610 _FormatBlockDevInfo(buf, device, 1)
612 logger.ToStdout(buf.getvalue().rstrip('\n'))
616 def SetInstanceParms(opts, args):
617 """Modifies an instance.
619 All parameters take effect only at the next restart of the instance.
622 opts - class with options as members
623 args - list with a single element, the instance name
625 memory - the new memory size
626 vcpus - the new number of cpus
627 mac - the new MAC address of the instance
630 if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac):
631 logger.ToStdout("Please give at least one of the parameters.")
634 op = opcodes.OpSetInstanceParms(instance_name=args[0], mem=opts.mem,
635 vcpus=opts.vcpus, ip=opts.ip,
636 bridge=opts.bridge, mac=opts.mac)
637 result = SubmitOpCode(op)
640 logger.ToStdout("Modified instance %s" % args[0])
641 for param, data in result:
642 logger.ToStdout(" - %-5s -> %s" % (param, data))
643 logger.ToStdout("Please don't forget that these parameters take effect"
644 " only at the next start of the instance.")
648 # options used in more than one cmd
649 node_opt = make_option("-n", "--node", dest="node", help="Target node",
652 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
655 # multi-instance selection options
656 m_force_multi = make_option("--force-multiple", dest="force_multi",
657 help="Do not ask for confirmation when more than"
658 " one instance is affected",
659 action="store_true", default=False)
661 m_pri_node_opt = make_option("--primary", dest="multi_mode",
662 help="Filter by nodes (primary only)",
663 const=_SHUTDOWN_NODES_PRI, action="store_const")
665 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
666 help="Filter by nodes (secondary only)",
667 const=_SHUTDOWN_NODES_SEC, action="store_const")
669 m_node_opt = make_option("--node", dest="multi_mode",
670 help="Filter by nodes (primary and secondary)",
671 const=_SHUTDOWN_NODES_BOTH, action="store_const")
673 m_clust_opt = make_option("--all", dest="multi_mode",
674 help="Select all instances in the cluster",
675 const=_SHUTDOWN_CLUSTER, action="store_const")
677 m_inst_opt = make_option("--instance", dest="multi_mode",
678 help="Filter by instance name [default]",
679 const=_SHUTDOWN_INSTANCES, action="store_const")
682 # this is defined separately due to readability only
685 make_option("-n", "--node", dest="node",
686 help="Target node and optional secondary node",
687 metavar="<pnode>[:<snode>]"),
688 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
690 default=20 * 1024, type="unit", metavar="<size>"),
691 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
693 default=4 * 1024, type="unit", metavar="<size>"),
695 cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
696 default=128, type="unit", metavar="<mem>"),
697 make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
698 default=1, type="int", metavar="<PROC>"),
699 make_option("-t", "--disk-template", dest="disk_template",
700 help="Custom disk setup (diskless, plain, local_raid1,"
701 " remote_raid1 or drbd)", default=None, metavar="TEMPL"),
702 make_option("-i", "--ip", dest="ip",
703 help="IP address ('none' [default], 'auto', or specify address)",
704 default='none', type="string", metavar="<ADDRESS>"),
705 make_option("--mac", dest="mac",
706 help="MAC address ('auto' [default], or specify address)",
707 default='auto', type="string", metavar="<MACADDRESS>"),
708 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
709 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
710 make_option("-b", "--bridge", dest="bridge",
711 help="Bridge to connect this instance to",
712 default=None, metavar="<bridge>"),
713 make_option("--no-start", dest="start", default=True,
714 action="store_false", help="Don't start the instance after"
716 make_option("--no-ip-check", dest="ip_check", default=True,
717 action="store_false", help="Don't check that the instance's IP"
718 " is alive (only valid with --no-start)"),
722 'add': (AddInstance, ARGS_ONE, add_opts,
724 "Creates and adds a new instance to the cluster"),
725 'add-mirror': (AddMDDRBDComponent, ARGS_ONE,
726 [DEBUG_OPT, node_opt,
727 make_option("-b", "--disk", dest="disk", metavar="sdX",
728 help=("The name of the instance disk for which to"
729 " add the mirror"))],
730 "-n node -b disk <instance>",
731 "Creates a new mirror for the instance"),
732 'console': (ConnectToInstanceConsole, ARGS_ONE, [DEBUG_OPT],
734 "Opens a console on the specified instance"),
735 'failover': (FailoverInstance, ARGS_ONE,
736 [DEBUG_OPT, FORCE_OPT,
737 make_option("--ignore-consistency", dest="ignore_consistency",
738 action="store_true", default=False,
739 help="Ignore the consistency of the disks on"
743 "Stops the instance and starts it on the backup node, using"
744 " the remote mirror (only for instances of type remote_raid1)"),
745 'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
746 "Show information on the specified instance"),
747 'list': (ListInstances, ARGS_NONE,
748 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
749 "", "Lists the instances and their status"),
750 'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
751 "[-f] <instance>", "Reinstall the instance"),
752 'remove': (RemoveInstance, ARGS_ONE,
753 [DEBUG_OPT, FORCE_OPT,
754 make_option("--ignore-failures", dest="ignore_failures",
755 action="store_true", default=False,
756 help=("Remove the instance from the cluster even"
757 " if there are failures during the removal"
758 " process (shutdown, disk removal, etc.)")),
760 "[-f] <instance>", "Shuts down the instance and removes it"),
761 'remove-mirror': (RemoveMDDRBDComponent, ARGS_ONE,
762 [DEBUG_OPT, node_opt,
763 make_option("-b", "--disk", dest="disk", metavar="sdX",
764 help=("The name of the instance disk"
765 " for which to add the mirror")),
766 make_option("-p", "--port", dest="port", metavar="PORT",
767 help=("The port of the drbd device"
768 " which to remove from the mirror"),
771 "-b disk -p port <instance>",
772 "Removes a mirror from the instance"),
773 'rename': (RenameInstance, ARGS_FIXED(2),
775 make_option("--no-ip-check", dest="ignore_ip",
776 help="Do not check that the IP of the new name"
778 default=False, action="store_true"),
780 "<instance> <new_name>", "Rename the instance"),
781 'replace-disks': (ReplaceDisks, ARGS_ONE,
783 make_option("-n", "--new-secondary", dest="new_secondary",
784 help=("New secondary node (for secondary"
785 " node change)"), metavar="NODE"),
786 make_option("-p", "--on-primary", dest="on_primary",
787 default=False, action="store_true",
788 help=("Replace the disk(s) on the primary"
789 " node (only for the drbd template)")),
790 make_option("-s", "--on-secondary", dest="on_secondary",
791 default=False, action="store_true",
792 help=("Replace the disk(s) on the secondary"
793 " node (only for the drbd template)")),
794 make_option("--disks", dest="disks", default=None,
795 help=("Comma-separated list of disks"
796 " to replace (e.g. sda) (optional,"
797 " defaults to all disks")),
799 "[-n NODE] <instance>",
800 "Replaces all disks for the instance"),
801 'modify': (SetInstanceParms, ARGS_ONE,
802 [DEBUG_OPT, FORCE_OPT,
803 cli_option("-m", "--memory", dest="mem",
805 default=None, type="unit", metavar="<mem>"),
806 make_option("-p", "--cpu", dest="vcpus",
807 help="Number of virtual CPUs",
808 default=None, type="int", metavar="<PROC>"),
809 make_option("-i", "--ip", dest="ip",
810 help="IP address ('none' or numeric IP)",
811 default=None, type="string", metavar="<ADDRESS>"),
812 make_option("-b", "--bridge", dest="bridge",
813 help="Bridge to connect this instance to",
814 default=None, type="string", metavar="<bridge>"),
815 make_option("--mac", dest="mac",
816 help="MAC address", default=None,
817 type="string", metavar="<MACADDRESS>"),
819 "<instance>", "Alters the parameters of an instance"),
820 'shutdown': (ShutdownInstance, ARGS_ANY,
821 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
822 m_clust_opt, m_inst_opt, m_force_multi],
823 "<instance>", "Stops an instance"),
824 'startup': (StartupInstance, ARGS_ANY,
825 [DEBUG_OPT, FORCE_OPT, m_force_multi,
826 make_option("-e", "--extra", dest="extra_args",
827 help="Extra arguments for the instance's kernel",
828 default=None, type="string", metavar="<PARAMS>"),
829 m_node_opt, m_pri_node_opt, m_sec_node_opt,
830 m_clust_opt, m_inst_opt,
832 "<instance>", "Starts an instance"),
834 'reboot': (RebootInstance, ARGS_ANY,
835 [DEBUG_OPT, m_force_multi,
836 make_option("-e", "--extra", dest="extra_args",
837 help="Extra arguments for the instance's kernel",
838 default=None, type="string", metavar="<PARAMS>"),
839 make_option("-t", "--type", dest="reboot_type",
840 help="Type of reboot: soft/hard/full",
841 default=constants.INSTANCE_REBOOT_SOFT,
842 type="string", metavar="<REBOOT>"),
843 make_option("--ignore-secondaries", dest="ignore_secondaries",
844 default=False, action="store_true",
845 help="Ignore errors from secondaries"),
846 m_node_opt, m_pri_node_opt, m_sec_node_opt,
847 m_clust_opt, m_inst_opt,
849 "<instance>", "Reboots an instance"),
850 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
852 "Activate an instance's disks"),
853 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
855 "Deactivate an instance's disks"),
856 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
857 "<node_name>", "List the tags of the given instance"),
858 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
859 "<node_name> tag...", "Add tags to the given instance"),
860 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
861 "<node_name> tag...", "Remove tags from given instance"),
864 if __name__ == '__main__':
865 sys.exit(GenericMain(commands,
866 override={"tag_type": constants.TAG_INSTANCE}))