Example #1
0
    def update(self, json_snippet=None):
        '''
        update the resource. Subclasses should provide a handle_update() method
        to customise update, the base-class handle_update will fail by default.
        '''
        assert json_snippet is not None, 'Must specify update json snippet'

        if self.state in (self.CREATE_IN_PROGRESS, self.UPDATE_IN_PROGRESS):
            raise exception.ResourceFailure(
                Exception('Resource update already requested'))

        logger.info('updating %s' % str(self))

        try:
            self.state_set(self.UPDATE_IN_PROGRESS)
            properties = Properties(self.properties_schema,
                                    json_snippet.get('Properties', {}),
                                    self.stack.resolve_runtime_data, self.name)
            properties.validate()
            tmpl_diff = self.update_template_diff(json_snippet)
            prop_diff = self.update_template_diff_properties(json_snippet)
            if callable(getattr(self, 'handle_update', None)):
                result = self.handle_update(json_snippet, tmpl_diff, prop_diff)
        except UpdateReplace:
            logger.debug("Resource %s update requires replacement" % self.name)
            raise
        except Exception as ex:
            logger.exception('update %s : %s' % (str(self), str(ex)))
            failure = exception.ResourceFailure(ex)
            self.state_set(self.UPDATE_FAILED, str(failure))
            raise failure
        else:
            self.t = self.stack.resolve_static_data(json_snippet)
            self.state_set(self.UPDATE_COMPLETE)
Example #2
0
 def test_heat_exception(self):
     base_exc = ValueError('sorry mom')
     heat_exc = exception.ResourceFailure(base_exc, None, action='UPDATE')
     exc = exception.ResourceFailure(heat_exc, None, action='UPDATE')
     self.assertEqual('ValueError', exc.error)
     self.assertEqual([], exc.path)
     self.assertEqual('sorry mom', exc.error_message)
Example #3
0
    def update(self, after, before=None, prev_resource=None):
        '''
        update the resource. Subclasses should provide a handle_update() method
        to customise update, the base-class handle_update will fail by default.
        '''
        action = self.UPDATE

        (cur_class_def, cur_ver) = self.implementation_signature()
        prev_ver = cur_ver
        if prev_resource is not None:
            (prev_class_def,
             prev_ver) = prev_resource.implementation_signature()
            if prev_class_def != cur_class_def:
                raise UpdateReplace(self.name)

        if before is None:
            before = self.parsed_template()
        if prev_ver == cur_ver and before == after:
            return

        if (self.action, self.status) in ((self.CREATE, self.IN_PROGRESS),
                                          (self.UPDATE, self.IN_PROGRESS),
                                          (self.ADOPT, self.IN_PROGRESS)):
            exc = Exception(_('Resource update already requested'))
            raise exception.ResourceFailure(exc, self, action)

        logger.info('updating %s' % str(self))

        try:
            self.updated_time = datetime.utcnow()
            self.state_set(action, self.IN_PROGRESS)
            properties = Properties(self.properties_schema,
                                    after.get('Properties', {}),
                                    self._resolve_runtime_data,
                                    self.name,
                                    self.context)
            properties.validate()
            tmpl_diff = self.update_template_diff(after, before)
            prop_diff = self.update_template_diff_properties(after, before)
            if callable(getattr(self, 'handle_update', None)):
                handle_data = self.handle_update(after, tmpl_diff, prop_diff)
                yield
                if callable(getattr(self, 'check_update_complete', None)):
                    while not self.check_update_complete(handle_data):
                        yield
        except UpdateReplace:
            with excutils.save_and_reraise_exception():
                logger.debug(_("Resource %s update requires replacement") %
                             self.name)
        except Exception as ex:
            logger.exception('update %s : %s' % (str(self), str(ex)))
            failure = exception.ResourceFailure(ex, self, action)
            self.state_set(action, self.FAILED, str(failure))
            raise failure
        else:
            self.json_snippet = copy.deepcopy(after)
            self.reparse()
            self.state_set(action, self.COMPLETE)
