Beispiel #1
0
    def update_statuses(self, callback, *args, **kwargs):
        """Update leases and reservations table after executing a callback."""
        try:
            # The callback() has to return a dictionary of
            # {reservation id: flags to update}.
            # e.g. {'dummyid': {'missing_resources': True}}
            reservation_flags = callback(*args, **kwargs)
        except Exception as e:
            LOG.exception(
                'Caught an exception while executing a callback. '
                '%s', str(e))

            # TODO(hiro-kobayashi): update statuses of related leases and
            # reservations. Depends on the state-machine blueprint.

        # Update flags of related leases and reservations.
        lease_ids = set([])
        for reservation_id, flags in reservation_flags.items():
            db_api.reservation_update(reservation_id, flags)
            LOG.debug('Reservation %s was updated: %s', reservation_id, flags)
            reservation = db_api.reservation_get(reservation_id)
            lease_ids.add(reservation['lease_id'])
        for lease_id in lease_ids:
            LOG.debug('Lease %s was updated: {"degraded": True}', lease_id)
            db_api.lease_update(lease_id, {'degraded': True})
Beispiel #2
0
    def _reallocate(self, allocation):
        """Allocate an alternative host.

        :param: allocation: allocation to change.
        :return: True if an alternative host was successfully allocated.
        """
        reservation = db_api.reservation_get(allocation['reservation_id'])
        pool = nova.ReservationPool()

        # Remove the failed host from the aggregate.
        if reservation['status'] == status.reservation.ACTIVE:
            host = db_api.host_get(allocation['compute_host_id'])
            pool.remove_computehost(reservation['aggregate_id'],
                                    host['service_name'])
            try:
                self.placement_client.delete_reservation_inventory(
                    host['service_name'], reservation['id'])
            except openstack_ex.ResourceProviderNotFound:
                pass

        # Allocate an alternative host.
        values = {}
        lease = db_api.lease_get(reservation['lease_id'])
        values['start_date'] = max(datetime.datetime.utcnow(),
                                   lease['start_date'])
        values['end_date'] = lease['end_date']
        specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount',
                 'resource_properties']
        for key in specs:
            values[key] = reservation[key]
        changed_hosts = self.pickup_hosts(reservation['id'], values)
        if len(changed_hosts['added']) == 0:
            db_api.host_allocation_destroy(allocation['id'])
            LOG.warn('Could not find alternative host for reservation %s '
                     '(lease: %s).', reservation['id'], lease['name'])
            return False
        else:
            new_host_id = changed_hosts['added'].pop()
            db_api.host_allocation_update(
                allocation['id'], {'compute_host_id': new_host_id})
            if reservation['status'] == status.reservation.ACTIVE:
                # Add the alternative host into the aggregate.
                new_host = db_api.host_get(new_host_id)
                pool.add_computehost(reservation['aggregate_id'],
                                     new_host['service_name'],
                                     stay_in=True)
                self.placement_client.update_reservation_inventory(
                    new_host['service_name'], reservation['id'], 1)
            LOG.warn('Resource changed for reservation %s (lease: %s).',
                     reservation['id'], lease['name'])

            return True
Beispiel #3
0
    def _update_flags(self, reservation_flags):
        """Update lease/reservation flags."""
        lease_ids = set([])

        for reservation_id, flags in reservation_flags.items():
            db_api.reservation_update(reservation_id, flags)
            LOG.debug('Reservation %s was updated: %s', reservation_id, flags)
            reservation = db_api.reservation_get(reservation_id)
            lease_ids.add(reservation['lease_id'])

        for lease_id in lease_ids:
            LOG.debug('Lease %s was updated: {"degraded": True}', lease_id)
            db_api.lease_update(lease_id, {'degraded': True})
