diff options
Diffstat (limited to 'onap-dcae-dcaepolicy-lib')
-rw-r--r-- | onap-dcae-dcaepolicy-lib/.coveragerc | 4 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/LICENSE.txt | 4 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/README.md | 93 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py | 9 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/dcae_policy.py | 420 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/policies_output.py | 148 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/pom.xml | 7 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/setup.py | 10 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tests/__init__.py | 19 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tests/log_ctx.py | 15 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py | 7 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py | 1270 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tox-local.ini | 3 | ||||
-rw-r--r-- | onap-dcae-dcaepolicy-lib/tox.ini | 9 |
14 files changed, 1684 insertions, 334 deletions
diff --git a/onap-dcae-dcaepolicy-lib/.coveragerc b/onap-dcae-dcaepolicy-lib/.coveragerc index 26ed8ac..43c7005 100644 --- a/onap-dcae-dcaepolicy-lib/.coveragerc +++ b/onap-dcae-dcaepolicy-lib/.coveragerc @@ -23,7 +23,3 @@ exclude_lines = if __name__ == .__main__.: ignore_errors = True - -[xml] -output = coverage-reports/coverage-onap_dcae_dcaepolicy_lib.xml - diff --git a/onap-dcae-dcaepolicy-lib/LICENSE.txt b/onap-dcae-dcaepolicy-lib/LICENSE.txt index 45ec201..0b6ac42 100644 --- a/onap-dcae-dcaepolicy-lib/LICENSE.txt +++ b/onap-dcae-dcaepolicy-lib/LICENSE.txt @@ -1,7 +1,5 @@ ============LICENSE_START======================================================= -org.onap.dcae -================================================================================ -Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. diff --git a/onap-dcae-dcaepolicy-lib/README.md b/onap-dcae-dcaepolicy-lib/README.md index ff37256..b46f0b1 100644 --- a/onap-dcae-dcaepolicy-lib/README.md +++ b/onap-dcae-dcaepolicy-lib/README.md @@ -1,4 +1,5 @@ # onap-dcae-dcaepolicy-lib - policy in dcae controller + - python-package to be used in cloudify plugins to maintain the policies lifecycle ## upload the python package to pypi server @@ -8,23 +9,26 @@ python setup.py sdist upload ``` --- -# usage in plugins -**requirements.txt** +## usage in plugins + +### **requirements.txt** + ```python onap-dcae-dcaepolicy-lib==1.0.0 ``` -**tasks.py** -- import +### **tasks.py** + +import ```python from onap_dcae_dcaepolicy_lib import Policies ``` -# examples of **@operation** with **@Policies.<>** decorator +### examples of **@operation** with **@Policies.<>** decorator -## Usage +### Usage: import the dcaepolicy-node-type.yaml into your blueprint to use the dcae.nodes.type node @@ -46,9 +50,12 @@ node_templates: Then the dcaepolicyplugin will bring the latest policy to the dcae.nodes.policy node during the install workflow of cloudify. ------- -## cloudify.interfaces.lifecycle.**configure** +--- + +### cloudify.interfaces.lifecycle.**configure** + - gather policy data into runtime_properties of policy consumer node + ```yaml cloudify.interfaces.lifecycle: configure: @@ -64,23 +71,15 @@ APPLICATION_CONFIG = "application_config" SERVICE_COMPONENT_NAME = "service_component_name" @operation -@Policies.gather_policies_to_node +@Policies.gather_policies_to_node() def node_configure(**kwargs): - """decorate with @Policies.gather_policies_to_node on policy consumer node to + """decorate with @Policies.gather_policies_to_node() on policy consumer node to prepopulate runtime_properties[POLICIES] """ - app_config = None - if APPLICATION_CONFIG in ctx.node.properties: - # dockerized blueprint puts the app config into property application_config - app_config = ctx.node.properties.get(APPLICATION_CONFIG) - else: - # CDAP components expect that in property app_config - app_config = ctx.node.properties.get("app_config") - - app_config = Policies.shallow_merge_policies_into(app_config) + app_config = ctx.node.properties.get(APPLICATION_CONFIG) + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config - ctx.logger.info("example: applied policy_configs to property app_config: {0}" \ - .format(json.dumps(app_config))) + ctx.logger.info("app_config: {0}".format(json.dumps(app_config))) if SERVICE_COMPONENT_NAME in ctx.instance.runtime_properties: ctx.logger.info("saving app_config({0}) to consul under key={1}" \ @@ -88,16 +87,6 @@ def node_configure(**kwargs): ctx.instance.runtime_properties[SERVICE_COMPONENT_NAME])) DiscoveryClient.put_kv(ctx.instance.runtime_properties[SERVICE_COMPONENT_NAME], app_config) - # alternative 1 - use the list of policy configs from policies in runtime_properties - policy_configs = Policies.get_policy_configs() - if policy_configs: - ctx.logger.warn("TBD: apply policy_configs: {0}".format(json.dumps(policy_configs))) - - # alternative 2 - use the policies dict by policy_id from runtime_properties - if POLICIES in ctx.instance.runtime_properties: - policies = ctx.instance.runtime_properties[POLICIES] - ctx.logger.warn("TBD: apply policies: {0}".format(json.dumps(policies))) - ctx.logger.info("deploying the demo component: {0}...".format(ctx.node.id)) demo_app = DemoApp(ctx.node.id) demo_app.start() @@ -105,8 +94,10 @@ def node_configure(**kwargs): demo_app.get_logs() ``` ------- -## execute-operation **policy-update** +--- + +### execute-operation **policy-update** + ```yaml dcae.interfaces.policy: policy_update: @@ -114,6 +105,7 @@ dcae.interfaces.policy: ``` execute-operation **policy-update** that gets a list of changed policy-configs + ```python from .discovery import DiscoveryClient @@ -123,25 +115,13 @@ APPLICATION_CONFIG = "application_config" SERVICE_COMPONENT_NAME = "service_component_name" @operation -@Policies.update_policies_on_node(configs_only=True) -def policy_update(updated_policies, **kwargs): +@Policies.update_policies_on_node() +def policy_update(updated_policies, removed_policies=None, policies=None, **kwargs): """decorate with @Policies.update_policies_on_node() to update runtime_properties[POLICIES] :updated_policies: contains the list of changed policy-configs when configs_only=True (default). Use configs_only=False to bring the full policy objects in :updated_policies:. """ - app_config = DiscoveryClient.get_value(ctx.instance.runtime_properties[SERVICE_COMPONENT_NAME]) - - # This is how to merge the policies into app_config object - app_config = Policies.shallow_merge_policies_into(app_config) - - ctx.logger.info("merged updated_policies {0} into app_config {1}" - .format(json.dumps(updated_policies), json.dumps(app_config))) - - ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config - - DiscoveryClient.put_kv(ctx.instance.runtime_properties[SERVICE_COMPONENT_NAME], app_config) - # example how to notify the dockerized component about the policy change notify_app_through_script = True if notify_app_through_script: @@ -149,24 +129,29 @@ def policy_update(updated_policies, **kwargs): .format(json.dumps(updated_policies), json.dumps(app_config))) demo_app = DemoApp(ctx.node.id) demo_app.notify_app_through_script( - POLICY_MESSAGE_TYPE, + "policies", updated_policies=updated_policies, - application_config=app_config + removed_policies=removed_policies, + policies=policies ) ``` example of the **changed\_policies** with **configs_only=True** + - list of config objects (preparsed from json string) + - manual mess produced by mock_policy_updater + ```json [{ - "policy_updated_from_ver": "2", - "policy_updated_to_ver": "3", - "updated_policy_id": "DCAE_alex.Config_db_client_policy_id_value", - "policy_hello": "world!", - "policy_updated_ts": "2017-08-17T21:49:39.279187Z" + "policy_updated_from_ver": "2", + "policy_updated_to_ver": "3", + "updated_policy_id": "DCAE_alex.Config_db_client_policy_id_value", + "policy_hello": "world!", + "policy_updated_ts": "2017-08-17T21:49:39.279187Z" }] ``` + --- example of **policies** in runtime_properties **before policy-update** diff --git a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py index b3658b1..f8a2498 100644 --- a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py +++ b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py @@ -1,8 +1,5 @@ -"""expose the Policies class on the package level""" - -# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,4 +16,6 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. -from .dcae_policy import Policies, POLICIES, POLICY_MESSAGE_TYPE +"""expose the Policies class on the package level""" + +from .dcae_policy import Policies diff --git a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/dcae_policy.py b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/dcae_policy.py index 6a277f3..65c2614 100644 --- a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/dcae_policy.py +++ b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/dcae_policy.py @@ -1,8 +1,5 @@ -"""dcae_policy contains decorators for the policy lifecycle in cloudify""" - -# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,124 +16,293 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +"""dcae_policy contains decorators for the policy lifecycle in cloudify""" + import json -import copy +import traceback +from copy import deepcopy from functools import wraps from cloudify import ctx from cloudify.context import NODE_INSTANCE from cloudify.exceptions import NonRecoverableError +from .policies_output import PoliciesOutput + POLICIES = 'policies' +POLICY_FILTERS = 'policy_filters' +POLICIES_FILTERED = 'policies_filtered' POLICY_ID = 'policy_id' POLICY_BODY = 'policy_body' POLICY_VERSION = "policyVersion" POLICY_CONFIG = 'config' + +POLICY_FILTER = 'policy_filter' +POLICY_FILTER_ID = 'policy_filter_id' + +POLICY_PERSISTENT = 'policy_persistent' DCAE_POLICY_TYPE = 'dcae.nodes.policy' -POLICY_MESSAGE_TYPE = 'policy' +DCAE_POLICIES_TYPE = 'dcae.nodes.policies' + +ACTION_GATHERED = "gathered" +ACTION_UPDATED = "updated" + +CONFIG_ATTRIBUTES = "configAttributes" class Policies(object): """static class for policy operations""" + _updated_policies = {} + _removed_policies = {} @staticmethod - def gather_policies_to_node(func): - """decorate with @Policies.gather_policies_to_node to - gather the policies from dcae.nodes.policy nodes this node depends on. + def _init(): + """init static members""" + Policies._updated_policies = {} + Policies._removed_policies = {} - Places the policies into runtime_properties["policies"]. + @staticmethod + def _set_policies(policies): + """store the :policies: in :runtime_properties[POLICIES]:""" + if not policies: + if POLICIES in ctx.instance.runtime_properties: + del ctx.instance.runtime_properties[POLICIES] + return - Call Policies.shallow_merge_policies_into(config) to merge the policies into config. - """ - def _merge_policy_with_node(target): - """get all properties of the policy node and add the actual policy""" - policy = dict(target.node.properties) - if POLICY_BODY in target.instance.runtime_properties: - policy[POLICY_BODY] = target.instance.runtime_properties[POLICY_BODY] - return policy + ctx.instance.runtime_properties[POLICIES] = policies - if not func: + @staticmethod + def _add_policy(policy_id, policy, policy_persistent, policies): + """only add the latest version of policy to policies""" + prev_policy = policies.get(policy_id) + prev_policy_persistent = (prev_policy or {}).get(POLICY_PERSISTENT) + + if not prev_policy \ + or (policy_persistent and not prev_policy_persistent) \ + or (policy_persistent == prev_policy_persistent and policy.get(POLICY_BODY)): + policy = deepcopy(policy) + policy[POLICY_PERSISTENT] = policy_persistent + policies[policy_id] = policy + + @staticmethod + def _gather_policy(target, policies): + """adds the policy from dcae.nodes.policy node to policies""" + if DCAE_POLICY_TYPE not in target.node.type_hierarchy: return + policy_id = target.node.properties.get(POLICY_ID) + if not policy_id: + return True + policy = deepcopy(dict(target.node.properties)) + policy_body = target.instance.runtime_properties.get(POLICY_BODY) + if policy_body: + policy[POLICY_BODY] = policy_body - @wraps(func) - def wrapper(*args, **kwargs): - """gather and save the policies from dcae.nodes.policy nodes this node related to""" + Policies._add_policy(policy_id, policy, True, policies) + return True + + @staticmethod + def _fix_policy_filter(policy_filter): + if CONFIG_ATTRIBUTES in policy_filter: + config_attributes = policy_filter.get(CONFIG_ATTRIBUTES) + if isinstance(config_attributes, dict): + return try: - if ctx.type == NODE_INSTANCE: - policies = dict([(rel.target.node.properties[POLICY_ID], \ - _merge_policy_with_node(rel.target)) \ - for rel in ctx.instance.relationships \ - if DCAE_POLICY_TYPE in rel.target.node.type_hierarchy \ - and POLICY_ID in rel.target.node.properties \ - and rel.target.node.properties[POLICY_ID] \ - ]) - if policies: - ctx.instance.runtime_properties[POLICIES] = policies - except Exception as ex: - error = "Failed to set the policies {0}".format(str(ex)) - ctx.logger.error(error) - raise NonRecoverableError(error) - - return func(*args, **kwargs) - return wrapper + config_attributes = json.loads(config_attributes) + if config_attributes and isinstance(config_attributes, dict): + policy_filter[CONFIG_ATTRIBUTES] = config_attributes + return + except (ValueError, TypeError): + pass + if config_attributes: + ctx.logger.warn("unexpected %s: %s", CONFIG_ATTRIBUTES, config_attributes) + del policy_filter[CONFIG_ATTRIBUTES] @staticmethod - def _update_policies_on_ctx(updated_policies): - """update policies in runtime_properties and return changed_policies""" - if POLICIES not in ctx.instance.runtime_properties: + def _gather_policies(target, policies, policy_filters): + """adds the policies and policy-filter from dcae.nodes.policies node to policies""" + if DCAE_POLICIES_TYPE not in target.node.type_hierarchy: return - if not updated_policies: - ctx.logger.error("update_policies_on_ctx - no updated_policies provided in arguments") + + property_policy_filter = target.node.properties.get(POLICY_FILTER) + if property_policy_filter: + policy_filter = deepcopy(dict( + (k, v) for (k, v) in dict(property_policy_filter).iteritems() + if v or isinstance(v, (int, float)) + )) + Policies._fix_policy_filter(policy_filter) + + if policy_filter: + policy_filters[target.instance.id] = { + POLICY_FILTER_ID : target.instance.id, + POLICY_FILTER : policy_filter + } + + filtered_policies = target.instance.runtime_properties.get(POLICIES_FILTERED) + if not filtered_policies or not isinstance(filtered_policies, dict): + return True + for (policy_id, policy) in filtered_policies.iteritems(): + Policies._add_policy(policy_id, policy, False, policies) + return True + + @staticmethod + def _get_policy_bodies_dict(policies): + """returns a dict of policy_id -> policy_body if policy_body exists""" + if not policies: + return {} + + return dict((policy_id, policy.get(POLICY_BODY)) + for policy_id, policy in policies.iteritems() if policy.get(POLICY_BODY) + ) + + @staticmethod + def gather_policies_to_node(): + """ + decorate with @Policies.gather_policies_to_node() to + gather the policies from dcae.nodes.policy nodes this node depends on. + + Places the policies into runtime_properties["policies"]. + + Stores <scn>:policies data in consul-kv + """ + def gather_policies_decorator(func): + """the decorator""" + if not func: + return + + @wraps(func) + def wrapper(*args, **kwargs): + """gather and save the policies from dcae.nodes.policy nodes this node related to""" + if ctx.type != NODE_INSTANCE: + raise NonRecoverableError("can only invoke gather_policies_to_node on node") + + policies_outputted = False + policy_bodies = [] + try: + policies = {} + policy_filters = {} + for rel in ctx.instance.relationships: + _ = Policies._gather_policy(rel.target, policies) \ + or Policies._gather_policies(rel.target, policies, policy_filters) + + Policies._set_policies(policies) + if policy_filters: + ctx.instance.runtime_properties[POLICY_FILTERS] = policy_filters + + policy_bodies = Policies._get_policy_bodies_dict(policies) + if policy_bodies: + policies_outputted = PoliciesOutput.store_policies(ACTION_GATHERED, policy_bodies) + except Exception as ex: + error = "Failed to set the policies {0}".format(str(ex)) + ctx.logger.error("{0}: {1}".format(error, traceback.format_exc())) + raise NonRecoverableError(error) + + func_result = func(*args, **kwargs) + + if not policies_outputted and policy_bodies: + PoliciesOutput.store_policies(ACTION_GATHERED, policy_bodies) + + return func_result + return wrapper + return gather_policies_decorator + + @staticmethod + def _update_policies(updated_policies, added_policies, removed_policies): + """ + filter and update policies in runtime_properties + and return the ordered filtered list of changed_policies + """ + Policies._init() + + if not updated_policies and not removed_policies and not added_policies: + ctx.logger.error("update_policies_on_ctx - no updated, added, or removed policies received") return - policies = ctx.instance.runtime_properties[POLICIES] - ctx.logger.info("update_policies_on_ctx: {0}".format(json.dumps(updated_policies))) - changed_policies = [] - ignored_policies = [] - unexpected_policies = [] - same_policies = [] + updated_policies = updated_policies or [] + added_policies = added_policies or {} + removed_policies = removed_policies or [] + + ctx.logger.info("updated_policies: {0}, added_policies: {1}, removed_policies: {2}" + .format(json.dumps(updated_policies), + json.dumps(added_policies), + json.dumps(removed_policies))) + + policies = ctx.instance.runtime_properties.get(POLICIES, {}) + policy_filters = ctx.instance.runtime_properties.get(POLICY_FILTERS, {}) + + for policy_id in removed_policies: + removed_policy = policies.get(policy_id) + if removed_policy and POLICY_BODY in removed_policy: + Policies._removed_policies[policy_id] = deepcopy(removed_policy) + if removed_policy.get(POLICY_PERSISTENT): + del policies[policy_id][POLICY_BODY] + else: + del policies[policy_id] + + new_policies = dict((policy_id, policy) + for policy_filter_id in policy_filters + for (policy_id, policy) in added_policies.get(policy_filter_id, {}) + .get(POLICIES, {}).iteritems()) + + ctx.logger.info("new_policies: {0}".format(json.dumps(new_policies))) + + for (policy_id, policy) in new_policies.iteritems(): + deployed_policy = policies.get(policy_id) + if not deployed_policy: + policies[policy_id] = policy + Policies._updated_policies[policy_id] = policy + continue + updated_policies.append(policy) + + skipped = {"ignored": [], "unexpected": [], "same": [], "duplicate": []} for policy in updated_policies: - if POLICY_ID not in policy or policy[POLICY_ID] not in policies: - ignored_policies.append(policy) + policy_id = policy.get(POLICY_ID) + if not policy_id or policy_id not in policies: + skipped["ignored"].append(policy) + continue + + if policy_id in Policies._updated_policies: + skipped["duplicate"].append(policy) + continue + + updated_policy_body = policy.get(POLICY_BODY, {}) + updated_policy_version = updated_policy_body.get(POLICY_VERSION) + if not updated_policy_version or POLICY_CONFIG not in updated_policy_body: + skipped["unexpected"].append(policy) continue - if POLICY_BODY not in policy or POLICY_VERSION not in policy[POLICY_BODY] \ - or not policy[POLICY_BODY][POLICY_VERSION]: - unexpected_policies.append(policy) + + deployed_policy = policies.get(policy_id) + deployed_policy_version = deployed_policy.get(POLICY_BODY, {}).get(POLICY_VERSION) + if updated_policy_version == deployed_policy_version: + skipped["same"].append(policy) continue - deployed_policy = policies[policy[POLICY_ID]].get(POLICY_BODY, {}) - new_policy_body = policy[POLICY_BODY] - if not deployed_policy or POLICY_VERSION not in deployed_policy \ - or not deployed_policy[POLICY_VERSION] \ - or deployed_policy[POLICY_VERSION] != new_policy_body[POLICY_VERSION]: - policies[policy[POLICY_ID]][POLICY_BODY] = new_policy_body - changed_policies.append(dict(policies[policy[POLICY_ID]])) - else: - same_policies.append(policy) - - if same_policies: - ctx.logger.info("same policies: {0}".format(json.dumps(same_policies))) - if ignored_policies: - ctx.logger.info("ignored policies: {0}".format(json.dumps(ignored_policies))) - if unexpected_policies: - ctx.logger.warn("unexpected policies: {0}".format(json.dumps(unexpected_policies))) - - if changed_policies: - ctx.instance.runtime_properties[POLICIES] = policies - return changed_policies + policies[policy_id][POLICY_BODY] = updated_policy_body + Policies._updated_policies[policy_id] = policy + + if skipped["same"] or skipped["ignored"] or skipped["unexpected"] or skipped["duplicate"]: + ctx.logger.info("skipped updates on policies: {0}".format(json.dumps(skipped))) + + if Policies._updated_policies or Policies._removed_policies: + Policies._set_policies(policies) + policy_bodies = Policies._get_policy_bodies_dict(policies) + PoliciesOutput.store_policies(ACTION_UPDATED, policy_bodies) @staticmethod - def update_policies_on_node(configs_only=True): - """decorate each policy_update operation with @Policies.update_policies_on_node to + def update_policies_on_node(): + """ + decorate each policy_update operation with @Policies.update_policies_on_node() to filter out the updated_policies to only what applies to the current node instance, update runtime_properties["policies"] - :configs_only: - set to True if expect to see only the config in updated_policies - instead of the whole policy object (False) + updates <scn>:policies data in consul-kv Passes through the filtered list of updated_policies that apply to the current node instance - :updated_policies: contains the list of changed policy-configs when configs_only=True. + :updated_policies: contains the list of changed policy_bodies + + :removed_policies: contains the list of removed policy_bodies + + :policies: contains the list of current policy_bodies """ def update_policies_decorator(func): """actual decorator""" @@ -144,56 +310,64 @@ class Policies(object): return @wraps(func) - def wrapper(updated_policies, **kwargs): - """update matching policies on context""" + def wrapper(updated_policies=None, added_policies=None, removed_policies=None, **kwargs): + """update matching policies on the node""" if ctx.type != NODE_INSTANCE: - return + raise NonRecoverableError("can only invoke update_policies_on_node on node") + + try: + Policies._update_policies(updated_policies, added_policies, removed_policies) + + updated_policies = Policies.get_policy_bodies( + selected_policies=Policies._updated_policies + ) - updated_policies = Policies._update_policies_on_ctx(updated_policies) - if updated_policies: - if configs_only: - updated_policies = [policy[POLICY_BODY][POLICY_CONFIG] \ - for policy in updated_policies \ - if POLICY_BODY in policy \ - and POLICY_CONFIG in policy[POLICY_BODY] \ - ] - return func(updated_policies, **kwargs) + removed_policies = Policies.get_policy_bodies( + selected_policies=Policies._removed_policies + ) + + except Exception as ex: + error = "Failed to update the policies {0}".format(str(ex)) + ctx.logger.error("{0}: {1}".format(error, traceback.format_exc())) + raise NonRecoverableError(error) + + if updated_policies or removed_policies: + return func(updated_policies, + removed_policies=removed_policies, + policies=Policies.get_policy_bodies(), + **kwargs) return wrapper return update_policies_decorator - @staticmethod - def get_policy_configs(): - """returns the list of policy configs from the runtime policies""" - if ctx.type != NODE_INSTANCE \ - or POLICIES not in ctx.instance.runtime_properties: - return - policies = ctx.instance.runtime_properties[POLICIES] - if not policies: + def cleanup_policies_on_node(func): + """ + decorate each delete operation with @Policies.cleanup_policies_on_node to + remove <scn>:policies data in consul-kv + """ + if not func: return - policy_configs = [policies[policy_id][POLICY_BODY][POLICY_CONFIG] \ - for policy_id in policies \ - if POLICY_BODY in policies[policy_id] \ - and POLICY_CONFIG in policies[policy_id][POLICY_BODY] \ - ] - return policy_configs - @staticmethod - def shallow_merge_policies_into(config): - """shallow merge the policy configs (dict) into config that is expected to be a dict""" - if config is None: - config = {} - policy_configs = Policies.get_policy_configs() - if not policy_configs or not isinstance(config, dict): - return config - - for policy_config in copy.deepcopy(policy_configs): - if not isinstance(policy_config, dict): - continue + @wraps(func) + def wrapper(**kwargs): + """remove policies in consul-kv""" + if ctx.type == NODE_INSTANCE: + try: + PoliciesOutput.delete_policies() + except Exception as ex: + error = "Failed to cleanup policies in consul-kv {0}".format(str(ex)) + ctx.logger.error("{0}: {1}".format(error, traceback.format_exc())) - config.update(policy_config) - for cfg_item in policy_config: - if policy_config[cfg_item] is None: - config.pop(cfg_item, None) + return func(**kwargs) + return wrapper + + @staticmethod + def get_policy_bodies(selected_policies=None): + """returns the list of policy_body objects if policy_body exists""" + if isinstance(selected_policies, dict): + return deepcopy([policy.get(POLICY_BODY) + for policy in selected_policies.values() if policy.get(POLICY_BODY)]) - return config + policies = ctx.instance.runtime_properties.get(POLICIES, {}) + return deepcopy([policy.get(POLICY_BODY) + for policy in policies.values() if policy.get(POLICY_BODY)]) diff --git a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/policies_output.py b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/policies_output.py new file mode 100644 index 0000000..d8125c0 --- /dev/null +++ b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/policies_output.py @@ -0,0 +1,148 @@ +# ================================================================================ +# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============LICENSE_END========================================================= +# +# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +"""client to talk to consul on standard port 8500""" + +import base64 +import json +import urllib +import uuid +from datetime import datetime + +import requests +from cloudify import ctx + + +class PoliciesOutput(object): + """static class for store-delete policies in consul kv""" + # it is safe to assume that consul agent is at consul:8500 + # define consul alis in /etc/hosts on cloudify manager vm + # $ cat /etc/hosts + # 127.0.0.1 localhost localhost.localdomain localhost4 localhost4.localdomain4 consul + CONSUL_TRANSACTION_URL = "http://consul:8500/v1/txn" + + POLICIES_EVENT = 'policies_event' + + POLICIES_FOLDER_MASK = "{0}:policies/{1}" + MAX_OPS_PER_TXN = 64 + + OPERATION_SET = "set" + OPERATION_DELETE = "delete" + OPERATION_DELETE_FOLDER = "delete-tree" + SERVICE_COMPONENT_NAME = "service_component_name" + + + @staticmethod + def _gen_txn_operation(verb, service_component_name, key=None, value=None): + """returns the properly formatted operation to be used inside transaction""" + key = PoliciesOutput.POLICIES_FOLDER_MASK.format( + service_component_name, urllib.quote(key or "") + ) + if value: + return {"KV": {"Verb": verb, "Key": key, "Value": base64.b64encode(value)}} + return {"KV": {"Verb": verb, "Key": key}} + + + @staticmethod + def _run_transaction(operation_name, txn): + """run a single transaction of several operations at consul /txn""" + if not txn: + return None + + response = None + try: + response = requests.put(PoliciesOutput.CONSUL_TRANSACTION_URL, json=txn) + except requests.exceptions.RequestException as ex: + ctx.logger.error( + "RequestException - failed to {0} at {1}: {2} on txn={3}" + .format(operation_name, PoliciesOutput.CONSUL_TRANSACTION_URL, + str(ex), json.dumps(txn))) + return None + + if response.status_code != requests.codes.ok: + ctx.logger.error( + "failed {0} for {1} {2}: text={3} txn={4}" + .format(response.status_code, operation_name, + PoliciesOutput.CONSUL_TRANSACTION_URL, response.text, json.dumps(txn))) + return None + ctx.logger.info( + "response {0} for {1} {2}: text={3} txn={4}" + .format(response.status_code, operation_name, + PoliciesOutput.CONSUL_TRANSACTION_URL, response.text, json.dumps(txn))) + return True + + + @staticmethod + def store_policies(action, policy_bodies): + """put the policy_bodies for service_component_name into consul-kv""" + service_component_name = ctx.instance.runtime_properties.get( + PoliciesOutput.SERVICE_COMPONENT_NAME + ) + if not service_component_name: + ctx.logger.warn("failed to find service_component_name to store_policies in consul-kv") + return False + + event = { + "action": action, + "timestamp": (datetime.utcnow().isoformat()[:-3] + 'Z'), + "update_id": str(uuid.uuid4()), + "policies_count": len(policy_bodies) + } + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = event + + store_policies = [ + PoliciesOutput._gen_txn_operation(PoliciesOutput.OPERATION_SET, service_component_name, + "items/" + policy_id, json.dumps(policy_body)) + for policy_id, policy_body in policy_bodies.iteritems() + ] + txn = [ + PoliciesOutput._gen_txn_operation( + PoliciesOutput.OPERATION_DELETE_FOLDER, service_component_name), + PoliciesOutput._gen_txn_operation( + PoliciesOutput.OPERATION_SET, service_component_name, "event", json.dumps(event)) + ] + idx_step = PoliciesOutput.MAX_OPS_PER_TXN - len(txn) + for idx in xrange(0, len(store_policies), idx_step): + txn += store_policies[idx : idx + idx_step] + if not PoliciesOutput._run_transaction("store_policies", txn): + return False + txn = [] + + PoliciesOutput._run_transaction("store_policies", txn) + return True + + + @staticmethod + def delete_policies(): + """delete policies for service_component_name in consul-kv""" + if PoliciesOutput.POLICIES_EVENT not in ctx.instance.runtime_properties: + return + + service_component_name = ctx.instance.runtime_properties.get( + PoliciesOutput.SERVICE_COMPONENT_NAME + ) + if not service_component_name: + ctx.logger.warn("failed to find service_component_name to delete_policies in consul-kv") + return + + delete_policies = [ + PoliciesOutput._gen_txn_operation( + PoliciesOutput.OPERATION_DELETE_FOLDER, service_component_name + ) + ] + PoliciesOutput._run_transaction("delete_policies", delete_policies) diff --git a/onap-dcae-dcaepolicy-lib/pom.xml b/onap-dcae-dcaepolicy-lib/pom.xml index d4a7a74..b7ae319 100644 --- a/onap-dcae-dcaepolicy-lib/pom.xml +++ b/onap-dcae-dcaepolicy-lib/pom.xml @@ -1,7 +1,7 @@ <?xml version="1.0"?> <!-- ================================================================================ -Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. ================================================================================ Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. @@ -28,7 +28,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. <groupId>org.onap.dcaegen2.utils</groupId> <artifactId>onap-dcae-dcaepolicy-lib</artifactId> <name>dcaegen2-utils-onap-dcae-dcaepolicy-lib</name> - <version>1.1.0-SNAPSHOT</version> + <version>2.4.0-SNAPSHOT</version> <url>http://maven.apache.org</url> <properties> @@ -38,9 +38,12 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. <!-- sonar.host.url>http://localhost:9000</sonar.host.url --> <!-- below are language dependent --> <!-- for Python --> + <sonar.junit.reportsPath>xunit-results.xml</sonar.junit.reportsPath> + <sonar.python.coverage.reportPath>coverage.xml</sonar.python.coverage.reportPath> <sonar.language>py</sonar.language> <sonar.pluginName>Python</sonar.pluginName> <sonar.inclusions>**/*.py</sonar.inclusions> + <sonar.exclusions>tests/*,setup.py</sonar.exclusions> <!-- for JavaScaript --> <!-- <sonar.language>js</sonar.language> diff --git a/onap-dcae-dcaepolicy-lib/setup.py b/onap-dcae-dcaepolicy-lib/setup.py index 7a4e71a..a2ff93f 100644 --- a/onap-dcae-dcaepolicy-lib/setup.py +++ b/onap-dcae-dcaepolicy-lib/setup.py @@ -1,8 +1,5 @@ -"""setup.py is used for package build and distribution""" - -# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,12 +16,14 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +"""setup.py is used for package build and distribution""" + from setuptools import setup, find_packages setup( name='onap-dcae-dcaepolicy-lib', description='lib of policy decorators to be used by cloudify plugins of dcae controller', - version="1.0.0", + version="2.4.0", author='Alex Shatov', author_email="alexs@att.com", license='Apache 2', @@ -33,7 +32,6 @@ setup( ], keywords='onap policy dcae controller cloudify plugin', classifiers=[ - 'Development Status :: 4 - Beta', 'Intended Audience :: Telecommunications Industry', 'Programming Language :: Python :: 2.7' ] diff --git a/onap-dcae-dcaepolicy-lib/tests/__init__.py b/onap-dcae-dcaepolicy-lib/tests/__init__.py new file mode 100644 index 0000000..6c84c78 --- /dev/null +++ b/onap-dcae-dcaepolicy-lib/tests/__init__.py @@ -0,0 +1,19 @@ +# ================================================================================ +# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved. +# ================================================================================ +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# ============LICENSE_END========================================================= +# +# ECOMP is a trademark and service mark of AT&T Intellectual Property. + +"""unit test the onap-dcae-dcaepolicy-lib""" diff --git a/onap-dcae-dcaepolicy-lib/tests/log_ctx.py b/onap-dcae-dcaepolicy-lib/tests/log_ctx.py index 9f5464d..0d82687 100644 --- a/onap-dcae-dcaepolicy-lib/tests/log_ctx.py +++ b/onap-dcae-dcaepolicy-lib/tests/log_ctx.py @@ -1,8 +1,5 @@ -""":@CtxLogger.log_ctx: decorator for logging the cloudify ctx before and after operation""" - -# org.onap.dcae # ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,7 +16,10 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +""":@CtxLogger.log_ctx: decorator for logging the cloudify ctx before and after operation""" + import json +import traceback from functools import wraps from cloudify import ctx @@ -102,7 +102,8 @@ class CtxLogger(object): ctx.logger.info("{0} context: {1}".format(\ func_name, json.dumps(CtxLogger.get_ctx_info()))) except Exception as ex: - ctx.logger.error("Failed to log the node context: {0}".format(str(ex))) + ctx.logger.error("Failed to log the node context: {0}: {1}" \ + .format(str(ex), traceback.format_exc())) @staticmethod def log_ctx(pre_log=True, after_log=False, exe_task=None): @@ -119,8 +120,8 @@ class CtxLogger(object): if ctx.type == NODE_INSTANCE and exe_task: ctx.instance.runtime_properties[exe_task] = func.__name__ except Exception as ex: - ctx.logger.error("Failed to set exe_task {0}: {1}".format(\ - exe_task, str(ex))) + ctx.logger.error("Failed to set exe_task {0}: {1}: {2}" \ + .format(exe_task, str(ex), traceback.format_exc())) if pre_log: CtxLogger.log_ctx_info('before ' + func.__name__) diff --git a/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py b/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py index 0c130c0..50aa46e 100644 --- a/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py +++ b/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py @@ -1,8 +1,5 @@ - # ============LICENSE_START======================================================= -# org.onap.dcae -# ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -19,6 +16,8 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +"""mock of cloudify context with relationships and type_hierrarchy""" + from cloudify.mocks import MockCloudifyContext, MockNodeInstanceContext, MockNodeContext TARGET_NODE_ID = "target_node_id" diff --git a/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py b/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py index 29b192c..4b3e39c 100644 --- a/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py +++ b/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py @@ -1,7 +1,5 @@ # ============LICENSE_START======================================================= -# org.onap.dcae -# ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. +# Copyright (c) 2017-2018 AT&T Intellectual Property. All rights reserved. # ================================================================================ # Licensed under the Apache License, Version 2.0 (the "License"); # you may not use this file except in compliance with the License. @@ -18,31 +16,43 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +"""tests of decorators around the cloudify operations to handle policy actions""" + +import copy import json import logging from datetime import datetime, timedelta +from functools import wraps import pytest - +import requests from cloudify import ctx -from cloudify.state import current_ctx from cloudify.exceptions import NonRecoverableError +from cloudify.state import current_ctx -from mock_cloudify_ctx import MockCloudifyContextFull, TARGET_NODE_ID, TARGET_NODE_NAME -from log_ctx import CtxLogger - -from onap_dcae_dcaepolicy_lib import Policies, POLICIES +from onap_dcae_dcaepolicy_lib import dcae_policy +from onap_dcae_dcaepolicy_lib.dcae_policy import Policies +from onap_dcae_dcaepolicy_lib.policies_output import PoliciesOutput +from tests.log_ctx import CtxLogger +from tests.mock_cloudify_ctx import (TARGET_NODE_ID, TARGET_NODE_NAME, + MockCloudifyContextFull) -DCAE_POLICY_TYPE = 'dcae.nodes.policy' POLICY_ID = 'policy_id' POLICY_VERSION = "policyVersion" POLICY_NAME = "policyName" POLICY_BODY = 'policy_body' POLICY_CONFIG = 'config' +CONFIG_NAME = "ConfigName" + MONKEYED_POLICY_ID = 'monkeyed.Config_peach' MONKEYED_POLICY_ID_2 = 'monkeyed.Config_peach_2' +MONKEYED_POLICY_ID_M = 'monkeyed.Config_multi' +MONKEYED_POLICY_ID_M_2 = 'monkeyed.Config_multi_2' +MONKEYED_POLICY_ID_M_3 = 'monkeyed.Config_multi_3' +MONKEYED_POLICY_ID_B = 'monkeyed.Config_both' APPLICATION_CONFIG = "application_config" -LOG_FILE = 'test_onap_dcae_dcaepolicy_lib.log' +LOG_FILE = 'logs/test_onap_dcae_dcaepolicy_lib.log' +LOREM_IPSUM = """Lorem ipsum dolor sit amet consectetur ametist""".split() RUN_TS = datetime.utcnow() @@ -66,20 +76,28 @@ class MonkeyedLogHandler(object): class MonkeyedPolicyBody(object): """policy body that policy-engine returns""" @staticmethod - def create_policy_body(policy_id, policy_version=1): + def create_policy_body(policy_id, policy_version=1, priority=None, **kwargs): """returns a fake policy-body""" prev_ver = policy_version - 1 timestamp = RUN_TS + timedelta(hours=prev_ver) - prev_ver = str(prev_ver) this_ver = str(policy_version) config = { - "policy_updated_from_ver": prev_ver, + "policy_updated_from_ver": str(prev_ver), "policy_updated_to_ver": this_ver, - "policy_hello": "world!", + "policy_hello": LOREM_IPSUM[prev_ver % len(LOREM_IPSUM)], "policy_updated_ts": timestamp.isoformat()[:-3] + 'Z', "updated_policy_id": policy_id } + config.update(copy.deepcopy(kwargs) or {}) + + matching_conditions = { + "ONAPName": "DCAE", + CONFIG_NAME: "alex_config_name" + } + if priority is not None: + matching_conditions["priority"] = priority + return { "policyConfigMessage": "Config Retrieved! ", "policyConfigStatus": "CONFIG_RETRIEVED", @@ -87,23 +105,29 @@ class MonkeyedPolicyBody(object): POLICY_NAME: "{0}.{1}.xml".format(policy_id, this_ver), POLICY_VERSION: this_ver, POLICY_CONFIG: config, - "matchingConditions": { - "ECOMPName": "DCAE", - "ConfigName": "alex_config_name" - }, + "matchingConditions": matching_conditions, "responseAttributes": {}, "property": None } @staticmethod - def create_policy(policy_id, policy_version=1): + def create_policy_bare(policy_id, policy_version=1, priority=None, **kwargs): """returns the whole policy object for policy_id and policy_version""" return { - POLICY_ID : policy_id, - POLICY_BODY : MonkeyedPolicyBody.create_policy_body(policy_id, policy_version) + POLICY_ID: policy_id, + POLICY_BODY: MonkeyedPolicyBody.create_policy_body( + policy_id, policy_version, priority, **kwargs) } @staticmethod + def create_policy(policy_id, policy_version=1, policy_persistent=True, priority=None, **kwargs): + """returns the whole policy object for policy_id and policy_version""" + policy = MonkeyedPolicyBody.create_policy_bare( + policy_id, policy_version, priority, **kwargs) + policy[dcae_policy.POLICY_PERSISTENT] = bool(policy_persistent) + return policy + + @staticmethod def is_the_same_dict(policy_body_1, policy_body_2): """check whether both policy_body objects are the same""" if not isinstance(policy_body_1, dict) or not isinstance(policy_body_2, dict): @@ -111,23 +135,25 @@ class MonkeyedPolicyBody(object): for key in policy_body_1.keys(): if key not in policy_body_2: return False - if isinstance(policy_body_1[key], dict): - return MonkeyedPolicyBody.is_the_same_dict( - policy_body_1[key], policy_body_2[key]) - if (policy_body_1[key] is None and policy_body_2[key] is not None) \ - or (policy_body_1[key] is not None and policy_body_2[key] is None) \ - or (policy_body_1[key] != policy_body_2[key]): + + val_1 = policy_body_1[key] + val_2 = policy_body_2[key] + if isinstance(val_1, dict) \ + and not MonkeyedPolicyBody.is_the_same_dict(val_1, val_2): + return False + if (val_1 is None and val_2 is not None) \ + or (val_1 is not None and val_2 is None) \ + or (val_1 != val_2): return False return True - class MonkeyedNode(object): """node in cloudify""" BLUEPRINT_ID = 'test_dcae_policy_bp_id' DEPLOYMENT_ID = 'test_dcae_policy_dpl_id' EXECUTION_ID = 'test_dcae_policy_exe_id' - def __init__(self, node_id, node_name, node_type, properties, + def __init__(self, node_id, node_name, node_type, properties=None, relationships=None, runtime_properties=None): self.node_id = node_id self.node_name = node_name @@ -142,121 +168,1119 @@ class MonkeyedNode(object): relationships=relationships, runtime_properties=runtime_properties ) - MonkeyedLogHandler.add_handler_to(self.ctx.logger) + +class MonkeyedResponse(object): + """Monkey response""" + def __init__(self, full_path, headers=None, resp_json=None): + self.full_path = full_path + self.status_code = 200 + self.headers = headers or {} + self.resp_json = resp_json + self.text = json.dumps(resp_json or {}) + + def json(self): + """returns json of response""" + return self.resp_json + + def raise_for_status(self): + """always happy""" + pass + +def get_app_config(): + """just get the config""" + config = copy.deepcopy(dict(ctx.instance.runtime_properties.get(APPLICATION_CONFIG, {}))) + if not config: + config = copy.deepcopy(dict(ctx.node.properties.get(APPLICATION_CONFIG, {}))) + return config + +def operation_node_configure(**kwargs): + """do the node-configure operation""" + ctx.logger.info("operation_node_configure kwargs: {0}".format(kwargs)) + + app_config = get_app_config() + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config + ctx.instance.runtime_properties[PoliciesOutput.SERVICE_COMPONENT_NAME] = "unit_test_scn" + ctx.logger.info("property app_config: {0}".format(json.dumps(app_config))) @CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='exe_task') -@Policies.gather_policies_to_node +@Policies.gather_policies_to_node() def node_configure(**kwargs): """decorate with @Policies.gather_policies_to_node on policy consumer node to bring all policies to runtime_properties["policies"] """ - ctx.logger.info("node_configure kwargs: {0}".format(kwargs)) + operation_node_configure(**kwargs) - app_config = None - if APPLICATION_CONFIG in ctx.node.properties: - # dockerized blueprint puts the app config into property application_config - app_config = ctx.node.properties.get(APPLICATION_CONFIG) +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='execute_operation') +@Policies.update_policies_on_node() +def policy_update(updated_policies, removed_policies=None, **kwargs): + """decorate with @Policies.update_policies_on_node() to update runtime_properties["policies"] - app_config = Policies.shallow_merge_policies_into(app_config) - ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config - ctx.logger.info("example: applied policy_configs to property app_config: {0}" \ - .format(json.dumps(app_config))) + :updated_policies: contains the list of changed policy-configs when configs_only=True (default). + """ + # This is how to merge the policies into default app_config object + # (runtime_properties[APPLICATION_CONFIG] = application_config) + app_config = get_app_config() + ctx.logger.info("app_config {0}".format(json.dumps(app_config))) - policy_configs = Policies.get_policy_configs() - if policy_configs: - ctx.logger.warn("TBD: apply policy_configs: {0}".format(json.dumps(policy_configs))) + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config @CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='execute_operation') -@Policies.update_policies_on_node(configs_only=True) -def policy_update(updated_policies, **kwargs): +@Policies.update_policies_on_node() +def policy_update_not_only_config(updated_policies, removed_policies=None, **kwargs): """decorate with @Policies.update_policies_on_node() to update runtime_properties["policies"] :updated_policies: contains the list of changed policy-configs when configs_only=True (default). - Use configs_only=False to bring the full policy objects in :updated_policies:. - - Use :Policies.shallow_merge_policies_into(): to merge the updated_policies into app_config """ - app_config = ctx.instance.runtime_properties[APPLICATION_CONFIG] + # This is how to merge the policies into default app_config object + # (runtime_properties[APPLICATION_CONFIG] = application_config) + app_config = get_app_config() + ctx.logger.info("app_config {0}".format(json.dumps(app_config))) + + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config - # This is how to merge the policies into app_config object - app_config = Policies.shallow_merge_policies_into(app_config) +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='execute_operation') +@Policies.update_policies_on_node() +def policy_update_many_calcs(updated_policies, removed_policies=None, policies=None, **kwargs): + """decorate with @Policies.update_policies_on_node() to update runtime_properties["policies"] - ctx.logger.info("merged updated_policies {0} into app_config {1}" - .format(json.dumps(updated_policies), json.dumps(app_config))) + :updated_policies: contains the list of changed policy-configs when configs_only=True (default). + """ + app_config = get_app_config() + ctx.logger.info("app_config {0}".format(json.dumps(app_config))) ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='exe_task') +@Policies.cleanup_policies_on_node +def node_delete(**kwargs): + """delete <service_component_name> records in consul-kv""" + operation_node_configure(**kwargs) + + +class CurrentCtx(object): + """cloudify context""" + _node_ms = None + + @staticmethod + def set_current_ctx(include_bad=True, include_good=True): + """set up the nodes for cloudify""" + Policies._init() + + def add_target_to_relationships(relationships, node): + """adds the node as the target of relationships""" + relationships.append({TARGET_NODE_ID: node.node_id, TARGET_NODE_NAME: node.node_name}) + + relationships = [] + if include_good: + node_policy = MonkeyedNode( + 'dcae_policy_node_id', + 'dcae_policy_node_name', + dcae_policy.DCAE_POLICY_TYPE, + {POLICY_ID: MONKEYED_POLICY_ID}, + None, + {POLICY_BODY: MonkeyedPolicyBody.create_policy_body( + MONKEYED_POLICY_ID, priority="1")} + ) + add_target_to_relationships(relationships, node_policy) + + if include_bad: + bad_policy_2 = MonkeyedNode( + 'bad_policy_2_node_id', + 'bad_policy_2_node_name', + dcae_policy.DCAE_POLICY_TYPE, + {POLICY_ID: MONKEYED_POLICY_ID_2}, + None, + None + ) + add_target_to_relationships(relationships, bad_policy_2) + + if include_good: + node_policy_2 = MonkeyedNode( + 'dcae_policy_node_id_2', + 'dcae_policy_node_name_2', + dcae_policy.DCAE_POLICY_TYPE, + {POLICY_ID: MONKEYED_POLICY_ID_2}, + None, + {POLICY_BODY: MonkeyedPolicyBody.create_policy_body( + MONKEYED_POLICY_ID_2, 4, priority="2")} + ) + add_target_to_relationships(relationships, node_policy_2) + + if include_bad: + bad_policy_3 = MonkeyedNode( + 'bad_policy_3_node_id', + 'bad_policy_3_node_name', + dcae_policy.DCAE_POLICY_TYPE, + {POLICY_ID: MONKEYED_POLICY_ID_2}, + None, + None + ) + add_target_to_relationships(relationships, bad_policy_3) + + bad_policy_4 = MonkeyedNode( + 'bad_policy_4_node_id', + 'bad_policy_4_node_name', + dcae_policy.DCAE_POLICY_TYPE, + None, + None, + None + ) + add_target_to_relationships(relationships, bad_policy_4) + + weird_policy_5 = MonkeyedNode( + 'weird_policy_5_node_id', + 'weird_policy_5_node_name', + dcae_policy.DCAE_POLICY_TYPE, + None, + None, + {POLICY_BODY: MonkeyedPolicyBody.create_policy_body( + MONKEYED_POLICY_ID, 3, priority="2")} + ) + add_target_to_relationships(relationships, weird_policy_5) + + if include_good: + node_policies = MonkeyedNode( + 'dcae_policies_node_id', + 'dcae_policies_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: { + POLICY_NAME: MONKEYED_POLICY_ID_M + ".*", + dcae_policy.CONFIG_ATTRIBUTES: json.dumps({ + CONFIG_NAME: "alex_config_name" + }) + }}, + None, + {dcae_policy.POLICIES_FILTERED: { + MONKEYED_POLICY_ID_M: + MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_M, 3)}} + ) + add_target_to_relationships(relationships, node_policies) + + node_policies_2 = MonkeyedNode( + 'dcae_policies_2_node_id', + 'dcae_policies_2_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: {POLICY_NAME: MONKEYED_POLICY_ID_M + ".*"}}, + None, + {dcae_policy.POLICIES_FILTERED: { + MONKEYED_POLICY_ID_M: + MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_M, 2)}} + ) + add_target_to_relationships(relationships, node_policies_2) + + policies_empty = MonkeyedNode( + 'dcae_policies_empty_node_id', + 'dcae_policies_empty_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: {"empty": None}}, + None, + None + ) + add_target_to_relationships(relationships, policies_empty) + + policies_empty_2 = MonkeyedNode( + 'dcae_policies_empty_2_node_id', + 'dcae_policies_empty_2_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + None, + None, + None + ) + add_target_to_relationships(relationships, policies_empty_2) + + non_policies = MonkeyedNode( + 'non_policies_node_id', + 'non_policies_node_name', + "non.policy.type" + ) + add_target_to_relationships(relationships, non_policies) + + if include_good: + node_policies_b = MonkeyedNode( + 'dcae_policies_b_node_id', + 'dcae_policies_b_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: {POLICY_NAME: MONKEYED_POLICY_ID_M + ".*"}}, + None, + {dcae_policy.POLICIES_FILTERED: { + MONKEYED_POLICY_ID_B: + MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_B, 1)}} + ) + add_target_to_relationships(relationships, node_policies_b) + + node_policies_b_2 = MonkeyedNode( + 'dcae_policies_b_2_node_id', + 'dcae_policies_b_2_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: {POLICY_NAME: MONKEYED_POLICY_ID_M + ".*"}}, + None, + {dcae_policy.POLICIES_FILTERED: { + MONKEYED_POLICY_ID_B: + MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_B, 2)}} + ) + add_target_to_relationships(relationships, node_policies_b_2) + + if include_good: + node_policy_b = MonkeyedNode( + 'dcae_policy_b_node_id', + 'dcae_policy_b_node_name', + dcae_policy.DCAE_POLICY_TYPE, + {POLICY_ID: MONKEYED_POLICY_ID_B}, + None, + {POLICY_BODY: MonkeyedPolicyBody.create_policy_body( + MONKEYED_POLICY_ID_B, 4, priority="1.5")} + ) + add_target_to_relationships(relationships, node_policy_b) + + node_policies_b_5 = MonkeyedNode( + 'dcae_policies_b_5_node_id', + 'dcae_policies_b_5_node_name', + dcae_policy.DCAE_POLICIES_TYPE, + {dcae_policy.POLICY_FILTER: {POLICY_NAME: MONKEYED_POLICY_ID_M + ".*"}}, + None, + {dcae_policy.POLICIES_FILTERED: { + MONKEYED_POLICY_ID_B: + MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_B, 5)}} + ) + add_target_to_relationships(relationships, node_policies_b_5) + + + CurrentCtx._node_ms = MonkeyedNode( + 'test_ms_id', 'test_ms_name', "ms.nodes.type", + {APPLICATION_CONFIG: MonkeyedPolicyBody.create_policy_body( + "no_policy", db_port="123", weather="snow")[POLICY_CONFIG] + }, + relationships + ) + current_ctx.set(CurrentCtx._node_ms.ctx) + MonkeyedLogHandler.add_handler_to(CurrentCtx._node_ms.ctx.logger) + + @staticmethod + def reset(): + """reset context""" + current_ctx.set(CurrentCtx._node_ms.ctx) + + +def monkeyed_consul_boom(full_path, json): + """boom on monkeypatch for the put to consul""" + raise requests.ConnectionError("monkey-boom") + + +@pytest.fixture() +def fix_consul_boom(monkeypatch): + """monkeyed discovery request.put""" + PoliciesOutput._lazy_inited = False + monkeypatch.setattr('requests.put', monkeyed_consul_boom) + yield fix_consul_boom + PoliciesOutput._lazy_inited = False + + +def monkeyed_consul_put(full_path, json): + """monkeypatch for the put to consul""" + return MonkeyedResponse(full_path) + + +@pytest.fixture() +def fix_consul(monkeypatch): + """monkeyed discovery request.put""" + PoliciesOutput._lazy_inited = False + monkeypatch.setattr('requests.put', monkeyed_consul_put) + yield fix_consul + PoliciesOutput._lazy_inited = False + + +def cfy_ctx(include_bad=True, include_good=True): + """test and safely clean up""" + def cfy_ctx_decorator(func): + """test and safely clean up""" + if not func: + return + + @wraps(func) + def ctx_wrapper(*args, **kwargs): + """test""" + try: + CurrentCtx.set_current_ctx(include_bad, include_good) + + func(*args, **kwargs) + + finally: + ctx.logger.info("MockCloudifyContextFull.clear") + MockCloudifyContextFull.clear() + current_ctx.clear() + + return ctx_wrapper + return cfy_ctx_decorator + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_gather_policies_to_node(): + """test gather_policies_to_node""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) def test_policies_to_node(): - """test gather_policies_to_node and policy_update""" - - node_policy = MonkeyedNode( - 'test_dcae_policy_node_id', - 'test_dcae_policy_node_name', - DCAE_POLICY_TYPE, - {POLICY_ID: MONKEYED_POLICY_ID}, - None, - {POLICY_BODY : MonkeyedPolicyBody.create_policy_body(MONKEYED_POLICY_ID)} - ) - node_policy_2 = MonkeyedNode( - 'test_dcae_policy_node_id_2', - 'test_dcae_policy_node_name_2', - DCAE_POLICY_TYPE, - {POLICY_ID: MONKEYED_POLICY_ID_2}, - None, - {POLICY_BODY : MonkeyedPolicyBody.create_policy_body(MONKEYED_POLICY_ID_2)} - ) - node_ms = MonkeyedNode('test_ms_id', 'test_ms_name', "ms.nodes.type", None, \ - [{TARGET_NODE_ID: node_policy.node_id, TARGET_NODE_NAME: node_policy.node_name}, - {TARGET_NODE_ID: node_policy_2.node_id, TARGET_NODE_NAME: node_policy_2.node_name} - ]) - - try: - current_ctx.set(node_ms.ctx) + """test gather_policies_to_node""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert MONKEYED_POLICY_ID in policies + expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID, priority="1") + policy = policies[MONKEYED_POLICY_ID] + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) + assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) + + assert MONKEYED_POLICY_ID_B in policies + expected_b = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_B, 4, priority="1.5") + policy = policies[MONKEYED_POLICY_ID_B] + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(expected_b))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_b) + assert MonkeyedPolicyBody.is_the_same_dict(expected_b, policy) + + assert MONKEYED_POLICY_ID_2 in policies + expected_2 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 4, priority="2") + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(expected_2))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_2) + assert MonkeyedPolicyBody.is_the_same_dict(expected_2, policy) + + assert MONKEYED_POLICY_ID_M in policies + expected_m = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M, 2, False) + policy = policies[MONKEYED_POLICY_ID_M] + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_M, json.dumps(expected_m))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_m) + assert MonkeyedPolicyBody.is_the_same_dict(expected_m, policy) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_update_policies(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) + assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) + + assert MONKEYED_POLICY_ID in policies + policy = policies[MONKEYED_POLICY_ID] + expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID, priority="1") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) + assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) + + assert MONKEYED_POLICY_ID_B in policies + policy = policies[MONKEYED_POLICY_ID_B] + expected_b = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_B, 4, priority="1.5") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_b) + assert MonkeyedPolicyBody.is_the_same_dict(expected_b, policy) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_update_not_only_config(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update_not_only_config(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) + assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) + + assert MONKEYED_POLICY_ID in policies + policy = policies[MONKEYED_POLICY_ID] + expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID, priority="1") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) + assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) + + assert MONKEYED_POLICY_ID_B in policies + policy = policies[MONKEYED_POLICY_ID_B] + expected_b = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_B, 4, priority="1.5") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_b) + assert MonkeyedPolicyBody.is_the_same_dict(expected_b, policy) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_update_policies_not(): + """test policy_update - ignore all policies with junk params""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + assert APPLICATION_CONFIG in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + app_config = runtime_properties[APPLICATION_CONFIG] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + ctx.logger.info("app_config: {0}".format(json.dumps(app_config))) + + expected_policies = copy.deepcopy(policies) + expected_app_config = copy.deepcopy(app_config) + + existing_policy = MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID, priority="1") + damaged_policy = MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_2) + del damaged_policy[POLICY_BODY][POLICY_CONFIG] + updated_policy = MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_M_3, 3) + added_policy = MonkeyedPolicyBody.create_policy_bare(MONKEYED_POLICY_ID_M_2, 4) + junk_policy_filter_id = "<<<junk_removed_policy_id>>>" + unexpected_removed_policy_id = "<<<junk_removed_policy_id>>>" + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + + assert MONKEYED_POLICY_ID in policies + assert MONKEYED_POLICY_ID_2 in policies + assert MONKEYED_POLICY_ID_M_2 not in policies + assert MONKEYED_POLICY_ID_M_3 not in policies + assert unexpected_removed_policy_id not in policies + assert junk_policy_filter_id not in runtime_properties.get(dcae_policy.POLICY_FILTERS, {}) + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update(updated_policies=[existing_policy, damaged_policy, updated_policy], + added_policies={ + junk_policy_filter_id: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}}, + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_2: damaged_policy}} + }, + removed_policies=[unexpected_removed_policy_id]) + + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + ctx.logger.info("policies not changed: {0}".format(json.dumps(policies))) + assert MonkeyedPolicyBody.is_the_same_dict(policies, expected_policies) + assert MonkeyedPolicyBody.is_the_same_dict(expected_policies, policies) + + ctx.logger.info("app_config not changed: {0}".format(json.dumps(app_config))) + assert MonkeyedPolicyBody.is_the_same_dict(app_config, expected_app_config) + assert MonkeyedPolicyBody.is_the_same_dict(expected_app_config, app_config) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_update_many_calcs(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update_many_calcs(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) + assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) + + assert MONKEYED_POLICY_ID in policies + policy = policies[MONKEYED_POLICY_ID] + expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID, priority="1") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) + assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) + + assert MONKEYED_POLICY_ID_B in policies + policy = policies[MONKEYED_POLICY_ID_B] + expected_b = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_B, 4, priority="1.5") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_b) + assert MonkeyedPolicyBody.is_the_same_dict(expected_b, policy) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_remove_all_policies(): + """test policy_update - remove all policies""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + remove_policy_ids = policies.keys() + + policy_update(updated_policies=None, added_policies=None, removed_policies=remove_policy_ids) + + ctx.logger.info("removed: {0}".format(remove_policy_ids)) + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + assert dcae_policy.POLICIES in runtime_properties + assert Policies.get_policy_bodies() == [] + + assert APPLICATION_CONFIG in runtime_properties + assert APPLICATION_CONFIG in ctx.node.properties + app_config = runtime_properties[APPLICATION_CONFIG] + expected_config = dict(ctx.node.properties[APPLICATION_CONFIG]) + ctx.logger.info("expected = default application_config: {0}".format(json.dumps(app_config))) + assert MonkeyedPolicyBody.is_the_same_dict(app_config, expected_config) + assert MonkeyedPolicyBody.is_the_same_dict(expected_config, app_config) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_remove_all_policies_twice(): + """test policy_update - remove all policies twice""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + remove_policy_ids = policies.keys() + + policy_update(updated_policies=None, added_policies=None, removed_policies=remove_policy_ids) + policy_update(updated_policies=None, added_policies=None, removed_policies=remove_policy_ids) + + ctx.logger.info("removed: {0}".format(remove_policy_ids)) + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + assert dcae_policy.POLICIES in runtime_properties + assert Policies.get_policy_bodies() == [] + + assert APPLICATION_CONFIG in runtime_properties + assert APPLICATION_CONFIG in ctx.node.properties + app_config = runtime_properties[APPLICATION_CONFIG] + expected_config = dict(ctx.node.properties[APPLICATION_CONFIG]) + ctx.logger.info("expected = default application_config: {0}".format(json.dumps(app_config))) + assert MonkeyedPolicyBody.is_the_same_dict(app_config, expected_config) + assert MonkeyedPolicyBody.is_the_same_dict(expected_config, app_config) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_remove_then_update(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + remove_policy_ids = policies.keys() + policy_update(updated_policies=None, added_policies=None, removed_policies=remove_policy_ids) + + updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) + assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) + + assert MONKEYED_POLICY_ID in policies + assert MONKEYED_POLICY_ID_B in policies + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_remove_update_many_calcs(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + remove_policy_ids = policies.keys() + policy_update_many_calcs(updated_policies=None, + added_policies=None, + removed_policies=remove_policy_ids) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) + + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update_many_calcs(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) + assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) + + assert MONKEYED_POLICY_ID in policies + assert MONKEYED_POLICY_ID_B in policies + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_bad_update_many_calcs(): + """test policy_update""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + damaged_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2, priority="aa20") + damaged_policy[POLICY_BODY][POLICY_CONFIG] = ["damaged config"] + + added_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_M_2, 2, + False, priority="1") + added_policy[POLICY_BODY][POLICY_CONFIG] = {"unexpected":"foo", "none": None} + + ctx.logger.info("policy_update: [{0}]".format(json.dumps(damaged_policy))) + + ctx.logger.info("policy[{0}]: not yet in policies".format(MONKEYED_POLICY_ID_M_2)) + assert MONKEYED_POLICY_ID_M_2 not in policies + + policy_filter_ids = runtime_properties.get(dcae_policy.POLICY_FILTERS, {}).keys() or ["--"] + + policy_update_many_calcs(updated_policies=[damaged_policy], + added_policies={ + policy_filter_ids[0]: { + dcae_policy.POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}} + }, + removed_policies=[MONKEYED_POLICY_ID_M]) + + ctx.logger.info("policy[{0}]: removed".format(MONKEYED_POLICY_ID_M)) + assert MONKEYED_POLICY_ID_M not in policies + + assert MONKEYED_POLICY_ID_M_2 in policies + policy = policies[MONKEYED_POLICY_ID_M_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_M_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, added_policy) + assert MonkeyedPolicyBody.is_the_same_dict(added_policy, policy) + + assert MONKEYED_POLICY_ID_2 in policies + policy = policies[MONKEYED_POLICY_ID_2] + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, damaged_policy) + assert MonkeyedPolicyBody.is_the_same_dict(damaged_policy, policy) + + assert MONKEYED_POLICY_ID in policies + policy = policies[MONKEYED_POLICY_ID] + expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID, priority="1") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) + assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) + + assert MONKEYED_POLICY_ID_B in policies + policy = policies[MONKEYED_POLICY_ID_B] + expected_b = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_B, 4, priority="1.5") + ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(expected_1))) + ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_B, json.dumps(policy))) + assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_b) + assert MonkeyedPolicyBody.is_the_same_dict(expected_b, policy) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True, include_good=False) +def test_bad_policies(): + """test bad policy nodes""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True, include_good=False) +def test_wrong_ctx_node_configure(): + """test wrong ctx""" + current_ctx.set(ctx.instance.relationships[0]) + ctx_type = ctx.type + + with pytest.raises(NonRecoverableError) as excinfo: + node_configure() + + CurrentCtx.reset() + ctx.logger.info("{0} not a node boom: {1}".format(ctx_type, str(excinfo.value))) + assert ctx_type == 'cloudify.relationships.depends_on' + assert str(excinfo.value) == "can only invoke gather_policies_to_node on node" + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True, include_good=False) +def test_wrong_ctx_policy_update(): + """test wrong ctx""" + no_policies = Policies.get_policy_bodies() + current_ctx.set(ctx.instance.relationships[0]) + ctx_type = ctx.type + + with pytest.raises(NonRecoverableError) as excinfo: + policy_update(updated_policies=None, added_policies=None, removed_policies=None) + + CurrentCtx.reset() + ctx.logger.info("{0} not a node boom: {1}".format(ctx_type, str(excinfo.value))) + assert ctx_type == 'cloudify.relationships.depends_on' + assert no_policies == [] + assert str(excinfo.value) == "can only invoke update_policies_on_node on node" + +def test_defenses_on_decorators(): + """test defenses of code mainly for code coverage""" + should_be_none = Policies.gather_policies_to_node()(None) + assert should_be_none is None + + should_be_none = Policies.update_policies_on_node()(None) + assert should_be_none is None + +def monkeyed_set_policies_boom(policies): + """monkeypatch for exception""" + raise Exception("Policies._set_policies boom: {0}".format(json.dumps(policies or {}))) + +@pytest.fixture() +def fix_boom_gather(monkeypatch): + """monkeyed boom""" + monkeypatch.setattr('onap_dcae_dcaepolicy_lib.dcae_policy.Policies._set_policies', + monkeyed_set_policies_boom) + return fix_boom_gather # provide the fixture value + +@cfy_ctx(include_bad=True, include_good=False) +@pytest.mark.usefixtures("fix_boom_gather") +def test_exception_on_gather(): + """test exception on gather""" + with pytest.raises(NonRecoverableError) as excinfo: node_configure() - runtime_properties = ctx.instance.runtime_properties - ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) - - assert POLICIES in runtime_properties - policies = runtime_properties[POLICIES] - ctx.logger.info("policies: {0}".format(json.dumps(policies))) - - assert MONKEYED_POLICY_ID in policies - expected_1 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID) - policy = policies[MONKEYED_POLICY_ID] - ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) - ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) - assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) - assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) - - assert MONKEYED_POLICY_ID_2 in policies - expected_2 = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2) - policy = policies[MONKEYED_POLICY_ID_2] - ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(expected_2))) - ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) - assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_2) - assert MonkeyedPolicyBody.is_the_same_dict(expected_2, policy) - - updated_policy = MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID_2, 2) - ctx.logger.info("policy_update: [{0}]".format(json.dumps(updated_policy))) - - policy_update([updated_policy]) - - assert MONKEYED_POLICY_ID_2 in policies - policy = policies[MONKEYED_POLICY_ID_2] - ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID_2, json.dumps(policy))) - assert MonkeyedPolicyBody.is_the_same_dict(policy, updated_policy) - assert MonkeyedPolicyBody.is_the_same_dict(updated_policy, policy) - - assert MONKEYED_POLICY_ID in policies - policy = policies[MONKEYED_POLICY_ID] - ctx.logger.info("expected[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(expected_1))) - ctx.logger.info("policy[{0}]: {1}".format(MONKEYED_POLICY_ID, json.dumps(policy))) - assert MonkeyedPolicyBody.is_the_same_dict(policy, expected_1) - assert MonkeyedPolicyBody.is_the_same_dict(expected_1, policy) - - finally: - MockCloudifyContextFull.clear() - current_ctx.clear() + ctx.logger.info("monkeyed_set_policies_boom: {0}".format(str(excinfo.value))) + assert str(excinfo.value).startswith("Failed to set the policies ") + +def monkeyed_update_policies_boom(policies): + """monkeypatch for exception""" + raise Exception("Policies._update_policies boom") + +@pytest.fixture() +def fix_boom_update_policies(monkeypatch): + """monkeyed boom""" + monkeypatch.setattr('onap_dcae_dcaepolicy_lib.dcae_policy.Policies._update_policies', + monkeyed_update_policies_boom) + return fix_boom_update_policies # provide the fixture value + +@cfy_ctx(include_bad=True, include_good=False) +@pytest.mark.usefixtures("fix_boom_update_policies") +def test_exception_on_update(): + """test exception on update_policies""" + with pytest.raises(NonRecoverableError) as excinfo: + policy_update(updated_policies=None, added_policies=None, removed_policies=None) + + ctx.logger.info("monkeyed_update_policies_boom: {0}".format(str(excinfo.value))) + assert str(excinfo.value).startswith("Failed to update the policies ") + +@cfy_ctx(include_bad=True, include_good=False) +def test_defenses_on_policy_update(): + """test defenses on policy_update""" + policy_update(updated_policies=None, added_policies=None, removed_policies=None) + ctx.logger.info("policy_update() ok") + +@cfy_ctx(include_bad=True, include_good=False) +def test_defenses_on_set_policies(): + """test defenses of code mainly for code coverage""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + Policies._set_policies({}) + + assert dcae_policy.POLICIES not in runtime_properties + + Policies._set_policies({}) + + assert dcae_policy.POLICIES not in runtime_properties + +@pytest.mark.usefixtures("fix_consul") +@cfy_ctx(include_bad=True) +def test_delete_node(): + """test delete""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + node_delete() + + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_node_no_consul(): + """test delete without consul""" + node_configure() + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES in runtime_properties + policies = runtime_properties[dcae_policy.POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + node_delete() + + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_node_no_policies(): + """test delete without consul and setup""" + + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = {} + ctx.instance.runtime_properties[PoliciesOutput.SERVICE_COMPONENT_NAME] = "delete_node_empty" + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_node_empty(): + """test delete without consul and setup""" + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_node_lost_scn(): + """test delete without consul and setup""" + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = {} + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_node_empty_config(): + """test delete without consul and setup""" + + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = {} + ctx.instance.runtime_properties[PoliciesOutput.SERVICE_COMPONENT_NAME] = "delete_node_empty" + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_ms_no_consul_addr(): + """test delete without consul and setup""" + + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = {} + ctx.instance.runtime_properties[PoliciesOutput.SERVICE_COMPONENT_NAME] = "delete_node_empty" + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + + +@pytest.mark.usefixtures("fix_consul_boom") +@cfy_ctx(include_bad=True) +def test_delete_bad_config(): + """test delete without consul and setup""" + + ctx.instance.runtime_properties[PoliciesOutput.POLICIES_EVENT] = {} + ctx.instance.runtime_properties[PoliciesOutput.SERVICE_COMPONENT_NAME] = "delete_node_empty" + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + assert dcae_policy.POLICIES not in runtime_properties + + node_delete() + diff --git a/onap-dcae-dcaepolicy-lib/tox-local.ini b/onap-dcae-dcaepolicy-lib/tox-local.ini index ed9879a..41d5708 100644 --- a/onap-dcae-dcaepolicy-lib/tox-local.ini +++ b/onap-dcae-dcaepolicy-lib/tox-local.ini @@ -1,3 +1,4 @@ +# tox -c tox-local.ini | tee -a logs/test_onap_dcae_dcaepolicy_lib.log 2>&1 [tox] envlist = py27 @@ -12,5 +13,3 @@ setenv = PYTHONPATH={toxinidir} # recreate = True commands=pytest -v --cov onap_dcae_dcaepolicy_lib --cov-report html - - diff --git a/onap-dcae-dcaepolicy-lib/tox.ini b/onap-dcae-dcaepolicy-lib/tox.ini index 78af290..9bace46 100644 --- a/onap-dcae-dcaepolicy-lib/tox.ini +++ b/onap-dcae-dcaepolicy-lib/tox.ini @@ -11,4 +11,11 @@ deps= pytest-cov setenv = PYTHONPATH={toxinidir} -commands=pytest --junitxml xunit-reports/xunit-result-onap_dcae_dcaepolicy_lib.xml --cov onap_dcae_dcaepolicy_lib --cov-report=xml +whitelist_externals= + rm + mkdir +commands= + rm -rf {toxinidir}/logs + mkdir {toxinidir}/logs + pytest --junitxml xunit-results.xml --cov onap_dcae_dcaepolicy_lib --cov-report xml + coverage xml |