Example #4
0
    def test_nested_exceptions(self):
        res = mock.Mock()
        res.name = 'frodo'
        res.stack.t.get_section_name.return_value = 'Resources'

        reason = ('Resource UPDATE failed: ValueError: resources.oops: '
                  'Test Resource failed oops')
        base_exc = exception.ResourceFailure(reason, res, action='UPDATE')
        exc = exception.ResourceFailure(base_exc, res, action='UPDATE')
        self.assertEqual(['Resources', 'frodo', 'resources', 'oops'], exc.path)
        self.assertEqual('ValueError', exc.error)
        self.assertEqual('Test Resource failed oops', exc.error_message)
Example #5
0
    def handle_signal(self, details=None):
        inputs, params = self._get_inputs_and_params(details)
        self._validate_signal_data(inputs, params)

        inputs_result = copy.deepcopy(self.properties[self.INPUT])
        params_result = copy.deepcopy(self.properties[self.PARAMS]) or {}
        # NOTE(prazumovsky): Signal can contains some data, interesting
        # for workflow, e.g. inputs. So, if signal data contains input
        # we update override inputs, other leaved defined in template.
        if inputs:
            inputs_result.update(inputs)
        if params:
            params_result.update(params)

        try:
            execution = self.client().executions.create(
                self._workflow_name(),
                workflow_input=jsonutils.dumps(inputs_result),
                **params_result)
        except Exception as ex:
            raise exception.ResourceFailure(ex, self)
        executions = [execution.id]
        if self.EXECUTIONS in self.data():
            executions.extend(self.data().get(self.EXECUTIONS).split(','))
        self.data_set(self.EXECUTIONS, ','.join(executions))
    def handle_signal(self, details=None):
        self._validate_signal_data(details)

        result_input = {}
        result_params = {}
        inputs, params = self._get_inputs_and_params(details)
        if inputs is not None:
            # NOTE(prazumovsky): Signal can contains some data, interesting
            # for workflow, e.g. inputs. So, if signal data contains input
            # we update override inputs, other leaved defined in template.
            for key, value in six.iteritems(self.properties.get(self.INPUT)):
                result_input.update({key: inputs.get(key) or value})
        if params is not None:
            if self.properties.get(self.PARAMS) is not None:
                result_params.update(self.properties.get(self.PARAMS))
            result_params.update(params)

        if not result_input and self.properties.get(self.INPUT):
            result_input.update(self.properties.get(self.INPUT))
        if not result_params and self.properties.get(self.PARAMS):
            result_params.update(self.properties.get(self.PARAMS))

        try:
            execution = self.client().executions.create(
                self._workflow_name(), jsonutils.dumps(result_input),
                **result_params)
        except Exception as ex:
            raise exception.ResourceFailure(ex, self)
        executions = [execution.id]
        if self.EXECUTIONS in self.data():
            executions.extend(self.data().get(self.EXECUTIONS).split(','))
        self.data_set(self.EXECUTIONS, ','.join(executions))
Example #7
0
    def _action_recorder(self, action, expected_exceptions=tuple()):
        '''Return a context manager to record the progress of an action.

        Upon entering the context manager, the state is set to IN_PROGRESS.
        Upon exiting, the state will be set to COMPLETE if no exception was
        raised, or FAILED otherwise. Non-exit exceptions will be translated
        to ResourceFailure exceptions.

        Expected exceptions are re-raised, with the Resource left in the
        IN_PROGRESS state.
        '''
        try:
            self.state_set(action, self.IN_PROGRESS)
            yield
        except expected_exceptions as ex:
            with excutils.save_and_reraise_exception():
                LOG.debug('%s', six.text_type(ex))
        except Exception as ex:
            LOG.info('%(action)s: %(info)s', {
                "action": action,
                "info": str(self)
            },
                     exc_info=True)
            failure = exception.ResourceFailure(ex, self, action)
            self.state_set(action, self.FAILED, six.text_type(failure))
            raise failure
        except:  # noqa
            with excutils.save_and_reraise_exception():
                try:
                    self.state_set(action, self.FAILED, '%s aborted' % action)
                except Exception:
                    LOG.exception(_('Error marking resource as failed'))
        else:
            self.state_set(action, self.COMPLETE)