Beispiel #4
0
    def update_resources(self, reservation_id):
        """Updates reserved resources in Nova.

        This method updates reserved resources in Compute service. If the
        reservation is in active status, it adds new allocated hosts into
        a reserved aggregate. If the reservation is not started yet, it
        updates a reserved flavor.
        """
        reservation = db_api.reservation_get(reservation_id)

        if reservation['status'] == 'active':
            pool = nova.ReservationPool()

            # Dict of number of instances to reserve on a host keyed by the
            # host id
            allocation_map = collections.defaultdict(lambda: 0)
            for allocation in db_api.host_allocation_get_all_by_values(
                    reservation_id=reservation['id']):
                host_id = allocation['compute_host_id']
                allocation_map[host_id] += 1

            for host_id, num in allocation_map.items():
                host = db_api.host_get(host_id)
                try:
                    pool.add_computehost(reservation['aggregate_id'],
                                         host['service_name'],
                                         stay_in=True)
                except mgr_exceptions.AggregateAlreadyHasHost:
                    pass
                except nova_exceptions.ClientException:
                    err_msg = ('Fail to add host %s to aggregate %s.' %
                               (host, reservation['aggregate_id']))
                    raise mgr_exceptions.NovaClientError(err_msg)
                self.placement_client.update_reservation_inventory(
                    host['hypervisor_hostname'], reservation['id'], num)
        else:
            try:
                self.nova.nova.flavors.delete(reservation['id'])
                self._create_flavor(reservation['id'], reservation['vcpus'],
                                    reservation['memory_mb'],
                                    reservation['disk_gb'],
                                    reservation['server_group_id'])
            except nova_exceptions.ClientException:
                LOG.exception(
                    "Failed to update Nova resources "
                    "for reservation %s", reservation['id'])
                raise mgr_exceptions.NovaClientError()
Beispiel #5
0
    def update_reservation(self, reservation_id, values):
        """Update reservation."""
        reservation = db_api.reservation_get(reservation_id)
        lease = db_api.lease_get(reservation['lease_id'])
        dates_before = {
            'start_date': lease['start_date'],
            'end_date': lease['end_date']
        }
        dates_after = {
            'start_date': values['start_date'],
            'end_date': values['end_date']
        }
        fip_reservation = db_api.fip_reservation_get(
            reservation['resource_id'])

        if ('network_id' in values
                and values.get('network_id') != fip_reservation['network_id']):
            raise manager_ex.CantUpdateFloatingIPReservation(
                msg="Updating network_id is not supported")

        required_fips = fip_reservation['required_floatingips']
        if ('required_floatingips' in values
                and values['required_floatingips'] != required_fips
                and values['required_floatingips'] != []):
            raise manager_ex.CantUpdateFloatingIPReservation(
                msg="Updating required_floatingips is not supported except "
                "with an empty list")

        self._update_allocations(dates_before, dates_after, reservation_id,
                                 reservation['status'], fip_reservation,
                                 values)
        updates = {}
        if 'amount' in values:
            updates['amount'] = values.get('amount')
        if updates:
            db_api.fip_reservation_update(fip_reservation['id'], updates)

        if ('required_floatingips' in values
                and values['required_floatingips'] != required_fips):
            db_api.required_fip_destroy_by_fip_reservation_id(
                fip_reservation['id'])
            for fip_address in values.get('required_floatingips'):
                fip_address_values = {
                    'address': fip_address,
                    'floatingip_reservation_id': fip_reservation['id']
                }
                db_api.required_fip_create(fip_address_values)
    def _reallocate(self, allocation):
        """Allocate an alternative host.

        :param: allocation: allocation to change.
        :return: True if an alternative host was successfully allocated.
        """
        reservation = db_api.reservation_get(allocation['reservation_id'])
        h_reservation = db_api.host_reservation_get(
            reservation['resource_id'])
        lease = db_api.lease_get(reservation['lease_id'])
        pool = nova.ReservationPool()

        # Remove the old host from the aggregate.
        if reservation['status'] == status.reservation.ACTIVE:
            host = db_api.host_get(allocation['compute_host_id'])
            pool.remove_computehost(h_reservation['aggregate_id'],
                                    host['service_name'])

        # Allocate an alternative host.
        start_date = max(datetime.datetime.utcnow(), lease['start_date'])
        new_hostids = self._matching_hosts(
            reservation['hypervisor_properties'],
            reservation['resource_properties'],
            '1-1', start_date, lease['end_date']
        )
        if not new_hostids:
            db_api.host_allocation_destroy(allocation['id'])
            LOG.warn('Could not find alternative host for reservation %s '
                     '(lease: %s).', reservation['id'], lease['name'])
            return False
        else:
            new_hostid = new_hostids.pop()
            db_api.host_allocation_update(allocation['id'],
                                          {'compute_host_id': new_hostid})
            LOG.warn('Resource changed for reservation %s (lease: %s).',
                     reservation['id'], lease['name'])
            if reservation['status'] == status.reservation.ACTIVE:
                # Add the alternative host into the aggregate.
                new_host = db_api.host_get(new_hostid)
                pool.add_computehost(h_reservation['aggregate_id'],
                                     new_host['service_name'])

            return True
