+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
+
+