Example #8
0
    def handle_create(self):
        '''Create the BIG-IP® LTM Virtual Server resource on the given device.

        :raises: ResourceFailure exception
        '''

        destination = '/{0}/{1}:{2}'.format(self.partition_name,
                                            self.properties[self.IP],
                                            self.properties[self.PORT])
        create_kwargs = {
            'name': self.properties[self.NAME],
            'partition': self.partition_name,
            'destination': destination,
            'sourceAddressTranslation': {
                'type': 'automap'
            }
        }

        if self.properties[self.DEFAULT_POOL]:
            create_kwargs['pool'] = self.properties[self.DEFAULT_POOL]

        if self.properties[self.VLANS]:
            create_kwargs['vlans'] = self.properties[self.VLANS]
            create_kwargs['vlansEnabled'] = True

        try:
            self.bigip.tm.ltm.virtuals.virtual.create(**create_kwargs)
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='CREATE')
Example #9
0
    def _check_execution(self, action, execution_id):
        """Check execution status.

        Returns False if in IDLE, RUNNING or PAUSED
        returns True if in SUCCESS
        raises ResourceFailure if in ERROR, CANCELLED
        raises ResourceUnknownState otherwise.
        """
        execution = self.client().executions.get(execution_id)
        LOG.debug('Mistral execution %(id)s is in state '
                  '%(state)s' % {
                      'id': execution_id,
                      'state': execution.state
                  })

        if execution.state in ('IDLE', 'RUNNING', 'PAUSED'):
            return False, {}

        if execution.state in ('SUCCESS', ):
            return True, jsonutils.loads(execution.output)

        if execution.state in ('ERROR', 'CANCELLED'):
            raise exception.ResourceFailure(exception_or_error=execution.state,
                                            resource=self,
                                            action=action)

        raise exception.ResourceUnknownStatus(
            resource_status=execution.state,
            result=_('Mistral execution is in unknown state.'))
Example #10
0
    def test_status_reason_resource(self):
        reason = ('Resource CREATE failed: ValueError: resources.oops: '
                  'Test Resource failed oops')

        exc = exception.ResourceFailure(reason, None, action='CREATE')
        self.assertEqual('ValueError', exc.error)
        self.assertEqual(['resources', 'oops'], exc.path)
        self.assertEqual('Test Resource failed oops', exc.error_message)
Example #11
0
    def _check_status_complete(self, expected_action, cookie=None):

        try:
            data = stack_object.Stack.get_status(self.context,
                                                 self.resource_id)
        except exception.NotFound:
            if expected_action == self.DELETE:
                return True
            # It's possible the engine handling the create hasn't persisted
            # the stack to the DB when we first start polling for state
            return False

        action, status, status_reason, updated_time = data

        if action != expected_action:
            return False

        # Has the action really started?
        #
        # The rpc call to update does not guarantee that the stack will be
        # placed into IN_PROGRESS by the time it returns (it runs stack.update
        # in a thread) so you could also have a situation where we get into
        # this method and the update hasn't even started.
        #
        # So we are using a mixture of state (action+status) and updated_at
        # to see if the action has actually progressed.
        # - very fast updates (like something with one RandomString) we will
        #   probably miss the state change, but we should catch the updated_at.
        # - very slow updates we won't see the updated_at for quite a while,
        #   but should see the state change.
        if cookie is not None:
            prev_state = cookie['previous']['state']
            prev_updated_at = cookie['previous']['updated_at']
            if (prev_updated_at == updated_time and
                    prev_state == (action, status)):
                return False

        if status == self.IN_PROGRESS:
            return False
        elif status == self.COMPLETE:
            # For operations where we do not take a resource lock
            # (i.e. legacy-style), check that the stack lock has been
            # released before reporting completeness.
            done = (self._should_lock_on_action(expected_action) or
                    stack_lock.StackLock.get_engine_id(
                self.context, self.resource_id) is None)
            if done:
                # Reset nested, to indicate we changed status
                self._nested = None
            return done
        elif status == self.FAILED:
            raise exception.ResourceFailure(status_reason, self,
                                            action=action)
        else:
            raise exception.ResourceUnknownStatus(
                resource_status=status,
                status_reason=status_reason,
                result=_('Stack unknown status'))
