Exemplo n.º 1
0
 def _exec_event(self, event):
     """Execute an event function"""
     event_fn = getattr(self, event['event_type'], None)
     if event_fn is None:
         raise exceptions.EventError(
             error='Event type %s is not supported'
                   % event['event_type'])
     try:
         event_fn(lease_id=event['lease_id'], event_id=event['id'])
     except common_ex.InvalidStatus:
         now = datetime.datetime.utcnow()
         if now < event['time'] + datetime.timedelta(
                 seconds=CONF.manager.event_max_retries * 10):
             # Set the event status UNDONE for retrying the event
             db_api.event_update(event['id'],
                                 {'status': status.event.UNDONE})
         else:
             db_api.event_update(event['id'],
                                 {'status': status.event.ERROR})
             LOG.exception('Error occurred while handling %s event for '
                           'lease %s.', event['event_type'],
                           event['lease_id'])
     except Exception:
         db_api.event_update(event['id'],
                             {'status': status.event.ERROR})
         LOG.exception('Error occurred while handling %s event for '
                       'lease %s.', event['event_type'], event['lease_id'])
     else:
         lease = db_api.lease_get(event['lease_id'])
         with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
             self._send_notification(
                 lease, ctx, events=['event.%s' % event['event_type']])
Exemplo n.º 2
0
    def _heal_reservation(self, reservation, host_ids):
        """Allocate alternative host(s) for the given reservation.

        :param reservation: A reservation that has allocations to change
        :param host_ids: Failed host ids
        :return: True if all the allocations in the given reservation
                 are successfully allocated
        """
        lease = db_api.lease_get(reservation['lease_id'])

        ret = True
        allocations = [
            alloc for alloc in reservation['computehost_allocations']
            if alloc['compute_host_id'] in host_ids]

        if reservation['affinity']:
            old_host_id = allocations[0]['compute_host_id']
            new_host_id = self._select_host(reservation, lease)

            self._pre_reallocate(reservation, old_host_id)

            if new_host_id is None:
                for allocation in allocations:
                    db_api.host_allocation_destroy(allocation['id'])
                LOG.warn('Could not find alternative host for '
                         'reservation %s (lease: %s).',
                         reservation['id'], lease['name'])
                ret = False
            else:
                for allocation in allocations:
                    db_api.host_allocation_update(
                        allocation['id'], {'compute_host_id': new_host_id})
                self._post_reallocate(
                    reservation, lease, new_host_id, len(allocations))

        else:
            new_host_ids = []
            for allocation in allocations:
                old_host_id = allocation['compute_host_id']
                new_host_id = self._select_host(reservation, lease)

                self._pre_reallocate(reservation, old_host_id)

                if new_host_id is None:
                    db_api.host_allocation_destroy(allocation['id'])
                    LOG.warn('Could not find alternative host for '
                             'reservation %s (lease: %s).',
                             reservation['id'], lease['name'])
                    ret = False
                    continue

                db_api.host_allocation_update(
                    allocation['id'], {'compute_host_id': new_host_id})
                new_host_ids.append(new_host_id)

            for new_host, num in collections.Counter(new_host_ids).items():
                self._post_reallocate(reservation, lease, new_host, num)

        return ret
Exemplo n.º 3
0
    def is_stable(cls, lease_id):
        """Check if the lease status is stable

        :param lease_id: Lease ID
        :return: True if the status is in (PENDING, ACTIVE, TERMINATED, ERROR)
        """
        lease = db_api.lease_get(lease_id)
        return (lease['status'] in cls.STABLE)
