def delete_lease(self, lease_id): lease = self.get_lease(lease_id) if (datetime.datetime.utcnow() < lease['start_date'] or datetime.datetime.utcnow() > lease['end_date']): with trusts.create_ctx_from_trust(lease['trust_id']) as ctx: for reservation in lease['reservations']: plugin = self.plugins[reservation['resource_type']] try: plugin.on_end(reservation['resource_id']) except (db_ex.ClimateDBException, RuntimeError): error_msg = "Failed to delete a reservation for a lease." lease_state = states.LeaseState(id=lease_id, action=states.lease.DELETE, status=states.lease.FAILED, status_reason=error_msg) lease_state.save() LOG.exception(error_msg) raise db_api.lease_destroy(lease_id) self._send_notification(lease, ctx, events=['delete']) else: error_msg = "Already started lease cannot be deleted" lease_state = states.LeaseState(id=lease_id, action=states.lease.DELETE, status=states.lease.FAILED, status_reason=error_msg) lease_state.save() raise common_ex.NotAuthorized(error_msg)
def test_state_attributes(self): self.leaseState = states.LeaseState(id=1, action=states.lease.CREATE, status=states.lease.IN_PROGRESS) self.assertEqual(self.leaseState.action, states.lease.CREATE) self.assertEqual(self.leaseState.status, states.lease.IN_PROGRESS) self.assertEqual(self.leaseState.status_reason, None)
def test_update_state_with_incorrect_action_status(self): self.leaseState = states.LeaseState(id=1) self.assertRaises(mgr_exc.InvalidStateUpdate, self.leaseState.update, action='foo', status=states.lease.IN_PROGRESS) self.assertRaises(mgr_exc.InvalidStateUpdate, self.leaseState.update, action=states.lease.CREATE, status='bar')
def test_save_state_with_nonexisting_lease(self): def fake_lease_update_raise(id, values): raise db_exc.ClimateDBException self.lease_update.side_effect = fake_lease_update_raise self.leaseState = states.LeaseState(id=1, autosave=False) self.leaseState.update(action=states.lease.CREATE, status=states.lease.IN_PROGRESS, status_reason="Creating Lease...") self.assertRaises(mgr_exc.InvalidState, self.leaseState.save)
def test_state_init_with_args(self): self.leaseState = states.LeaseState(id=1, action=states.lease.CREATE, status=states.lease.IN_PROGRESS) self.assertEqual(0, self.lease_get.call_count) expected = { 'action': 'CREATE', 'status': 'IN_PROGRESS', 'status_reason': None } self.assertEqual(expected, self.leaseState.current())
def test_update_state_with_noautosave(self): self.leaseState = states.LeaseState(id=1, autosave=False) self.leaseState.update(action=states.lease.CREATE, status=states.lease.IN_PROGRESS, status_reason="Creating Lease...") expected = { 'action': 'CREATE', 'status': 'IN_PROGRESS', 'status_reason': "Creating Lease..." } self.assertEqual(0, self.lease_update.call_count) self.assertEqual(expected, self.leaseState.current())
def test_save_state(self): self.leaseState = states.LeaseState(id=1, autosave=False) self.leaseState.update(action=states.lease.CREATE, status=states.lease.IN_PROGRESS, status_reason="Creating Lease...") self.assertEqual(0, self.lease_update.call_count) self.leaseState.save() values = { 'action': 'CREATE', 'status': 'IN_PROGRESS', 'status_reason': "Creating Lease..." } self.lease_update.assert_called_once_with(1, values)
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 = 'DONE' if action_time == 'on_start': lease_action = states.lease.START status_reason = "Starting lease..." elif action_time == 'on_end': lease_action = states.lease.STOP status_reason = "Stopping lease..." else: raise AttributeError("action_time is %s instead of either on_start or on_end" % action_time) lease_state = states.LeaseState(id=lease_id, action=lease_action, status=states.lease.IN_PROGRESS, status_reason=status_reason) lease_state.save() for reservation in lease['reservations']: resource_type = reservation['resource_type'] try: self.resource_actions[resource_type][action_time]( reservation['resource_id'] ) except common_ex.ClimateException: LOG.exception("Failed to execute action %(action)s " "for lease %(lease)s" % { 'action': action_time, 'lease': lease_id, }) event_status = 'ERROR' db_api.reservation_update(reservation['id'], {'status': '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}) if event_status == 'DONE': lease_status = states.lease.COMPLETE if action_time == 'on_start': status_reason = "Successfully started lease" elif action_time == 'on_end': status_reason = "Successfully stopped lease" else: raise AttributeError("action_time is %s instead of either on_start or on_end" % action_time) elif event_status == 'ERROR': lease_status = states.lease.FAILED if action_time == 'on_start': status_reason = "Failed to start lease" elif action_time == 'on_end': status_reason = "Failed to stop lease" else: raise AttributeError("action_time is %s instead of either on_start or on_end" % action_time) else: raise AttributeError("event_status is %s instead of either DONE or ERROR" % event_status) lease_state.update(action=lease_action, status=lease_status, status_reason=status_reason) lease_state.save()
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_notification', 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) 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 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.ClimateException as e: LOG.error("Invalid before_end_date param. %s" % e.message) raise e # TODO(frossigneux) rollback if an exception is raised for reservation in ( db_api.reservation_get_all_by_lease_id(lease_id)): reservation['start_date'] = values['start_date'] reservation['end_date'] = values['end_date'] resource_type = reservation['resource_type'] self.plugins[resource_type].update_reservation( reservation['id'], reservation) 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.ClimateException( '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.ClimateException( '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) db_api.lease_update(lease_id, values) lease_state = states.LeaseState(id=lease_id, action=states.lease.UPDATE, status=states.lease.COMPLETE, status_reason="Successfully updated lease") lease_state.save() 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 create_lease(self, lease_values): """Create a lease with reservations. Return either the model of created lease or None if any error. """ try: trust_id = lease_values.pop('trust_id') except KeyError: raise exceptions.MissingTrustId() # Remove and keep reservation values reservations = lease_values.pop("reservations", []) # Create the lease without the reservations start_date = lease_values['start_date'] end_date = lease_values['end_date'] 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) end_date = self._date_from_string(end_date) if start_date < now: raise common_ex.NotAuthorized( 'Start date must later than current date') with trusts.create_ctx_from_trust(trust_id) as ctx: lease_values['user_id'] = ctx.user_id lease_values['project_id'] = ctx.project_id lease_values['start_date'] = start_date lease_values['end_date'] = end_date if not lease_values.get('events'): lease_values['events'] = [] lease_values['events'].append({'event_type': 'start_lease', 'time': start_date, 'status': 'UNDONE'}) lease_values['events'].append({'event_type': 'end_lease', 'time': end_date, 'status': 'UNDONE'}) before_end_date = lease_values.get('before_end_notification', 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.ClimateException as e: LOG.error("Invalid before_end_date param. %s" % e.message) raise e elif CONF.manager.notify_hours_before_lease_end > 0: delta = datetime.timedelta( hours=CONF.manager.notify_hours_before_lease_end) before_end_date = lease_values['end_date'] - delta if before_end_date: event = {'event_type': 'before_end_lease', 'status': 'UNDONE'} lease_values['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.ClimateDBDuplicateEntry: LOG.exception('Cannot create a lease - duplicated lease name') raise exceptions.LeaseNameAlreadyExists( name=lease_values['name']) except db_ex.ClimateDBException: 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'] resource_type = reservation['resource_type'] if resource_type in self.plugins: self.plugins[resource_type].create_reservation( reservation) else: raise exceptions.UnsupportedResourceType( resource_type) except (exceptions.UnsupportedResourceType, common_ex.ClimateException): LOG.exception("Failed to create reservation for a lease. " "Rollback the lease and associated " "reservations") db_api.lease_destroy(lease_id) raise else: lease_state = states.LeaseState(id=lease['id'], action=states.lease.CREATE, status=states.lease.COMPLETE, status_reason="Successfully created lease") lease_state.save() lease = db_api.lease_get(lease['id']) self._send_notification(lease, ctx, events=['create']) return lease
def test_state_init_with_no_existing_lease(self): self.lease_get.return_value = None self.leaseState = states.LeaseState(id=1) expected = {'action': None, 'status': None, 'status_reason': None} self.assertEqual(expected, self.leaseState.current())
def test_state_init(self): self.leaseState = states.LeaseState(id=1) self.lease_get.assert_called_once_with(1) expected = {'action': None, 'status': None, 'status_reason': None} self.assertEqual(expected, self.leaseState.current())