def test_post(self):
        resp = self.app.post_json(
            '/v2/dynamic_actions', {
                'name': 'dummy_action',
                'class_name': 'DummyAction',
                'code_source_id': self.code_source_id
            })

        # Check the structure of the response.
        self.assertEqual(201, resp.status_int)

        dyn_action = resp.json

        self.assertEqual('dummy_action', dyn_action['name'])
        self.assertEqual('DummyAction', dyn_action['class_name'])
        self.assertEqual(self.code_source_id, dyn_action['code_source_id'])

        # Make sure the action can be found via the system action provider
        # and it's fully functioning.
        provider = actions.get_system_action_provider()

        action_desc = provider.find('dummy_action')

        self.assertIsNotNone(action_desc)

        action = action_desc.instantiate({}, None)

        self.assertIsNotNone(action)

        self.assertEqual("Hello from the dummy action 1!", action.run(None))
Esempio n. 2
0
    def _get_action_descriptor(self):
        res = None

        action_name = self._get_action_name()
        namespace = self.wf_ex.workflow_namespace

        provider = action_service.get_system_action_provider()

        wb_name = self.wf_ex.runtime_context.get('wb_name')

        if wb_name:
            # First try to find the action within the same workbook
            # that the workflow belongs to.
            res = provider.find('%s.%s' % (wb_name, action_name),
                                namespace=namespace)

        if res is None:
            # Now try to find by the action name as it appears in the
            # workflow text.
            res = provider.find(action_name, namespace=namespace)

        if res is None:
            raise exc.MistralException(
                "Failed to find action [action_name=%s, namespace=%s]" %
                (action_name, namespace))

        return res
Esempio n. 3
0
    def _visit_hierarchy(self, callback):
        callback_result = callback(self, None)

        action_spec = self.spec

        visited = {self.name}

        while action_spec:
            base_name = action_spec.get_base()

            if base_name in visited:
                raise ValueError('Found a cycle in an ad-hoc action chain '
                                 '[action_name=%s, duplicate_action_name=%s]' %
                                 (self.name, base_name))

            visited.add(base_name)

            system_provider = action_service.get_system_action_provider()

            base_action_desc = system_provider.find(base_name, self.namespace)

            if base_action_desc is None:
                raise exc.InvalidActionException(
                    "Failed to find base action [action_name=%s namespace=%s] "
                    % (base_name, self.namespace))

            # For every ad-hoc action in the hierarchy invoke the callback.
            callback_result = callback(base_action_desc, callback_result)

            if isinstance(base_action_desc, AdHocActionDescriptor):
                action_spec = base_action_desc.spec
            else:
                action_spec = None

        return callback_result
Esempio n. 4
0
    def get(self, identifier, namespace=''):
        """Return the named action.

        :param identifier: ID or name of the Action to get.
        :param namespace: The namespace of the action.
        """

        acl.enforce('actions:get', context.ctx())

        LOG.debug("Fetch action [identifier=%s]", identifier)

        action_provider = action_service.get_system_action_provider()

        # Here we assume that the action search might involve DB operations
        # so we need to apply the regular retrying logic as everywhere else.
        action_desc = rest_utils.rest_retry_on_db_error(action_provider.find)(
            identifier, namespace=namespace)

        if action_desc is None:
            # TODO(rakhmerov): We need to change exception class so that
            # it's not DB specific. But it should be associated with the
            # same HTTP code.
            raise exc.DBEntityNotFoundError(
                'Action not found [name=%s, namespace=%s]' %
                (identifier, namespace))

        return _action_descriptor_to_resource(action_desc)
Esempio n. 5
0
def build_action_by_name(action_name, namespace=''):
    action_desc = action_service.get_system_action_provider().find(
        action_name, namespace=namespace)

    if action_desc is None:
        raise exc.InvalidActionException(
            "Failed to find action [action_name=%s]" % action_name)

    return actions.RegularAction(action_desc)
Esempio n. 6
0
    def start(self):
        super(ExecutorServer, self).start()

        action_heartbeat_sender.start()

        if self._setup_profiler:
            profiler_utils.setup('mistral-executor', cfg.CONF.executor.host)

        # Initialize action providers to make sure all action classes
        # are initially imported.
        action_service.get_system_action_provider()

        # Initialize and start RPC server.

        self._rpc_server = rpc.get_rpc_server_driver()(cfg.CONF.executor)
        self._rpc_server.register_endpoint(self)

        self._rpc_server.run(executor='threading')

        self._notify_started('Executor server started.')
