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"
44 "name", "os", "pnode", "status", "oper_ram",
47 def _ExpandMultiNames(mode, names):
48 """Expand the given names using the passed mode.
51 - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
52 _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
53 - names, which is a list of names; for cluster, it must be empty,
54 and for node and instance it must be a list of valid item
55 names (short names are valid as usual, e.g. node1 instead of
58 For _SHUTDOWN_CLUSTER, all instances will be returned. For
59 _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
60 primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
61 instances having those nodes as either primary or secondary will be
62 returned. For _SHUTDOWN_INSTANCES, the given instances will be
66 if mode == _SHUTDOWN_CLUSTER:
68 raise errors.OpPrereqError("Cluster filter mode takes no arguments")
69 op = opcodes.OpQueryInstances(output_fields=["name"], names=[])
70 idata = SubmitOpCode(op)
71 inames = [row[0] for row in idata]
73 elif mode in (_SHUTDOWN_NODES_BOTH,
77 raise errors.OpPrereqError("No node names passed")
78 op = opcodes.OpQueryNodes(output_fields=["name", "pinst_list",
79 "sinst_list"], names=names)
80 ndata = SubmitOpCode(op)
81 ipri = [row[1] for row in ndata]
82 pri_names = list(itertools.chain(*ipri))
83 isec = [row[2] for row in ndata]
84 sec_names = list(itertools.chain(*isec))
85 if mode == _SHUTDOWN_NODES_BOTH:
86 inames = pri_names + sec_names
87 elif mode == _SHUTDOWN_NODES_PRI:
89 elif mode == _SHUTDOWN_NODES_SEC:
92 raise errors.ProgrammerError("Unhandled shutdown type")
94 elif mode == _SHUTDOWN_INSTANCES:
96 raise errors.OpPrereqError("No instance names passed")
97 op = opcodes.OpQueryInstances(output_fields=["name"], names=names)
98 idata = SubmitOpCode(op)
99 inames = [row[0] for row in idata]
102 raise errors.OpPrereqError("Unknown mode '%s'" % mode)
107 def _ConfirmOperation(inames, text):
108 """Ask the user to confirm an operation on a list of instances.
110 This function is used to request confirmation for doing an operation
111 on a given list of instances.
113 The inames argument is what the selection algorithm computed, and
114 the text argument is the operation we should tell the user to
115 confirm (e.g. 'shutdown' or 'startup').
117 Returns: boolean depending on user's confirmation.
121 msg = ("The %s will operate on %d instances.\n"
122 "Do you want to continue?" % (text, count))
123 affected = ("\nAffected instances:\n" +
124 "\n".join([" %s" % name for name in inames]))
126 choices = [('y', True, 'Yes, execute the %s' % text),
127 ('n', False, 'No, abort the %s' % text)]
130 choices.insert(1, ('v', 'v', 'View the list of affected instances'))
135 choice = AskUser(ask, choices)
138 choice = AskUser(msg + affected, choices)
142 def _TransformPath(user_input):
143 """Transform a user path into a canonical value.
145 This function transforms the a path passed as textual information
146 into the constants that the LU code expects.
150 if user_input.lower() == "default":
151 result_path = constants.VALUE_DEFAULT
152 elif user_input.lower() == "none":
153 result_path = constants.VALUE_NONE
155 if not os.path.isabs(user_input):
156 raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
158 result_path = user_input
160 result_path = constants.VALUE_DEFAULT
165 def ListInstances(opts, args):
166 """List instances and their properties.
169 if opts.output is None:
170 selected_fields = _LIST_DEF_FIELDS
171 elif opts.output.startswith("+"):
172 selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
174 selected_fields = opts.output.split(",")
176 op = opcodes.OpQueryInstances(output_fields=selected_fields, names=[])
177 output = SubmitOpCode(op)
179 if not opts.no_headers:
181 "name": "Instance", "os": "OS", "pnode": "Primary_node",
182 "snodes": "Secondary_Nodes", "admin_state": "Autostart",
183 "oper_state": "Running", "admin_ram": "Configured_memory",
184 "oper_ram": "Memory", "disk_template": "Disk_template",
185 "ip": "IP Address", "mac": "MAC Address",
186 "bridge": "Bridge", "vcpus": "VCPUs",
187 "sda_size": "Disk/0", "sdb_size": "Disk/1",
193 if opts.human_readable:
194 unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
198 numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
200 # change raw values to nicer strings
202 for idx, field in enumerate(selected_fields):
204 if field == "snodes":
205 val = ",".join(val) or "-"
206 elif field == "admin_state":
211 elif field == "oper_state":
218 elif field == "oper_ram":
221 elif field == "sda_size" or field == "sdb_size":
226 data = GenerateTable(separator=opts.separator, headers=headers,
227 fields=selected_fields, unitfields=unitfields,
228 numfields=numfields, data=output)
231 logger.ToStdout(line)
236 def AddInstance(opts, args):
237 """Add an instance to the cluster.
240 opts - class with options as members
241 args - list with a single element, the instance name
243 mem - amount of memory to allocate to instance (MiB)
244 size - amount of disk space to allocate to instance (MiB)
245 os - which OS to run on instance
246 node - node to run new instance on
251 (pnode, snode) = SplitNodeOption(opts.node)
253 kernel_path = _TransformPath(opts.kernel_path)
254 initrd_path = _TransformPath(opts.initrd_path)
256 op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
257 disk_size=opts.size, swap_size=opts.swap,
258 disk_template=opts.disk_template,
259 mode=constants.INSTANCE_CREATE,
260 os_type=opts.os, pnode=pnode,
261 snode=snode, vcpus=opts.vcpus,
262 ip=opts.ip, bridge=opts.bridge,
263 start=opts.start, ip_check=opts.ip_check,
264 wait_for_sync=opts.wait_for_sync,
266 kernel_path=kernel_path,
267 initrd_path=initrd_path,
268 hvm_boot_order=opts.hvm_boot_order,
269 file_storage_dir=opts.file_storage_dir,
270 file_driver=opts.file_driver,
271 iallocator=opts.iallocator)
276 def ReinstallInstance(opts, args):
277 """Reinstall an instance.
280 opts - class with options as members
281 args - list containing a single element, the instance name
284 instance_name = args[0]
287 usertext = ("This will reinstall the instance %s and remove"
288 " all data. Continue?") % instance_name
289 if not AskUser(usertext):
292 op = opcodes.OpReinstallInstance(instance_name=instance_name,
299 def RemoveInstance(opts, args):
300 """Remove an instance.
303 opts - class with options as members
304 args - list containing a single element, the instance name
307 instance_name = args[0]
311 usertext = ("This will remove the volumes of the instance %s"
312 " (including mirrors), thus removing all the data"
313 " of the instance. Continue?") % instance_name
314 if not AskUser(usertext):
317 op = opcodes.OpRemoveInstance(instance_name=instance_name,
318 ignore_failures=opts.ignore_failures)
323 def RenameInstance(opts, args):
324 """Rename an instance.
327 opts - class with options as members
328 args - list containing two elements, the instance name and the new name
331 op = opcodes.OpRenameInstance(instance_name=args[0],
333 ignore_ip=opts.ignore_ip)
339 def ActivateDisks(opts, args):
340 """Activate an instance's disks.
342 This serves two purposes:
343 - it allows one (as long as the instance is not running) to mount
344 the disks and modify them from the node
345 - it repairs inactive secondary drbds
348 instance_name = args[0]
349 op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
350 disks_info = SubmitOpCode(op)
351 for host, iname, nname in disks_info:
352 print "%s:%s:%s" % (host, iname, nname)
356 def DeactivateDisks(opts, args):
357 """Command-line interface for _ShutdownInstanceBlockDevices.
359 This function takes the instance name, looks for its primary node
360 and the tries to shutdown its block devices on that node.
363 instance_name = args[0]
364 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
369 def StartupInstance(opts, args):
370 """Startup an instance.
373 opts - class with options as members
374 args - list containing a single element, the instance name
377 if opts.multi_mode is None:
378 opts.multi_mode = _SHUTDOWN_INSTANCES
379 inames = _ExpandMultiNames(opts.multi_mode, args)
381 raise errors.OpPrereqError("Selection filter does not match any instances")
382 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
383 if not (opts.force_multi or not multi_on
384 or _ConfirmOperation(inames, "startup")):
387 op = opcodes.OpStartupInstance(instance_name=name,
389 extra_args=opts.extra_args)
391 logger.ToStdout("Starting up %s" % name)
396 def RebootInstance(opts, args):
397 """Reboot an instance
400 opts - class with options as members
401 args - list containing a single element, the instance name
404 if opts.multi_mode is None:
405 opts.multi_mode = _SHUTDOWN_INSTANCES
406 inames = _ExpandMultiNames(opts.multi_mode, args)
408 raise errors.OpPrereqError("Selection filter does not match any instances")
409 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
410 if not (opts.force_multi or not multi_on
411 or _ConfirmOperation(inames, "reboot")):
414 op = opcodes.OpRebootInstance(instance_name=name,
415 reboot_type=opts.reboot_type,
416 ignore_secondaries=opts.ignore_secondaries)
422 def ShutdownInstance(opts, args):
423 """Shutdown an instance.
426 opts - class with options as members
427 args - list containing a single element, the instance name
430 if opts.multi_mode is None:
431 opts.multi_mode = _SHUTDOWN_INSTANCES
432 inames = _ExpandMultiNames(opts.multi_mode, args)
434 raise errors.OpPrereqError("Selection filter does not match any instances")
435 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
436 if not (opts.force_multi or not multi_on
437 or _ConfirmOperation(inames, "shutdown")):
440 op = opcodes.OpShutdownInstance(instance_name=name)
442 logger.ToStdout("Shutting down %s" % name)
447 def ReplaceDisks(opts, args):
448 """Replace the disks of an instance
451 opts - class with options as members
452 args - list with a single element, the instance name
455 instance_name = args[0]
456 new_2ndary = opts.new_secondary
457 iallocator = opts.iallocator
458 if opts.disks is None:
459 disks = ["sda", "sdb"]
461 disks = opts.disks.split(",")
462 if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
463 mode = constants.REPLACE_DISK_ALL
464 elif opts.on_primary: # only on primary:
465 mode = constants.REPLACE_DISK_PRI
466 if new_2ndary is not None or iallocator is not None:
467 raise errors.OpPrereqError("Can't change secondary node on primary disk"
469 elif opts.on_secondary is not None or iallocator is not None:
471 mode = constants.REPLACE_DISK_SEC
473 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
474 remote_node=new_2ndary, mode=mode,
475 iallocator=iallocator)
480 def FailoverInstance(opts, args):
481 """Failover an instance.
483 The failover is done by shutting it down on its present node and
484 starting it on the secondary.
487 opts - class with options as members
488 args - list with a single element, the instance name
490 force - whether to failover without asking questions.
493 instance_name = args[0]
497 usertext = ("Failover will happen to image %s."
498 " This requires a shutdown of the instance. Continue?" %
500 if not AskUser(usertext):
503 op = opcodes.OpFailoverInstance(instance_name=instance_name,
504 ignore_consistency=opts.ignore_consistency)
509 def ConnectToInstanceConsole(opts, args):
510 """Connect to the console of an instance.
513 opts - class with options as members
514 args - list with a single element, the instance name
517 instance_name = args[0]
519 op = opcodes.OpConnectConsole(instance_name=instance_name)
520 cmd = SubmitOpCode(op)
522 if opts.show_command:
523 print utils.ShellQuoteArgs(cmd)
525 # drop lock and exec so other commands can run while we have console
528 os.execvp(cmd[0], cmd)
530 sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
531 (cmd, " ".join(argv)))
535 def _FormatBlockDevInfo(buf, dev, indent_level):
536 """Show block device information.
538 This is only used by ShowInstanceConfig(), but it's too big to be
539 left for an inline definition.
542 def helper(buf, dtype, status):
543 """Format one line for physical device status."""
545 buf.write("not active\n")
547 (path, major, minor, syncp, estt, degr, ldisk) = status
551 major_string = str(major)
556 minor_string = str(minor)
558 buf.write("%s (%s:%s)" % (path, major_string, minor_string))
559 if dtype in (constants.LD_MD_R1, constants.LD_DRBD7, constants.LD_DRBD8):
560 if syncp is not None:
561 sync_text = "*RECOVERING* %5.2f%%," % syncp
563 sync_text += " ETA %ds" % estt
565 sync_text += " ETA unknown"
567 sync_text = "in sync"
569 degr_text = "*DEGRADED*"
573 ldisk_text = " *MISSING DISK*"
576 buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
577 elif dtype == constants.LD_LV:
579 ldisk_text = " *FAILED* (failed drive?)"
582 buf.write(ldisk_text)
585 if dev["iv_name"] is not None:
586 data = " - %s, " % dev["iv_name"]
589 data += "type: %s" % dev["dev_type"]
590 if dev["logical_id"] is not None:
591 data += ", logical_id: %s" % (dev["logical_id"],)
592 elif dev["physical_id"] is not None:
593 data += ", physical_id: %s" % (dev["physical_id"],)
594 buf.write("%*s%s\n" % (2*indent_level, "", data))
595 buf.write("%*s primary: " % (2*indent_level, ""))
596 helper(buf, dev["dev_type"], dev["pstatus"])
599 buf.write("%*s secondary: " % (2*indent_level, ""))
600 helper(buf, dev["dev_type"], dev["sstatus"])
603 for child in dev["children"]:
604 _FormatBlockDevInfo(buf, child, indent_level+1)
607 def ShowInstanceConfig(opts, args):
608 """Compute instance run-time status.
612 op = opcodes.OpQueryInstanceData(instances=args)
613 result = SubmitOpCode(op)
616 logger.ToStdout("No instances.")
621 for instance_name in result:
622 instance = result[instance_name]
623 buf.write("Instance name: %s\n" % instance["name"])
624 buf.write("State: configured to be %s, actual state is %s\n" %
625 (instance["config_state"], instance["run_state"]))
626 buf.write(" Nodes:\n")
627 buf.write(" - primary: %s\n" % instance["pnode"])
628 buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"]))
629 buf.write(" Operating system: %s\n" % instance["os"])
630 buf.write(" Allocated network port: %s\n" % instance["network_port"])
631 if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
632 kpath = "(default: %s)" % constants.XEN_KERNEL
634 kpath = instance["kernel_path"]
635 buf.write(" Kernel path: %s\n" % kpath)
636 if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
637 initrd = "(default: %s)" % constants.XEN_INITRD
638 elif instance["initrd_path"] == constants.VALUE_NONE:
641 initrd = instance["initrd_path"]
642 buf.write(" initrd: %s\n" % initrd)
643 buf.write(" HVM boot order: %s\n" % instance["hvm_boot_order"])
644 buf.write(" Hardware:\n")
645 buf.write(" - VCPUs: %d\n" % instance["vcpus"])
646 buf.write(" - memory: %dMiB\n" % instance["memory"])
647 buf.write(" - NICs: %s\n" %
648 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
650 for mac, ip, bridge in instance["nics"]]))
651 buf.write(" Block devices:\n")
653 for device in instance["disks"]:
654 _FormatBlockDevInfo(buf, device, 1)
656 logger.ToStdout(buf.getvalue().rstrip('\n'))
660 def SetInstanceParams(opts, args):
661 """Modifies an instance.
663 All parameters take effect only at the next restart of the instance.
666 opts - class with options as members
667 args - list with a single element, the instance name
669 memory - the new memory size
670 vcpus - the new number of cpus
671 mac - the new MAC address of the instance
674 if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
675 opts.kernel_path or opts.initrd_path or opts.hvm_boot_order):
676 logger.ToStdout("Please give at least one of the parameters.")
679 kernel_path = _TransformPath(opts.kernel_path)
680 initrd_path = _TransformPath(opts.initrd_path)
681 if opts.hvm_boot_order == 'default':
682 hvm_boot_order = constants.VALUE_DEFAULT
684 hvm_boot_order = opts.hvm_boot_order
686 op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
687 vcpus=opts.vcpus, ip=opts.ip,
688 bridge=opts.bridge, mac=opts.mac,
689 kernel_path=opts.kernel_path,
690 initrd_path=opts.initrd_path,
691 hvm_boot_order=hvm_boot_order)
692 result = SubmitOpCode(op)
695 logger.ToStdout("Modified instance %s" % args[0])
696 for param, data in result:
697 logger.ToStdout(" - %-5s -> %s" % (param, data))
698 logger.ToStdout("Please don't forget that these parameters take effect"
699 " only at the next start of the instance.")
703 # options used in more than one cmd
704 node_opt = make_option("-n", "--node", dest="node", help="Target node",
707 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
710 # multi-instance selection options
711 m_force_multi = make_option("--force-multiple", dest="force_multi",
712 help="Do not ask for confirmation when more than"
713 " one instance is affected",
714 action="store_true", default=False)
716 m_pri_node_opt = make_option("--primary", dest="multi_mode",
717 help="Filter by nodes (primary only)",
718 const=_SHUTDOWN_NODES_PRI, action="store_const")
720 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
721 help="Filter by nodes (secondary only)",
722 const=_SHUTDOWN_NODES_SEC, action="store_const")
724 m_node_opt = make_option("--node", dest="multi_mode",
725 help="Filter by nodes (primary and secondary)",
726 const=_SHUTDOWN_NODES_BOTH, action="store_const")
728 m_clust_opt = make_option("--all", dest="multi_mode",
729 help="Select all instances in the cluster",
730 const=_SHUTDOWN_CLUSTER, action="store_const")
732 m_inst_opt = make_option("--instance", dest="multi_mode",
733 help="Filter by instance name [default]",
734 const=_SHUTDOWN_INSTANCES, action="store_const")
737 # this is defined separately due to readability only
740 make_option("-n", "--node", dest="node",
741 help="Target node and optional secondary node",
742 metavar="<pnode>[:<snode>]"),
743 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
745 default=20 * 1024, type="unit", metavar="<size>"),
746 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
748 default=4 * 1024, type="unit", metavar="<size>"),
750 cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
751 default=128, type="unit", metavar="<mem>"),
752 make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
753 default=1, type="int", metavar="<PROC>"),
754 make_option("-t", "--disk-template", dest="disk_template",
755 help="Custom disk setup (diskless, file, plain or drbd)",
756 default=None, metavar="TEMPL"),
757 make_option("-i", "--ip", dest="ip",
758 help="IP address ('none' [default], 'auto', or specify address)",
759 default='none', type="string", metavar="<ADDRESS>"),
760 make_option("--mac", dest="mac",
761 help="MAC address ('auto' [default], or specify address)",
762 default='auto', type="string", metavar="<MACADDRESS>"),
763 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
764 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
765 make_option("-b", "--bridge", dest="bridge",
766 help="Bridge to connect this instance to",
767 default=None, metavar="<bridge>"),
768 make_option("--no-start", dest="start", default=True,
769 action="store_false", help="Don't start the instance after"
771 make_option("--no-ip-check", dest="ip_check", default=True,
772 action="store_false", help="Don't check that the instance's IP"
773 " is alive (only valid with --no-start)"),
774 make_option("--kernel", dest="kernel_path",
775 help="Path to the instances' kernel (or 'default')",
777 type="string", metavar="<FILENAME>"),
778 make_option("--initrd", dest="initrd_path",
779 help="Path to the instances' initrd (or 'none', or 'default')",
781 type="string", metavar="<FILENAME>"),
782 make_option("--hvm-boot-order", dest="hvm_boot_order",
783 help="Boot device order for HVM (one or more of [acdn])",
784 default=None, type="string", metavar="<BOOTORDER>"),
785 make_option("--file-storage-dir", dest="file_storage_dir",
786 help="Relative path under default cluster-wide file storage dir"
787 " to store file-based disks", default=None,
789 make_option("--file-driver", dest="file_driver", help="Driver to use"
790 " for image files", default="loop", metavar="<DRIVER>"),
791 make_option("--iallocator", metavar="<NAME>",
792 help="Select nodes for the instance automatically using the"
793 " <NAME> iallocator plugin", default=None, type="string"),
797 'add': (AddInstance, ARGS_ONE, add_opts,
798 "Creates and adds a new instance to the cluster"),
799 'console': (ConnectToInstanceConsole, ARGS_ONE,
801 make_option("--show-cmd", dest="show_command",
802 action="store_true", default=False,
803 help=("Show command instead of executing it"))],
804 "Opens a console on the specified instance"),
805 'failover': (FailoverInstance, ARGS_ONE,
806 [DEBUG_OPT, FORCE_OPT,
807 make_option("--ignore-consistency", dest="ignore_consistency",
808 action="store_true", default=False,
809 help="Ignore the consistency of the disks on"
812 "Stops the instance and starts it on the backup node, using"
813 " the remote mirror (only for instances of type drbd)"),
814 'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT],
815 "Show information on the specified instance"),
816 'list': (ListInstances, ARGS_NONE,
817 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT],
818 "Lists the instances and their status. The available fields are"
819 " (see the man page for details): status, oper_state, oper_ram,"
820 " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
821 " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
822 " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
824 'reinstall': (ReinstallInstance, ARGS_ONE, [DEBUG_OPT, FORCE_OPT, os_opt],
825 "Reinstall a stopped instance"),
826 'remove': (RemoveInstance, ARGS_ONE,
827 [DEBUG_OPT, FORCE_OPT,
828 make_option("--ignore-failures", dest="ignore_failures",
829 action="store_true", default=False,
830 help=("Remove the instance from the cluster even"
831 " if there are failures during the removal"
832 " process (shutdown, disk removal, etc.)")),
834 "Shuts down the instance and removes it"),
835 'rename': (RenameInstance, ARGS_FIXED(2),
837 make_option("--no-ip-check", dest="ignore_ip",
838 help="Do not check that the IP of the new name"
840 default=False, action="store_true"),
842 "Rename the instance"),
843 'replace-disks': (ReplaceDisks, ARGS_ONE,
845 make_option("-n", "--new-secondary", dest="new_secondary",
846 help=("New secondary node (for secondary"
847 " node change)"), metavar="NODE"),
848 make_option("-p", "--on-primary", dest="on_primary",
849 default=False, action="store_true",
850 help=("Replace the disk(s) on the primary"
851 " node (only for the drbd template)")),
852 make_option("-s", "--on-secondary", dest="on_secondary",
853 default=False, action="store_true",
854 help=("Replace the disk(s) on the secondary"
855 " node (only for the drbd template)")),
856 make_option("--disks", dest="disks", default=None,
857 help=("Comma-separated list of disks"
858 " to replace (e.g. sda) (optional,"
859 " defaults to all disks")),
860 make_option("--iallocator", metavar="<NAME>",
861 help="Select new secondary for the instance"
862 " automatically using the"
863 " <NAME> iallocator plugin (enables"
864 " secondary node replacement)",
865 default=None, type="string"),
867 "Replaces all disks for the instance"),
868 'modify': (SetInstanceParams, ARGS_ONE,
869 [DEBUG_OPT, FORCE_OPT,
870 cli_option("-m", "--memory", dest="mem",
872 default=None, type="unit", metavar="<mem>"),
873 make_option("-p", "--cpu", dest="vcpus",
874 help="Number of virtual CPUs",
875 default=None, type="int", metavar="<PROC>"),
876 make_option("-i", "--ip", dest="ip",
877 help="IP address ('none' or numeric IP)",
878 default=None, type="string", metavar="<ADDRESS>"),
879 make_option("-b", "--bridge", dest="bridge",
880 help="Bridge to connect this instance to",
881 default=None, type="string", metavar="<bridge>"),
882 make_option("--mac", dest="mac",
883 help="MAC address", default=None,
884 type="string", metavar="<MACADDRESS>"),
885 make_option("--kernel", dest="kernel_path",
886 help="Path to the instances' kernel (or"
887 " 'default')", default=None,
888 type="string", metavar="<FILENAME>"),
889 make_option("--initrd", dest="initrd_path",
890 help="Path to the instances' initrd (or 'none', or"
891 " 'default')", default=None,
892 type="string", metavar="<FILENAME>"),
893 make_option("--hvm-boot-order", dest="hvm_boot_order",
894 help="boot device order for HVM"
895 "(either one or more of [acdn] or 'default')",
896 default=None, type="string", metavar="<BOOTORDER>"),
898 "Alters the parameters of an instance"),
899 'shutdown': (ShutdownInstance, ARGS_ANY,
900 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
901 m_clust_opt, m_inst_opt, m_force_multi],
902 "Stops an instance"),
903 'startup': (StartupInstance, ARGS_ANY,
904 [DEBUG_OPT, FORCE_OPT, m_force_multi,
905 make_option("-e", "--extra", dest="extra_args",
906 help="Extra arguments for the instance's kernel",
907 default=None, type="string", metavar="<PARAMS>"),
908 m_node_opt, m_pri_node_opt, m_sec_node_opt,
909 m_clust_opt, m_inst_opt,
911 "Starts an instance"),
913 'reboot': (RebootInstance, ARGS_ANY,
914 [DEBUG_OPT, m_force_multi,
915 make_option("-e", "--extra", dest="extra_args",
916 help="Extra arguments for the instance's kernel",
917 default=None, type="string", metavar="<PARAMS>"),
918 make_option("-t", "--type", dest="reboot_type",
919 help="Type of reboot: soft/hard/full",
920 default=constants.INSTANCE_REBOOT_SOFT,
921 type="string", metavar="<REBOOT>"),
922 make_option("--ignore-secondaries", dest="ignore_secondaries",
923 default=False, action="store_true",
924 help="Ignore errors from secondaries"),
925 m_node_opt, m_pri_node_opt, m_sec_node_opt,
926 m_clust_opt, m_inst_opt,
928 "Reboots an instance"),
929 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT],
930 "Activate an instance's disks"),
931 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT],
932 "Deactivate an instance's disks"),
933 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
934 "List the tags of the given instance"),
935 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
936 "Add tags to the given instance"),
937 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
938 "Remove tags from given instance"),
942 'activate_block_devs': 'activate-disks',
943 'replace_disks': 'replace-disks',
948 if __name__ == '__main__':
949 sys.exit(GenericMain(commands, aliases=aliases,
950 override={"tag_type": constants.TAG_INSTANCE}))