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)
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
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.')
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'])
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
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'])
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
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