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
26 from optparse import make_option
27 from cStringIO import StringIO
29 from ganeti.cli import *
30 from ganeti import cli
31 from ganeti import opcodes
32 from ganeti import logger
33 from ganeti import constants
34 from ganeti import utils
35 from ganeti import errors
38 _SHUTDOWN_CLUSTER = "cluster"
39 _SHUTDOWN_NODES_BOTH = "nodes"
40 _SHUTDOWN_NODES_PRI = "nodes-pri"
41 _SHUTDOWN_NODES_SEC = "nodes-sec"
42 _SHUTDOWN_INSTANCES = "instances"
48 "name", "hypervisor", "os", "pnode", "status", "oper_ram",
52 def _ExpandMultiNames(mode, names):
53 """Expand the given names using the passed mode.
56 - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
57 _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
58 - names, which is a list of names; for cluster, it must be empty,
59 and for node and instance it must be a list of valid item
60 names (short names are valid as usual, e.g. node1 instead of
63 For _SHUTDOWN_CLUSTER, all instances will be returned. For
64 _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
65 primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
66 instances having those nodes as either primary or secondary will be
67 returned. For _SHUTDOWN_INSTANCES, the given instances will be
71 if mode == _SHUTDOWN_CLUSTER:
73 raise errors.OpPrereqError("Cluster filter mode takes no arguments")
75 idata = client.QueryInstances([], ["name"])
76 inames = [row[0] for row in idata]
78 elif mode in (_SHUTDOWN_NODES_BOTH,
82 raise errors.OpPrereqError("No node names passed")
84 ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
85 ipri = [row[1] for row in ndata]
86 pri_names = list(itertools.chain(*ipri))
87 isec = [row[2] for row in ndata]
88 sec_names = list(itertools.chain(*isec))
89 if mode == _SHUTDOWN_NODES_BOTH:
90 inames = pri_names + sec_names
91 elif mode == _SHUTDOWN_NODES_PRI:
93 elif mode == _SHUTDOWN_NODES_SEC:
96 raise errors.ProgrammerError("Unhandled shutdown type")
98 elif mode == _SHUTDOWN_INSTANCES:
100 raise errors.OpPrereqError("No instance names passed")
102 idata = client.QueryInstances(names, ["name"])
103 inames = [row[0] for row in idata]
106 raise errors.OpPrereqError("Unknown mode '%s'" % mode)
111 def _ConfirmOperation(inames, text):
112 """Ask the user to confirm an operation on a list of instances.
114 This function is used to request confirmation for doing an operation
115 on a given list of instances.
117 The inames argument is what the selection algorithm computed, and
118 the text argument is the operation we should tell the user to
119 confirm (e.g. 'shutdown' or 'startup').
121 Returns: boolean depending on user's confirmation.
125 msg = ("The %s will operate on %d instances.\n"
126 "Do you want to continue?" % (text, count))
127 affected = ("\nAffected instances:\n" +
128 "\n".join([" %s" % name for name in inames]))
130 choices = [('y', True, 'Yes, execute the %s' % text),
131 ('n', False, 'No, abort the %s' % text)]
134 choices.insert(1, ('v', 'v', 'View the list of affected instances'))
139 choice = AskUser(ask, choices)
142 choice = AskUser(msg + affected, choices)
146 def _TransformPath(user_input):
147 """Transform a user path into a canonical value.
149 This function transforms the a path passed as textual information
150 into the constants that the LU code expects.
154 if user_input.lower() == "default":
155 result_path = constants.VALUE_DEFAULT
156 elif user_input.lower() == "none":
157 result_path = constants.VALUE_NONE
159 if not os.path.isabs(user_input):
160 raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
162 result_path = user_input
164 result_path = constants.VALUE_DEFAULT
169 def ListInstances(opts, args):
170 """List instances and their properties.
173 if opts.output is None:
174 selected_fields = _LIST_DEF_FIELDS
175 elif opts.output.startswith("+"):
176 selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
178 selected_fields = opts.output.split(",")
180 output = GetClient().QueryInstances([], selected_fields)
182 if not opts.no_headers:
184 "name": "Instance", "os": "OS", "pnode": "Primary_node",
185 "snodes": "Secondary_Nodes", "admin_state": "Autostart",
186 "oper_state": "Running",
187 "oper_ram": "Memory", "disk_template": "Disk_template",
188 "ip": "IP_address", "mac": "MAC_address",
190 "sda_size": "Disk/0", "sdb_size": "Disk/1",
191 "status": "Status", "tags": "Tags",
192 "network_port": "Network_port",
193 "hv/kernel_path": "Kernel_path",
194 "hv/initrd_path": "Initrd_path",
195 "hv/boot_order": "HVM_boot_order",
196 "hv/acpi": "HVM_ACPI",
198 "hv/cdrom_image_path": "HVM_CDROM_image_path",
199 "hv/nic_type": "HVM_NIC_type",
200 "hv/disk_type": "HVM_Disk_type",
201 "hv/vnc_bind_address": "VNC_bind_address",
202 "serial_no": "SerialNo", "hypervisor": "Hypervisor",
203 "hvparams": "Hypervisor_parameters",
204 "be/memory": "Configured_memory",
206 "be/auto_balance": "Auto_balance",
211 if opts.human_readable:
212 unitfields = ["be/memory", "oper_ram", "sda_size", "sdb_size"]
216 numfields = ["be/memory", "oper_ram", "sda_size", "sdb_size", "be/vcpus",
219 list_type_fields = ("tags",)
220 # change raw values to nicer strings
222 for idx, field in enumerate(selected_fields):
224 if field == "snodes":
225 val = ",".join(val) or "-"
226 elif field == "admin_state":
231 elif field == "oper_state":
238 elif field == "oper_ram":
241 elif field == "sda_size" or field == "sdb_size":
244 elif field in list_type_fields:
250 data = GenerateTable(separator=opts.separator, headers=headers,
251 fields=selected_fields, unitfields=unitfields,
252 numfields=numfields, data=output)
255 logger.ToStdout(line)
260 def AddInstance(opts, args):
261 """Add an instance to the cluster.
264 opts - class with options as members
265 args - list with a single element, the instance name
267 mem - amount of memory to allocate to instance (MiB)
268 size - amount of disk space to allocate to instance (MiB)
269 os - which OS to run on instance
270 node - node to run new instance on
275 (pnode, snode) = SplitNodeOption(opts.node)
280 hypervisor, hvparams = opts.hypervisor
282 ValidateBeParams(opts.beparams)
284 ## kernel_path = _TransformPath(opts.kernel_path)
285 ## initrd_path = _TransformPath(opts.initrd_path)
287 ## hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
288 ## hvm_pae = opts.hvm_pae == _VALUE_TRUE
290 ## if ((opts.hvm_cdrom_image_path is not None) and
291 ## (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
292 ## hvm_cdrom_image_path = None
294 ## hvm_cdrom_image_path = opts.hvm_cdrom_image_path
296 op = opcodes.OpCreateInstance(instance_name=instance,
297 disk_size=opts.size, swap_size=opts.swap,
298 disk_template=opts.disk_template,
299 mode=constants.INSTANCE_CREATE,
300 os_type=opts.os, pnode=pnode,
302 ip=opts.ip, bridge=opts.bridge,
303 start=opts.start, ip_check=opts.ip_check,
304 wait_for_sync=opts.wait_for_sync,
306 hypervisor=hypervisor,
308 beparams=opts.beparams,
309 iallocator=opts.iallocator,
310 file_storage_dir=opts.file_storage_dir,
311 file_driver=opts.file_driver,
314 SubmitOrSend(op, opts)
318 def BatchCreate(opts, args):
319 """Create instances on a batched base.
321 This function reads a json with instances defined in the form:
323 {"instance-name": {"disk_size": 25,
326 "backend": { "memory": 512,
329 "primary_node": "firstnode",
330 "secondary_node": "secondnode",
331 "iallocator": "dumb"}}
333 primary_node and secondary_node has precedence over iallocator.
336 opts: The parsed command line options
337 args: Argument passed to the command in our case the json file
340 _DEFAULT_SPECS = {"disk_size": 20 * 1024,
341 "swap_size": 4 * 1024,
344 "primary_node": None,
345 "secondary_node": None,
352 "file_storage_dir": None,
353 "file_driver": 'loop'}
355 def _PopulateWithDefaults(spec):
356 """Returns a new hash combined with default values."""
357 dict = _DEFAULT_SPECS.copy()
362 """Validate the instance specs."""
363 # Validate fields required under any circumstances
364 for required_field in ('os', 'template'):
365 if required_field not in spec:
366 raise errors.OpPrereqError('Required field "%s" is missing.' %
368 # Validate special fields
369 if spec['primary_node'] is not None:
370 if (spec['template'] in constants.DTS_NET_MIRROR and
371 spec['secondary_node'] is None):
372 raise errors.OpPrereqError('Template requires secondary node, but'
373 ' there was no secondary provided.')
374 elif spec['iallocator'] is None:
375 raise errors.OpPrereqError('You have to provide at least a primary_node'
376 ' or an iallocator.')
378 if (spec['hypervisor'] and
379 not isinstance(spec['hypervisor'], dict)):
380 raise errors.OpPrereqError('Hypervisor parameters must be a dict.')
382 json_filename = args[0]
383 fd = open(json_filename, 'r')
385 instance_data = simplejson.load(fd)
389 # Iterate over the instances and do:
390 # * Populate the specs with default value
391 # * Validate the instance specs
392 for (name, specs) in instance_data.iteritems():
393 specs = _PopulateWithDefaults(specs)
398 if specs['hypervisor']:
399 hypervisor, hvparams = specs['hypervisor'].iteritems()
401 op = opcodes.OpCreateInstance(instance_name=name,
402 disk_size=specs['disk_size'],
403 swap_size=specs['swap_size'],
404 disk_template=specs['template'],
405 mode=constants.INSTANCE_CREATE,
407 pnode=specs['primary_node'],
408 snode=specs['secondary_node'],
409 ip=specs['ip'], bridge=specs['bridge'],
410 start=specs['start'],
411 ip_check=specs['ip_check'],
414 iallocator=specs['iallocator'],
415 hypervisor=hypervisor,
417 beparams=specs['backend'],
418 file_storage_dir=specs['file_storage_dir'],
419 file_driver=specs['file_driver'])
421 print '%s: %s' % (name, cli.SendJob([op]))
426 def ReinstallInstance(opts, args):
427 """Reinstall an instance.
430 opts - class with options as members
431 args - list containing a single element, the instance name
434 instance_name = args[0]
436 if opts.select_os is True:
437 op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
438 result = SubmitOpCode(op)
441 logger.ToStdout("Can't get the OS list")
444 logger.ToStdout("Available OS templates:")
448 logger.ToStdout("%3s: %s" % (number, entry[0]))
449 choices.append(("%s" % number, entry[0], entry[0]))
452 choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
453 selected = AskUser("Enter OS template name or number (or x to abort):",
456 if selected == 'exit':
457 logger.ToStdout("User aborted reinstall, exiting")
465 usertext = ("This will reinstall the instance %s and remove"
466 " all data. Continue?") % instance_name
467 if not AskUser(usertext):
470 op = opcodes.OpReinstallInstance(instance_name=instance_name,
472 SubmitOrSend(op, opts)
477 def RemoveInstance(opts, args):
478 """Remove an instance.
481 opts - class with options as members
482 args - list containing a single element, the instance name
485 instance_name = args[0]
489 usertext = ("This will remove the volumes of the instance %s"
490 " (including mirrors), thus removing all the data"
491 " of the instance. Continue?") % instance_name
492 if not AskUser(usertext):
495 op = opcodes.OpRemoveInstance(instance_name=instance_name,
496 ignore_failures=opts.ignore_failures)
497 SubmitOrSend(op, opts)
501 def RenameInstance(opts, args):
502 """Rename an instance.
505 opts - class with options as members
506 args - list containing two elements, the instance name and the new name
509 op = opcodes.OpRenameInstance(instance_name=args[0],
511 ignore_ip=opts.ignore_ip)
512 SubmitOrSend(op, opts)
516 def ActivateDisks(opts, args):
517 """Activate an instance's disks.
519 This serves two purposes:
520 - it allows one (as long as the instance is not running) to mount
521 the disks and modify them from the node
522 - it repairs inactive secondary drbds
525 instance_name = args[0]
526 op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
527 disks_info = SubmitOrSend(op, opts)
528 for host, iname, nname in disks_info:
529 print "%s:%s:%s" % (host, iname, nname)
533 def DeactivateDisks(opts, args):
534 """Command-line interface for _ShutdownInstanceBlockDevices.
536 This function takes the instance name, looks for its primary node
537 and the tries to shutdown its block devices on that node.
540 instance_name = args[0]
541 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
542 SubmitOrSend(op, opts)
546 def GrowDisk(opts, args):
547 """Command-line interface for _ShutdownInstanceBlockDevices.
549 This function takes the instance name, looks for its primary node
550 and the tries to shutdown its block devices on that node.
555 amount = utils.ParseUnit(args[2])
556 op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount,
557 wait_for_sync=opts.wait_for_sync)
558 SubmitOrSend(op, opts)
562 def StartupInstance(opts, args):
563 """Startup an instance.
566 opts - class with options as members
567 args - list containing a single element, the instance name
570 if opts.multi_mode is None:
571 opts.multi_mode = _SHUTDOWN_INSTANCES
572 inames = _ExpandMultiNames(opts.multi_mode, args)
574 raise errors.OpPrereqError("Selection filter does not match any instances")
575 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
576 if not (opts.force_multi or not multi_on
577 or _ConfirmOperation(inames, "startup")):
580 op = opcodes.OpStartupInstance(instance_name=name,
582 extra_args=opts.extra_args)
584 logger.ToStdout("Starting up %s" % name)
586 SubmitOrSend(op, opts)
587 except JobSubmittedException, err:
588 _, txt = FormatError(err)
589 logger.ToStdout("%s" % txt)
593 def RebootInstance(opts, args):
594 """Reboot an instance
597 opts - class with options as members
598 args - list containing a single element, the instance name
601 if opts.multi_mode is None:
602 opts.multi_mode = _SHUTDOWN_INSTANCES
603 inames = _ExpandMultiNames(opts.multi_mode, args)
605 raise errors.OpPrereqError("Selection filter does not match any instances")
606 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
607 if not (opts.force_multi or not multi_on
608 or _ConfirmOperation(inames, "reboot")):
611 op = opcodes.OpRebootInstance(instance_name=name,
612 reboot_type=opts.reboot_type,
613 ignore_secondaries=opts.ignore_secondaries)
615 SubmitOrSend(op, opts)
619 def ShutdownInstance(opts, args):
620 """Shutdown an instance.
623 opts - class with options as members
624 args - list containing a single element, the instance name
627 if opts.multi_mode is None:
628 opts.multi_mode = _SHUTDOWN_INSTANCES
629 inames = _ExpandMultiNames(opts.multi_mode, args)
631 raise errors.OpPrereqError("Selection filter does not match any instances")
632 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
633 if not (opts.force_multi or not multi_on
634 or _ConfirmOperation(inames, "shutdown")):
637 op = opcodes.OpShutdownInstance(instance_name=name)
639 logger.ToStdout("Shutting down %s" % name)
641 SubmitOrSend(op, opts)
642 except JobSubmittedException, err:
643 _, txt = FormatError(err)
644 logger.ToStdout("%s" % txt)
648 def ReplaceDisks(opts, args):
649 """Replace the disks of an instance
652 opts - class with options as members
653 args - list with a single element, the instance name
656 instance_name = args[0]
657 new_2ndary = opts.new_secondary
658 iallocator = opts.iallocator
659 if opts.disks is None:
660 disks = ["sda", "sdb"]
662 disks = opts.disks.split(",")
663 if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
664 mode = constants.REPLACE_DISK_ALL
665 elif opts.on_primary: # only on primary:
666 mode = constants.REPLACE_DISK_PRI
667 if new_2ndary is not None or iallocator is not None:
668 raise errors.OpPrereqError("Can't change secondary node on primary disk"
670 elif opts.on_secondary is not None or iallocator is not None:
672 mode = constants.REPLACE_DISK_SEC
674 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
675 remote_node=new_2ndary, mode=mode,
676 iallocator=iallocator)
677 SubmitOrSend(op, opts)
681 def FailoverInstance(opts, args):
682 """Failover an instance.
684 The failover is done by shutting it down on its present node and
685 starting it on the secondary.
688 opts - class with options as members
689 args - list with a single element, the instance name
691 force - whether to failover without asking questions.
694 instance_name = args[0]
698 usertext = ("Failover will happen to image %s."
699 " This requires a shutdown of the instance. Continue?" %
701 if not AskUser(usertext):
704 op = opcodes.OpFailoverInstance(instance_name=instance_name,
705 ignore_consistency=opts.ignore_consistency)
706 SubmitOrSend(op, opts)
710 def ConnectToInstanceConsole(opts, args):
711 """Connect to the console of an instance.
714 opts - class with options as members
715 args - list with a single element, the instance name
718 instance_name = args[0]
720 op = opcodes.OpConnectConsole(instance_name=instance_name)
721 cmd = SubmitOpCode(op)
723 if opts.show_command:
724 print utils.ShellQuoteArgs(cmd)
727 os.execvp(cmd[0], cmd)
729 sys.stderr.write("Can't run console command %s with arguments:\n'%s'" %
730 (cmd, " ".join(argv)))
734 def _FormatBlockDevInfo(buf, dev, indent_level, static):
735 """Show block device information.
737 This is only used by ShowInstanceConfig(), but it's too big to be
738 left for an inline definition.
741 def helper(buf, dtype, status):
742 """Format one line for physical device status."""
744 buf.write("not active\n")
746 (path, major, minor, syncp, estt, degr, ldisk) = status
750 major_string = str(major)
755 minor_string = str(minor)
757 buf.write("%s (%s:%s)" % (path, major_string, minor_string))
758 if dtype in (constants.LD_DRBD8, ):
759 if syncp is not None:
760 sync_text = "*RECOVERING* %5.2f%%," % syncp
762 sync_text += " ETA %ds" % estt
764 sync_text += " ETA unknown"
766 sync_text = "in sync"
768 degr_text = "*DEGRADED*"
772 ldisk_text = " *MISSING DISK*"
775 buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
776 elif dtype == constants.LD_LV:
778 ldisk_text = " *FAILED* (failed drive?)"
781 buf.write(ldisk_text)
784 if dev["iv_name"] is not None:
785 data = " - %s, " % dev["iv_name"]
788 data += "type: %s" % dev["dev_type"]
789 if dev["logical_id"] is not None:
790 data += ", logical_id: %s" % (dev["logical_id"],)
791 elif dev["physical_id"] is not None:
792 data += ", physical_id: %s" % (dev["physical_id"],)
793 buf.write("%*s%s\n" % (2*indent_level, "", data))
795 buf.write("%*s primary: " % (2*indent_level, ""))
796 helper(buf, dev["dev_type"], dev["pstatus"])
798 if dev["sstatus"] and not static:
799 buf.write("%*s secondary: " % (2*indent_level, ""))
800 helper(buf, dev["dev_type"], dev["sstatus"])
803 for child in dev["children"]:
804 _FormatBlockDevInfo(buf, child, indent_level+1, static)
807 def ShowInstanceConfig(opts, args):
808 """Compute instance run-time status.
812 op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
813 result = SubmitOpCode(op)
815 logger.ToStdout("No instances.")
820 for instance_name in result:
821 instance = result[instance_name]
822 buf.write("Instance name: %s\n" % instance["name"])
823 buf.write("State: configured to be %s" % instance["config_state"])
825 buf.write(", actual state is %s" % instance["run_state"])
827 ##buf.write("Considered for memory checks in cluster verify: %s\n" %
828 ## instance["auto_balance"])
829 buf.write(" Nodes:\n")
830 buf.write(" - primary: %s\n" % instance["pnode"])
831 buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"]))
832 buf.write(" Operating system: %s\n" % instance["os"])
833 if instance.has_key("network_port"):
834 buf.write(" Allocated network port: %s\n" % instance["network_port"])
835 buf.write(" Hypervisor: %s\n" % instance["hypervisor"])
836 if instance["hypervisor"] == constants.HT_XEN_PVM:
837 hvattrs = ((constants.HV_KERNEL_PATH, "kernel path"),
838 (constants.HV_INITRD_PATH, "initrd path"))
839 elif instance["hypervisor"] == constants.HT_XEN_HVM:
840 hvattrs = ((constants.HV_BOOT_ORDER, "boot order"),
841 (constants.HV_ACPI, "ACPI"),
842 (constants.HV_PAE, "PAE"),
843 (constants.HV_CDROM_IMAGE_PATH, "virtual CDROM"),
844 (constants.HV_NIC_TYPE, "NIC type"),
845 (constants.HV_DISK_TYPE, "Disk type"),
846 (constants.HV_VNC_BIND_ADDRESS, "VNC bind address"),
848 # custom console information for HVM
849 vnc_bind_address = instance["hv_actual"][constants.HV_VNC_BIND_ADDRESS]
850 if vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
851 vnc_console_port = "%s:%s" % (instance["pnode"],
852 instance["network_port"])
853 elif vnc_bind_address == constants.LOCALHOST_IP_ADDRESS:
854 vnc_console_port = "%s:%s on node %s" % (vnc_bind_address,
855 instance["network_port"],
858 vnc_console_port = "%s:%s" % (vnc_bind_address,
859 instance["network_port"])
860 buf.write(" - console connection: vnc to %s\n" % vnc_console_port)
863 # auto-handle other hypervisor types
864 hvattrs = [(key, key) for key in instance["hv_actual"]]
866 for key, desc in hvattrs:
867 if key in instance["hv_instance"]:
868 val = instance["hv_instance"][key]
870 val = "default (%s)" % instance["hv_actual"][key]
871 buf.write(" - %s: %s\n" % (desc, val))
872 buf.write(" Hardware:\n")
873 buf.write(" - VCPUs: %d\n" %
874 instance["be_actual"][constants.BE_VCPUS])
875 buf.write(" - memory: %dMiB\n" %
876 instance["be_actual"][constants.BE_MEMORY])
877 buf.write(" - NICs: %s\n" %
878 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
880 for mac, ip, bridge in instance["nics"]]))
881 buf.write(" Block devices:\n")
883 for device in instance["disks"]:
884 _FormatBlockDevInfo(buf, device, 1, opts.static)
886 logger.ToStdout(buf.getvalue().rstrip('\n'))
890 def SetInstanceParams(opts, args):
891 """Modifies an instance.
893 All parameters take effect only at the next restart of the instance.
896 opts - class with options as members
897 args - list with a single element, the instance name
899 mac - the new MAC address of the instance
902 if not (opts.ip or opts.bridge or opts.mac or
903 opts.hypervisor or opts.beparams):
904 logger.ToStdout("Please give at least one of the parameters.")
907 if constants.BE_MEMORY in opts.beparams:
908 opts.beparams[constants.BE_MEMORY] = utils.ParseUnit(
909 opts.beparams[constants.BE_MEMORY])
911 op = opcodes.OpSetInstanceParams(instance_name=args[0],
913 bridge=opts.bridge, mac=opts.mac,
914 hvparams=opts.hypervisor,
915 beparams=opts.beparams,
918 # even if here we process the result, we allow submit only
919 result = SubmitOrSend(op, opts)
922 logger.ToStdout("Modified instance %s" % args[0])
923 for param, data in result:
924 logger.ToStdout(" - %-5s -> %s" % (param, data))
925 logger.ToStdout("Please don't forget that these parameters take effect"
926 " only at the next start of the instance.")
930 # options used in more than one cmd
931 node_opt = make_option("-n", "--node", dest="node", help="Target node",
934 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
937 # multi-instance selection options
938 m_force_multi = make_option("--force-multiple", dest="force_multi",
939 help="Do not ask for confirmation when more than"
940 " one instance is affected",
941 action="store_true", default=False)
943 m_pri_node_opt = make_option("--primary", dest="multi_mode",
944 help="Filter by nodes (primary only)",
945 const=_SHUTDOWN_NODES_PRI, action="store_const")
947 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
948 help="Filter by nodes (secondary only)",
949 const=_SHUTDOWN_NODES_SEC, action="store_const")
951 m_node_opt = make_option("--node", dest="multi_mode",
952 help="Filter by nodes (primary and secondary)",
953 const=_SHUTDOWN_NODES_BOTH, action="store_const")
955 m_clust_opt = make_option("--all", dest="multi_mode",
956 help="Select all instances in the cluster",
957 const=_SHUTDOWN_CLUSTER, action="store_const")
959 m_inst_opt = make_option("--instance", dest="multi_mode",
960 help="Filter by instance name [default]",
961 const=_SHUTDOWN_INSTANCES, action="store_const")
964 # this is defined separately due to readability only
967 make_option("-n", "--node", dest="node",
968 help="Target node and optional secondary node",
969 metavar="<pnode>[:<snode>]"),
970 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
972 default=20 * 1024, type="unit", metavar="<size>"),
973 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
975 default=4 * 1024, type="unit", metavar="<size>"),
977 keyval_option("-B", "--backend", dest="beparams",
978 type="keyval", default={},
979 help="Backend parameters"),
980 make_option("-t", "--disk-template", dest="disk_template",
981 help="Custom disk setup (diskless, file, plain or drbd)",
982 default=None, metavar="TEMPL"),
983 make_option("-i", "--ip", dest="ip",
984 help="IP address ('none' [default], 'auto', or specify address)",
985 default='none', type="string", metavar="<ADDRESS>"),
986 make_option("--mac", dest="mac",
987 help="MAC address ('auto' [default], or specify address)",
988 default='auto', type="string", metavar="<MACADDRESS>"),
989 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
990 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
991 make_option("-b", "--bridge", dest="bridge",
992 help="Bridge to connect this instance to",
993 default=None, metavar="<bridge>"),
994 make_option("--no-start", dest="start", default=True,
995 action="store_false", help="Don't start the instance after"
997 make_option("--no-ip-check", dest="ip_check", default=True,
998 action="store_false", help="Don't check that the instance's IP"
999 " is alive (only valid with --no-start)"),
1000 make_option("--file-storage-dir", dest="file_storage_dir",
1001 help="Relative path under default cluster-wide file storage dir"
1002 " to store file-based disks", default=None,
1004 make_option("--file-driver", dest="file_driver", help="Driver to use"
1005 " for image files", default="loop", metavar="<DRIVER>"),
1006 make_option("--iallocator", metavar="<NAME>",
1007 help="Select nodes for the instance automatically using the"
1008 " <NAME> iallocator plugin", default=None, type="string"),
1009 ikv_option("-H", "--hypervisor", dest="hypervisor",
1010 help="Hypervisor and hypervisor options, in the format"
1011 " hypervisor:option=value,option=value,...", default=None,
1012 type="identkeyval"),
1017 'add': (AddInstance, ARGS_ONE, add_opts,
1018 "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
1019 "Creates and adds a new instance to the cluster"),
1020 'batch-create': (BatchCreate, ARGS_ONE,
1022 "<instances_file.json>",
1023 "Create a bunch of instances based on specs in the file."),
1024 'console': (ConnectToInstanceConsole, ARGS_ONE,
1026 make_option("--show-cmd", dest="show_command",
1027 action="store_true", default=False,
1028 help=("Show command instead of executing it"))],
1029 "[--show-cmd] <instance>",
1030 "Opens a console on the specified instance"),
1031 'failover': (FailoverInstance, ARGS_ONE,
1032 [DEBUG_OPT, FORCE_OPT,
1033 make_option("--ignore-consistency", dest="ignore_consistency",
1034 action="store_true", default=False,
1035 help="Ignore the consistency of the disks on"
1040 "Stops the instance and starts it on the backup node, using"
1041 " the remote mirror (only for instances of type drbd)"),
1042 'info': (ShowInstanceConfig, ARGS_ANY,
1044 make_option("-s", "--static", dest="static",
1045 action="store_true", default=False,
1046 help="Only show configuration data, not runtime data"),
1047 ], "[-s] [<instance>...]",
1048 "Show information on the specified instance(s)"),
1049 'list': (ListInstances, ARGS_NONE,
1050 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
1051 "Lists the instances and their status. The available fields are"
1052 " (see the man page for details): status, oper_state, oper_ram,"
1053 " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
1054 " ip, mac, bridge, sda_size, sdb_size, vcpus, serial_no,"
1056 " The default field"
1057 " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
1059 'reinstall': (ReinstallInstance, ARGS_ONE,
1060 [DEBUG_OPT, FORCE_OPT, os_opt,
1061 make_option("--select-os", dest="select_os",
1062 action="store_true", default=False,
1063 help="Interactive OS reinstall, lists available"
1064 " OS templates for selection"),
1067 "[-f] <instance>", "Reinstall a stopped instance"),
1068 'remove': (RemoveInstance, ARGS_ONE,
1069 [DEBUG_OPT, FORCE_OPT,
1070 make_option("--ignore-failures", dest="ignore_failures",
1071 action="store_true", default=False,
1072 help=("Remove the instance from the cluster even"
1073 " if there are failures during the removal"
1074 " process (shutdown, disk removal, etc.)")),
1077 "[-f] <instance>", "Shuts down the instance and removes it"),
1078 'rename': (RenameInstance, ARGS_FIXED(2),
1080 make_option("--no-ip-check", dest="ignore_ip",
1081 help="Do not check that the IP of the new name"
1083 default=False, action="store_true"),
1086 "<instance> <new_name>", "Rename the instance"),
1087 'replace-disks': (ReplaceDisks, ARGS_ONE,
1089 make_option("-n", "--new-secondary", dest="new_secondary",
1090 help=("New secondary node (for secondary"
1091 " node change)"), metavar="NODE"),
1092 make_option("-p", "--on-primary", dest="on_primary",
1093 default=False, action="store_true",
1094 help=("Replace the disk(s) on the primary"
1095 " node (only for the drbd template)")),
1096 make_option("-s", "--on-secondary", dest="on_secondary",
1097 default=False, action="store_true",
1098 help=("Replace the disk(s) on the secondary"
1099 " node (only for the drbd template)")),
1100 make_option("--disks", dest="disks", default=None,
1101 help=("Comma-separated list of disks"
1102 " to replace (e.g. sda) (optional,"
1103 " defaults to all disks")),
1104 make_option("--iallocator", metavar="<NAME>",
1105 help="Select new secondary for the instance"
1106 " automatically using the"
1107 " <NAME> iallocator plugin (enables"
1108 " secondary node replacement)",
1109 default=None, type="string"),
1112 "[-s|-p|-n NODE] <instance>",
1113 "Replaces all disks for the instance"),
1114 'modify': (SetInstanceParams, ARGS_ONE,
1115 [DEBUG_OPT, FORCE_OPT,
1116 make_option("-i", "--ip", dest="ip",
1117 help="IP address ('none' or numeric IP)",
1118 default=None, type="string", metavar="<ADDRESS>"),
1119 make_option("-b", "--bridge", dest="bridge",
1120 help="Bridge to connect this instance to",
1121 default=None, type="string", metavar="<bridge>"),
1122 make_option("--mac", dest="mac",
1123 help="MAC address", default=None,
1124 type="string", metavar="<MACADDRESS>"),
1125 keyval_option("-H", "--hypervisor", type="keyval",
1126 default={}, dest="hypervisor",
1127 help="Change hypervisor parameters"),
1128 keyval_option("-B", "--backend", type="keyval",
1129 default={}, dest="beparams",
1130 help="Change backend parameters"),
1133 "<instance>", "Alters the parameters of an instance"),
1134 'shutdown': (ShutdownInstance, ARGS_ANY,
1135 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1136 m_clust_opt, m_inst_opt, m_force_multi,
1139 "<instance>", "Stops an instance"),
1140 'startup': (StartupInstance, ARGS_ANY,
1141 [DEBUG_OPT, FORCE_OPT, m_force_multi,
1142 make_option("-e", "--extra", dest="extra_args",
1143 help="Extra arguments for the instance's kernel",
1144 default=None, type="string", metavar="<PARAMS>"),
1145 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1146 m_clust_opt, m_inst_opt,
1149 "<instance>", "Starts an instance"),
1151 'reboot': (RebootInstance, ARGS_ANY,
1152 [DEBUG_OPT, m_force_multi,
1153 make_option("-e", "--extra", dest="extra_args",
1154 help="Extra arguments for the instance's kernel",
1155 default=None, type="string", metavar="<PARAMS>"),
1156 make_option("-t", "--type", dest="reboot_type",
1157 help="Type of reboot: soft/hard/full",
1158 default=constants.INSTANCE_REBOOT_HARD,
1159 type="string", metavar="<REBOOT>"),
1160 make_option("--ignore-secondaries", dest="ignore_secondaries",
1161 default=False, action="store_true",
1162 help="Ignore errors from secondaries"),
1163 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1164 m_clust_opt, m_inst_opt,
1167 "<instance>", "Reboots an instance"),
1168 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1170 "Activate an instance's disks"),
1171 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1173 "Deactivate an instance's disks"),
1174 'grow-disk': (GrowDisk, ARGS_FIXED(3),
1175 [DEBUG_OPT, SUBMIT_OPT,
1176 make_option("--no-wait-for-sync",
1177 dest="wait_for_sync", default=True,
1178 action="store_false",
1179 help="Don't wait for sync (DANGEROUS!)"),
1181 "<instance> <disk> <size>", "Grow an instance's disk"),
1182 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1183 "<instance_name>", "List the tags of the given instance"),
1184 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1185 "<instance_name> tag...", "Add tags to the given instance"),
1186 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1187 "<instance_name> tag...", "Remove tags from given instance"),
1191 'activate_block_devs': 'activate-disks',
1192 'replace_disks': 'replace-disks',
1197 if __name__ == '__main__':
1198 sys.exit(GenericMain(commands, aliases=aliases,
1199 override={"tag_type": constants.TAG_INSTANCE}))