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
22 # pylint: disable-msg=W0401,W0614
23 # W0401: Wildcard import ganeti.cli
24 # W0614: Unused import %s from wildcard import (since we need cli)
30 from optparse import make_option
31 from cStringIO import StringIO
33 from ganeti.cli import *
34 from ganeti import cli
35 from ganeti import opcodes
36 from ganeti import constants
37 from ganeti import utils
38 from ganeti import errors
41 _SHUTDOWN_CLUSTER = "cluster"
42 _SHUTDOWN_NODES_BOTH = "nodes"
43 _SHUTDOWN_NODES_PRI = "nodes-pri"
44 _SHUTDOWN_NODES_SEC = "nodes-sec"
45 _SHUTDOWN_INSTANCES = "instances"
51 "name", "hypervisor", "os", "pnode", "status", "oper_ram",
55 def _ExpandMultiNames(mode, names):
56 """Expand the given names using the passed mode.
59 - mode, which can be one of _SHUTDOWN_CLUSTER, _SHUTDOWN_NODES_BOTH,
60 _SHUTDOWN_NODES_PRI, _SHUTDOWN_NODES_SEC or _SHUTDOWN_INSTANCES
61 - names, which is a list of names; for cluster, it must be empty,
62 and for node and instance it must be a list of valid item
63 names (short names are valid as usual, e.g. node1 instead of
66 For _SHUTDOWN_CLUSTER, all instances will be returned. For
67 _SHUTDOWN_NODES_PRI/SEC, all instances having those nodes as
68 primary/secondary will be shutdown. For _SHUTDOWN_NODES_BOTH, all
69 instances having those nodes as either primary or secondary will be
70 returned. For _SHUTDOWN_INSTANCES, the given instances will be
74 if mode == _SHUTDOWN_CLUSTER:
76 raise errors.OpPrereqError("Cluster filter mode takes no arguments")
78 idata = client.QueryInstances([], ["name"])
79 inames = [row[0] for row in idata]
81 elif mode in (_SHUTDOWN_NODES_BOTH,
85 raise errors.OpPrereqError("No node names passed")
87 ndata = client.QueryNodes(names, ["name", "pinst_list", "sinst_list"])
88 ipri = [row[1] for row in ndata]
89 pri_names = list(itertools.chain(*ipri))
90 isec = [row[2] for row in ndata]
91 sec_names = list(itertools.chain(*isec))
92 if mode == _SHUTDOWN_NODES_BOTH:
93 inames = pri_names + sec_names
94 elif mode == _SHUTDOWN_NODES_PRI:
96 elif mode == _SHUTDOWN_NODES_SEC:
99 raise errors.ProgrammerError("Unhandled shutdown type")
101 elif mode == _SHUTDOWN_INSTANCES:
103 raise errors.OpPrereqError("No instance names passed")
105 idata = client.QueryInstances(names, ["name"])
106 inames = [row[0] for row in idata]
109 raise errors.OpPrereqError("Unknown mode '%s'" % mode)
114 def _ConfirmOperation(inames, text):
115 """Ask the user to confirm an operation on a list of instances.
117 This function is used to request confirmation for doing an operation
118 on a given list of instances.
120 The inames argument is what the selection algorithm computed, and
121 the text argument is the operation we should tell the user to
122 confirm (e.g. 'shutdown' or 'startup').
124 Returns: boolean depending on user's confirmation.
128 msg = ("The %s will operate on %d instances.\n"
129 "Do you want to continue?" % (text, count))
130 affected = ("\nAffected instances:\n" +
131 "\n".join([" %s" % name for name in inames]))
133 choices = [('y', True, 'Yes, execute the %s' % text),
134 ('n', False, 'No, abort the %s' % text)]
137 choices.insert(1, ('v', 'v', 'View the list of affected instances'))
142 choice = AskUser(ask, choices)
145 choice = AskUser(msg + affected, choices)
149 def _TransformPath(user_input):
150 """Transform a user path into a canonical value.
152 This function transforms the a path passed as textual information
153 into the constants that the LU code expects.
157 if user_input.lower() == "default":
158 result_path = constants.VALUE_DEFAULT
159 elif user_input.lower() == "none":
160 result_path = constants.VALUE_NONE
162 if not os.path.isabs(user_input):
163 raise errors.OpPrereqError("Path '%s' is not an absolute filename" %
165 result_path = user_input
167 result_path = constants.VALUE_DEFAULT
172 def ListInstances(opts, args):
173 """List instances and their properties.
176 if opts.output is None:
177 selected_fields = _LIST_DEF_FIELDS
178 elif opts.output.startswith("+"):
179 selected_fields = _LIST_DEF_FIELDS + opts.output[1:].split(",")
181 selected_fields = opts.output.split(",")
183 output = GetClient().QueryInstances([], selected_fields)
185 if not opts.no_headers:
187 "name": "Instance", "os": "OS", "pnode": "Primary_node",
188 "snodes": "Secondary_Nodes", "admin_state": "Autostart",
189 "oper_state": "Running",
190 "oper_ram": "Memory", "disk_template": "Disk_template",
191 "ip": "IP_address", "mac": "MAC_address",
193 "sda_size": "Disk/0", "sdb_size": "Disk/1",
194 "status": "Status", "tags": "Tags",
195 "network_port": "Network_port",
196 "hv/kernel_path": "Kernel_path",
197 "hv/initrd_path": "Initrd_path",
198 "hv/boot_order": "HVM_boot_order",
199 "hv/acpi": "HVM_ACPI",
201 "hv/cdrom_image_path": "HVM_CDROM_image_path",
202 "hv/nic_type": "HVM_NIC_type",
203 "hv/disk_type": "HVM_Disk_type",
204 "hv/vnc_bind_address": "VNC_bind_address",
205 "serial_no": "SerialNo", "hypervisor": "Hypervisor",
206 "hvparams": "Hypervisor_parameters",
207 "be/memory": "Configured_memory",
209 "be/auto_balance": "Auto_balance",
214 if opts.human_readable:
215 unitfields = ["be/memory", "oper_ram", "sda_size", "sdb_size"]
219 numfields = ["be/memory", "oper_ram", "sda_size", "sdb_size", "be/vcpus",
222 list_type_fields = ("tags",)
223 # change raw values to nicer strings
225 for idx, field in enumerate(selected_fields):
227 if field == "snodes":
228 val = ",".join(val) or "-"
229 elif field == "admin_state":
234 elif field == "oper_state":
241 elif field == "oper_ram":
244 elif field == "sda_size" or field == "sdb_size":
247 elif field in list_type_fields:
253 data = GenerateTable(separator=opts.separator, headers=headers,
254 fields=selected_fields, unitfields=unitfields,
255 numfields=numfields, data=output)
263 def AddInstance(opts, args):
264 """Add an instance to the cluster.
267 opts - class with options as members
268 args - list with a single element, the instance name
270 mem - amount of memory to allocate to instance (MiB)
271 size - amount of disk space to allocate to instance (MiB)
272 os - which OS to run on instance
273 node - node to run new instance on
278 (pnode, snode) = SplitNodeOption(opts.node)
283 hypervisor, hvparams = opts.hypervisor
285 ValidateBeParams(opts.beparams)
287 ## kernel_path = _TransformPath(opts.kernel_path)
288 ## initrd_path = _TransformPath(opts.initrd_path)
290 ## hvm_acpi = opts.hvm_acpi == _VALUE_TRUE
291 ## hvm_pae = opts.hvm_pae == _VALUE_TRUE
293 ## if ((opts.hvm_cdrom_image_path is not None) and
294 ## (opts.hvm_cdrom_image_path.lower() == constants.VALUE_NONE)):
295 ## hvm_cdrom_image_path = None
297 ## hvm_cdrom_image_path = opts.hvm_cdrom_image_path
299 op = opcodes.OpCreateInstance(instance_name=instance,
300 disk_size=opts.size, swap_size=opts.swap,
301 disk_template=opts.disk_template,
302 mode=constants.INSTANCE_CREATE,
303 os_type=opts.os, pnode=pnode,
305 ip=opts.ip, bridge=opts.bridge,
306 start=opts.start, ip_check=opts.ip_check,
307 wait_for_sync=opts.wait_for_sync,
309 hypervisor=hypervisor,
311 beparams=opts.beparams,
312 iallocator=opts.iallocator,
313 file_storage_dir=opts.file_storage_dir,
314 file_driver=opts.file_driver,
317 SubmitOrSend(op, opts)
321 def BatchCreate(opts, args):
322 """Create instances on a batched base.
324 This function reads a json with instances defined in the form:
326 {"instance-name": {"disk_size": 25,
329 "backend": { "memory": 512,
332 "primary_node": "firstnode",
333 "secondary_node": "secondnode",
334 "iallocator": "dumb"}}
336 primary_node and secondary_node has precedence over iallocator.
339 opts: The parsed command line options
340 args: Argument passed to the command in our case the json file
343 _DEFAULT_SPECS = {"disk_size": 20 * 1024,
344 "swap_size": 4 * 1024,
347 "primary_node": None,
348 "secondary_node": None,
355 "file_storage_dir": None,
356 "file_driver": 'loop'}
358 def _PopulateWithDefaults(spec):
359 """Returns a new hash combined with default values."""
360 mydict = _DEFAULT_SPECS.copy()
365 """Validate the instance specs."""
366 # Validate fields required under any circumstances
367 for required_field in ('os', 'template'):
368 if required_field not in spec:
369 raise errors.OpPrereqError('Required field "%s" is missing.' %
371 # Validate special fields
372 if spec['primary_node'] is not None:
373 if (spec['template'] in constants.DTS_NET_MIRROR and
374 spec['secondary_node'] is None):
375 raise errors.OpPrereqError('Template requires secondary node, but'
376 ' there was no secondary provided.')
377 elif spec['iallocator'] is None:
378 raise errors.OpPrereqError('You have to provide at least a primary_node'
379 ' or an iallocator.')
381 if (spec['hypervisor'] and
382 not isinstance(spec['hypervisor'], dict)):
383 raise errors.OpPrereqError('Hypervisor parameters must be a dict.')
385 json_filename = args[0]
386 fd = open(json_filename, 'r')
388 instance_data = simplejson.load(fd)
392 # Iterate over the instances and do:
393 # * Populate the specs with default value
394 # * Validate the instance specs
395 for (name, specs) in instance_data.iteritems():
396 specs = _PopulateWithDefaults(specs)
401 if specs['hypervisor']:
402 hypervisor, hvparams = specs['hypervisor'].iteritems()
404 op = opcodes.OpCreateInstance(instance_name=name,
405 disk_size=specs['disk_size'],
406 swap_size=specs['swap_size'],
407 disk_template=specs['template'],
408 mode=constants.INSTANCE_CREATE,
410 pnode=specs['primary_node'],
411 snode=specs['secondary_node'],
412 ip=specs['ip'], bridge=specs['bridge'],
413 start=specs['start'],
414 ip_check=specs['ip_check'],
417 iallocator=specs['iallocator'],
418 hypervisor=hypervisor,
420 beparams=specs['backend'],
421 file_storage_dir=specs['file_storage_dir'],
422 file_driver=specs['file_driver'])
424 ToStdout("%s: %s", name, cli.SendJob([op]))
429 def ReinstallInstance(opts, args):
430 """Reinstall an instance.
433 opts - class with options as members
434 args - list containing a single element, the instance name
437 instance_name = args[0]
439 if opts.select_os is True:
440 op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
441 result = SubmitOpCode(op)
444 ToStdout("Can't get the OS list")
447 ToStdout("Available OS templates:")
451 ToStdout("%3s: %s", number, entry[0])
452 choices.append(("%s" % number, entry[0], entry[0]))
455 choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
456 selected = AskUser("Enter OS template name or number (or x to abort):",
459 if selected == 'exit':
460 ToStdout("User aborted reinstall, exiting")
468 usertext = ("This will reinstall the instance %s and remove"
469 " all data. Continue?") % instance_name
470 if not AskUser(usertext):
473 op = opcodes.OpReinstallInstance(instance_name=instance_name,
475 SubmitOrSend(op, opts)
480 def RemoveInstance(opts, args):
481 """Remove an instance.
484 opts - class with options as members
485 args - list containing a single element, the instance name
488 instance_name = args[0]
492 usertext = ("This will remove the volumes of the instance %s"
493 " (including mirrors), thus removing all the data"
494 " of the instance. Continue?") % instance_name
495 if not AskUser(usertext):
498 op = opcodes.OpRemoveInstance(instance_name=instance_name,
499 ignore_failures=opts.ignore_failures)
500 SubmitOrSend(op, opts)
504 def RenameInstance(opts, args):
505 """Rename an instance.
508 opts - class with options as members
509 args - list containing two elements, the instance name and the new name
512 op = opcodes.OpRenameInstance(instance_name=args[0],
514 ignore_ip=opts.ignore_ip)
515 SubmitOrSend(op, opts)
519 def ActivateDisks(opts, args):
520 """Activate an instance's disks.
522 This serves two purposes:
523 - it allows one (as long as the instance is not running) to mount
524 the disks and modify them from the node
525 - it repairs inactive secondary drbds
528 instance_name = args[0]
529 op = opcodes.OpActivateInstanceDisks(instance_name=instance_name)
530 disks_info = SubmitOrSend(op, opts)
531 for host, iname, nname in disks_info:
532 ToStdout("%s:%s:%s", host, iname, nname)
536 def DeactivateDisks(opts, args):
537 """Command-line interface for _ShutdownInstanceBlockDevices.
539 This function takes the instance name, looks for its primary node
540 and the tries to shutdown its block devices on that node.
543 instance_name = args[0]
544 op = opcodes.OpDeactivateInstanceDisks(instance_name=instance_name)
545 SubmitOrSend(op, opts)
549 def GrowDisk(opts, args):
550 """Command-line interface for _ShutdownInstanceBlockDevices.
552 This function takes the instance name, looks for its primary node
553 and the tries to shutdown its block devices on that node.
558 amount = utils.ParseUnit(args[2])
559 op = opcodes.OpGrowDisk(instance_name=instance, disk=disk, amount=amount,
560 wait_for_sync=opts.wait_for_sync)
561 SubmitOrSend(op, opts)
565 def StartupInstance(opts, args):
566 """Startup an instance.
569 opts - class with options as members
570 args - list containing a single element, the instance name
573 if opts.multi_mode is None:
574 opts.multi_mode = _SHUTDOWN_INSTANCES
575 inames = _ExpandMultiNames(opts.multi_mode, args)
577 raise errors.OpPrereqError("Selection filter does not match any instances")
578 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
579 if not (opts.force_multi or not multi_on
580 or _ConfirmOperation(inames, "startup")):
583 op = opcodes.OpStartupInstance(instance_name=name,
585 extra_args=opts.extra_args)
587 ToStdout("Starting up %s", name)
589 SubmitOrSend(op, opts)
590 except JobSubmittedException, err:
591 _, txt = FormatError(err)
596 def RebootInstance(opts, args):
597 """Reboot an instance
600 opts - class with options as members
601 args - list containing a single element, the instance name
604 if opts.multi_mode is None:
605 opts.multi_mode = _SHUTDOWN_INSTANCES
606 inames = _ExpandMultiNames(opts.multi_mode, args)
608 raise errors.OpPrereqError("Selection filter does not match any instances")
609 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
610 if not (opts.force_multi or not multi_on
611 or _ConfirmOperation(inames, "reboot")):
614 op = opcodes.OpRebootInstance(instance_name=name,
615 reboot_type=opts.reboot_type,
616 ignore_secondaries=opts.ignore_secondaries)
618 SubmitOrSend(op, opts)
622 def ShutdownInstance(opts, args):
623 """Shutdown an instance.
626 opts - class with options as members
627 args - list containing a single element, the instance name
630 if opts.multi_mode is None:
631 opts.multi_mode = _SHUTDOWN_INSTANCES
632 inames = _ExpandMultiNames(opts.multi_mode, args)
634 raise errors.OpPrereqError("Selection filter does not match any instances")
635 multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
636 if not (opts.force_multi or not multi_on
637 or _ConfirmOperation(inames, "shutdown")):
640 op = opcodes.OpShutdownInstance(instance_name=name)
642 ToStdout("Shutting down %s", name)
644 SubmitOrSend(op, opts)
645 except JobSubmittedException, err:
646 _, txt = FormatError(err)
651 def ReplaceDisks(opts, args):
652 """Replace the disks of an instance
655 opts - class with options as members
656 args - list with a single element, the instance name
659 instance_name = args[0]
660 new_2ndary = opts.new_secondary
661 iallocator = opts.iallocator
662 if opts.disks is None:
663 disks = ["sda", "sdb"]
665 disks = opts.disks.split(",")
666 if opts.on_primary == opts.on_secondary: # no -p or -s passed, or both passed
667 mode = constants.REPLACE_DISK_ALL
668 elif opts.on_primary: # only on primary:
669 mode = constants.REPLACE_DISK_PRI
670 if new_2ndary is not None or iallocator is not None:
671 raise errors.OpPrereqError("Can't change secondary node on primary disk"
673 elif opts.on_secondary is not None or iallocator is not None:
675 mode = constants.REPLACE_DISK_SEC
677 op = opcodes.OpReplaceDisks(instance_name=args[0], disks=disks,
678 remote_node=new_2ndary, mode=mode,
679 iallocator=iallocator)
680 SubmitOrSend(op, opts)
684 def FailoverInstance(opts, args):
685 """Failover an instance.
687 The failover is done by shutting it down on its present node and
688 starting it on the secondary.
691 opts - class with options as members
692 args - list with a single element, the instance name
694 force - whether to failover without asking questions.
697 instance_name = args[0]
701 usertext = ("Failover will happen to image %s."
702 " This requires a shutdown of the instance. Continue?" %
704 if not AskUser(usertext):
707 op = opcodes.OpFailoverInstance(instance_name=instance_name,
708 ignore_consistency=opts.ignore_consistency)
709 SubmitOrSend(op, opts)
713 def ConnectToInstanceConsole(opts, args):
714 """Connect to the console of an instance.
717 opts - class with options as members
718 args - list with a single element, the instance name
721 instance_name = args[0]
723 op = opcodes.OpConnectConsole(instance_name=instance_name)
724 cmd = SubmitOpCode(op)
726 if opts.show_command:
727 ToStdout("%s", utils.ShellQuoteArgs(cmd))
730 os.execvp(cmd[0], cmd)
732 ToStderr("Can't run console command %s with arguments:\n'%s'",
733 cmd[0], " ".join(cmd))
737 def _FormatBlockDevInfo(buf, dev, indent_level, static):
738 """Show block device information.
740 This is only used by ShowInstanceConfig(), but it's too big to be
741 left for an inline definition.
744 def helper(buf, dtype, status):
745 """Format one line for physical device status."""
747 buf.write("not active\n")
749 (path, major, minor, syncp, estt, degr, ldisk) = status
753 major_string = str(major)
758 minor_string = str(minor)
760 buf.write("%s (%s:%s)" % (path, major_string, minor_string))
761 if dtype in (constants.LD_DRBD8, ):
762 if syncp is not None:
763 sync_text = "*RECOVERING* %5.2f%%," % syncp
765 sync_text += " ETA %ds" % estt
767 sync_text += " ETA unknown"
769 sync_text = "in sync"
771 degr_text = "*DEGRADED*"
775 ldisk_text = " *MISSING DISK*"
778 buf.write(" %s, status %s%s" % (sync_text, degr_text, ldisk_text))
779 elif dtype == constants.LD_LV:
781 ldisk_text = " *FAILED* (failed drive?)"
784 buf.write(ldisk_text)
787 if dev["iv_name"] is not None:
788 data = " - %s, " % dev["iv_name"]
791 data += "type: %s" % dev["dev_type"]
792 if dev["logical_id"] is not None:
793 data += ", logical_id: %s" % (dev["logical_id"],)
794 elif dev["physical_id"] is not None:
795 data += ", physical_id: %s" % (dev["physical_id"],)
796 buf.write("%*s%s\n" % (2*indent_level, "", data))
798 buf.write("%*s primary: " % (2*indent_level, ""))
799 helper(buf, dev["dev_type"], dev["pstatus"])
801 if dev["sstatus"] and not static:
802 buf.write("%*s secondary: " % (2*indent_level, ""))
803 helper(buf, dev["dev_type"], dev["sstatus"])
806 for child in dev["children"]:
807 _FormatBlockDevInfo(buf, child, indent_level+1, static)
810 def ShowInstanceConfig(opts, args):
811 """Compute instance run-time status.
815 op = opcodes.OpQueryInstanceData(instances=args, static=opts.static)
816 result = SubmitOpCode(op)
818 ToStdout("No instances.")
823 for instance_name in result:
824 instance = result[instance_name]
825 buf.write("Instance name: %s\n" % instance["name"])
826 buf.write("State: configured to be %s" % instance["config_state"])
828 buf.write(", actual state is %s" % instance["run_state"])
830 ##buf.write("Considered for memory checks in cluster verify: %s\n" %
831 ## instance["auto_balance"])
832 buf.write(" Nodes:\n")
833 buf.write(" - primary: %s\n" % instance["pnode"])
834 buf.write(" - secondaries: %s\n" % ", ".join(instance["snodes"]))
835 buf.write(" Operating system: %s\n" % instance["os"])
836 if instance.has_key("network_port"):
837 buf.write(" Allocated network port: %s\n" % instance["network_port"])
838 buf.write(" Hypervisor: %s\n" % instance["hypervisor"])
839 if instance["hypervisor"] == constants.HT_XEN_PVM:
840 hvattrs = ((constants.HV_KERNEL_PATH, "kernel path"),
841 (constants.HV_INITRD_PATH, "initrd path"))
842 elif instance["hypervisor"] == constants.HT_XEN_HVM:
843 hvattrs = ((constants.HV_BOOT_ORDER, "boot order"),
844 (constants.HV_ACPI, "ACPI"),
845 (constants.HV_PAE, "PAE"),
846 (constants.HV_CDROM_IMAGE_PATH, "virtual CDROM"),
847 (constants.HV_NIC_TYPE, "NIC type"),
848 (constants.HV_DISK_TYPE, "Disk type"),
849 (constants.HV_VNC_BIND_ADDRESS, "VNC bind address"),
851 # custom console information for HVM
852 vnc_bind_address = instance["hv_actual"][constants.HV_VNC_BIND_ADDRESS]
853 if vnc_bind_address == constants.BIND_ADDRESS_GLOBAL:
854 vnc_console_port = "%s:%s" % (instance["pnode"],
855 instance["network_port"])
856 elif vnc_bind_address == constants.LOCALHOST_IP_ADDRESS:
857 vnc_console_port = "%s:%s on node %s" % (vnc_bind_address,
858 instance["network_port"],
861 vnc_console_port = "%s:%s" % (vnc_bind_address,
862 instance["network_port"])
863 buf.write(" - console connection: vnc to %s\n" % vnc_console_port)
866 # auto-handle other hypervisor types
867 hvattrs = [(key, key) for key in instance["hv_actual"]]
869 for key, desc in hvattrs:
870 if key in instance["hv_instance"]:
871 val = instance["hv_instance"][key]
873 val = "default (%s)" % instance["hv_actual"][key]
874 buf.write(" - %s: %s\n" % (desc, val))
875 buf.write(" Hardware:\n")
876 buf.write(" - VCPUs: %d\n" %
877 instance["be_actual"][constants.BE_VCPUS])
878 buf.write(" - memory: %dMiB\n" %
879 instance["be_actual"][constants.BE_MEMORY])
880 buf.write(" - NICs: %s\n" %
881 ", ".join(["{MAC: %s, IP: %s, bridge: %s}" %
883 for mac, ip, bridge in instance["nics"]]))
884 buf.write(" Block devices:\n")
886 for device in instance["disks"]:
887 _FormatBlockDevInfo(buf, device, 1, opts.static)
889 ToStdout(buf.getvalue().rstrip('\n'))
893 def SetInstanceParams(opts, args):
894 """Modifies an instance.
896 All parameters take effect only at the next restart of the instance.
899 opts - class with options as members
900 args - list with a single element, the instance name
902 mac - the new MAC address of the instance
905 if not (opts.ip or opts.bridge or opts.mac or
906 opts.hypervisor or opts.beparams):
907 ToStderr("Please give at least one of the parameters.")
910 if constants.BE_MEMORY in opts.beparams:
911 opts.beparams[constants.BE_MEMORY] = utils.ParseUnit(
912 opts.beparams[constants.BE_MEMORY])
914 op = opcodes.OpSetInstanceParams(instance_name=args[0],
916 bridge=opts.bridge, mac=opts.mac,
917 hvparams=opts.hypervisor,
918 beparams=opts.beparams,
921 # even if here we process the result, we allow submit only
922 result = SubmitOrSend(op, opts)
925 ToStdout("Modified instance %s", args[0])
926 for param, data in result:
927 ToStdout(" - %-5s -> %s", param, data)
928 ToStdout("Please don't forget that these parameters take effect"
929 " only at the next start of the instance.")
933 # options used in more than one cmd
934 node_opt = make_option("-n", "--node", dest="node", help="Target node",
937 os_opt = cli_option("-o", "--os-type", dest="os", help="What OS to run",
940 # multi-instance selection options
941 m_force_multi = make_option("--force-multiple", dest="force_multi",
942 help="Do not ask for confirmation when more than"
943 " one instance is affected",
944 action="store_true", default=False)
946 m_pri_node_opt = make_option("--primary", dest="multi_mode",
947 help="Filter by nodes (primary only)",
948 const=_SHUTDOWN_NODES_PRI, action="store_const")
950 m_sec_node_opt = make_option("--secondary", dest="multi_mode",
951 help="Filter by nodes (secondary only)",
952 const=_SHUTDOWN_NODES_SEC, action="store_const")
954 m_node_opt = make_option("--node", dest="multi_mode",
955 help="Filter by nodes (primary and secondary)",
956 const=_SHUTDOWN_NODES_BOTH, action="store_const")
958 m_clust_opt = make_option("--all", dest="multi_mode",
959 help="Select all instances in the cluster",
960 const=_SHUTDOWN_CLUSTER, action="store_const")
962 m_inst_opt = make_option("--instance", dest="multi_mode",
963 help="Filter by instance name [default]",
964 const=_SHUTDOWN_INSTANCES, action="store_const")
967 # this is defined separately due to readability only
970 make_option("-n", "--node", dest="node",
971 help="Target node and optional secondary node",
972 metavar="<pnode>[:<snode>]"),
973 cli_option("-s", "--os-size", dest="size", help="Disk size, in MiB unless"
975 default=20 * 1024, type="unit", metavar="<size>"),
976 cli_option("--swap-size", dest="swap", help="Swap size, in MiB unless a"
978 default=4 * 1024, type="unit", metavar="<size>"),
980 keyval_option("-B", "--backend", dest="beparams",
981 type="keyval", default={},
982 help="Backend parameters"),
983 make_option("-t", "--disk-template", dest="disk_template",
984 help="Custom disk setup (diskless, file, plain or drbd)",
985 default=None, metavar="TEMPL"),
986 make_option("-i", "--ip", dest="ip",
987 help="IP address ('none' [default], 'auto', or specify address)",
988 default='none', type="string", metavar="<ADDRESS>"),
989 make_option("--mac", dest="mac",
990 help="MAC address ('auto' [default], or specify address)",
991 default='auto', type="string", metavar="<MACADDRESS>"),
992 make_option("--no-wait-for-sync", dest="wait_for_sync", default=True,
993 action="store_false", help="Don't wait for sync (DANGEROUS!)"),
994 make_option("-b", "--bridge", dest="bridge",
995 help="Bridge to connect this instance to",
996 default=None, metavar="<bridge>"),
997 make_option("--no-start", dest="start", default=True,
998 action="store_false", help="Don't start the instance after"
1000 make_option("--no-ip-check", dest="ip_check", default=True,
1001 action="store_false", help="Don't check that the instance's IP"
1002 " is alive (only valid with --no-start)"),
1003 make_option("--file-storage-dir", dest="file_storage_dir",
1004 help="Relative path under default cluster-wide file storage dir"
1005 " to store file-based disks", default=None,
1007 make_option("--file-driver", dest="file_driver", help="Driver to use"
1008 " for image files", default="loop", metavar="<DRIVER>"),
1009 make_option("--iallocator", metavar="<NAME>",
1010 help="Select nodes for the instance automatically using the"
1011 " <NAME> iallocator plugin", default=None, type="string"),
1012 ikv_option("-H", "--hypervisor", dest="hypervisor",
1013 help="Hypervisor and hypervisor options, in the format"
1014 " hypervisor:option=value,option=value,...", default=None,
1015 type="identkeyval"),
1020 'add': (AddInstance, ARGS_ONE, add_opts,
1021 "[...] -t disk-type -n node[:secondary-node] -o os-type <name>",
1022 "Creates and adds a new instance to the cluster"),
1023 'batch-create': (BatchCreate, ARGS_ONE,
1025 "<instances_file.json>",
1026 "Create a bunch of instances based on specs in the file."),
1027 'console': (ConnectToInstanceConsole, ARGS_ONE,
1029 make_option("--show-cmd", dest="show_command",
1030 action="store_true", default=False,
1031 help=("Show command instead of executing it"))],
1032 "[--show-cmd] <instance>",
1033 "Opens a console on the specified instance"),
1034 'failover': (FailoverInstance, ARGS_ONE,
1035 [DEBUG_OPT, FORCE_OPT,
1036 make_option("--ignore-consistency", dest="ignore_consistency",
1037 action="store_true", default=False,
1038 help="Ignore the consistency of the disks on"
1043 "Stops the instance and starts it on the backup node, using"
1044 " the remote mirror (only for instances of type drbd)"),
1045 'info': (ShowInstanceConfig, ARGS_ANY,
1047 make_option("-s", "--static", dest="static",
1048 action="store_true", default=False,
1049 help="Only show configuration data, not runtime data"),
1050 ], "[-s] [<instance>...]",
1051 "Show information on the specified instance(s)"),
1052 'list': (ListInstances, ARGS_NONE,
1053 [DEBUG_OPT, NOHDR_OPT, SEP_OPT, USEUNITS_OPT, FIELDS_OPT], "",
1054 "Lists the instances and their status. The available fields are"
1055 " (see the man page for details): status, oper_state, oper_ram,"
1056 " name, os, pnode, snodes, admin_state, admin_ram, disk_template,"
1057 " ip, mac, bridge, sda_size, sdb_size, vcpus, serial_no,"
1059 " The default field"
1060 " list is (in order): %s." % ", ".join(_LIST_DEF_FIELDS),
1062 'reinstall': (ReinstallInstance, ARGS_ONE,
1063 [DEBUG_OPT, FORCE_OPT, os_opt,
1064 make_option("--select-os", dest="select_os",
1065 action="store_true", default=False,
1066 help="Interactive OS reinstall, lists available"
1067 " OS templates for selection"),
1070 "[-f] <instance>", "Reinstall a stopped instance"),
1071 'remove': (RemoveInstance, ARGS_ONE,
1072 [DEBUG_OPT, FORCE_OPT,
1073 make_option("--ignore-failures", dest="ignore_failures",
1074 action="store_true", default=False,
1075 help=("Remove the instance from the cluster even"
1076 " if there are failures during the removal"
1077 " process (shutdown, disk removal, etc.)")),
1080 "[-f] <instance>", "Shuts down the instance and removes it"),
1081 'rename': (RenameInstance, ARGS_FIXED(2),
1083 make_option("--no-ip-check", dest="ignore_ip",
1084 help="Do not check that the IP of the new name"
1086 default=False, action="store_true"),
1089 "<instance> <new_name>", "Rename the instance"),
1090 'replace-disks': (ReplaceDisks, ARGS_ONE,
1092 make_option("-n", "--new-secondary", dest="new_secondary",
1093 help=("New secondary node (for secondary"
1094 " node change)"), metavar="NODE"),
1095 make_option("-p", "--on-primary", dest="on_primary",
1096 default=False, action="store_true",
1097 help=("Replace the disk(s) on the primary"
1098 " node (only for the drbd template)")),
1099 make_option("-s", "--on-secondary", dest="on_secondary",
1100 default=False, action="store_true",
1101 help=("Replace the disk(s) on the secondary"
1102 " node (only for the drbd template)")),
1103 make_option("--disks", dest="disks", default=None,
1104 help=("Comma-separated list of disks"
1105 " to replace (e.g. sda) (optional,"
1106 " defaults to all disks")),
1107 make_option("--iallocator", metavar="<NAME>",
1108 help="Select new secondary for the instance"
1109 " automatically using the"
1110 " <NAME> iallocator plugin (enables"
1111 " secondary node replacement)",
1112 default=None, type="string"),
1115 "[-s|-p|-n NODE] <instance>",
1116 "Replaces all disks for the instance"),
1117 'modify': (SetInstanceParams, ARGS_ONE,
1118 [DEBUG_OPT, FORCE_OPT,
1119 make_option("-i", "--ip", dest="ip",
1120 help="IP address ('none' or numeric IP)",
1121 default=None, type="string", metavar="<ADDRESS>"),
1122 make_option("-b", "--bridge", dest="bridge",
1123 help="Bridge to connect this instance to",
1124 default=None, type="string", metavar="<bridge>"),
1125 make_option("--mac", dest="mac",
1126 help="MAC address", default=None,
1127 type="string", metavar="<MACADDRESS>"),
1128 keyval_option("-H", "--hypervisor", type="keyval",
1129 default={}, dest="hypervisor",
1130 help="Change hypervisor parameters"),
1131 keyval_option("-B", "--backend", type="keyval",
1132 default={}, dest="beparams",
1133 help="Change backend parameters"),
1136 "<instance>", "Alters the parameters of an instance"),
1137 'shutdown': (ShutdownInstance, ARGS_ANY,
1138 [DEBUG_OPT, m_node_opt, m_pri_node_opt, m_sec_node_opt,
1139 m_clust_opt, m_inst_opt, m_force_multi,
1142 "<instance>", "Stops an instance"),
1143 'startup': (StartupInstance, ARGS_ANY,
1144 [DEBUG_OPT, FORCE_OPT, m_force_multi,
1145 make_option("-e", "--extra", dest="extra_args",
1146 help="Extra arguments for the instance's kernel",
1147 default=None, type="string", metavar="<PARAMS>"),
1148 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1149 m_clust_opt, m_inst_opt,
1152 "<instance>", "Starts an instance"),
1154 'reboot': (RebootInstance, ARGS_ANY,
1155 [DEBUG_OPT, m_force_multi,
1156 make_option("-e", "--extra", dest="extra_args",
1157 help="Extra arguments for the instance's kernel",
1158 default=None, type="string", metavar="<PARAMS>"),
1159 make_option("-t", "--type", dest="reboot_type",
1160 help="Type of reboot: soft/hard/full",
1161 default=constants.INSTANCE_REBOOT_HARD,
1162 type="string", metavar="<REBOOT>"),
1163 make_option("--ignore-secondaries", dest="ignore_secondaries",
1164 default=False, action="store_true",
1165 help="Ignore errors from secondaries"),
1166 m_node_opt, m_pri_node_opt, m_sec_node_opt,
1167 m_clust_opt, m_inst_opt,
1170 "<instance>", "Reboots an instance"),
1171 'activate-disks': (ActivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1173 "Activate an instance's disks"),
1174 'deactivate-disks': (DeactivateDisks, ARGS_ONE, [DEBUG_OPT, SUBMIT_OPT],
1176 "Deactivate an instance's disks"),
1177 'grow-disk': (GrowDisk, ARGS_FIXED(3),
1178 [DEBUG_OPT, SUBMIT_OPT,
1179 make_option("--no-wait-for-sync",
1180 dest="wait_for_sync", default=True,
1181 action="store_false",
1182 help="Don't wait for sync (DANGEROUS!)"),
1184 "<instance> <disk> <size>", "Grow an instance's disk"),
1185 'list-tags': (ListTags, ARGS_ONE, [DEBUG_OPT],
1186 "<instance_name>", "List the tags of the given instance"),
1187 'add-tags': (AddTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1188 "<instance_name> tag...", "Add tags to the given instance"),
1189 'remove-tags': (RemoveTags, ARGS_ATLEAST(1), [DEBUG_OPT, TAG_SRC_OPT],
1190 "<instance_name> tag...", "Remove tags from given instance"),
1194 'activate_block_devs': 'activate-disks',
1195 'replace_disks': 'replace-disks',
1200 if __name__ == '__main__':
1201 sys.exit(GenericMain(commands, aliases=aliases,
1202 override={"tag_type": constants.TAG_INSTANCE}))