Example #1
0
 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)
Example #2
0
 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 _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
Example #5
0
 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
Example #6
0
 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)