Esempio n. 7
0
def _build_action(action_ex):
    if isinstance(action_ex, models.WorkflowExecution):
        return actions.WorkflowAction(wf_name=action_ex.name,
                                      action_ex=action_ex)

    action_desc = action_service.get_system_action_provider().find(
        action_ex.name, action_ex.workflow_namespace)

    if action_desc is None:
        raise exc.InvalidActionException(
            "Failed to find action [action_name=%s]" % action_ex.name)

    return actions.RegularAction(action_desc, action_ex)
Esempio n. 8
0
    def test_get_system_action_provider(self):
        self.override_config('load_action_generators', False,
                             'legacy_action_provider')
        self.override_config('only_builtin_actions', True,
                             'legacy_action_provider')

        system_provider = actions.get_system_action_provider()

        action_descs = system_provider.find_all()

        for a in action_descs:
            print(a)

        self.assertTrue(len(action_descs) > 0)

        self.assertTrue(
            all([
                a_d.action_class.__module__.startswith('mistral.')
                for a_d in action_descs
            ]))
    def test_delete(self):
        resp = self._create_dynamic_action()

        # Check the structure of the response
        self.assertEqual(201, resp.status_int)

        resp = self.app.get('/v2/dynamic_actions/dummy_action')

        self.assertEqual(200, resp.status_int)

        self.app.delete('/v2/dynamic_actions/dummy_action')

        resp = self.app.get('/v2/dynamic_actions/dummy_action',
                            expect_errors=True)

        self.assertEqual(404, resp.status_int)

        # Make sure the system action provider doesn't find an action
        # descriptor for the action.
        provider = actions.get_system_action_provider()

        self.assertIsNone(provider.find('dummy_action'))
Esempio n. 10
0
    def get_all(self,
                marker=None,
                limit=None,
                sort_keys='name',
                sort_dirs='asc',
                fields='',
                created_at=None,
                name=None,
                scope=None,
                tags=None,
                updated_at=None,
                description=None,
                definition=None,
                is_system=None,
                input=None,
                namespace=''):
        """Return all actions.

        :param marker: Optional. Pagination marker for large data sets.
        :param limit: Optional. Maximum number of resources to return in a
                      single result. Default value is None for backward
                      compatibility.
        :param sort_keys: Optional. Columns to sort results by.
                          Default: name.
        :param sort_dirs: Optional. Directions to sort corresponding to
                          sort_keys, "asc" or "desc" can be chosen.
                          Default: asc.
        :param fields: Optional. A specified list of fields of the resource to
                       be returned. 'id' will be included automatically in
                       fields if it's provided, since it will be used when
                       constructing 'next' link.
        :param name: Optional. Keep only resources with a specific name.
        :param scope: Optional. Keep only resources with a specific scope.
        :param definition: Optional. Keep only resources with a specific
                           definition.
        :param is_system: Optional. Keep only system actions or ad-hoc
                          actions (if False).
        :param input: Optional. Keep only resources with a specific input.
        :param description: Optional. Keep only resources with a specific
                            description.
        :param tags: Optional. Keep only resources containing specific tags.
        :param created_at: Optional. Keep only resources created at a specific
                           time and date.
        :param updated_at: Optional. Keep only resources with specific latest
                           update time and date.
        :param namespace: Optional. The namespace of the action.
        """
        acl.enforce('actions:list', context.ctx())

        filters = filter_utils.create_filters_from_request_params(
            created_at=created_at,
            name=name,
            scope=scope,
            tags=tags,
            updated_at=updated_at,
            description=description,
            definition=definition,
            is_system=is_system,
            input=input,
            namespace=namespace)

        LOG.debug(
            "Fetch actions. marker=%s, limit=%s, sort_keys=%s, "
            "sort_dirs=%s, filters=%s", marker, limit, sort_keys, sort_dirs,
            filters)

        sort_keys = ['name'] if sort_keys is None else sort_keys
        sort_dirs = ['asc'] if sort_dirs is None else sort_dirs
        fields = [] if fields is None else fields

        if fields and 'name' not in fields:
            fields.insert(0, 'name')

        rest_utils.validate_query_params(limit, sort_keys, sort_dirs)

        action_provider = action_service.get_system_action_provider()

        # Here we assume that the action search might involve DB operations
        # so we need to apply the regular retrying logic as everywhere else.
        action_descriptors = rest_utils.rest_retry_on_db_error(
            action_provider.find_all)(namespace=namespace,
                                      limit=limit,
                                      sort_fields=sort_keys,
                                      sort_dirs=sort_dirs,
                                      filters=filters)

        # We can't guarantee that at this point the collection of action
        # descriptors is properly filtered and sorted.

        # Apply filters.
        action_descriptors = filter(
            lambda a_d: filter_utils.match_filters(a_d, filters),
            action_descriptors)

        # Apply sorting.
        def compare_(a_d1, a_d2):
            # TODO(rakhmerov): Implement properly
            return 0

        action_descriptors = sorted(action_descriptors,
                                    key=functools.cmp_to_key(compare_))

        if limit and limit > 0:
            action_descriptors = action_descriptors[0:limit]

        action_resources = [
            _action_descriptor_to_resource(a_d) for a_d in action_descriptors
        ]

        # TODO(rakhmerov): Fix pagination so that it doesn't work with
        # the 'id' field as a marker. We can't use IDs anymore. "name"
        # seems a good candidate for this.
        return resources.Actions.convert_with_links(
            action_resources,
            limit,
            pecan.request.application_url,
            sort_keys=','.join(sort_keys),
            sort_dirs=','.join(sort_dirs),
            **filters)
