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"
46 "name", "os", "pnode", "status", "oper_ram",
50 def _ExpandMultiNames(mode, names):
51 """Expand the given names using the passed mode.
54 - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
55 _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
56 - names, which is a list of names; for cluster, it must be empty,
57 and for node and instance it must be a list of valid item
58 names (short names are valid as usual, e.g. node1 instead of
61 For _SHUTDOWN_CLUSTER, all instances will be returned. For
62 _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
63 primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
64 instances having those nodes as either primary or secondary will be
65 returned. For _SHUTDOWN_INSTANCES, the given instances will be
69 if mode == _SHUTDOWN_CLUSTER:
71 raise errors.OpPrereqError("Cluster filter mode takes no arguments")
73 idata = client.QueryInstances([], ["name"])
74 inames = [row[0] for row in idata]
76 elif mode in (_SHUTDOWN_NODES_BOTH,
80 raise errors.OpPrereqError("No node names passed")
82 ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
83 ipri = [row[1] for row in ndata]
84 pri_names = list(itertools.chain(*ipri))
85 isec = [row[2] for row in ndata]
86 sec_names = list(itertools.chain(*isec))
87 if mode == _SHUTDOWN_NODES_BOTH:
88 inames = pri_names + sec_names
89 elif mode == _SHUTDOWN_NODES_PRI:
91 elif mode == _SHUTDOWN_NODES_SEC:
94 raise errors.ProgrammerError("Unhandled shutdown type")
96 elif mode == _SHUTDOWN_INSTANCES:
98 raise errors.OpPrereqError("No instance names passed")
100 idata = client.QueryInstances(names, ["name"])
101 inames = [row[0] for row in idata]
104 raise errors.OpPrereqError("Unknown mode '%s'" % mode)
109 def _ConfirmOperation(inames, text):
110 """Ask the user to confirm an operation on a list of instances.
112 This function is used to request confirmation for doing an operation
113 on a given list of instances.
115 The inames argument is what the selection algorithm computed, and
116 the text argument is the operation we should tell the user to
117 confirm (e.g. 'shutdown' or 'startup').
119 Returns: boolean depending on user's confirmation.
123 msg = ("The %s will operate on %d instances.\n"
124 "Do you want to continue?" % (text, count))
125 affected = ("\nAffected instances:\n" +
126 "\n".join([" %s" % name for name in inames]))
128 choices = [('y', True, 'Yes, execute the %s' % text),
129 ('n', False, 'No, abort the %s' % text)]
132 choices.insert(1, ('v', 'v', 'View the list of affected instances'))
137 choice = AskUser(ask, choices)
140 choice = AskUser(msg + affected, choices)
144 def _TransformPath(user_input):
145 """Transform a user path into a canonical value.
147 This function transforms the a path passed as textual information
148 into the constants that the LU code expects.
152 if user_input.lower() == "default":
153 result_path = constants.VALUE_DEFAULT
154 elif user_input.lower() == "none":
155 result_path = constants.VALUE_NONE
157 if not os.path.isabs(user_input):
158 raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
160 result_path = user_input
162 result_path = constants.VALUE_DEFAULT
167 def ListInstances(opts, args):
168 """List instances and their properties.
171 if opts.output is None:
172 selected_fields = _LIST_DEF_FIELDS
173 elif opts.output.startswith("+"):
174 selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
176 selected_fields = opts.output.split(",")
178 output = GetClient().QueryInstances([], selected_fields)
180 if not opts.no_headers:
182 "name": "Instance", "os": "OS", "pnode": "Primary_node",
183 "snodes": "Secondary_Nodes", "admin_state": "Autostart",
184 "oper_state": "Running", "admin_ram": "Configured_memory",
185 "oper_ram": "Memory", "disk_template": "Disk_template",
186 "ip": "IP_address", "mac": "MAC_address",
187 "bridge": "Bridge", "vcpus": "VCPUs",
188 "sda_size": "Disk/0", "sdb_size": "Disk/1",
189 "status": "Status", "tags": "Tags",
190 "network_port": "Network_port",
191 "kernel_path": "Kernel_path",
192 "initrd_path": "Initrd_path",
193 "hvm_boot_order": "HVM_boot_order",
194 "hvm_acpi": "HVM_ACPI",
195 "hvm_pae": "HVM_PAE",
196 "hvm_cdrom_image_path": "HVM_CDROM_image_path",
197 "hvm_nic_type": "HVM_NIC_type",
198 "hvm_disk_type": "HVM_disk_type",
199 "vnc_bind_address": "VNC_bind_address",
204 if opts.human_readable:
205 unitfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size"]
209 numfields = ["admin_ram", "oper_ram", "sda_size", "sdb_size", "vcpus"]
211 list_type_fields = ("tags",)
212 # change raw values to nicer strings
214 for idx, field in enumerate(selected_fields):
216 if field == "snodes":
217 val = ",".join(val) or "-"
218 elif field == "admin_state":
223 elif field == "oper_state":
230 elif field == "oper_ram":
233 elif field == "sda_size" or field == "sdb_size":
236 elif field in list_type_fields:
240 data = GenerateTable(separator=opts.separator, headers=headers,
241 fields=selected_fields, unitfields=unitfields,
242 numfields=numfields, data=output)
245 logger.ToStdout(line)
250 def AddInstance(opts, args):
251 """Add an instance to the cluster.
254 opts - class with options as members
255 args - list with a single element, the instance name
257 mem - amount of memory to allocate to instance (MiB)
258 size - amount of disk space to allocate to instance (MiB)
259 os - which OS to run on instance
260 node - node to run new instance on
265 (pnode, snode) = SplitNodeOption(opts.node)
267 kernel_path = _TransformPath(opts.kernel_path)
268 initrd_path = _TransformPath(opts.initrd_path)
270 hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
271 hvm_pae = opts.hvm_pae == _VALUE_TRUE
273 if ((opts.hvm_cdrom_image_path is not None) and
274 (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
275 hvm_cdrom_image_path = None
277 hvm_cdrom_image_path = opts.hvm_cdrom_image_path
279 op = opcodes.OpCreateInstance(instance_name=instance, mem_size=opts.mem,
280 disk_size=opts.size, swap_size=opts.swap,
281 disk_template=opts.disk_template,
282 mode=constants.INSTANCE_CREATE,
283 os_type=opts.os, pnode=pnode,
284 snode=snode, vcpus=opts.vcpus,
285 ip=opts.ip, bridge=opts.bridge,
286 start=opts.start, ip_check=opts.ip_check,
287 wait_for_sync=opts.wait_for_sync,
289 kernel_path=kernel_path,
290 initrd_path=initrd_path,
291 iallocator=opts.iallocator,
292 hvm_boot_order=opts.hvm_boot_order,
293 file_storage_dir=opts.file_storage_dir,
294 file_driver=opts.file_driver,
295 hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
296 hvm_cdrom_image_path=hvm_cdrom_image_path,
297 vnc_bind_address=opts.vnc_bind_address,
298 hvm_nic_type=opts.hvm_nic_type,
299 hvm_disk_type=opts.hvm_disk_type)
301 SubmitOrSend(op, opts)
305 def ReinstallInstance(opts, args):
306 """Reinstall an instance.
309 opts - class with options as members
310 args - list containing a single element, the instance name
313 instance_name = args[0]
315 if opts.select_os is True:
316 op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
317 result = SubmitOpCode(op)
320 logger.ToStdout("Can't get the OS list")
323 logger.ToStdout("Available OS templates:")
327 logger.ToStdout("%3s: %s" % (number, entry[0]))
328 choices.append(("%s" % number, entry[0], entry[0]))
331 choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
332 selected = AskUser("Enter OS template name or number (or x to abort):",
335 if selected == 'exit':
336 logger.ToStdout("User aborted reinstall, exiting")
344 usertext = ("This will reinstall the instance %s and remove"
345 " all data. Continue?") % instance_name
346 if not AskUser(usertext):
349 op = opcodes.OpReinstallInstance(instance_name=instance_name,
351 SubmitOrSend(op, opts)
356 def RemoveInstance(opts, args):
357 """Remove an instance.
360 opts - class with options as members
361 args - list containing a single element, the instance name
364 instance_name = args[0]
368 usertext = ("This will remove the volumes of the instance %s"
369 " (including mirrors), thus removing all the data"
370 " of the instance. Continue?") % instance_name
371 if not AskUser(usertext):
374 op = opcodes.OpRemoveInstance(instance_name=instance_name,
375 ignore_failures=opts.ignore_failures)
376 SubmitOrSend(op, opts)
380 def RenameInstance(opts, args):
381 """Rename an instance.
384 opts - class with options as members
385 args - list containing two elements, the instance name and the new name
388 op = opcodes.OpRenameInstance(instance_name=args[0],
390 ignore_ip=opts.ignore_ip)
391 SubmitOrSend(op, opts)
395 def ActivateDisks(opts, args):
396 """Activate an instance's disks.
398 This serves two purposes:
399 - it allows one (as long as the instance is not running) to mount
400 the disks and modify them from the node
401 - it repairs inactive secondary drbds
404 instance_name = args[0]
405 op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
406 disks_info = SubmitOrSend(op, opts)
407 for host, iname, nname in disks_info:
408 print "%s:%s:%s" % (host, iname, nname)
412 def DeactivateDisks(opts, args):
413 """Command-line interface for _ShutdownInstanceBlockDevices.
415 This function takes the instance name, looks for its primary node
416 and the tries to shutdown its block devices on that node.
419 instance_name = args[0]
420 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
421 SubmitOrSend(op, opts)
425 def GrowDisk(opts, args):
426 """Command-line interface for _ShutdownInstanceBlockDevices.
428 This function takes the instance name, looks for its primary node
429 and the tries to shutdown its block devices on that node.
434 amount = utils.ParseUnit(args[2])
435 op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount)
436 SubmitOrSend(op, opts)
440 def StartupInstance(opts, args):
441 """Startup an instance.
444 opts - class with options as members
445 args - list containing a single element, the instance name
448 if opts.multi_mode is None:
449 opts.multi_mode = _SHUTDOWN_INSTANCES
450 inames = _ExpandMultiNames(opts.multi_mode, args)
452 raise errors.OpPrereqError("Selection filter does not match any instances")
453 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
454 if not (opts.force_multi or not multi_on
455 or _ConfirmOperation(inames, "startup")):
458 op = opcodes.OpStartupInstance(instance_name=name,
460 extra_args=opts.extra_args)
462 logger.ToStdout("Starting up %s" % name)
464 SubmitOrSend(op, opts)
465 except JobSubmittedException, err:
466 _, txt = FormatError(err)
467 logger.ToStdout("%s" % txt)
471 def RebootInstance(opts, args):
472 """Reboot an instance
475 opts - class with options as members
476 args - list containing a single element, the instance name
479 if opts.multi_mode is None:
480 opts.multi_mode = _SHUTDOWN_INSTANCES
481 inames = _ExpandMultiNames(opts.multi_mode, args)
483 raise errors.OpPrereqError("Selection filter does not match any instances")
484 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
485 if not (opts.force_multi or not multi_on
486 or _ConfirmOperation(inames, "reboot")):
489 op = opcodes.OpRebootInstance(instance_name=name,
490 reboot_type=opts.reboot_type,
491 ignore_secondaries=opts.ignore_secondaries)
493 SubmitOrSend(op, opts)
497 def ShutdownInstance(opts, args):
498 """Shutdown an instance.
501 opts - class with options as members
502 args - list containing a single element, the instance name
505 if opts.multi_mode is None:
506 opts.multi_mode = _SHUTDOWN_INSTANCES
507 inames = _ExpandMultiNames(opts.multi_mode, args)
509 raise errors.OpPrereqError("Selection filter does not match any instances")
510 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
511 if not (opts.force_multi or not multi_on
512 or _ConfirmOperation(inames, "shutdown")):
515 op = opcodes.OpShutdownInstance(instance_name=name)
517 logger.ToStdout("Shutting down %s" % name)
519 SubmitOrSend(op, opts)
520 except JobSubmittedException, err:
521 _, txt = FormatError(err)
522 logger.ToStdout("%s" % txt)
526 def ReplaceDisks(opts, args):
527 """Replace the disks of an instance
530 opts - class with options as members
531 args - list with a single element, the instance name
534 instance_name = args[0]
535 new_2ndary = opts.new_secondary
536 iallocator = opts.iallocator
537 if opts.disks is None:
538 disks = ["sda", "sdb"]
540 disks = opts.disks.split(",")
541 if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
542 mode = constants.REPLACE_DISK_ALL
543 elif opts.on_primary: # only on primary:
544 mode = constants.REPLACE_DISK_PRI
545 if new_2ndary is not None or iallocator is not None:
546 raise errors.OpPrereqError("Can't change secondary node on primary disk"
548 elif opts.on_secondary is not None or iallocator is not None:
550 mode = constants.REPLACE_DISK_SEC
552 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
553 remote_node=new_2ndary, mode=mode,
554 iallocator=iallocator)
555 SubmitOrSend(op, opts)
559 def FailoverInstance(opts, args):
560 """Failover an instance.
562 The failover is done by shutting it down on its present node and
563 starting it on the secondary.
566 opts - class with options as members
567 args - list with a single element, the instance name
569 force - whether to failover without asking questions.
572 instance_name = args[0]
576 usertext = ("Failover will happen to image %s."
577 " This requires a shutdown of the instance. Continue?" %
579 if not AskUser(usertext):
582 op = opcodes.OpFailoverInstance(instance_name=instance_name,
583 ignore_consistency=opts.ignore_consistency)
584 SubmitOrSend(op, opts)
588 def ConnectToInstanceConsole(opts, args):
589 """Connect to the console of an instance.
592 opts - class with options as members
593 args - list with a single element, the instance name
596 instance_name = args[0]
598 op = opcodes.OpConnectConsole(instance_name=instance_name)
599 cmd = SubmitOpCode(op)
601 if opts.show_command:
602 print utils.ShellQuoteArgs(cmd)
605 os.execvp(cmd[0], cmd)
607 sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
608 (cmd, " ".join(argv)))
612 def _FormatBlockDevInfo(buf, dev, indent_level):
613 """Show block device information.
615 This is only used by ShowInstanceConfig(), but it's too big to be
616 left for an inline definition.
619 def helper(buf, dtype, status):
620 """Format one line for physical device status."""
622 buf.write("not active\n")
624 (path, major, minor, syncp, estt, degr, ldisk) = status
628 major_string = str(major)
633 minor_string = str(minor)
635 buf.write("%s (%s:%s)" % (path, major_string, minor_string))
636 if dtype in (constants.LD_DRBD8, ):
637 if syncp is not None:
638 sync_text = "*RECOVERING* %5.2f%%," % syncp
640 sync_text += " ETA %ds" % estt
642 sync_text += " ETA unknown"
644 sync_text = "in sync"
646 degr_text = "*DEGRADED*"
650 ldisk_text = " *MISSING DISK*"
653 buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
654 elif dtype == constants.LD_LV:
656 ldisk_text = " *FAILED* (failed drive?)"
659 buf.write(ldisk_text)
662 if dev["iv_name"] is not None:
663 data = " - %s, " % dev["iv_name"]
666 data += "type: %s" % dev["dev_type"]
667 if dev["logical_id"] is not None:
668 data += ", logical_id: %s" % (dev["logical_id"],)
669 elif dev["physical_id"] is not None:
670 data += ", physical_id: %s" % (dev["physical_id"],)
671 buf.write("%*s%s\n" % (2*indent_level, "", data))
672 buf.write("%*s primary: " % (2*indent_level, ""))
673 helper(buf, dev["dev_type"], dev["pstatus"])
676 buf.write("%*s secondary: " % (2*indent_level, ""))
677 helper(buf, dev["dev_type"], dev["sstatus"])
680 for child in dev["children"]:
681 _FormatBlockDevInfo(buf, child, indent_level+1)
684 def ShowInstanceConfig(opts, args):
685 """Compute instance run-time status.
689 op = opcodes.OpQueryInstanceData(instances=args)
690 result = SubmitOpCode(op)
691 hvm_parameters = ("hvm_acpi", "hvm_pae", "hvm_cdrom_image_path",
692 "hvm_boot_order", "hvm_nic_type", "hvm_disk_type")
694 pvm_parameters = ("kernel_path", "initrd_path")
697 logger.ToStdout("No instances.")
702 for instance_name in result:
703 instance = result[instance_name]
704 buf.write("Instance name: %s\n" % instance["name"])
705 buf.write("State: configured to be %s, actual state is %s\n" %
706 (instance["config_state"], instance["run_state"]))
707 buf.write(" Nodes:\n")
708 buf.write(" - primary: %s\n" % instance["pnode"])
709 buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"]))
710 buf.write(" Operating system: %s\n" % instance["os"])
711 if instance.has_key("network_port"):
712 buf.write(" Allocated network port: %s\n" % instance["network_port"])
713 if False not in map(instance.has_key, pvm_parameters):
714 if instance["kernel_path"] in (None, constants.VALUE_DEFAULT):
715 kpath = "(default: %s)" % constants.XEN_KERNEL
717 kpath = instance["kernel_path"]
718 buf.write(" Kernel path: %s\n" % kpath)
719 if instance["initrd_path"] in (None, constants.VALUE_DEFAULT):
720 initrd = "(default: %s)" % constants.XEN_INITRD
721 elif instance["initrd_path"] == constants.VALUE_NONE:
724 initrd = instance["initrd_path"]
725 buf.write(" initrd: %s\n" % initrd)
726 if False not in map(instance.has_key, hvm_parameters):
728 buf.write(" - boot order: %s\n" % instance["hvm_boot_order"])
729 buf.write(" - ACPI support: %s\n" % instance["hvm_acpi"])
730 buf.write(" - PAE support: %s\n" % instance["hvm_pae"])
731 buf.write(" - virtual CDROM: %s\n" % instance["hvm_cdrom_image_path"])
732 buf.write(" - virtual NIC type: %s\n" % instance["hvm_nic_type"])
733 buf.write(" - virtual disk type: %s\n" % instance["hvm_disk_type"])
734 if instance.has_key("vnc_bind_address"):
735 buf.write(" VNC bind address: %s\n" % instance["vnc_bind_address"])
736 buf.write(" VNC console port: %s\n" % instance["vnc_console_port"])
737 buf.write(" Hardware:\n")
738 buf.write(" - VCPUs: %d\n" % instance["vcpus"])
739 buf.write(" - memory: %dMiB\n" % instance["memory"])
740 buf.write(" - NICs: %s\n" %
741 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
743 for mac, ip, bridge in instance["nics"]]))
744 buf.write(" Block devices:\n")
746 for device in instance["disks"]:
747 _FormatBlockDevInfo(buf, device, 1)
749 logger.ToStdout(buf.getvalue().rstrip('\n'))
753 def SetInstanceParams(opts, args):
754 """Modifies an instance.
756 All parameters take effect only at the next restart of the instance.
759 opts - class with options as members
760 args - list with a single element, the instance name
762 memory - the new memory size
763 vcpus - the new number of cpus
764 mac - the new MAC address of the instance
767 if not (opts.mem or opts.vcpus or opts.ip or opts.bridge or opts.mac or
768 opts.kernel_path or opts.initrd_path or opts.hvm_boot_order or
769 opts.hvm_acpi or opts.hvm_pae or opts.hvm_cdrom_image_path or
770 opts.vnc_bind_address or opts.hvm_nic_type or opts.hvm_disk_type):
771 logger.ToStdout("Please give at least one of the parameters.")
774 kernel_path = _TransformPath(opts.kernel_path)
775 initrd_path = _TransformPath(opts.initrd_path)
776 if opts.hvm_boot_order == 'default':
777 hvm_boot_order = constants.VALUE_DEFAULT
779 hvm_boot_order = opts.hvm_boot_order
781 if opts.hvm_acpi is None:
782 hvm_acpi = opts.hvm_acpi
784 hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
786 if opts.hvm_pae is None:
787 hvm_pae = opts.hvm_pae
789 hvm_pae = opts.hvm_pae == _VALUE_TRUE
791 if opts.hvm_nic_type == constants.VALUE_NONE:
794 hvm_nic_type = opts.hvm_nic_type
796 if opts.hvm_disk_type == constants.VALUE_NONE:
799 hvm_disk_type = opts.hvm_disk_type
801 op = opcodes.OpSetInstanceParams(instance_name=args[0], mem=opts.mem,
802 vcpus=opts.vcpus, ip=opts.ip,
803 bridge=opts.bridge, mac=opts.mac,
804 kernel_path=opts.kernel_path,
805 initrd_path=opts.initrd_path,
806 hvm_boot_order=hvm_boot_order,
807 hvm_acpi=hvm_acpi, hvm_pae=hvm_pae,
808 hvm_cdrom_image_path=
809 opts.hvm_cdrom_image_path,
810 vnc_bind_address=opts.vnc_bind_address,
811 hvm_nic_type=hvm_nic_type,
812 hvm_disk_type=hvm_disk_type,
815 # even if here we process the result, we allow submit only
816 result = SubmitOrSend(op, opts)
819 logger.ToStdout("Modified instance %s" % args[0])
820 for param, data in result:
821 logger.ToStdout(" - %-5s -> %s" % (param, data))
822 logger.ToStdout("Please don't forget that these parameters take effect"
823 " only at the next start of the instance.")
827 # options used in more than one cmd
828 node_opt = make_option("-n", "--node", dest="node", help="Target node",
831 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
834 # multi-instance selection options
835 m_force_multi = make_option("--force-multiple", dest="force_multi",
836 help="Do not ask for confirmation when more than"
837 " one instance is affected",
838 action="store_true", default=False)
840 m_pri_node_opt = make_option("--primary", dest="multi_mode",
841 help="Filter by nodes (primary only)",
842 const=_SHUTDOWN_NODES_PRI, action="store_const")
844 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
845 help="Filter by nodes (secondary only)",
846 const=_SHUTDOWN_NODES_SEC, action="store_const")
848 m_node_opt = make_option("--node", dest="multi_mode",
849 help="Filter by nodes (primary and secondary)",
850 const=_SHUTDOWN_NODES_BOTH, action="store_const")
852 m_clust_opt = make_option("--all", dest="multi_mode",
853 help="Select all instances in the cluster",
854 const=_SHUTDOWN_CLUSTER, action="store_const")
856 m_inst_opt = make_option("--instance", dest="multi_mode",
857 help="Filter by instance name [default]",
858 const=_SHUTDOWN_INSTANCES, action="store_const")
861 # this is defined separately due to readability only
864 make_option("-n", "--node", dest="node",
865 help="Target node and optional secondary node",
866 metavar="<pnode>[:<snode>]"),
867 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
869 default=20 * 1024, type="unit", metavar="<size>"),
870 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
872 default=4 * 1024, type="unit", metavar="<size>"),
874 cli_option("-m", "--memory", dest="mem", help="Memory size (in MiB)",
875 default=128, type="unit", metavar="<mem>"),
876 make_option("-p", "--cpu", dest="vcpus", help="Number of virtual CPUs",
877 default=1, type="int", metavar="<PROC>"),
878 make_option("-t", "--disk-template", dest="disk_template",
879 help="Custom disk setup (diskless, file, plain or drbd)",
880 default=None, metavar="TEMPL"),
881 make_option("-i", "--ip", dest="ip",
882 help="IP address ('none' [default], 'auto', or specify address)",
883 default='none', type="string", metavar="<ADDRESS>"),
884 make_option("--mac", dest="mac",
885 help="MAC address ('auto' [default], or specify address)",
886 default='auto', type="string", metavar="<MACADDRESS>"),
887 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
888 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
889 make_option("-b", "--bridge", dest="bridge",
890 help="Bridge to connect this instance to",
891 default=None, metavar="<bridge>"),
892 make_option("--no-start", dest="start", default=True,
893 action="store_false", help="Don't start the instance after"
895 make_option("--no-ip-check", dest="ip_check", default=True,
896 action="store_false", help="Don't check that the instance's IP"
897 " is alive (only valid with --no-start)"),
898 make_option("--kernel", dest="kernel_path",
899 help="Path to the instances' kernel (or 'default')",
901 type="string", metavar="<FILENAME>"),
902 make_option("--initrd", dest="initrd_path",
903 help="Path to the instances' initrd (or 'none', or 'default')",
905 type="string", metavar="<FILENAME>"),
906 make_option("--hvm-boot-order", dest="hvm_boot_order",
907 help="Boot device order for HVM (one or more of [acdn])",
908 default=None, type="string", metavar="<BOOTORDER>"),
909 make_option("--file-storage-dir", dest="file_storage_dir",
910 help="Relative path under default cluster-wide file storage dir"
911 " to store file-based disks", default=None,
913 make_option("--file-driver", dest="file_driver", help="Driver to use"
914 " for image files", default="loop", metavar="<DRIVER>"),
915 make_option("--iallocator", metavar="<NAME>",
916 help="Select nodes for the instance automatically using the"
917 " <NAME> iallocator plugin", default=None, type="string"),
918 make_option("--hvm-acpi", dest="hvm_acpi",
919 help="ACPI support for HVM (true|false)",
920 metavar="<BOOL>", choices=["true", "false"]),
921 make_option("--hvm-nic-type", dest="hvm_nic_type",
922 help="Type of virtual NIC for HVM "
923 "(rtl8139,ne2k_pci,ne2k_isa,paravirtual)",
924 metavar="NICTYPE", choices=[constants.HT_HVM_NIC_RTL8139,
925 constants.HT_HVM_NIC_NE2K_PCI,
926 constants.HT_HVM_NIC_NE2K_ISA,
927 constants.HT_HVM_DEV_PARAVIRTUAL],
928 default=constants.HT_HVM_NIC_RTL8139),
929 make_option("--hvm-disk-type", dest="hvm_disk_type",
930 help="Type of virtual disks for HVM (ioemu,paravirtual)",
931 metavar="DISKTYPE", choices=[constants.HT_HVM_DEV_IOEMU,
932 constants.HT_HVM_DEV_PARAVIRTUAL],
933 default=constants.HT_HVM_DEV_IOEMU,),
934 make_option("--hvm-pae", dest="hvm_pae",
935 help="PAE support for HVM (true|false)",
936 metavar="<BOOL>", choices=["true", "false"]),
937 make_option("--hvm-cdrom-image-path", dest="hvm_cdrom_image_path",
938 help="CDROM image path for HVM (absolute path or None)",
939 default=None, type="string", metavar="<CDROMIMAGE>"),
940 make_option("--vnc-bind-address", dest="vnc_bind_address",
941 help="bind address for VNC (IP address)",
942 default=None, type="string", metavar="<VNCADDRESS>"),
947 'add': (AddInstance, ARGS_ONE, add_opts,
948 "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
949 "Creates and adds a new instance to the cluster"),
950 'console': (ConnectToInstanceConsole, ARGS_ONE,
952 make_option("--show-cmd", dest="show_command",
953 action="store_true", default=False,
954 help=("Show command instead of executing it"))],
955 "[--show-cmd] <instance>",
956 "Opens a console on the specified instance"),
957 'failover': (FailoverInstance, ARGS_ONE,
958 [DEBUG_OPT, FORCE_OPT,
959 make_option("--ignore-consistency", dest="ignore_consistency",
960 action="store_true", default=False,
961 help="Ignore the consistency of the disks on"
966 "Stops the instance and starts it on the backup node, using"
967 " the remote mirror (only for instances of type drbd)"),
968 'info': (ShowInstanceConfig, ARGS_ANY, [DEBUG_OPT], "[<instance>...]",
969 "Show information on the specified instance"),
970 'list': (ListInstances, ARGS_NONE,
971 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
972 "Lists the instances and their status. The available fields are"
973 " (see the man page for details): status, oper_state, oper_ram,"
974 " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
975 " ip, mac, bridge, sda_size, sdb_size, vcpus. The default field"
976 " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
978 'reinstall': (ReinstallInstance, ARGS_ONE,
979 [DEBUG_OPT, FORCE_OPT, os_opt,
980 make_option("--select-os", dest="select_os",
981 action="store_true", default=False,
982 help="Interactive OS reinstall, lists available"
983 " OS templates for selection"),
986 "[-f] <instance>", "Reinstall a stopped instance"),
987 'remove': (RemoveInstance, ARGS_ONE,
988 [DEBUG_OPT, FORCE_OPT,
989 make_option("--ignore-failures", dest="ignore_failures",
990 action="store_true", default=False,
991 help=("Remove the instance from the cluster even"
992 " if there are failures during the removal"
993 " process (shutdown, disk removal, etc.)")),
996 "[-f] <instance>", "Shuts down the instance and removes it"),
997 'rename': (RenameInstance, ARGS_FIXED(2),
999 make_option("--no-ip-check", dest="ignore_ip",
1000 help="Do not check that the IP of the new name"
1002 default=False, action="store_true"),
1005 "<instance> <new_name>", "Rename the instance"),
1006 'replace-disks': (ReplaceDisks, ARGS_ONE,
1008 make_option("-n", "--new-secondary", dest="new_secondary",
1009 help=("New secondary node (for secondary"
1010 " node change)"), metavar="NODE"),
1011 make_option("-p", "--on-primary", dest="on_primary",
1012 default=False, action="store_true",
1013 help=("Replace the disk(s) on the primary"
1014 " node (only for the drbd template)")),
1015 make_option("-s", "--on-secondary", dest="on_secondary",
1016 default=False, action="store_true",
1017 help=("Replace the disk(s) on the secondary"
1018 " node (only for the drbd template)")),
1019 make_option("--disks", dest="disks", default=None,
1020 help=("Comma-separated list of disks"
1021 " to replace (e.g. sda) (optional,"
1022 " defaults to all disks")),
1023 make_option("--iallocator", metavar="<NAME>",
1024 help="Select new secondary for the instance"
1025 " automatically using the"
1026 " <NAME> iallocator plugin (enables"
1027 " secondary node replacement)",
1028 default=None, type="string"),
1031 "[-s|-p|-n NODE] <instance>",
1032 "Replaces all disks for the instance"),
1033 'modify': (SetInstanceParams, ARGS_ONE,
1034 [DEBUG_OPT, FORCE_OPT,
1035 cli_option("-m", "--memory", dest="mem",
1037 default=None, type="unit", metavar="<mem>"),
1038 make_option("-p", "--cpu", dest="vcpus",
1039 help="Number of virtual CPUs",
1040 default=None, type="int", metavar="<PROC>"),
1041 make_option("-i", "--ip", dest="ip",
1042 help="IP address ('none' or numeric IP)",
1043 default=None, type="string", metavar="<ADDRESS>"),
1044 make_option("-b", "--bridge", dest="bridge",
1045 help="Bridge to connect this instance to",
1046 default=None, type="string", metavar="<bridge>"),
1047 make_option("--mac", dest="mac",
1048 help="MAC address", default=None,
1049 type="string", metavar="<MACADDRESS>"),
1050 make_option("--kernel", dest="kernel_path",
1051 help="Path to the instances' kernel (or"
1052 " 'default')", default=None,
1053 type="string", metavar="<FILENAME>"),
1054 make_option("--initrd", dest="initrd_path",
1055 help="Path to the instances' initrd (or 'none', or"
1056 " 'default')", default=None,
1057 type="string", metavar="<FILENAME>"),
1058 make_option("--hvm-boot-order", dest="hvm_boot_order",
1059 help="boot device order for HVM"
1060 "(either one or more of [acdn] or 'default')",
1061 default=None, type="string", metavar="<BOOTORDER>"),
1062 make_option("--hvm-acpi", dest="hvm_acpi",
1063 help="ACPI support for HVM (true|false)",
1064 metavar="<BOOL>", choices=["true", "false"]),
1065 make_option("--hvm-pae", dest="hvm_pae",
1066 help="PAE support for HVM (true|false)",
1067 metavar="<BOOL>", choices=["true", "false"]),
1068 make_option("--hvm-cdrom-image-path",
1069 dest="hvm_cdrom_image_path",
1070 help="CDROM image path for HVM"
1071 "(absolute path or None)",
1072 default=None, type="string", metavar="<CDROMIMAGE>"),
1073 make_option("--hvm-nic-type", dest="hvm_nic_type",
1074 help="Type of virtual NIC for HVM "
1075 "(rtl8139,ne2k_pci,ne2k_isa,paravirtual)",
1077 choices=[constants.HT_HVM_NIC_RTL8139,
1078 constants.HT_HVM_NIC_NE2K_PCI,
1079 constants.HT_HVM_NIC_NE2K_ISA,
1080 constants.HT_HVM_DEV_PARAVIRTUAL],
1082 make_option("--hvm-disk-type", dest="hvm_disk_type",
1083 help="Type of virtual disks for HVM "
1084 "(ioemu,paravirtual)",
1086 choices=[constants.HT_HVM_DEV_IOEMU,
1087 constants.HT_HVM_DEV_PARAVIRTUAL],
1089 make_option("--vnc-bind-address", dest="vnc_bind_address",
1090 help="bind address for VNC (IP address)",
1091 default=None, type="string", metavar="<VNCADDRESS>"),
1094 "<instance>", "Alters the parameters of an instance"),
1095 'shutdown': (ShutdownInstance, ARGS_ANY,
1096 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1097 m_clust_opt, m_inst_opt, m_force_multi,
1100 "<instance>", "Stops an instance"),
1101 'startup': (StartupInstance, ARGS_ANY,
1102 [DEBUG_OPT, FORCE_OPT, m_force_multi,
1103 make_option("-e", "--extra", dest="extra_args",
1104 help="Extra arguments for the instance's kernel",
1105 default=None, type="string", metavar="<PARAMS>"),
1106 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1107 m_clust_opt, m_inst_opt,
1110 "<instance>", "Starts an instance"),
1112 'reboot': (RebootInstance, ARGS_ANY,
1113 [DEBUG_OPT, m_force_multi,
1114 make_option("-e", "--extra", dest="extra_args",
1115 help="Extra arguments for the instance's kernel",
1116 default=None, type="string", metavar="<PARAMS>"),
1117 make_option("-t", "--type", dest="reboot_type",
1118 help="Type of reboot: soft/hard/full",
1119 default=constants.INSTANCE_REBOOT_SOFT,
1120 type="string", metavar="<REBOOT>"),
1121 make_option("--ignore-secondaries", dest="ignore_secondaries",
1122 default=False, action="store_true",
1123 help="Ignore errors from secondaries"),
1124 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1125 m_clust_opt, m_inst_opt,
1128 "<instance>", "Reboots an instance"),
1129 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1131 "Activate an instance's disks"),
1132 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1134 "Deactivate an instance's disks"),
1135 'grow-disk': (GrowDisk, ARGS_FIXED(3), [DEBUG_OPT, SUBMIT_OPT],
1136 "<instance> <disk> <size>", "Grow an instance's disk"),
1137 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1138 "<instance_name>", "List the tags of the given instance"),
1139 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1140 "<instance_name> tag...", "Add tags to the given instance"),
1141 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1142 "<instance_name> tag...", "Remove tags from given instance"),
1146 'activate_block_devs': 'activate-disks',
1147 'replace_disks': 'replace-disks',
1152 if __name__ == '__main__':
1153 sys.exit(GenericMain(commands, aliases=aliases,
1154 override={"tag_type": constants.TAG_INSTANCE}))