+ os_type=opts.os, pnode=pnode,
+ snode=snode,
+ start=opts.start, ip_check=opts.ip_check,
+ wait_for_sync=opts.wait_for_sync,
+ hypervisor=hypervisor,
+ hvparams=hvparams,
+ beparams=opts.beparams,
+ iallocator=opts.iallocator,
+ file_storage_dir=opts.file_storage_dir,
+ file_driver=opts.file_driver,
+ )
+
+ SubmitOrSend(op, opts)
+ return 0
+
+
+def BatchCreate(opts, args):
+ """Create instances using a definition file.
+
+ This function reads a json file with instances defined
+ in the form::
+
+ {"instance-name":{
+ "disk_size": [20480],
+ "template": "drbd",
+ "backend": {
+ "memory": 512,
+ "vcpus": 1 },
+ "os": "debootstrap",
+ "primary_node": "firstnode",
+ "secondary_node": "secondnode",
+ "iallocator": "dumb"}
+ }
+
+ Note that I{primary_node} and I{secondary_node} have precedence over
+ I{iallocator}.
+
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should contain one element, the json filename
+ @rtype: int
+ @return: the desired exit code
+
+ """
+ _DEFAULT_SPECS = {"disk_size": [20 * 1024],
+ "backend": {},
+ "iallocator": None,
+ "primary_node": None,
+ "secondary_node": None,
+ "ip": 'none',
+ "mac": 'auto',
+ "bridge": None,
+ "start": True,
+ "ip_check": True,
+ "hypervisor": None,
+ "hvparams": {},
+ "file_storage_dir": None,
+ "file_driver": 'loop'}
+
+ def _PopulateWithDefaults(spec):
+ """Returns a new hash combined with default values."""
+ mydict = _DEFAULT_SPECS.copy()
+ mydict.update(spec)
+ return mydict
+
+ def _Validate(spec):
+ """Validate the instance specs."""
+ # Validate fields required under any circumstances
+ for required_field in ('os', 'template'):
+ if required_field not in spec:
+ raise errors.OpPrereqError('Required field "%s" is missing.' %
+ required_field)
+ # Validate special fields
+ if spec['primary_node'] is not None:
+ if (spec['template'] in constants.DTS_NET_MIRROR and
+ spec['secondary_node'] is None):
+ raise errors.OpPrereqError('Template requires secondary node, but'
+ ' there was no secondary provided.')
+ elif spec['iallocator'] is None:
+ raise errors.OpPrereqError('You have to provide at least a primary_node'
+ ' or an iallocator.')
+
+ if (spec['hvparams'] and
+ not isinstance(spec['hvparams'], dict)):
+ raise errors.OpPrereqError('Hypervisor parameters must be a dict.')
+
+ json_filename = args[0]
+ try:
+ fd = open(json_filename, 'r')
+ instance_data = simplejson.load(fd)
+ fd.close()
+ except Exception, err:
+ ToStderr("Can't parse the instance definition file: %s" % str(err))
+ return 1
+
+ jex = JobExecutor()
+
+ # Iterate over the instances and do:
+ # * Populate the specs with default value
+ # * Validate the instance specs
+ i_names = utils.NiceSort(instance_data.keys())
+ for name in i_names:
+ specs = instance_data[name]
+ specs = _PopulateWithDefaults(specs)
+ _Validate(specs)
+
+ hypervisor = specs['hypervisor']
+ hvparams = specs['hvparams']
+
+ disks = []
+ for elem in specs['disk_size']:
+ try:
+ size = utils.ParseUnit(elem)
+ except ValueError, err:
+ raise errors.OpPrereqError("Invalid disk size '%s' for"
+ " instance %s: %s" %
+ (elem, name, err))
+ disks.append({"size": size})
+
+ nic0 = {'ip': specs['ip'], 'bridge': specs['bridge'], 'mac': specs['mac']}
+
+ utils.ForceDictType(specs['backend'], constants.BES_PARAMETER_TYPES)
+ utils.ForceDictType(hvparams, constants.HVS_PARAMETER_TYPES)
+
+ op = opcodes.OpCreateInstance(instance_name=name,
+ disks=disks,
+ disk_template=specs['template'],
+ mode=constants.INSTANCE_CREATE,
+ os_type=specs['os'],
+ pnode=specs['primary_node'],
+ snode=specs['secondary_node'],
+ nics=[nic0],
+ start=specs['start'],
+ ip_check=specs['ip_check'],
+ wait_for_sync=True,
+ iallocator=specs['iallocator'],
+ hypervisor=hypervisor,
+ hvparams=hvparams,
+ beparams=specs['backend'],
+ file_storage_dir=specs['file_storage_dir'],
+ file_driver=specs['file_driver'])
+
+ jex.QueueJob(name, op)
+ # we never want to wait, just show the submitted job IDs
+ jex.WaitOrShow(False)
+
+ return 0
+
+
+def ReinstallInstance(opts, args):
+ """Reinstall an instance.
+
+ @param opts: the command line options selected by the user
+ @type args: list
+ @param args: should contain only one element, the name of the
+ instance to be reinstalled
+ @rtype: int
+ @return: the desired exit code
+
+ """
+ # first, compute the desired name list
+ if opts.multi_mode is None:
+ opts.multi_mode = _SHUTDOWN_INSTANCES
+
+ inames = _ExpandMultiNames(opts.multi_mode, args)
+ if not inames:
+ raise errors.OpPrereqError("Selection filter does not match any instances")
+
+ # second, if requested, ask for an OS
+ if opts.select_os is True:
+ op = opcodes.OpDiagnoseOS(output_fields=["name", "valid"], names=[])
+ result = SubmitOpCode(op)
+
+ if not result:
+ ToStdout("Can't get the OS list")
+ return 1
+
+ ToStdout("Available OS templates:")
+ number = 0
+ choices = []
+ for entry in result:
+ ToStdout("%3s: %s", number, entry[0])
+ choices.append(("%s" % number, entry[0], entry[0]))
+ number = number + 1
+
+ choices.append(('x', 'exit', 'Exit gnt-instance reinstall'))
+ selected = AskUser("Enter OS template number (or x to abort):",
+ choices)
+
+ if selected == 'exit':
+ ToStderr("User aborted reinstall, exiting")
+ return 1
+
+ os_name = selected
+ else:
+ os_name = opts.os
+
+ # third, get confirmation: multi-reinstall requires --force-multi
+ # *and* --force, single-reinstall just --force
+ multi_on = opts.multi_mode != _SHUTDOWN_INSTANCES or len(inames) > 1
+ if multi_on:
+ warn_msg = "Note: this will remove *all* data for the below instances!\n"
+ if not ((opts.force_multi and opts.force) or
+ _ConfirmOperation(inames, "reinstall", extra=warn_msg)):
+ return 1
+ else:
+ if not opts.force:
+ usertext = ("This will reinstall the instance %s and remove"
+ " all data. Continue?") % instance_name
+ if not AskUser(usertext):
+ return 1
+
+ jex = JobExecutor(verbose=multi_on)
+ for instance_name in inames:
+ op = opcodes.OpReinstallInstance(instance_name=instance_name,
+ os_type=os_name)
+ jex.QueueJob(instance_name, op)
+
+ jex.WaitOrShow(not opts.submit_only)