def _basic_action(self, lease_id, event_id, action_time, reservation_status=None): """Commits basic lease actions such as starting and ending.""" lease = self.get_lease(lease_id) event_status = status.event.DONE for reservation in lease['reservations']: resource_type = reservation['resource_type'] try: if reservation_status is not None: if not status.reservation.is_valid_transition( reservation['status'], reservation_status): raise common_ex.InvalidStatus self.resource_actions[resource_type][action_time]( reservation['resource_id'] ) except common_ex.BlazarException: LOG.exception("Failed to execute action %(action)s " "for lease %(lease)s", {'action': action_time, 'lease': lease_id}) event_status = status.event.ERROR db_api.reservation_update( reservation['id'], {'status': status.reservation.ERROR}) else: if reservation_status is not None: db_api.reservation_update(reservation['id'], {'status': reservation_status}) db_api.event_update(event_id, {'status': event_status}) return event_status
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 _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.') events = db_api.event_get_all_sorted_by_filters( sort_key='time', sort_dir='asc', filters={'status': status.event.UNDONE, 'time': {'op': 'le', 'border': datetime.datetime.utcnow()}} ) if not events: return LOG.info("Trying to execute events: %s", events) for event in events: if not status.LeaseStatus.is_stable(event['lease_id']): LOG.info("Skip event %s because the status of the lease %s " "is still transitional", event, event['lease_id']) continue 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 %s handling.', event['id'])
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 _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']])
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