Example #12
0
def DeleteTemplateSideEffect(F5SysiAppTemplate):
    F5SysiAppTemplate.get_bigip()
    F5SysiAppTemplate.bigip.tm.sys.application.templates.template.load.\
        side_effect = exception.ResourceFailure(
            mock.MagicMock(),
            None,
            action='DELETE'
        )
    return F5SysiAppTemplate
Example #13
0
 def test_std_exception_with_resource(self):
     base_exc = ValueError('sorry mom')
     res = mock.Mock()
     res.name = 'fred'
     res.stack.t.get_section_name.return_value = 'Resources'
     exc = exception.ResourceFailure(base_exc, res, action='UPDATE')
     self.assertEqual('ValueError', exc.error)
     self.assertEqual(['Resources', 'fred'], exc.path)
     self.assertEqual('sorry mom', exc.error_message)
Example #14
0
def DeleteServiceSideEffect(F5SysiAppService):
    F5SysiAppService.get_bigip()
    F5SysiAppService.bigip.tm.sys.application.services.service.load.\
        side_effect = exception.ResourceFailure(
            mock.MagicMock(),
            None,
            action='Delete'
        )
    return F5SysiAppService
Example #15
0
def DeletePartitionSideEffect(F5SysPartition):
    F5SysPartition.get_bigip()
    F5SysPartition.bigip.tm.sys.folders.folder.load.\
        side_effect = exception.ResourceFailure(
            mock.MagicMock(),
            None,
            action='Delete'
        )
    return F5SysPartition
Example #16
0
    def test_status_reason_general_res(self):
        res = mock.Mock()
        res.name = 'fred'
        res.stack.t.get_section_name.return_value = 'Resources'

        reason = ('something strange happened')
        exc = exception.ResourceFailure(reason, res, action='CREATE')
        self.assertEqual('', exc.error)
        self.assertEqual(['Resources', 'fred'], exc.path)
        self.assertEqual('something strange happened', exc.error_message)
Example #17
0
    def handle_create(self):
        '''Save the configuration on the BIG-IP® device.

        :raises: ResourceFailure exception
        '''

        try:
            self.bigip.tm.sys.config.exec_cmd('save')
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='CREATE')
Example #18
0
 def handle_create(self):
     super(Workflow, self).handle_create()
     props = self.prepare_properties(self.properties)
     try:
         workflow = self.client().workflows.create(props)
     except Exception as ex:
         raise exception.ResourceFailure(ex, self)
     # NOTE(prazumovsky): Mistral uses unique names for resource
     # identification.
     self.resource_id_set(workflow[0].name)
Example #19
0
    def update(self, after, before=None):
        '''
        update the resource. Subclasses should provide a handle_update() method
        to customise update, the base-class handle_update will fail by default.
        '''
        action = self.UPDATE

        if before is None:
            before = self.parsed_template()

        if (self.action, self.status) in ((self.CREATE, self.IN_PROGRESS),
                                          (self.UPDATE, self.IN_PROGRESS)):
            exc = Exception('Resource update already requested')
            raise exception.ResourceFailure(exc, self, action)

        logger.info('updating %s' % str(self))

        try:
            self.state_set(action, self.IN_PROGRESS)
            properties = Properties(self.properties_schema,
                                    after.get('Properties', {}),
                                    self._resolve_runtime_data, self.name)
            properties.validate()
            tmpl_diff = self.update_template_diff(after, before)
            prop_diff = self.update_template_diff_properties(after, before)
            if callable(getattr(self, 'handle_update', None)):
                handle_data = self.handle_update(after, tmpl_diff, prop_diff)
                yield
                if callable(getattr(self, 'check_update_complete', None)):
                    while not self.check_update_complete(handle_data):
                        yield
        except UpdateReplace:
            logger.debug("Resource %s update requires replacement" % self.name)
            raise
        except Exception as ex:
            logger.exception('update %s : %s' % (str(self), str(ex)))
            failure = exception.ResourceFailure(ex, self, action)
            self.state_set(action, self.FAILED, str(failure))
            raise failure
        else:
            self.t = self.stack.resolve_static_data(after)
            self.state_set(action, self.COMPLETE)