Beispiel #7
0
    def update_reservation(self, reservation_id, values):
        """Update reservation."""
        reservation = db_api.reservation_get(reservation_id)
        lease = db_api.lease_get(reservation['lease_id'])

        if (not [
                x for x in values.keys() if x in
            ['min', 'max', 'hypervisor_properties', 'resource_properties']
        ] and values['start_date'] >= lease['start_date']
                and values['end_date'] <= lease['end_date']):
            # Nothing to update
            return

        dates_before = {
            'start_date': lease['start_date'],
            'end_date': lease['end_date']
        }
        dates_after = {
            'start_date': values['start_date'],
            'end_date': values['end_date']
        }
        host_reservation = db_api.host_reservation_get(
            reservation['resource_id'])
        self._update_allocations(dates_before, dates_after, reservation_id,
                                 reservation['status'], host_reservation,
                                 values)

        updates = {}
        if 'min' in values or 'max' in values:
            count_range = str(
                values.get('min', host_reservation['count_range'].split(
                    '-')[0])) + '-' + str(
                        values.get(
                            'max',
                            host_reservation['count_range'].split('-')[1]))
            updates['count_range'] = count_range
        if 'hypervisor_properties' in values:
            updates['hypervisor_properties'] = values.get(
                'hypervisor_properties')
        if 'resource_properties' in values:
            updates['resource_properties'] = values.get('resource_properties')
        if updates:
            db_api.host_reservation_update(host_reservation['id'], updates)
Beispiel #8
0
    def update_resources(self, reservation_id):
        """Updates reserved resources in Nova.

        This method updates reserved resources in Compute service. If the
        reservation is in active status, it adds new allocated hosts into
        a reserved aggregate. If the reservation is not started yet, it
        updates a reserved flavor.
        """
        reservation = db_api.reservation_get(reservation_id)

        if reservation['status'] == 'active':
            pool = nova.ReservationPool()
            for allocation in db_api.host_allocation_get_all_by_values(
                    reservation_id=reservation['id']):
                host = db_api.host_get(allocation['compute_host_id'])
                try:
                    pool.add_computehost(reservation['aggregate_id'],
                                         host['service_name'],
                                         stay_in=True)
                except mgr_exceptions.AggregateAlreadyHasHost:
                    pass
                except nova_exceptions.ClientException:
                    err_msg = ('Fail to add host %s to aggregate %s.' %
                               (host, reservation['aggregate_id']))
                    raise mgr_exceptions.NovaClientError(err_msg)
        else:
            try:
                self.nova.nova.flavors.delete(reservation['id'])
                self._create_flavor(reservation['id'], reservation['vcpus'],
                                    reservation['memory_mb'],
                                    reservation['disk_gb'],
                                    reservation['server_group_id'])
            except nova_exceptions.ClientException:
                LOG.exception(
                    "Failed to update Nova resources "
                    "for reservation %s", reservation['id'])
                raise mgr_exceptions.NovaClientError()