Exemplo n.º 4
0
            def wrapper(*args, **kwargs):
                # Update a lease status
                lease_id = kwargs['lease_id']
                l = db_api.lease_get(lease_id)
                if cls.is_valid_transition(l['status'],
                                           transition,
                                           lease_id=lease_id):
                    db_api.lease_update(lease_id,
                                        {'status': transition})
                    LOG.debug('Status of lease %s changed from %s to %s.',
                              lease_id, l['status'], transition)
                else:
                    LOG.warn('Aborting %s. '
                             'Invalid lease status transition from %s to %s.',
                             func.__name__, l['status'],
                             transition)
                    raise exceptions.InvalidStatus

                # Executing the wrapped function
                try:
                    result = func(*args, **kwargs)
                except Exception as e:
                    LOG.error('Lease %s went into ERROR status. %s',
                              lease_id, str(e))
                    db_api.lease_update(lease_id,
                                        {'status': cls.ERROR})
                    raise e

                # Update a lease status if it exists
                if db_api.lease_get(lease_id):
                    next_status = cls.derive_stable_status(lease_id)
                    if (next_status in result_in
                            and cls.is_valid_transition(transition,
                                                        next_status,
                                                        lease_id=lease_id)):
                        db_api.lease_update(lease_id,
                                            {'status': next_status})
                        LOG.debug('Status of lease %s changed from %s to %s.',
                                  lease_id, transition, next_status)
                    else:
                        LOG.error('Lease %s went into ERROR status.',
                                  lease_id)
                        db_api.lease_update(lease_id, {'status': cls.ERROR})
                        raise exceptions.InvalidStatus

                return result
Exemplo n.º 5
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
Exemplo n.º 6
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)
Exemplo n.º 7
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'])
        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
Exemplo n.º 8
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)
Exemplo n.º 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
Exemplo n.º 10
0
    def update_lease(self, lease_id, values):
        if not values:
            return db_api.lease_get(lease_id)

        if len(values) == 1 and 'name' in values:
            db_api.lease_update(lease_id, values)
            return db_api.lease_get(lease_id)

        lease = db_api.lease_get(lease_id)
        start_date = values.get(
            'start_date',
            datetime.datetime.strftime(lease['start_date'], LEASE_DATE_FORMAT))
        end_date = values.get(
            'end_date',
            datetime.datetime.strftime(lease['end_date'], LEASE_DATE_FORMAT))
        before_end_date = values.get('before_end_date', None)

        start_date, end_date, now = self._parse_lease_dates(start_date,
                                                            end_date)

        values['start_date'] = start_date
        values['end_date'] = end_date

        self._check_for_invalid_date_inputs(lease, values, now)

        with trusts.create_ctx_from_trust(lease['trust_id']):
            if before_end_date:
                try:
                    before_end_date = self._date_from_string(before_end_date)
                    self._check_date_within_lease_limits(before_end_date,
                                                         values)
                except common_ex.BlazarException as e:
                    LOG.error("Invalid before_end_date param. %s", str(e))
                    raise e

            reservations = values.get('reservations', [])
            existing_reservations = (
                db_api.reservation_get_all_by_lease_id(lease_id))

            try:
                invalid_ids = set([r['id'] for r in reservations]).difference(
                    [r['id'] for r in existing_reservations])
            except KeyError:
                raise exceptions.MissingParameter(param='reservation ID')

            if invalid_ids:
                raise common_ex.InvalidInput(
                    'Please enter valid reservation IDs. Invalid reservation '
                    'IDs are: %s' % ','.join([str(id) for id in invalid_ids]))

            try:
                [
                    self.plugins[r['resource_type']] for r
                    in (reservations + existing_reservations)]
            except KeyError:
                raise exceptions.CantUpdateParameter(param='resource_type')

            existing_allocs = self._existing_allocations(existing_reservations)

            if reservations:
                new_reservations = reservations
                new_allocs = self._allocation_candidates(values,
                                                         existing_reservations)
            else:
                # User is not updating reservation parameters, e.g., is only
                # adjusting lease start/end dates.
                new_reservations = existing_reservations
                new_allocs = existing_allocs

            try:
                self.enforcement.check_update(context.current(), lease, values,
                                              existing_allocs, new_allocs,
                                              existing_reservations,
                                              new_reservations)
            except common_ex.NotAuthorized as e:
                LOG.error("Enforcement checks failed. %s", str(e))
                raise common_ex.NotAuthorized(e)

            # TODO(frossigneux) rollback if an exception is raised
            for reservation in existing_reservations:
                v = {}
                v['start_date'] = values['start_date']
                v['end_date'] = values['end_date']
                try:
                    v.update([r for r in reservations
                              if r['id'] == reservation['id']].pop())
                except IndexError:
                    pass
                resource_type = v.get('resource_type',
                                      reservation['resource_type'])

                if resource_type != reservation['resource_type']:
                    raise exceptions.CantUpdateParameter(
                        param='resource_type')
                self.plugins[resource_type].update_reservation(
                    reservation['id'], v)

        event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'start_lease'
            }
        )
        if not event:
            raise common_ex.BlazarException(
                'Start lease event not found')
        db_api.event_update(event['id'], {'time': values['start_date']})

        event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'end_lease'
            }
        )
        if not event:
            raise common_ex.BlazarException(
                'End lease event not found')
        db_api.event_update(event['id'], {'time': values['end_date']})

        notifications = ['update']
        self._update_before_end_event(lease, values, notifications,
                                      before_end_date)

        try:
            del values['reservations']
        except KeyError:
            pass
        db_api.lease_update(lease_id, values)

        lease = db_api.lease_get(lease_id)
        with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
            self._send_notification(lease, ctx, events=notifications)

        return lease