Example #20
0
 def _check_replace_restricted(self, res):
     registry = res.stack.env.registry
     restricted_actions = registry.get_rsrc_restricted_actions(res.name)
     existing_res = self.existing_stack[res.name]
     if 'replace' in restricted_actions:
         ex = exception.ResourceActionRestricted(action='replace')
         failure = exception.ResourceFailure(ex, existing_res,
                                             existing_res.UPDATE)
         existing_res._add_event(existing_res.UPDATE, existing_res.FAILED,
                                 six.text_type(ex))
         raise failure
Example #21
0
 def handle_update(self, json_snippet, tmpl_diff, prop_diff):
     if prop_diff:
         props = json_snippet.properties(self.properties_schema,
                                         self.context)
         new_props = self.prepare_properties(props)
         try:
             workflow = self.client().workflows.update(new_props)
         except Exception as ex:
             raise exception.ResourceFailure(ex, self)
         self.data_set(self.NAME, workflow[0].name)
         self.resource_id_set(workflow[0].name)
Example #22
0
    def delete(self):
        '''
        Delete the resource. Subclasses should provide a handle_delete() method
        to customise deletion.
        '''
        action = self.DELETE

        if (self.action, self.status) == (self.DELETE, self.COMPLETE):
            return
        # No need to delete if the resource has never been created
        if self.action == self.INIT:
            return

        initial_state = self.state

        logger.info(_('deleting %s') % str(self))

        try:
            self.state_set(action, self.IN_PROGRESS)

            if self.abandon_in_progress:
                deletion_policy = RETAIN
            else:
                deletion_policy = self.t.get('DeletionPolicy', DELETE)
            handle_data = None
            if deletion_policy == DELETE:
                if callable(getattr(self, 'handle_delete', None)):
                    handle_data = self.handle_delete()
                    yield
            elif deletion_policy == SNAPSHOT:
                if callable(getattr(self, 'handle_snapshot_delete', None)):
                    handle_data = self.handle_snapshot_delete(initial_state)
                    yield

            if (deletion_policy != RETAIN and callable(
                    getattr(self, 'check_delete_complete', None))):
                while not self.check_delete_complete(handle_data):
                    yield

        except Exception as ex:
            logger.exception(_('Delete %s') % str(self))
            failure = exception.ResourceFailure(ex, self, self.action)
            self.state_set(action, self.FAILED, six.text_type(failure))
            raise failure
        except:
            with excutils.save_and_reraise_exception():
                try:
                    self.state_set(action, self.FAILED, 'Deletion aborted')
                except Exception:
                    logger.exception(
                        _('Error marking resource deletion '
                          'failed'))
        else:
            self.state_set(action, self.COMPLETE)
Example #23
0
    def handle_delete(self):
        '''Delete the BigIP Pool resource on the given device.

        :raises: ResourceFailure
        '''

        self.get_bigip()
        try:
            self.bigip.pool.delete(self.properties[self.NAME])
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='DELETE')
Example #24
0
    def handle_delete(self):
        '''Deletes the iApp Service

        :raises: Resource Failure # TODO Change to proper exception
        '''

        self.get_bigip()

        try:
            self.bigip.iapp.delete_service(name=self.properties[self.NAME])
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='DELETE')
Example #25
0
    def handle_delete(self):
        '''Delete the BigIP Virtual Server resource on the given device.

        :raises: ResourceFailure exception
        '''

        self.get_bigip()

        try:
            self.bigip.virtual_server.delete(name=self.properties[self.NAME])
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='DELETE')
Example #26
0
    def handle_create(self):
        '''Create the iApp® Template on the BIG-IP®.

        :raises: ResourceFailure
        '''

        self._validate_template_partition()
        try:
            template = self.bigip.tm.sys.application.templates.template
            template.create(**self.template_dict)
        except Exception as ex:
            raise exception.ResourceFailure(ex, None, action='CREATE')