Beispiel #9
0
    def update_reservation(self, reservation_id, new_values):
        """Updates an instance reservation with requested parameters.

        This method allows users to update an instance reservation under the
        following conditions.
        - If an instance reservation has not started yet
             - vcpus, memory_mb disk_gb and amount can be updateable unless
               Blazar can accommodate the new request.
        - If an instance reservation has already started
             - only amount is increasable.
        """
        self._validate_reservation_params(new_values)

        reservation = db_api.reservation_get(reservation_id)
        lease = db_api.lease_get(reservation['lease_id'])

        updatable = [
            'vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount',
            'resource_properties'
        ]
        if (not any([k in updatable for k in new_values.keys()])
                and new_values['start_date'] >= lease['start_date']
                and new_values['end_date'] <= lease['end_date']):
            # no update because of just shortening the reservation time
            return

        if (reservation['status'] == 'active'
                and any([k in updatable[:-1] for k in new_values.keys()])):
            msg = "An active reservation only accepts to update amount."
            raise mgr_exceptions.InvalidStateUpdate(msg)

        if reservation['status'] == 'error':
            msg = "An error reservation doesn't accept an updating request."
            raise mgr_exceptions.InvalidStateUpdate(msg)

        if new_values.get('affinity', None):
            new_values['affinity'] = bool_from_string(new_values['affinity'],
                                                      default=None)

        for key in updatable:
            if key not in new_values:
                new_values[key] = reservation[key]

        changed_hosts = self.pickup_hosts(reservation_id, new_values)

        if (reservation['status'] == 'active'
                and len(changed_hosts['removed']) > 0):
            err_msg = ("Instance reservation doesn't allow to reduce/replace "
                       "reserved instance slots when the reservation is in "
                       "active status.")
            raise mgr_exceptions.CantUpdateParameter(err_msg)

        db_api.instance_reservation_update(
            reservation['resource_id'],
            {key: new_values[key]
             for key in updatable})

        self.update_host_allocations(changed_hosts['added'],
                                     changed_hosts['removed'], reservation_id)

        try:
            self.update_resources(reservation_id)
        except mgr_exceptions.NovaClientError:
            raise
Beispiel #10
0
    def heal_reservations(self, failed_resources):
        """Heal reservations which suffer from resource failures.

        :param: failed_resources: a list of failed hosts.
        :return: a dictionary of {reservation id: flags to update}
                 e.g. {'de27786d-bd96-46bb-8363-19c13b2c6657':
                       {'missing_resources': True}}
        """
        reservation_flags = {}

        failed_allocs = []
        for host in failed_resources:
            failed_allocs += db_api.host_allocation_get_all_by_values(
                compute_host_id=host['id'])

        for alloc in failed_allocs:
            reservation = db_api.reservation_get(alloc['reservation_id'])
            if reservation['resource_type'] != plugin.RESOURCE_TYPE:
                continue
            lease = db_api.lease_get(reservation['lease_id'])
            host_reservation = None
            pool = None

            # Remove the failed host from the aggregate.
            if reservation['status'] == status.reservation.ACTIVE:
                host = db_api.host_get(alloc['compute_host_id'])
                host_reservation = db_api.host_reservation_get(
                    reservation['resource_id'])
                with trusts.create_ctx_from_trust(lease['trust_id']):
                    pool = nova.ReservationPool()
                    pool.remove_computehost(host_reservation['aggregate_id'],
                                            host['service_name'])

            # Allocate alternative resource.
            start_date = max(datetime.datetime.utcnow(), lease['start_date'])
            new_hostids = self._matching_hosts(
                reservation['hypervisor_properties'],
                reservation['resource_properties'], '1-1', start_date,
                lease['end_date'])
            if not new_hostids:
                if reservation['id'] not in reservation_flags:
                    reservation_flags[reservation['id']] = {}
                reservation_flags[reservation['id']].update(
                    {'missing_resources': True})
                db_api.host_allocation_destroy(alloc['id'])
                LOG.warn(
                    'Could not find alternative host for reservation %s '
                    '(lease: %s).', reservation['id'], lease['name'])
            else:
                new_hostid = new_hostids.pop()
                db_api.host_allocation_update(alloc['id'],
                                              {'compute_host_id': new_hostid})
                if reservation['status'] == status.reservation.ACTIVE:
                    # Add the alternative host into the aggregate.
                    new_host = db_api.host_get(new_hostid)
                    with trusts.create_ctx_from_trust(lease['trust_id']):
                        pool.add_computehost(host_reservation['aggregate_id'],
                                             new_host['service_name'])
                    if reservation['id'] not in reservation_flags:
                        reservation_flags[reservation['id']] = {}
                    reservation_flags[reservation['id']].update(
                        {'resources_changed': True})
                LOG.warn('Resource changed for reservation %s (lease: %s).',
                         reservation['id'], lease['name'])

        return reservation_flags