Exemplo n.º 11
0
    def create_lease(self, lease_values):
        """Create a lease with reservations.

        Return either the model of created lease or None if any error.
        """
        lease_values['status'] = status.lease.CREATING

        try:
            trust_id = lease_values.pop('trust_id')
        except KeyError:
            raise exceptions.MissingTrustId()

        self.validate_params(lease_values, ['name', 'start_date', 'end_date'])

        # Remove and keep event and reservation values
        events = lease_values.pop("events", [])
        reservations = lease_values.pop("reservations", [])
        for res in reservations:
            self.validate_params(res, ['resource_type'])

        # Create the lease without the reservations
        start_date, end_date, now = self._parse_lease_dates(
            lease_values['start_date'], lease_values['end_date'])

        if start_date < now:
            raise common_ex.InvalidInput(
                'Start date must be later than current date')

        if end_date <= start_date:
            raise common_ex.InvalidInput(
                'End date must be later than start date.')

        with trusts.create_ctx_from_trust(trust_id) as ctx:
            # NOTE(priteau): We should not get user_id from ctx, because we are
            # in the context of the trustee (blazar user).
            # lease_values['user_id'] is set in blazar/api/v1/service.py
            lease_values['project_id'] = ctx.project_id
            lease_values['start_date'] = start_date
            lease_values['end_date'] = end_date

            allocations = self._allocation_candidates(
                lease_values, reservations)
            try:
                self.enforcement.check_create(
                    context.current(), lease_values, reservations, allocations)
            except common_ex.NotAuthorized as e:
                LOG.error("Enforcement checks failed. %s", str(e))
                raise common_ex.NotAuthorized(e)

            events.append({'event_type': 'start_lease',
                           'time': start_date,
                           'status': status.event.UNDONE})
            events.append({'event_type': 'end_lease',
                           'time': end_date,
                           'status': status.event.UNDONE})

            before_end_date = lease_values.get('before_end_date', None)
            if before_end_date:
                # incoming param. Validation check
                try:
                    before_end_date = self._date_from_string(
                        before_end_date)
                    self._check_date_within_lease_limits(before_end_date,
                                                         lease_values)
                except common_ex.BlazarException as e:
                    LOG.error("Invalid before_end_date param. %s", str(e))
                    raise e
            elif CONF.manager.minutes_before_end_lease > 0:
                delta = datetime.timedelta(
                    minutes=CONF.manager.minutes_before_end_lease)
                before_end_date = lease_values['end_date'] - delta

            if before_end_date:
                event = {'event_type': 'before_end_lease',
                         'status': status.event.UNDONE}
                events.append(event)
                self._update_before_end_event_date(event, before_end_date,
                                                   lease_values)

            try:
                if trust_id:
                    lease_values.update({'trust_id': trust_id})
                lease = db_api.lease_create(lease_values)
                lease_id = lease['id']
            except db_ex.BlazarDBDuplicateEntry:
                LOG.exception('Cannot create a lease - duplicated lease name')
                raise exceptions.LeaseNameAlreadyExists(
                    name=lease_values['name'])
            except db_ex.BlazarDBException:
                LOG.exception('Cannot create a lease')
                raise
            else:
                try:
                    for reservation in reservations:
                        reservation['lease_id'] = lease['id']
                        reservation['start_date'] = lease['start_date']
                        reservation['end_date'] = lease['end_date']
                        self._create_reservation(reservation)
                except Exception:
                    LOG.exception("Failed to create reservation for a lease. "
                                  "Rollback the lease and associated "
                                  "reservations")
                    db_api.lease_destroy(lease_id)
                    raise

                try:
                    for event in events:
                        event['lease_id'] = lease['id']
                        db_api.event_create(event)
                except (exceptions.UnsupportedResourceType,
                        common_ex.BlazarException):
                    LOG.exception("Failed to create event for a lease. "
                                  "Rollback the lease and associated "
                                  "reservations")
                    db_api.lease_destroy(lease_id)
                    raise

                else:
                    db_api.lease_update(
                        lease_id,
                        {'status': status.lease.PENDING})
                    lease = db_api.lease_get(lease_id)
                    self._send_notification(lease, ctx, events=['create'])
                    return lease
