Example #1
0
    def _update_before_end_event(self, old_lease, new_lease,
                                 notifications, before_end_date=None):
        event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': old_lease['id'],
                'event_type': 'before_end_lease'
            }
        )
        if event:
            # NOTE(casanch1) do nothing if the event does not exist.
            # This is for backward compatibility
            update_values = {}
            if not before_end_date:
                # before_end_date needs to be calculated based on
                # previous delta
                prev_before_end_delta = old_lease['end_date'] - event['time']
                before_end_date = new_lease['end_date'] - prev_before_end_delta

            self._update_before_end_event_date(update_values, before_end_date,
                                               new_lease)
            if event['status'] == status.event.DONE:
                update_values['status'] = status.event.UNDONE
                notifications.append('event.before_end_lease.stop')

            db_api.event_update(event['id'], update_values)
Example #2
0
    def is_valid_combination(cls, lease_id, status):
        """Validator for the combination of statuses.

        Check if the combination of statuses of lease, reservations and events
        is valid

        :param lease_id: Lease ID
        :param status: Lease status
        :return: True if the combination is valid
        """

        # Validate reservation statuses
        reservations = db_api.reservation_get_all_by_lease_id(lease_id)
        if any([
                r['status'] not in COMBINATIONS[status]['reservation']
                for r in reservations
        ]):
            return False

        # Validate event statuses
        for event_type in ('start_lease', 'end_lease'):
            event = db_api.event_get_first_sorted_by_filters(
                'lease_id', 'asc', {
                    'lease_id': lease_id,
                    'event_type': event_type
                })
            if event['status'] not in COMBINATIONS[status][event_type]:
                return False

        return True
Example #3
0
    def _event(self):
        """Tries to commit event.

        If there is an event in Blazar DB to be done, do it and change its
        status to 'DONE'.
        """
        LOG.debug('Trying to get event from DB.')
        event = db_api.event_get_first_sorted_by_filters(
            sort_key='time',
            sort_dir='asc',
            filters={'status': status.event.UNDONE}
        )

        if not event:
            return

        if event['time'] < datetime.datetime.utcnow():
            db_api.event_update(event['id'],
                                {'status': status.event.IN_PROGRESS})
            try:
                eventlet.spawn_n(
                    service_utils.with_empty_context(self._exec_event),
                    event)
            except Exception:
                db_api.event_update(event['id'],
                                    {'status': status.event.ERROR})
                LOG.exception('Error occurred while event handling.')
Example #4
0
    def delete_lease(self, lease_id):
        lease = self.get_lease(lease_id)
        if (datetime.datetime.utcnow() >= lease['start_date'] and
                datetime.datetime.utcnow() <= lease['end_date']):
            start_event = db_api.event_get_first_sorted_by_filters(
                'lease_id',
                'asc',
                {
                    'lease_id': lease_id,
                    'event_type': 'start_lease'
                }
            )
            if not start_event:
                raise common_ex.BlazarException(
                    'start_lease event for lease %s not found' % lease_id)
            end_event = db_api.event_get_first_sorted_by_filters(
                'lease_id',
                'asc',
                {
                    'lease_id': lease_id,
                    'event_type': 'end_lease',
                    'status': status.event.UNDONE
                }
            )
            if not end_event:
                raise common_ex.BlazarException(
                    'end_lease event for lease %s not found' % lease_id)
            db_api.event_update(end_event['id'],
                                {'status': status.event.IN_PROGRESS})

        with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
            for reservation in lease['reservations']:
                if reservation['status'] != status.reservation.DELETED:
                    plugin = self.plugins[reservation['resource_type']]
                    try:
                        plugin.on_end(reservation['resource_id'])
                    except (db_ex.BlazarDBException, RuntimeError):
                        LOG.exception("Failed to delete a reservation "
                                      "for a lease.")
                        raise
            db_api.lease_destroy(lease_id)
            self._send_notification(lease, ctx, events=['delete'])
Example #5
0
    def derive_stable_status(cls, lease_id):
        """Derive stable lease status.

        This derives a lease status from statuses of reservations and events.

        :param lease_id: Lease ID
        :return: Derived lease status
        """

        # Possible lease statuses. Key is a tuple of (lease_start event
        # status, lease_end event status)
        possible_statuses = {
            (EventStatus.UNDONE, EventStatus.UNDONE): cls.PENDING,
            (EventStatus.DONE, EventStatus.UNDONE): cls.ACTIVE,
            (EventStatus.DONE, EventStatus.DONE): cls.TERMINATED
        }

        # Derive a lease status from event statuses
        event_statuses = {}
        for event_type in ('start_lease', 'end_lease'):
            event = db_api.event_get_first_sorted_by_filters(
                'lease_id', 'asc', {
                    'lease_id': lease_id,
                    'event_type': event_type
                })
            event_statuses[event_type] = event['status']
        try:
            status = possible_statuses[(event_statuses['start_lease'],
                                        event_statuses['end_lease'])]
        except KeyError:
            status = cls.ERROR

        # Check the combination of statuses.
        if cls.is_valid_combination(lease_id, status):
            return status
        else:
            return cls.ERROR
Example #6
0
    def delete_lease(self, lease_id):
        lease = self.get_lease(lease_id)

        start_event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'start_lease',
            }
        )
        if not start_event:
            raise common_ex.BlazarException(
                'start_lease event for lease %s not found' % lease_id)

        end_event = db_api.event_get_first_sorted_by_filters(
            'lease_id',
            'asc',
            {
                'lease_id': lease_id,
                'event_type': 'end_lease',
            }
        )
        if not end_event:
            raise common_ex.BlazarException(
                'end_lease event for lease %s not found' % lease_id)

        lease_already_started = start_event['status'] != status.event.UNDONE
        lease_not_started = not lease_already_started
        lease_already_ended = end_event['status'] != status.event.UNDONE
        lease_not_ended = not lease_already_ended

        end_lease = lease_already_started and lease_not_ended

        if end_lease:
            db_api.event_update(end_event['id'],
                                {'status': status.event.IN_PROGRESS})

        with trusts.create_ctx_from_trust(lease['trust_id']) as ctx:
            reservations = lease['reservations']

            if lease_not_started or lease_not_ended:
                # Only run the on_end enforcement if we're explicitly
                # ending the lease for the first time OR if we're terminating
                # it before the lease ever started. It's important to run
                # on_end in the second case to inform enforcement that the
                # lease is no longer in play.
                allocations = self._existing_allocations(reservations)
                try:
                    self.enforcement.on_end(ctx, lease, allocations)
                except Exception as e:
                    LOG.error(e)

            for reservation in reservations:
                if reservation['status'] != status.reservation.DELETED:
                    plugin = self.plugins[reservation['resource_type']]
                    try:
                        plugin.on_end(reservation['resource_id'])
                    except (db_ex.BlazarDBException, RuntimeError):
                        LOG.exception("Failed to delete a reservation "
                                      "for a lease.")
                        raise
            db_api.lease_destroy(lease_id)
            self._send_notification(lease, ctx, events=['delete'])
Example #7
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
Example #8
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