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