Esempio n. 11
0
    def setUp(self):
        super(EngineTestCase, self).setUp()

        # We assume that most tests don't need a remote executor.
        # But this option can be overridden on a test level, if needed,
        # because an executor instance (local or a client to a remote one)
        # is obtained by engine dynamically on every need.
        self.override_config('type', 'local', 'executor')

        # Get transport here to let oslo.messaging setup default config
        # before changing the rpc_backend to the fake driver; otherwise,
        # oslo.messaging will throw exception.
        messaging.get_transport(cfg.CONF)

        # Set the transport to 'fake' for Engine tests.
        cfg.CONF.set_default('transport_url', 'fake:/')

        # Drop all RPC objects (transport, clients).
        rpc_base.cleanup()
        rpc_clients.cleanup()
        exe.cleanup()

        self.threads = []

        # Start remote executor.
        if cfg.CONF.executor.type == 'remote':
            LOG.info("Starting remote executor threads...")

            self.executor_client = rpc_clients.get_executor_client()

            exe_svc = executor_server.get_oslo_service(setup_profiler=False)

            self.executor = exe_svc.executor

            self.threads.append(eventlet.spawn(launch_service, exe_svc))

            self.addCleanup(exe_svc.stop, True)
        else:
            self.executor = exe.get_executor(cfg.CONF.executor.type)

        # Start remote notifier.
        if cfg.CONF.notifier.type == 'remote':
            LOG.info("Starting remote notifier threads...")

            self.notifier_client = rpc_clients.get_notifier_client()

            notif_svc = notif_server.get_oslo_service(setup_profiler=False)

            self.notifier = notif_svc.notifier
            self.threads.append(eventlet.spawn(launch_service, notif_svc))
            self.addCleanup(notif_svc.stop, True)

        # Start engine.
        LOG.info("Starting engine threads...")

        self.engine_client = rpc_clients.get_engine_client()

        eng_svc = engine_server.get_oslo_service(setup_profiler=False)

        self.engine = eng_svc.engine
        self.threads.append(eventlet.spawn(launch_service, eng_svc))
        self.addCleanup(eng_svc.stop, True)

        self.addOnException(self.print_executions)
        self.addCleanup(self.kill_threads)

        # This call ensures that all plugged in action providers are
        # properly initialized.
        action_service.get_system_action_provider()

        # Make sure that both services fully started, otherwise
        # the test may run too early.
        if cfg.CONF.executor.type == 'remote':
            exe_svc.wait_started()

        if cfg.CONF.notifier.type == 'remote':
            notif_svc.wait_started()

        eng_svc.wait_started()