Beispiel #11
0
    def heal_reservations(self, failed_resources):
        """Heal reservations which suffer from resource failures.

        :param: failed_resources: a list of failed hosts.
        :return: a dictionary of {reservation id: flags to update}
                 e.g. {'de27786d-bd96-46bb-8363-19c13b2c6657':
                       {'missing_resources': True}}
        """
        reservation_flags = {}

        failed_allocs = []
        for host in failed_resources:
            failed_allocs += db_api.host_allocation_get_all_by_values(
                compute_host_id=host['id'])

        for alloc in failed_allocs:
            reservation = db_api.reservation_get(alloc['reservation_id'])
            if reservation['resource_type'] != RESOURCE_TYPE:
                continue
            pool = None

            # Remove the failed host from the aggregate.
            if reservation['status'] == status.reservation.ACTIVE:
                host = db_api.host_get(alloc['compute_host_id'])
                pool = nova.ReservationPool()
                pool.remove_computehost(reservation['aggregate_id'],
                                        host['service_name'])

            # Allocate alternative resource.
            values = {}
            lease = db_api.lease_get(reservation['lease_id'])
            values['start_date'] = max(datetime.datetime.utcnow(),
                                       lease['start_date'])
            values['end_date'] = lease['end_date']
            specs = ['vcpus', 'memory_mb', 'disk_gb', 'affinity', 'amount']
            for key in specs:
                values[key] = reservation[key]
            changed_hosts = self.pickup_hosts(reservation['id'], values)
            if len(changed_hosts['added']) == 0:
                if reservation['id'] not in reservation_flags:
                    reservation_flags[reservation['id']] = {}
                reservation_flags[reservation['id']].update(
                    {'missing_resources': True})
                db_api.host_allocation_destroy(alloc['id'])
                LOG.warn(
                    'Could not find alternative host for reservation %s '
                    '(lease: %s).', reservation['id'], lease['name'])
            else:
                new_host_id = changed_hosts['added'].pop()
                db_api.host_allocation_update(alloc['id'],
                                              {'compute_host_id': new_host_id})
                if reservation['status'] == status.reservation.ACTIVE:
                    # Add the alternative host into the aggregate.
                    new_host = db_api.host_get(new_host_id)
                    pool.add_computehost(reservation['aggregate_id'],
                                         new_host['service_name'],
                                         stay_in=True)
                    if reservation['id'] not in reservation_flags:
                        reservation_flags[reservation['id']] = {}
                    reservation_flags[reservation['id']].update(
                        {'resources_changed': True})

                LOG.warn('Resource changed for reservation %s (lease: %s).',
                         reservation['id'], lease['name'])

        return reservation_flags