def __init__(self, tester): self.tester = tester self.notify_trigger = ResourceReference.to_string_reference( pack=NOTIFY_TRIGGER_TYPE['pack'], name=NOTIFY_TRIGGER_TYPE['name']) self.action_trigger = ResourceReference.to_string_reference( pack=ACTION_TRIGGER_TYPE['pack'], name=ACTION_TRIGGER_TYPE['name'])
def _register_rules_from_pack(self, pack, rules): registered_count = 0 for rule in rules: LOG.debug('Loading rule from %s.', rule) try: content = self._meta_loader.load(rule) pack_field = content.get('pack', None) if not pack_field: content['pack'] = pack pack_field = pack if pack_field != pack: raise Exception('Model is in pack "%s" but field "pack" is different: %s' % (pack, pack_field)) rule_api = RuleAPI(**content) rule_api.validate() rule_db = RuleAPI.to_model(rule_api) # Migration from rule without pack to rule with pack. # There might be a rule with same name but in pack `default` # generated in migration script. In this case, we want to # delete so we don't have duplicates. if pack_field != DEFAULT_PACK_NAME: try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=DEFAULT_PACK_NAME) LOG.debug('Looking for rule %s in pack %s', content['name'], DEFAULT_PACK_NAME) existing = Rule.get_by_ref(rule_ref) LOG.debug('Existing = %s', existing) if existing: LOG.debug('Found rule in pack default: %s; Deleting.', rule_ref) Rule.delete(existing) except: LOG.exception('Exception deleting rule from %s pack.', DEFAULT_PACK_NAME) try: rule_ref = ResourceReference.to_string_reference(name=content['name'], pack=content['pack']) existing = Rule.get_by_ref(rule_ref) if existing: rule_db.id = existing.id LOG.debug('Found existing rule: %s with id: %s', rule_ref, existing.id) except ValueError: LOG.debug('Rule %s not found. Creating new one.', rule) try: rule_db = Rule.add_or_update(rule_db) extra = {'rule_db': rule_db} LOG.audit('Rule updated. Rule %s from %s.', rule_db, rule, extra=extra) except Exception: LOG.exception('Failed to create rule %s.', rule_api.name) except: LOG.exception('Failed registering rule from %s.', rule) else: registered_count += 1 return registered_count
def __init__(self, connection, queues, trigger_dispatcher=None): super(Notifier, self).__init__(connection, queues) self._trigger_dispatcher = trigger_dispatcher self._notify_trigger = ResourceReference.to_string_reference( pack=NOTIFY_TRIGGER_TYPE["pack"], name=NOTIFY_TRIGGER_TYPE["name"] ) self._action_trigger = ResourceReference.to_string_reference( pack=ACTION_TRIGGER_TYPE["pack"], name=ACTION_TRIGGER_TYPE["name"] )
def __init__(self, q_connection=None, trigger_dispatcher=None): self._queue_consumer = LiveActionUpdateQueueConsumer(q_connection, self) self._consumer_thread = None self._trigger_dispatcher = trigger_dispatcher self._notify_trigger = ResourceReference.to_string_reference( pack=NOTIFY_TRIGGER_TYPE['pack'], name=NOTIFY_TRIGGER_TYPE['name']) self._action_trigger = ResourceReference.to_string_reference( pack=ACTION_TRIGGER_TYPE['pack'], name=ACTION_TRIGGER_TYPE['name'])
def _get_executions(self, **kw): action_ref = kw.get('action', None) if action_ref: action_name = ResourceReference.get_name(action_ref) action_pack = ResourceReference.get_pack(action_ref) del kw['action'] kw['action.name'] = action_name kw['action.pack'] = action_pack return super(ActionExecutionHistoryController, self)._get_all(**kw)
def user_has_rule_action_permission(user_db, action_ref): """ Check that the currently logged-in has necessary permissions on the action used / referenced inside the rule. Note: Rules can reference actions which don't yet exist in the system. """ if not cfg.CONF.rbac.enable: return True action_db = action_utils.get_action_by_ref(ref=action_ref) if not action_db: # We allow rules to be created for actions which don't yet exist in the # system ref = ResourceReference.from_string_reference(ref=action_ref) action_db = ActionDB(pack=ref.pack, name=ref.name, ref=action_ref) action_resolver = resolvers.get_resolver_for_resource_type(ResourceType.ACTION) has_action_permission = action_resolver.user_has_resource_db_permission( user_db=user_db, resource_db=action_db, permission_type=PermissionType.ACTION_EXECUTE) if has_action_permission: return True return False
def to_model(cls, rule): name = getattr(rule, 'name', None) description = getattr(rule, 'description', None) # Create a trigger for the provided rule trigger_db = TriggerService.create_trigger_db_from_rule(rule) trigger = reference.get_str_resource_ref_from_model(trigger_db) criteria = dict(getattr(rule, 'criteria', {})) pack = getattr(rule, 'pack', DEFAULT_PACK_NAME) ref = ResourceReference.to_string_reference(pack=pack, name=name) # Validate criteria validator.validate_criteria(criteria) # Validate trigger parameters validator.validate_trigger_parameters(trigger_db=trigger_db) action = ActionExecutionSpecDB(ref=rule.action['ref'], parameters=rule.action['parameters']) enabled = rule.enabled tags = TagsHelper.to_model(getattr(rule, 'tags', [])) model = cls.model(name=name, description=description, pack=pack, ref=ref, trigger=trigger, criteria=criteria, action=action, enabled=enabled, tags=tags) return model
def _register_trigger_type(self, trigger_definition, attempt_no=0): LOG.debug('Attempt no %s to register trigger %s.', attempt_no, trigger_definition['name']) ref = ResourceReference.to_string_reference(pack=trigger_definition['pack'], name=trigger_definition['name']) if self._is_triggertype_exists(ref): return payload = json.dumps(trigger_definition) try: r = requests.post(url=self._trigger_type_endpoint, data=payload, headers=self._http_post_headers, timeout=self._timeout) if r.status_code == httplib.CREATED: LOG.info('Registered trigger %s.', trigger_definition['name']) elif r.status_code == httplib.CONFLICT: LOG.info('Trigger %s is already registered.', trigger_definition['name']) else: LOG.error('Seeing status code %s on an attempt to register trigger %s.', r.status_code, trigger_definition['name']) except requests.exceptions.ConnectionError: if attempt_no < self._max_attempts: self._retry_wait = self._retry_wait * (attempt_no + 1) LOG.debug(' ConnectionError. Will retry in %ss.', self._retry_wait) eventlet.spawn_after(self._retry_wait, self._register_trigger_type, trigger_definition=trigger_definition, attempt_no=(attempt_no + 1)) else: LOG.warn('Failed to register trigger %s. ' % trigger_definition['name'] + ' Exceeded max attempts to register trigger.') except: LOG.exception('Failed to register trigger %s.', trigger_definition['name'])
def run(self, action_parameters): liveaction_db = action_utils.get_liveaction_by_id(self.liveaction_id) exc = ActionExecution.get(liveaction__id=str(liveaction_db.id)) # Assemble and dispatch trigger trigger_ref = ResourceReference.to_string_reference( pack=INQUIRY_TRIGGER['pack'], name=INQUIRY_TRIGGER['name'] ) trigger_payload = { "id": str(exc.id), "route": self.route } self.trigger_dispatcher.dispatch(trigger_ref, trigger_payload) # We only want to request a pause if this has a parent if liveaction_db.context.get("parent"): # Get the root liveaction and request that it pauses root_liveaction = action_service.get_root_liveaction(liveaction_db) action_service.request_pause( root_liveaction, self.context.get('user', None) ) result = { "schema": self.schema, "roles": self.roles_param, "users": self.users_param, "route": self.route, "ttl": self.ttl } return (LIVEACTION_STATUS_PENDING, result, None)
def to_model(cls, alias): name = alias.name description = getattr(alias, "description", None) pack = alias.pack ref = ResourceReference.to_string_reference(pack=pack, name=name) enabled = getattr(alias, "enabled", True) action_ref = alias.action_ref formats = alias.formats ack = getattr(alias, "ack", None) result = getattr(alias, "result", None) extra = getattr(alias, "extra", None) model = cls.model( name=name, description=description, pack=pack, ref=ref, enabled=enabled, action_ref=action_ref, formats=formats, ack=ack, result=result, extra=extra, ) return model
def get_trigger_db(trigger): # TODO: This method should die in a fire if isinstance(trigger, str) or isinstance(trigger, unicode): # Assume reference was passed in ref_obj = ResourceReference.from_string_reference(ref=trigger) return _get_trigger_db_by_name_and_pack(name=ref_obj.name, pack=ref_obj.pack) if isinstance(trigger, dict): name = trigger.get('name', None) pack = trigger.get('pack', None) if name and pack: return _get_trigger_db_by_name_and_pack(name=name, pack=pack) return _get_trigger_db(type=trigger['type'], parameters=trigger.get('parameters', {})) if isinstance(trigger, object): name = getattr(trigger, 'name', None) pack = getattr(trigger, 'pack', None) parameters = getattr(trigger, 'parameters', {}) trigger_db = None if name and pack: trigger_db = _get_trigger_db_by_name_and_pack(name=name, pack=pack) else: trigger_db = _get_trigger_db(type=trigger.type, parameters=parameters) return trigger_db else: raise Exception('Unrecognized object')
def post_trigger(action_execution): if not ACTION_SENSOR_ENABLED: return try: payload = json.dumps({ 'trigger': ResourceReference.to_string_reference( pack=ACTION_TRIGGER_TYPE['pack'], name=ACTION_TRIGGER_TYPE['name']), 'payload': { 'execution_id': str(action_execution.id), 'status': action_execution.status, 'start_timestamp': str(action_execution.start_timestamp), 'action_name': action_execution.action, 'parameters': action_execution.parameters, 'result': action_execution.result } }) LOG.debug('POSTing %s for %s. Payload - %s.', ACTION_TRIGGER_TYPE['name'], action_execution.id, payload) r = requests.post(TRIGGER_INSTANCE_ENDPOINT, data=payload, headers=HTTP_POST_HEADER, timeout=TIMEOUT) except: LOG.exception('Failed to fire trigger for action_execution %s.', str(action_execution.id)) else: if r.status_code in [200, 201, 202]: LOG.debug('POSTed actionexecution %s as a trigger.', action_execution.id) else: LOG.warn('Seeing status code %s on an attempt to post triggerinstance for %s.', r.status_code, action_execution.id)
def to_model(cls, rule): kwargs = {} kwargs['name'] = getattr(rule, 'name', None) kwargs['description'] = getattr(rule, 'description', None) # Create a trigger for the provided rule trigger_db = TriggerService.create_trigger_db_from_rule(rule) kwargs['trigger'] = reference.get_str_resource_ref_from_model(trigger_db) # Validate trigger parameters validator.validate_trigger_parameters(trigger_db=trigger_db) kwargs['pack'] = getattr(rule, 'pack', DEFAULT_PACK_NAME) kwargs['ref'] = ResourceReference.to_string_reference(pack=kwargs['pack'], name=kwargs['name']) # Validate criteria kwargs['criteria'] = dict(getattr(rule, 'criteria', {})) validator.validate_criteria(kwargs['criteria']) kwargs['action'] = ActionExecutionSpecDB(ref=rule.action['ref'], parameters=rule.action.get('parameters', {})) rule_type = dict(getattr(rule, 'type', {})) if rule_type: kwargs['type'] = RuleTypeSpecDB(ref=rule_type['ref'], parameters=rule_type.get('parameters', {})) kwargs['enabled'] = getattr(rule, 'enabled', False) kwargs['tags'] = TagsHelper.to_model(getattr(rule, 'tags', [])) model = cls.model(**kwargs) return model
def _register_internal_trigger_type(trigger_definition): try: trigger_type_db = create_trigger_type_db(trigger_type=trigger_definition) except (NotUniqueError, StackStormDBObjectConflictError): # We ignore conflict error since this operation is idempotent and race is not an issue LOG.debug('Internal trigger type "%s" already exists, ignoring...' % (trigger_definition['name']), exc_info=True) ref = ResourceReference.to_string_reference(name=trigger_definition['name'], pack=trigger_definition['pack']) trigger_type_db = get_trigger_type_db(ref) if trigger_type_db: LOG.debug('Registered internal trigger: %s.', trigger_definition['name']) # trigger types with parameters do no require a shadow trigger. if trigger_type_db and not trigger_type_db.parameters_schema: try: trigger_db = create_shadow_trigger(trigger_type_db) extra = {'trigger_db': trigger_db} LOG.audit('Trigger created for parameter-less internal TriggerType. Trigger.id=%s' % (trigger_db.id), extra=extra) except StackStormDBObjectConflictError: LOG.debug('Shadow trigger "%s" already exists. Ignoring.', trigger_type_db.get_reference().ref, exc_info=True) except (ValidationError, ValueError): LOG.exception('Validation failed in shadow trigger. TriggerType=%s.', trigger_type_db.get_reference().ref) raise return trigger_type_db
def _get_by_ref_or_id(self, ref_or_id, exclude_fields=None, include_fields=None): """ Retrieve resource object by an id of a reference. Note: This method throws StackStormDBObjectNotFoundError exception if the object is not found in the database. """ if exclude_fields and include_fields: msg = ('exclude_fields and include_fields arguments are mutually exclusive. ' 'You need to provide either one or another, but not both.') raise ValueError(msg) if ResourceReference.is_resource_reference(ref_or_id): # references always contain a dot and id's can't contain it is_reference = True else: is_reference = False if is_reference: resource_db = self._get_by_ref(resource_ref=ref_or_id, exclude_fields=exclude_fields, include_fields=include_fields) else: resource_db = self._get_by_id(resource_id=ref_or_id, exclude_fields=exclude_fields, include_fields=include_fields) if not resource_db: msg = 'Resource with a reference or id "%s" not found' % (ref_or_id) raise StackStormDBObjectNotFoundError(msg) return resource_db
def _register_action(self, pack, action): content = self._meta_loader.load(action) pack_field = content.get('pack', None) if not pack_field: content['pack'] = pack pack_field = pack if pack_field != pack: raise Exception('Model is in pack "%s" but field "pack" is different: %s' % (pack, pack_field)) action_api = ActionAPI(**content) action_api.validate() action_validator.validate_action(action_api) model = ActionAPI.to_model(action_api) action_ref = ResourceReference.to_string_reference(pack=pack, name=str(content['name'])) existing = action_utils.get_action_by_ref(action_ref) if not existing: LOG.debug('Action %s not found. Creating new one with: %s', action_ref, content) else: LOG.debug('Action %s found. Will be updated from: %s to: %s', action_ref, existing, model) model.id = existing.id try: model = Action.add_or_update(model) extra = {'action_db': model} LOG.audit('Action updated. Action %s from %s.', model, action, extra=extra) except Exception: LOG.exception('Failed to write action to db %s.', model.name) raise
def dispatch(self, actionexec_db): action_ref = ResourceReference.from_string_reference(ref=actionexec_db.action) (action_db, _) = get_action_by_dict( {'name': action_ref.name, 'pack': action_ref.pack}) runnertype_db = get_runnertype_by_name(action_db.runner_type['name']) runner_type = runnertype_db.name LOG.info('Dispatching runner for Action "%s"', actionexec_db) LOG.debug(' liverunner_type: %s', runner_type) LOG.debug(' RunnerType: %s', runnertype_db) LOG.debug(' ActionExecution: %s', actionexec_db) # Get runner instance. runner = self._get_runner(runnertype_db) LOG.debug('Runner instance for RunnerType "%s" is: %s', runnertype_db.name, runner) # Invoke pre_run, run, post_run cycle. result, actionexec_db = self._do_run(runner, runnertype_db, action_db, actionexec_db) LOG.debug('runner do_run result: %s', result) actionsensor.post_trigger(actionexec_db) LOG.audit('ActionExecution complete.', extra={'actionexecution': actionexec_db.to_serializable_dict()}) return result
def register_trigger_type(trigger_definition, attempt_no=0): LOG.debug('Attempt no %s to register trigger %s.', attempt_no, trigger_definition['name']) ref = ResourceReference.to_string_reference(pack=trigger_definition['pack'], name=trigger_definition['name']) if _is_triggertype_exists(ref): return payload = json.dumps(trigger_definition) try: r = requests.post(url=TRIGGER_TYPE_ENDPOINT, data=payload, headers=HTTP_POST_HEADER, timeout=TIMEOUT) if r.status_code == httplib.CREATED: LOG.info('Registered trigger %s.', trigger_definition['name']) elif r.status_code == httplib.CONFLICT: LOG.info('Trigger %s is already registered.', trigger_definition['name']) else: LOG.error('Seeing status code %s on an attempt to register trigger %s.', r.status_code, trigger_definition['name']) except requests.exceptions.ConnectionError: if attempt_no < MAX_ATTEMPTS: retry_wait = RETRY_WAIT * (attempt_no + 1) LOG.debug(' ConnectionError. Will retry in %ss.', retry_wait) eventlet.spawn_after(retry_wait, register_trigger_type, trigger_definition=trigger_definition, attempt_no=(attempt_no + 1)) else: LOG.warn('Failed to register trigger %s. Exceeded max attempts to register trigger.', trigger_definition['name']) except: LOG.exception('Failed to register trigger %s.', trigger_definition['name'])
def test_create_or_update_trigger_db_simple_triggers(self): test_fixtures = { 'triggertypes': ['triggertype1.yaml'] } loader = FixturesLoader() fixtures = loader.save_fixtures_to_db(fixtures_pack='generic', fixtures_dict=test_fixtures) triggertypes = fixtures['triggertypes'] trigger_type_ref = ResourceReference.to_string_reference( name=triggertypes['triggertype1.yaml']['name'], pack=triggertypes['triggertype1.yaml']['pack']) trigger = { 'name': triggertypes['triggertype1.yaml']['name'], 'pack': triggertypes['triggertype1.yaml']['pack'], 'type': trigger_type_ref } trigger_service.create_or_update_trigger_db(trigger) triggers = Trigger.get_all() self.assertTrue(len(triggers) == 1, 'Only one trigger should be created.') self.assertTrue(triggers[0]['name'] == triggertypes['triggertype1.yaml']['name']) # Try adding duplicate trigger_service.create_or_update_trigger_db(trigger) triggers = Trigger.get_all() self.assertTrue(len(triggers) == 1, 'Only one trigger should be present.') self.assertTrue(triggers[0]['name'] == triggertypes['triggertype1.yaml']['name'])
def test_exception_thrown_when_rule_creation_no_trigger_yes_triggertype(self): test_fixtures = { 'triggertypes': ['triggertype1.yaml'] } loader = FixturesLoader() fixtures = loader.save_fixtures_to_db(fixtures_pack='generic', fixtures_dict=test_fixtures) triggertypes = fixtures['triggertypes'] trigger_type_ref = ResourceReference.to_string_reference( name=triggertypes['triggertype1.yaml']['name'], pack=triggertypes['triggertype1.yaml']['pack']) rule = { 'name': 'fancyrule', 'trigger': { 'type': trigger_type_ref }, 'criteria': { }, 'action': { 'ref': 'core.local', 'parameters': { 'cmd': 'date' } } } rule_api = RuleAPI(**rule) self.assertRaises(TriggerDoesNotExistException, trigger_service.create_trigger_db_from_rule, rule_api)
def test_get_all(self): holder = TimersHolder() for _, model in TestTimersHolder.MODELS.items(): holder.add_trigger( ref=ResourceReference.to_string_reference(pack=model["pack"], name=model["name"]), trigger=model ) self.assertEqual(len(holder.get_all()), 5)
def to_model(cls, action): name = getattr(action, "name", None) description = getattr(action, "description", None) enabled = bool(getattr(action, "enabled", True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {"name": str(action.runner_type)} parameters = getattr(action, "parameters", dict()) tags = TagsHelper.to_model(getattr(action, "tags", [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, "notify", None): notify = NotificationsHelper.to_model(action.notify) else: notify = None model = cls.model( name=name, description=description, enable=enabled, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, notify=notify, ref=ref, ) return model
def _get_trigger_instance_db_from_file(self, file_path): data = self._meta_loader.load(file_path=file_path) instance = TriggerInstanceDB(**data) trigger_ref = ResourceReference.from_string_reference(instance['trigger']) trigger_db = TriggerDB(pack=trigger_ref.pack, name=trigger_ref.name, type=trigger_ref.ref) return instance, trigger_db
def test_chain_runner_task_is_canceled_while_running(self, request): # Second task in the action is CANCELED, make sure runner doesn't get stuck in an infinite # loop chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_2_PATH chain_runner.action = ACTION_1 original_run_action = chain_runner._run_action def mock_run_action(*args, **kwargs): original_live_action = args[0] if original_live_action.action == 'wolfpack.a2': status = LIVEACTION_STATUS_CANCELED else: status = LIVEACTION_STATUS_SUCCEEDED request.return_value = (DummyActionExecution(status=status), None) liveaction = original_run_action(*args, **kwargs) return liveaction chain_runner._run_action = mock_run_action action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() status, _, _ = chain_runner.run({}) self.assertEqual(status, LIVEACTION_STATUS_CANCELED) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # Chain count should be 2 since the last task doesn't get called since the second one was # canceled self.assertEqual(request.call_count, 2)
def to_model(cls, action): name = getattr(action, 'name', None) description = getattr(action, 'description', None) enabled = bool(getattr(action, 'enabled', True)) entry_point = str(action.entry_point) pack = str(action.pack) runner_type = {'name': str(action.runner_type)} parameters = getattr(action, 'parameters', dict()) tags = TagsHelper.to_model(getattr(action, 'tags', [])) ref = ResourceReference.to_string_reference(pack=pack, name=name) if getattr(action, 'notify', None): notify = NotificationsHelper.to_model(action.notify) else: # We use embedded document model for ``notify`` in action model. If notify is # set notify to None, Mongoengine interprets ``None`` as unmodified # field therefore doesn't delete the embedded document. Therefore, we need # to use an empty document. notify = NotificationsHelper.to_model({}) model = cls.model(name=name, description=description, enabled=enabled, entry_point=entry_point, pack=pack, runner_type=runner_type, tags=tags, parameters=parameters, notify=notify, ref=ref) return model
def test_get_one_by_ref(self): rule_name = RuleViewControllerTestCase.RULE_1.name rule_pack = RuleViewControllerTestCase.RULE_1.pack ref = ResourceReference.to_string_reference(name=rule_name, pack=rule_pack) get_resp = self.__do_get_one(ref) self.assertEqual(get_resp.json['name'], rule_name) self.assertEqual(get_resp.status_int, http_client.OK)
def test_chain_runner_dependent_results_param(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_DEP_RESULTS_INPUT chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() chain_runner.run({'s1': 1}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) if six.PY2: expected_values = [{u'p1': u'1'}, {u'p1': u'1'}, {u'out': u"{'c2': {'o1': '1'}, 'c1': {'o1': '1'}}"}] else: expected_values = [{'p1': '1'}, {'p1': '1'}, {'out': "{'c1': {'o1': '1'}, 'c2': {'o1': '1'}}"}] # Each of the call_args must be one of self.assertEqual(request.call_count, 3) for call_args in request.call_args_list: self.assertTrue(call_args[0][0].parameters in expected_values) expected_values.remove(call_args[0][0].parameters) self.assertEqual(len(expected_values), 0, 'Not all expected values received.')
def test_chain_runner_chain_second_task_times_out(self, request): # Second task in the chain times out so the action chain status should be timeout chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_2_PATH chain_runner.action = ACTION_1 original_run_action = chain_runner._run_action def mock_run_action(*args, **kwargs): original_live_action = args[0] liveaction = original_run_action(*args, **kwargs) if original_live_action.action == 'wolfpack.a2': # Mock a timeout for second task liveaction.status = LIVEACTION_STATUS_TIMED_OUT return liveaction chain_runner._run_action = mock_run_action action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() status, _, _ = chain_runner.run({}) self.assertEqual(status, LIVEACTION_STATUS_TIMED_OUT) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # based on the chain the callcount is known to be 3. Not great but works. self.assertEqual(request.call_count, 3)
def _get_api_models_from_disk(artifact_type, pack_dir=None): loader = ContentPackLoader() artifacts = None if pack_dir: artifacts_dir = loader.get_content_from_pack(pack_dir, artifact_type) pack_name = os.path.basename(os.path.normpath(pack_dir)) artifacts = {pack_name: artifacts_dir} else: packs_dirs = content_utils.get_packs_base_paths() artifacts = loader.get_content(packs_dirs, artifact_type) artifacts_dict = {} for pack_name, pack_path in artifacts.items(): artifacts_paths = registrar.get_resources_from_pack(pack_path) for artifact_path in artifacts_paths: artifact = meta_loader.load(artifact_path) if artifact_type == "sensors": sensors_dir = os.path.dirname(artifact_path) sensor_file_path = os.path.join(sensors_dir, artifact["entry_point"]) artifact["artifact_uri"] = "file://" + sensor_file_path name = artifact.get("name", None) or artifact.get("class_name", None) if not artifact.get("pack", None): artifact["pack"] = pack_name ref = ResourceReference.to_string_reference(name=name, pack=pack_name) API_MODEL = API_MODELS_ARTIFACT_TYPES[artifact_type] # Following conversions are required because we add some fields with # default values in db model. If we don't do these conversions, # we'll see a unnecessary diff for those fields. artifact_api = API_MODEL(**artifact) artifact_db = API_MODEL.to_model(artifact_api) artifact_api = API_MODEL.from_model(artifact_db) artifacts_dict[ref] = artifact_api return artifacts_dict
def get_by_ref(cls, ref): if not ref: return None ref_obj = ResourceReference.from_string_reference(ref=ref) result = cls.query(name=ref_obj.name, pack=ref_obj.pack).first() return result
def _get_liveaction_model(self, action_db, params): status = action_constants.LIVEACTION_STATUS_REQUESTED start_timestamp = date_utils.get_datetime_utc_now() action_ref = ResourceReference(name=action_db.name, pack=action_db.pack).ref parameters = params context = {'user': cfg.CONF.system_user.user} liveaction_db = LiveActionDB(status=status, start_timestamp=start_timestamp, action=action_ref, parameters=parameters, context=context) return liveaction_db
def test_chain_runner_dict_param_temp(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_DICT_TEMP_PATH chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.container_service = RunnerContainerService() chain_runner.pre_run() chain_runner.run({'s1': 1, 's2': 2, 's3': 3, 's4': 4}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) expected_value = {"p1": {"p1.3": "[3, 4]", "p1.2": "2", "p1.1": "1"}} mock_args, _ = request.call_args self.assertEqual(mock_args[0].parameters, expected_value)
def test_chain_runner_failure_path(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_1_PATH chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.container_service = RunnerContainerService() chain_runner.pre_run() status, _, _ = chain_runner.run({}) self.assertEqual(status, LIVEACTION_STATUS_FAILED) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # based on the chain the callcount is known to be 2. Not great but works. self.assertEqual(request.call_count, 2)
def test_chain_runner_success_path(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_1_PATH chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.liveaction.notify = CHAIN_NOTIFY_DB chain_runner.container_service = RunnerContainerService() chain_runner.pre_run() chain_runner.run({}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # based on the chain the callcount is known to be 3. Not great but works. self.assertEqual(request.call_count, 3)
def test_get_one_by_ref(self): post_resp = self.__do_post(RulesControllerTestCase.RULE_1) rule_name = post_resp.json['name'] rule_pack = post_resp.json['pack'] ref = ResourceReference.to_string_reference(name=rule_name, pack=rule_pack) rule_id = post_resp.json['id'] get_resp = self.__do_get_one(ref) self.assertEqual(get_resp.json['name'], rule_name) self.assertEqual(get_resp.status_int, http_client.OK) self.__do_delete(rule_id) post_resp = self.__do_post(RulesControllerTestCase.RULE_SPACE) rule_name = post_resp.json['name'] rule_pack = post_resp.json['pack'] ref = ResourceReference.to_string_reference(name=rule_name, pack=rule_pack) rule_id = post_resp.json['id'] get_resp = self.__do_get_one(ref) self.assertEqual(get_resp.json['name'], rule_name) self.assertEqual(get_resp.status_int, http_client.OK) self.__do_delete(rule_id)
def test_chain_runner_list_param_temp(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_LIST_TEMP_PATH chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference( name=ACTION_1.name, pack=ACTION_1.pack ) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() chain_runner.run({"s1": 1, "s2": 2, "s3": 3, "s4": 4}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) mock_args, _ = request.call_args self.assertEqual(mock_args[0].parameters, {"p1": "[2, 3, 4]"})
def _transform_action(spec, action_key, input_key): if action_key not in spec or spec.get(action_key) == 'st2.action': return if spec.get(action_key) == 'st2.callback': raise Exception('st2.callback is deprecated.') # Convert parameters that are inline (i.e. action: some_action var1={$.value1} var2={$.value2}) # and split it to action name and input dict as illustrated below. # # action: some_action # input: # var1: $.value1 # var2: $.value2 # # This step to separate the action name and the input parameters is required # to wrap them with the st2.action proxy. # # action: st2.action # input: # ref: some_action # parameters: # var1: $.value1 # var2: $.value2 _eval_inline_params(spec, action_key, input_key) action_ref = spec.get(action_key) if ResourceReference.is_resource_reference(action_ref): ref = ResourceReference.from_string_reference(ref=action_ref) actions = Action.query(name=ref.name, pack=ref.pack) action = actions.first() if actions else None else: action = None if action: spec[action_key] = 'st2.action' spec[input_key] = {'ref': action_ref, 'parameters': spec[input_key]}
def test_chain_runner_vars_action_params(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_WITH_ACTIONPARAM_VARS chain_runner.action = ACTION_2 action_ref = ResourceReference.to_string_reference(name=ACTION_2.name, pack=ACTION_2.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() chain_runner.run({'input_a': 'two'}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) expected_value = {'inttype': 1, 'strtype': 'two', 'booltype': True} mock_args, _ = request.call_args self.assertEqual(mock_args[0].parameters, expected_value)
def to_model(cls, action): model = super(cls, cls).to_model(action) model.enabled = bool(action.enabled) model.entry_point = str(action.entry_point) model.pack = str(action.pack) model.runner_type = {'name': str(action.runner_type)} model.parameters = getattr(action, 'parameters', dict()) model.tags = TagsHelper.to_model(getattr(action, 'tags', [])) model.ref = ResourceReference.to_string_reference(pack=model.pack, name=model.name) if getattr(action, 'notify', None): model.notify = NotificationsHelper.to_model(action.notify) return model
def _get_trigger_api_given_rule(rule): trigger = rule.trigger triggertype_ref = ResourceReference.from_string_reference(trigger.get('type')) trigger_dict = {} trigger_name = trigger.get('name', None) if trigger_name: trigger_dict['name'] = trigger_name trigger_dict['pack'] = triggertype_ref.pack trigger_dict['type'] = triggertype_ref.ref trigger_dict['parameters'] = rule.trigger.get('parameters', {}) trigger_api = TriggerAPI(**trigger_dict) return trigger_api
def test_chain_runner_success_task_action_call_with_no_params(self, request): # Make sure that the runner doesn't explode if task definition contains # no "params" section chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_ACTION_CALL_NO_PARAMS_PATH chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.liveaction.notify = CHAIN_NOTIFY_DB chain_runner.pre_run() chain_runner.run({}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # based on the chain the callcount is known to be 3. Not great but works. self.assertEqual(request.call_count, 3)
def test_params_and_parameters_attributes_both_work(self, _): action_ref = ResourceReference.to_string_reference( name=ACTION_2.name, pack=ACTION_2.pack ) # "params" attribute used chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_ACTION_PARAMS_ATTRIBUTE chain_runner.action = ACTION_2 chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() original_build_liveaction_object = chain_runner._build_liveaction_object def mock_build_liveaction_object(action_node, resolved_params, parent_context): # Verify parameters are correctly passed to the action self.assertEqual(resolved_params, {"pparams": "v1"}) original_build_liveaction_object( action_node=action_node, resolved_params=resolved_params, parent_context=parent_context, ) chain_runner._build_liveaction_object = mock_build_liveaction_object action_parameters = {} status, output, _ = chain_runner.run(action_parameters=action_parameters) self.assertEqual(status, LIVEACTION_STATUS_SUCCEEDED) # "parameters" attribute used chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_ACTION_PARAMETERS_ATTRIBUTE chain_runner.action = ACTION_2 chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() def mock_build_liveaction_object(action_node, resolved_params, parent_context): # Verify parameters are correctly passed to the action self.assertEqual(resolved_params, {"pparameters": "v1"}) original_build_liveaction_object( action_node=action_node, resolved_params=resolved_params, parent_context=parent_context, ) chain_runner._build_liveaction_object = mock_build_liveaction_object action_parameters = {} status, output, _ = chain_runner.run(action_parameters=action_parameters) self.assertEqual(status, LIVEACTION_STATUS_SUCCEEDED)
def get_by_ref(cls, ref, only_fields: Optional[List[str]] = None): """ :param: only_field: Optional lists if fields to retrieve. If not specified, it defaults to all fields. """ if not ref: return None ref_obj = ResourceReference.from_string_reference(ref=ref) result = cls.query(name=ref_obj.name, pack=ref_obj.pack, only_fields=only_fields).first() return result
def _to_trigger(self, body): trigger = body.get('trigger', '') trigger_ref = None try: trigger_ref = ResourceReference.from_string_reference(ref=trigger) except InvalidResourceReferenceError: LOG.debug('Unable to parse reference.', exc_info=True) return { 'name': trigger_ref.name if trigger_ref else None, 'pack': trigger_ref.pack if trigger_ref else None, 'type': body.get('type', ''), 'parameters': {} }, body['payload']
def to_model(cls, alias): name = alias.name description = getattr(alias, 'description', None) pack = alias.pack ref = ResourceReference.to_string_reference(pack=pack, name=name) enabled = getattr(alias, 'enabled', True) action_ref = alias.action_ref formats = alias.formats ack = getattr(alias, 'ack', None) result = getattr(alias, 'result', None) model = cls.model(name=name, description=description, pack=pack, ref=ref, enabled=enabled, action_ref=action_ref, formats=formats, ack=ack, result=result) return model
def test_get_all_filters_filter_by_type(self): holder = TimersHolder() for _, model in TestTimersHolder.MODELS.items(): holder.add_trigger( ref=ResourceReference.to_string_reference(pack=model["pack"], name=model["name"]), trigger=model, ) self.assertEqual( len(holder.get_all(timer_type=INTERVAL_TIMER_TRIGGER_REF)), 3) self.assertEqual( len(holder.get_all(timer_type=DATE_TIMER_TRIGGER_REF)), 1) self.assertEqual( len(holder.get_all(timer_type=CRON_TIMER_TRIGGER_REF)), 1)
def validate_action(action_api): runner_db = _get_runner_model(action_api) # Check if pack is valid. if not _is_valid_pack(action_api.pack): packs_base_paths = get_packs_base_paths() packs_base_paths = ','.join(packs_base_paths) msg = ('Content pack "%s" is not found or doesn\'t contain actions directory. ' 'Searched in: %s' % (action_api.pack, packs_base_paths)) raise ValueValidationException(msg) # Check if parameters defined are valid. action_ref = ResourceReference.to_string_reference(pack=action_api.pack, name=action_api.name) _validate_parameters(action_ref, action_api.parameters, runner_db.runner_parameters)
def _get_api_models_from_db(persistence_model, pack_dir=None): filters = {} if pack_dir: pack_name = os.path.basename(os.path.normpath(pack_dir)) filters = {'pack': pack_name} models = persistence_model.query(**filters) models_dict = {} for model in models: model_pack = getattr(model, 'pack', None) or DEFAULT_PACK_NAME model_ref = ResourceReference.to_string_reference(name=model.name, pack=model_pack) if getattr(model, 'id', None): del model.id API_MODEL = API_MODELS_PERSISTENT_MODELS[persistence_model] models_dict[model_ref] = API_MODEL.from_model(model) return models_dict
def test_to_string_reference(self): ref = ResourceReference.to_string_reference(pack='mapack', name='moname') self.assertEqual(ref, 'mapack.moname') expected_msg = r'Pack name should not contain "\."' self.assertRaisesRegexp(ValueError, expected_msg, ResourceReference.to_string_reference, pack='pack.invalid', name='bar') expected_msg = 'Both pack and name needed for building' self.assertRaisesRegexp(ValueError, expected_msg, ResourceReference.to_string_reference, pack='pack', name=None) expected_msg = 'Both pack and name needed for building' self.assertRaisesRegexp(ValueError, expected_msg, ResourceReference.to_string_reference, pack=None, name='name')
def get_model_by_resource_ref(db_api, ref): """ Retrieve a DB model based on the resource reference. :param db_api: Class of the object to retrieve. :type db_api: ``object`` :param ref: Resource reference. :type ref: ``str`` :return: Retrieved object. """ ref_obj = ResourceReference.from_string_reference(ref=ref) result = db_api.query(name=ref_obj.name, pack=ref_obj.pack).first() return result
def test_chain_runner_no_default(self, request): chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_NO_DEFAULT chain_runner.action = ACTION_1 action_ref = ResourceReference.to_string_reference(name=ACTION_1.name, pack=ACTION_1.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() chain_runner.run({}) self.assertNotEqual(chain_runner.chain_holder.actionchain, None) # In case of this chain default_node is the first_node. default_node = chain_runner.chain_holder.actionchain.default first_node = chain_runner.chain_holder.actionchain.chain[0] self.assertEqual(default_node, first_node.name) # based on the chain the callcount is known to be 3. Not great but works. self.assertEqual(request.call_count, 3)
def _get_trigger_db(trigger): # TODO: This method should die in a fire # XXX: Do not make this method public. if isinstance(trigger, dict): name = trigger.get('name', None) pack = trigger.get('pack', None) if name and pack: ref = ResourceReference.to_string_reference(name=name, pack=pack) return get_trigger_db_by_ref(ref) return get_trigger_db_given_type_and_params(type=trigger['type'], parameters=trigger.get('parameters', {})) else: raise Exception('Unrecognized object')
def _delete_shadow_trigger(triggertype_db): # shadow Trigger's have the same name as the shadowed TriggerType. triggertype_ref = ResourceReference(name=triggertype_db.name, pack=triggertype_db.pack) trigger_db = TriggerService.get_trigger_db_by_ref(triggertype_ref.ref) if not trigger_db: LOG.warn('No shadow trigger found for %s. Will skip delete.', triggertype_db) return try: Trigger.delete(trigger_db) except Exception: LOG.exception('Database delete encountered exception during delete of id="%s". ', trigger_db.id) extra = {'trigger_db': trigger_db} LOG.audit('Trigger deleted. Trigger.id=%s' % (trigger_db.id), extra=extra)
def _get_liveaction_model(self, params): status = 'initializing' start_timestamp = date_utils.get_datetime_utc_now() action_ref = ResourceReference(name=ParamsUtilsTest.action_db.name, pack=ParamsUtilsTest.action_db.pack).ref liveaction_db = LiveActionDB(status=status, start_timestamp=start_timestamp, action=action_ref, parameters=params) liveaction_db.context = { 'source_channel': 'awesome', 'api_user': '******' } return liveaction_db
def _get_filters(self, **kwargs): filters = copy.deepcopy(kwargs) ref = filters.get('ref', None) if ref: try: ref_obj = ResourceReference.from_string_reference(ref=ref) except InvalidResourceReferenceError: raise filters['name'] = ref_obj.name filters['pack'] = ref_obj.pack del filters['ref'] return filters
def _get_by_ref(self, resource_ref, exclude_fields=None, include_fields=None): if exclude_fields and include_fields: msg = ('exclude_fields and include_fields arguments are mutually exclusive. ' 'You need to provide either one or another, but not both.') raise ValueError(msg) try: ref = ResourceReference.from_string_reference(ref=resource_ref) except Exception: return None resource_db = self.access.query(name=ref.name, pack=ref.pack, exclude_fields=exclude_fields, only_fields=include_fields).first() return resource_db
def test_update_liveaction_result_with_dotted_key(self): liveaction_db = LiveActionDB() liveaction_db.status = "initializing" liveaction_db.start_timestamp = get_datetime_utc_now() liveaction_db.action = ResourceReference( name=ActionDBUtilsTestCase.action_db.name, pack=ActionDBUtilsTestCase.action_db.pack, ).ref params = { "actionstr": "foo", "some_key_that_aint_exist_in_action_or_runner": "bar", "runnerint": 555, } liveaction_db.parameters = params liveaction_db = LiveAction.add_or_update(liveaction_db) origliveaction_db = copy.copy(liveaction_db) # Update by id. newliveaction_db = action_db_utils.update_liveaction_status( status="running", liveaction_id=liveaction_db.id) # Verify id didn't change. self.assertEqual(origliveaction_db.id, newliveaction_db.id) self.assertEqual(newliveaction_db.status, "running") # Verify that state is published. self.assertTrue(LiveActionPublisher.publish_state.called) LiveActionPublisher.publish_state.assert_called_once_with( newliveaction_db, "running") now = get_datetime_utc_now() status = "succeeded" result = {"a": 1, "b": True, "a.b.c": "abc"} context = {"third_party_id": uuid.uuid4().hex} newliveaction_db = action_db_utils.update_liveaction_status( status=status, result=result, context=context, end_timestamp=now, liveaction_id=liveaction_db.id, ) self.assertEqual(origliveaction_db.id, newliveaction_db.id) self.assertEqual(newliveaction_db.status, status) self.assertIn("a.b.c", list(result.keys())) self.assertDictEqual(newliveaction_db.result, result) self.assertDictEqual(newliveaction_db.context, context) self.assertEqual(newliveaction_db.end_timestamp, now)
def create_or_update_trigger_type_db(trigger_type): """ Create or update a trigger type db object in the db given trigger_type definition as dict. :param trigger_type: Trigger type model. :type trigger_type: ``dict`` :rtype: ``object`` """ assert isinstance(trigger_type, dict) trigger_type_api = TriggerTypeAPI(**trigger_type) trigger_type_api.validate() trigger_type_api = TriggerTypeAPI.to_model(trigger_type_api) ref = ResourceReference.to_string_reference(name=trigger_type_api.name, pack=trigger_type_api.pack) existing_trigger_type_db = get_trigger_type_db(ref) if existing_trigger_type_db: is_update = True else: is_update = False if is_update: trigger_type_api.id = existing_trigger_type_db.id try: trigger_type_db = TriggerType.add_or_update(trigger_type_api) except StackStormDBObjectConflictError: # Operation is idempotent and trigger could have already been created by # another process. Ignore object already exists because it simply means # there was a race and object is already in the database. trigger_type_db = get_trigger_type_db(ref) is_update = True extra = {'trigger_type_db': trigger_type_db} if is_update: LOG.audit('TriggerType updated. TriggerType.id=%s' % (trigger_type_db.id), extra=extra) else: LOG.audit('TriggerType created. TriggerType.id=%s' % (trigger_type_db.id), extra=extra) return trigger_type_db
def get_resource_ref_from_model(model): """ Return a ResourceReference given db_model. :param model: DB model that contains name and pack. :type model: ``object`` :return: ResourceReference. """ try: name = model.name pack = model.pack except AttributeError: raise Exception('Cannot build ResourceReference for model: %s. Name or pack missing.' % model) return ResourceReference(name=name, pack=pack)
def test_action_chain_runner_referenced_action_doesnt_exist(self, mock_request): # Action referenced by a task doesn't exist, should result in a top level error chain_runner = acr.get_runner() chain_runner.entry_point = CHAIN_WITH_INVALID_ACTION chain_runner.action = ACTION_2 action_ref = ResourceReference.to_string_reference(name=ACTION_2.name, pack=ACTION_2.pack) chain_runner.liveaction = LiveActionDB(action=action_ref) chain_runner.pre_run() action_parameters = {} status, output, _ = chain_runner.run(action_parameters=action_parameters) expected_error = ('Failed to run task "c1". Action with reference "wolfpack.a2" ' 'doesn\'t exist.') self.assertEqual(status, LIVEACTION_STATUS_FAILED) self.assertIn(expected_error, output['error']) self.assertIn('Traceback', output['traceback'])