def rpc_call_wrapper(context, topic, msg): """Stub out the scheduler creating the instance entry""" if topic == FLAGS.scheduler_topic and \ msg['method'] == 'run_instance': scheduler = scheduler_driver.Scheduler instance = scheduler().create_instance_db_entry( context, msg['args']['request_spec']) return [scheduler_driver.encode_instance(instance)] else: return orig_rpc_call(context, topic, msg)
def _provision_resource_locally(self, context, weighted_host, request_spec, kwargs): """Create the requested resource in this Zone.""" instance = self.create_instance_db_entry(context, request_spec) driver.cast_to_compute_host(context, weighted_host.host, 'run_instance', instance_uuid=instance['uuid'], **kwargs) inst = driver.encode_instance(instance, local=True) # So if another instance is created, create_instance_db_entry will # actually create a new entry, instead of assume it's been created # already del request_spec['instance_properties']['uuid'] return inst
def schedule_run_instance(self, context, request_spec, *_args, **_kwargs): num_instances = request_spec.get('num_instances', 1) instances = [] for num in xrange(num_instances): host = self._schedule_instance(context, request_spec['instance_properties'], *_args, **_kwargs) instance_ref = self.create_instance_db_entry(context, request_spec) driver.cast_to_compute_host(context, host, 'run_instance', instance_uuid=instance_ref['uuid'], **_kwargs) instances.append(driver.encode_instance(instance_ref)) # So if we loop around, create_instance_db_entry will actually # create a new entry, instead of assume it's been created # already del request_spec['instance_properties']['uuid'] return instances
def schedule_run_instance(self, context, request_spec, *_args, **kwargs): """Create and run an instance or instances""" elevated = context.elevated() num_instances = request_spec.get('num_instances', 1) instances = [] for num in xrange(num_instances): host = self._schedule(context, 'compute', request_spec, **kwargs) instance = self.create_instance_db_entry(elevated, request_spec) driver.cast_to_compute_host(context, host, 'run_instance', instance_uuid=instance['uuid'], **kwargs) instances.append(driver.encode_instance(instance)) # So if we loop around, create_instance_db_entry will actually # create a new entry, instead of assume it's been created # already del request_spec['instance_properties']['uuid'] return instances
class DistributedScheduler(driver.Scheduler): """Scheduler that can work across any engine deployment, from simple deployments to multiple nested zones. """ def __init__(self, *args, **kwargs): super(DistributedScheduler, self).__init__(*args, **kwargs) self.cost_function_cache = {} self.options = scheduler_options.SchedulerOptions() def schedule(self, context, topic, method, *args, **kwargs): """The schedule() contract requires we return the one best-suited host for this request. NOTE: We're only focused on compute instances right now, so this method will always raise NoValidHost().""" msg = _("No host selection for %s defined." % topic) raise exception.NoValidHost(reason=msg) def schedule_run_instance(self, context, request_spec, *args, **kwargs): """This method is called from engine.compute.api to provision an instance. However we need to look at the parameters being passed in to see if this is a request to: 1. Create build plan (a list of WeightedHosts) and then provision, or 2. Use the WeightedHost information in the request parameters to simply create the instance (either in this zone or a child zone). returns a list of the instances created. """ elevated = context.elevated() num_instances = request_spec.get('num_instances', 1) LOG.debug( _("Attempting to build %(num_instances)d instance(s)") % locals()) weighted_hosts = [] # Having a 'blob' hint means we've already provided a build plan. # We need to turn this back into a WeightedHost object. blob = request_spec.get('blob', None) if blob: weighted_hosts.append(self._make_weighted_host_from_blob(blob)) else: # No plan ... better make one. weighted_hosts = self._schedule(elevated, "compute", request_spec, *args, **kwargs) if not weighted_hosts: raise exception.NoValidHost(reason=_("")) instances = [] for num in xrange(num_instances): if not weighted_hosts: break weighted_host = weighted_hosts.pop(0) instance = None if weighted_host.host: instance = self._provision_resource_locally( elevated, weighted_host, request_spec, kwargs) else: instance = self._ask_child_zone_to_create_instance( elevated, weighted_host, request_spec, kwargs) if instance: instances.append(instance) return instances def schedule_prep_resize(self, context, request_spec, *args, **kwargs): """Select a target for resize. Selects a target host for the instance, post-resize, and casts the prep_resize operation to it. """ # We need the new instance type ID... instance_type_id = kwargs['instance_type_id'] elevated = context.elevated() LOG.debug( _("Attempting to determine target host for resize to " "instance type %(instance_type_id)s") % locals()) # Convert it to an actual instance type instance_type = db.instance_type_get(elevated, instance_type_id) # Now let's grab a possibility hosts = self._schedule(elevated, 'compute', request_spec, *args, **kwargs) if not hosts: raise exception.NoValidHost(reason=_("")) host = hosts.pop(0) # Forward off to the host driver.cast_to_host(context, 'compute', host.host, 'prep_resize', **kwargs) def select(self, context, request_spec, *args, **kwargs): """Select returns a list of weights and zone/host information corresponding to the best hosts to service the request. Any internal zone information will be encrypted so as not to reveal anything about our inner layout. """ elevated = context.elevated() weighted_hosts = self._schedule(elevated, "compute", request_spec, *args, **kwargs) return [weighted_host.to_dict() for weighted_host in weighted_hosts] def _call_zone_method(self, context, method, specs, zones): """Call engineclient zone method. Broken out for testing.""" return api.call_zone_method(context, method, specs=specs, zones=zones) def _provision_resource_locally(self, context, weighted_host, request_spec, kwargs): """Create the requested resource in this Zone.""" instance = self.create_instance_db_entry(context, request_spec) driver.cast_to_compute_host(context, weighted_host.host, 'run_instance', instance_uuid=instance['uuid'], **kwargs) inst = driver.encode_instance(instance, local=True) # So if another instance is created, create_instance_db_entry will # actually create a new entry, instead of assume it's been created # already del request_spec['instance_properties']['uuid'] return inst def _make_weighted_host_from_blob(self, blob): """Returns the decrypted blob as a WeightedHost object or None if invalid. Broken out for testing. """ decryptor = crypto.decryptor(FLAGS.build_plan_encryption_key) try: json_entry = decryptor(blob) # Extract our WeightedHost values wh_dict = json.loads(json_entry) host = wh_dict.get('host', None) blob = wh_dict.get('blob', None) zone = wh_dict.get('zone', None) return least_cost.WeightedHost(wh_dict['weight'], host=host, blob=blob, zone=zone) except M2Crypto.EVP.EVPError: raise InvalidBlob() def _ask_child_zone_to_create_instance(self, context, weighted_host, request_spec, kwargs): """Once we have determined that the request should go to one of our children, we need to fabricate a new POST /servers/ call with the same parameters that were passed into us. This request is always for a single instance. Note that we have to reverse engineer from our args to get back the image, flavor, ipgroup, etc. since the original call could have come in from EC2 (which doesn't use these things). """ instance_type = request_spec['instance_type'] instance_properties = request_spec['instance_properties'] name = instance_properties['display_name'] image_ref = instance_properties['image_ref'] meta = instance_properties['metadata'] flavor_id = instance_type['flavorid'] reservation_id = instance_properties['reservation_id'] files = kwargs['injected_files'] zone = db.zone_get(context.elevated(), weighted_host.zone) zone_name = zone.name url = zone.api_url LOG.debug( _("Forwarding instance create call to zone '%(zone_name)s'. " "ReservationID=%(reservation_id)s") % locals()) engine = None try: # This operation is done as the caller, not the zone admin. engine = engineclient.Client(zone.username, zone.password, None, url, token=context.auth_token, region_name=zone_name) engine.authenticate() except engineclient_exceptions.BadRequest, e: raise exception.NotAuthorized( _("Bad credentials attempting " "to talk to zone at %(url)s.") % locals()) # NOTE(Vek): Engineclient has two different calling conventions # for this call, depending on whether you're using # 1.0 or 1.1 API: in 1.0, there's an ipgroups # argument after flavor_id which isn't present in # 1.1. To work around this, all the extra # arguments are passed as keyword arguments # (there's a reasonable default for ipgroups in the # engineclient call). instance = engine.servers.create(name, image_ref, flavor_id, meta=meta, files=files, zone_blob=weighted_host.blob, reservation_id=reservation_id) return driver.encode_instance(instance._info, local=False)