Exemplo n.º 12
0
 def get_lease(self, lease_id):
     return db_api.lease_get(lease_id)
Exemplo n.º 13
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
Exemplo n.º 14
0
    def update_lease(self, lease_id, values):
        if not values:
            return db_api.lease_get(lease_id)

        if len(values) == 1 and 'name' in values:
            db_api.lease_update(lease_id, values)
            return db_api.lease_get(lease_id)

        lease = db_api.lease_get(lease_id)
        start_date = values.get(
            'start_date',
            datetime.datetime.strftime(lease['start_date'], LEASE_DATE_FORMAT))
        end_date = values.get(
            'end_date',
            datetime.datetime.strftime(lease['end_date'], LEASE_DATE_FORMAT))
        before_end_date = values.get('before_end_date', None)

        now = datetime.datetime.utcnow()
        now = datetime.datetime(now.year,
                                now.month,
                                now.day,
                                now.hour,
                                now.minute)
        if start_date == 'now':
            start_date = now
        else:
            start_date = self._date_from_string(start_date)
        if end_date == 'now':
            end_date = now
        else:
            end_date = self._date_from_string(end_date)

        values['start_date'] = start_date
        values['end_date'] = end_date

        if (lease['start_date'] < now and
                values['start_date'] != lease['start_date']):
            raise common_ex.NotAuthorized(
                'Cannot modify the start date of already started leases')

        if (lease['start_date'] > now and
                values['start_date'] < now):
            raise common_ex.NotAuthorized(
                'Start date must be later than current date')

        if lease['end_date'] < now:
            raise common_ex.NotAuthorized(
                'Terminated leases can only be renamed')

        if (values['end_date'] < now or
           values['end_date'] < values['start_date']):
            raise common_ex.NotAuthorized(
                'End date must be later than current and start date')

        with trusts.create_ctx_from_trust(lease['trust_id']):
            if before_end_date:
                try:
                    before_end_date = self._date_from_string(before_end_date)
                    self._check_date_within_lease_limits(before_end_date,
                                                         values)
                except common_ex.BlazarException as e:
                    LOG.error("Invalid before_end_date param. %s", str(e))
                    raise e

            # TODO(frossigneux) rollback if an exception is raised
            reservations = values.get('reservations', [])
            for reservation in (
                    db_api.reservation_get_all_by_lease_id(lease_id)):
                v = {}
                v['start_date'] = values['start_date']
                v['end_date'] = values['end_date']
                try:
                    v.update([r for r in reservations
                              if r['id'] == reservation['id']].pop())
                except KeyError:
                    raise exceptions.MissingParameter(param='reservation ID')
                except IndexError:
                    pass
                resource_type = v.get('resource_type',
                                      reservation['resource_type'])
                if resource_type != reservation['resource_type']:
                    raise exceptions.CantUpdateParameter(
                        param='resource_type')
                self.plugins[resource_type].update_reservation(
                    reservation['id'], v)

        event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'start_lease'
            }
        )
        if not event:
            raise common_ex.BlazarException(
                'Start lease event not found')
        db_api.event_update(event['id'], {'time': values['start_date']})

        event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'end_lease'
            }
        )
        if not event:
            raise common_ex.BlazarException(
                'End lease event not found')
        db_api.event_update(event['id'], {'time': values['end_date']})

        notifications = ['update']
        self._update_before_end_event(lease, values, notifications,
                                      before_end_date)

        try:
            del values['reservations']
        except KeyError:
            pass
        db_api.lease_update(lease_id, values)

        lease = db_api.lease_get(lease_id)
        with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
            self._send_notification(lease, ctx, events=notifications)

        return lease
Exemplo n.º 15
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