Example #27
0
    def test_update_modify_replace_failed_delete(self):
        # patch in a dummy property schema for GenericResource
        dummy_schema = {'Foo': {'Type': 'String'}}
        generic_rsrc.GenericResource.properties_schema = dummy_schema

        tmpl = {
            'Resources': {
                'AResource': {
                    'Type': 'GenericResourceType',
                    'Properties': {
                        'Foo': 'abc'
                    }
                }
            }
        }

        self.stack = parser.Stack(self.ctx,
                                  'update_test_stack',
                                  template.Template(tmpl),
                                  disable_rollback=True)
        self.stack.store()
        self.stack.create()
        self.assertEqual(self.stack.state, parser.Stack.CREATE_COMPLETE)

        tmpl2 = {
            'Resources': {
                'AResource': {
                    'Type': 'GenericResourceType',
                    'Properties': {
                        'Foo': 'xyz'
                    }
                }
            }
        }

        updated_stack = parser.Stack(self.ctx, 'updated_stack',
                                     template.Template(tmpl2))

        # Calls to GenericResource.handle_update will raise
        # resource.UpdateReplace because we've not specified the modified
        # key/property in update_allowed_keys/update_allowed_properties

        # make the update fail deleting the existing resource
        self.m.StubOutWithMock(resource.Resource, 'destroy')
        exc = exception.ResourceFailure(Exception())
        resource.Resource.destroy().AndRaise(exc)
        self.m.ReplayAll()

        self.stack.update(updated_stack)
        self.assertEqual(self.stack.state, parser.Stack.UPDATE_FAILED)
        self.m.VerifyAll()
        # Unset here so destroy() is not stubbed for stack.delete cleanup
        self.m.UnsetStubs()
Example #28
0
 def test_rollback_is_not_triggered_on_rollback_disabled_stack(
         self, mock_tr, mock_cru, mock_crc, mock_pcr, mock_csc):
     self.stack.disable_rollback = True
     self.stack.store()
     dummy_ex = exception.ResourceNotAvailable(
         resource_name=self.resource.name)
     mock_cru.side_effect = exception.ResourceFailure(
         dummy_ex, self.resource, action=self.stack.CREATE)
     self.worker.check_resource(self.ctx, self.resource.id,
                                self.stack.current_traversal, {},
                                self.is_update, None)
     self.assertFalse(mock_tr.called)
Example #29
0
 def test_resource_update_failure_purges_db_for_stack_failure(
         self, mock_cru, mock_crc, mock_pcr, mock_csc):
     self.stack.disable_rollback = True
     self.stack.store()
     self.stack.purge_db = mock.Mock()
     dummy_ex = exception.ResourceNotAvailable(
         resource_name=self.resource.name)
     mock_cru.side_effect = exception.ResourceFailure(
         dummy_ex, self.resource, action=self.resource.UPDATE)
     self.worker.check_resource(self.ctx, self.resource.id,
                                self.stack.current_traversal, {},
                                self.is_update, None)
     self.assertTrue(self.stack.purge_db.called)
Example #30
0
 def handle_update(self, json_snippet=None, tmpl_diff=None, prop_diff=None):
     update_allowed = [self.INPUT, self.PARAMS, self.DESCRIPTION]
     for prop in update_allowed:
         if prop in prop_diff:
             del prop_diff[prop]
     if len(prop_diff) > 0:
         new_props = self.prepare_properties(tmpl_diff['Properties'])
         try:
             workflow = self.client().workflows.update(new_props)
         except Exception as ex:
             raise exception.ResourceFailure(ex, self)
         self.data_set(self.NAME, workflow[0].name)
         self.resource_id_set(workflow[0].name)