From 90f21051fc4347a84a2c079c994e03abbfec5c62 Mon Sep 17 00:00:00 2001 From: Alex Shatov Date: Wed, 10 Jan 2018 11:39:32 -0500 Subject: variable collection of policies per component * new feature variable collection of policies per component in DCAE * massive refactoring * Unit Test coverage 100% * moved module docstring below the license text Change-Id: I5ba392cb5c42ec136306772163c370d64974ae3c Issue-ID: DCAEGEN2-249 Signed-off-by: Alex Shatov --- .../onap_dcae_dcaepolicy_lib/__init__.py | 4 +- .../onap_dcae_dcaepolicy_lib/dcae_policy.py | 556 ++++++-- .../onap_dcae_dcaepolicy_lib/utils.py | 121 ++ onap-dcae-dcaepolicy-lib/pom.xml | 2 +- onap-dcae-dcaepolicy-lib/setup.py | 6 +- onap-dcae-dcaepolicy-lib/tests/__init__.py | 20 + onap-dcae-dcaepolicy-lib/tests/log_ctx.py | 12 +- .../tests/mock_cloudify_ctx.py | 3 +- onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py | 1391 ++++++++++++++++++-- onap-dcae-dcaepolicy-lib/tox-local.ini | 3 +- 10 files changed, 1888 insertions(+), 230 deletions(-) create mode 100644 onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/utils.py create mode 100644 onap-dcae-dcaepolicy-lib/tests/__init__.py 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..cebaa39 100644 --- a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py +++ b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/__init__.py @@ -1,5 +1,3 @@ -"""expose the Policies class on the package level""" - # org.onap.dcae # ================================================================================ # Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. @@ -19,4 +17,6 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. +"""expose the Policies class on the package level""" + from .dcae_policy import Policies, POLICIES, POLICY_MESSAGE_TYPE 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..740f736 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,5 +1,3 @@ -"""dcae_policy contains decorators for the policy lifecycle in cloudify""" - # org.onap.dcae # ================================================================================ # Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. @@ -19,111 +17,369 @@ # # 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 +from copy import deepcopy from functools import wraps +import traceback from cloudify import ctx from cloudify.context import NODE_INSTANCE from cloudify.exceptions import NonRecoverableError +from .utils import Utils + POLICIES = 'policies' +POLICY_FILTERS = 'policy_filters' +POLICIES_FILTERED = 'policies_filtered' +POLICY_APPLY_ORDER = 'policy_apply_order' +POLICY_APPLY_ORDER_CLAUSE = 'policy_apply_order_clause' +POLICY_DEFAULTED_FIELDS = 'policy_defaulted_fields' POLICY_ID = 'policy_id' POLICY_BODY = 'policy_body' POLICY_VERSION = "policyVersion" POLICY_CONFIG = 'config' +APPLICATION_CONFIG = 'application_config' +POLICY_FILTER = 'policy_filter' +POLICY_FILTER_ID = 'policy_filter_id' + +POLICY_PERSISTENT = 'policy_persistent' DCAE_POLICY_TYPE = 'dcae.nodes.policy' +DCAE_POLICIES_TYPE = 'dcae.nodes.policies' POLICY_MESSAGE_TYPE = 'policy' 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 _get_config_from_policy(policy): + """returns the config field from policy object""" + return (policy or {}).get(POLICY_BODY, {}).get(POLICY_CONFIG) + + @staticmethod + def _store_policy_apply_order_clause(policy_apply_order_path): + """ + Find the field :policy_apply_order_path: that is provided as an optional parameter + to gather_policies_to_node decorator. - Call Policies.shallow_merge_policies_into(config) to merge the policies into config. + Parse the field-pathes found in the :policy_apply_order_path: field. + + Store the result into :runtime_properties[POLICY_APPLY_ORDER_CLAUSE]: + + Example: + policy_apply_order_path = "docker_config:policy:apply_order" + will take the list of field-pathes from the field + properties: + docker_config: + policy: + apply_order: """ - 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 + if not policy_apply_order_path: + return + + policy_apply_order_clause = Utils.get_field_value( + dict(ctx.node.properties), + policy_apply_order_path + ) - if not func: + if not policy_apply_order_clause: + ctx.logger.warn("not found policy_apply_order_path: {0} in node.properties" + .format(policy_apply_order_path)) return - @wraps(func) - def wrapper(*args, **kwargs): - """gather and save the policies from dcae.nodes.policy nodes this node related to""" - 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 + ctx.instance.runtime_properties[POLICY_APPLY_ORDER_CLAUSE] = policy_apply_order_clause @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 _set_policy_apply_order(policies): + """ + Calculates, sorts and stores the policy_apply_order for policies. + + Sorting is done based on the list of field-pathes + specified in :runtime_properties[POLICY_APPLY_ORDER_CLAUSE]: + + The apply_order field is expected to be formatted as the list of strings. + Each string can contain the path to the field inside the policy_body object + with the same delimiter of ":" (semicolon). + To convert the field to decimal, use ::number suffix after the field name. + To specify the descending order, add "desc" after the whitespace. + + Example: + apply_order = ["matchingConditions:priority::number desc", + "config:foo desc nulls-last", "config:db_client"] + + this will return the policies starting with the highest decimal value in "priority" + field inside the "matchingConditions". + Then the policies with the same "priority" values will be sorted in descending order + by the string value in the field "foo" found inside the "config". + Then the policies with the same "priority" and "foo" values will be sorted + by the string value in the field "db_client" found inside the "config". + Then the policies with the same "priority" and "foo" and "db_client" field values will + further be always sorted by "policy_id" - no need to specify that in apply_order. + Sorting by policy_id insures the uniqueness and predictability of the policy apply_order. + + An invalid field-path will result in the value of None that brings this record upfront. + """ + policy_apply_order = [policy_id + for (policy_id, policy) in policies.iteritems() + if Policies._get_config_from_policy(policy)] + + if not policy_apply_order: + ctx.instance.runtime_properties[POLICY_APPLY_ORDER] = policy_apply_order return - if not updated_policies: - ctx.logger.error("update_policies_on_ctx - no updated_policies provided in arguments") + + policy_apply_order.sort() + + policy_apply_order_clause = ctx.instance.runtime_properties.get(POLICY_APPLY_ORDER_CLAUSE) + if not policy_apply_order_clause: + ctx.instance.runtime_properties[POLICY_APPLY_ORDER] = policy_apply_order + return + + if not isinstance(policy_apply_order_clause, list): + policy_apply_order_clause = [policy_apply_order_clause] + + for clause_item in reversed(policy_apply_order_clause): + f_path, f_type, desc, n_last = Utils.parse_clause_item(clause_item) + + if not f_path: + continue + + policy_apply_order.sort( + key=lambda policy_id, fpath=f_path, ftype=f_type, reverse=desc, nulls_last=n_last: + Utils.key_with_none_in_sort( + reverse, nulls_last, + Utils.get_field_value( + policies.get(policy_id, {}).get(POLICY_BODY, {}), + fpath, + field_type=ftype + ) + ), reverse=desc + ) + + ctx.instance.runtime_properties[POLICY_APPLY_ORDER] = policy_apply_order + + @staticmethod + def _set_policy_defaulted_fields(policies): + """ + Keeps track and stores the dict of field names of removed policies into + :runtime_properties[POLICY_DEFAULTED_FIELDS]: + """ + policy_defaulted_fields = ctx.instance.runtime_properties.get(POLICY_DEFAULTED_FIELDS, {}) + policy_defaulted_fields.update( + (k, True) + for policy in Policies._removed_policies.itervalues() + for k in Policies._get_config_from_policy(policy) or {} + ) + if policies: + for policy in policies.itervalues(): + for k in Policies._get_config_from_policy(policy) or {}: + if k in policy_defaulted_fields: + del policy_defaulted_fields[k] + + ctx.instance.runtime_properties[POLICY_DEFAULTED_FIELDS] = policy_defaulted_fields + + @staticmethod + def _set_policies(policies): + """ + store the :policies: in :runtime_properties[POLICIES]: + + and build an index on policies into :runtime_properties[POLICY_APPLY_ORDER]: + + and keep track of fields from previously :removed: policy-configs in + :runtime_properties[POLICY_DEFAULTED_FIELDS]: to reset them to default values + on merging the policies into config + """ + Policies._set_policy_defaulted_fields(policies) + if not policies: + if POLICIES in ctx.instance.runtime_properties: + del ctx.instance.runtime_properties[POLICIES] + if POLICY_APPLY_ORDER in ctx.instance.runtime_properties: + del ctx.instance.runtime_properties[POLICY_APPLY_ORDER] + return + + ctx.instance.runtime_properties[POLICIES] = policies + Policies._set_policy_apply_order(policies) + + @staticmethod + def gather_policies_to_node(policy_apply_order_path=None): + """ + 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"]. + + Call Policies.calc_latest_application_config() to apply policies onto application_config. + """ + def gather_policies_decorator(func): + """the decorator""" + if not func: + return + + 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 + + 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 + + add_policy(policy_id, policy, True, policies) + return True + + 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 + + property_policy_filter = target.node.properties.get(POLICY_FILTER) + if property_policy_filter: + policy_filter = dict( + (k, v) for (k, v) in dict(property_policy_filter).iteritems() + if v or isinstance(v, (int, float)) + ) + if policy_filter: + policy_filters[target.instance.id] = { + POLICY_FILTER_ID : target.instance.id, + POLICY_FILTER : deepcopy(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(): + add_policy(policy_id, policy, False, policies) + return True + + @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") + + try: + Policies._store_policy_apply_order_clause(policy_apply_order_path) + + policies = {} + policy_filters = {} + for rel in ctx.instance.relationships: + _ = gather_policy(rel.target, policies) \ + or gather_policies(rel.target, policies, policy_filters) + + Policies._set_policies(policies) + if policy_filters: + ctx.instance.runtime_properties[POLICY_FILTERS] = policy_filters + 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) + + return func(*args, **kwargs) + 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: + Policies._removed_policies[policy_id] = deepcopy(removed_policy) + if not removed_policy.get(POLICY_PERSISTENT): + del policies[policy_id] + elif POLICY_BODY in removed_policy: + del policies[policy_id][POLICY_BODY] + + new_policies = dict((p_id, policy) + for policy_filter_id in policy_filters + for (p_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" : []} 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_BODY not in policy or POLICY_VERSION not in policy[POLICY_BODY] \ - or not policy[POLICY_BODY][POLICY_VERSION]: - unexpected_policies.append(policy) + + 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 + + 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"]: + ctx.logger.info("skipped policies: {0}".format(json.dumps(skipped))) + + if Policies._updated_policies or Policies._removed_policies: + Policies._set_policies(policies) @staticmethod def update_policies_on_node(configs_only=True): @@ -144,56 +400,148 @@ 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 = deepcopy(Policies._get_ordered_policies( + selected_policies=Policies._updated_policies + )) + removed_policies = deepcopy(Policies._removed_policies.values()) - 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) + updated_policies = Utils.remove_empties( + [Policies._get_config_from_policy(policy) + for policy in updated_policies or []] + ) + removed_policies = [Policies._get_config_from_policy(policy) + for policy in removed_policies or []] + + 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, **kwargs) return wrapper return update_policies_decorator + @staticmethod + def _get_ordered_policies(selected_policies=None): + """ + returns the ordered list of selected policies from the runtime policies + ordered by policy_id values + """ + policies = ctx.instance.runtime_properties.get(POLICIES) + apply_order = ctx.instance.runtime_properties.get(POLICY_APPLY_ORDER) + if not policies or not apply_order: + return [] + + if selected_policies is None: + return [policies[policy_id] for policy_id in apply_order] + + if not selected_policies: + return [] + + return [policies[policy_id] for policy_id in apply_order if policy_id in selected_policies] @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: - 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 + """ + returns the list of policy configs from the runtime policies + ordered by policy_id values + """ + if ctx.type != NODE_INSTANCE: + return [] + + ordered_policies = Policies._get_ordered_policies() + return Utils.remove_empties( + [Policies._get_config_from_policy(policy) + for policy in ordered_policies] + ) @staticmethod - def shallow_merge_policies_into(config): - """shallow merge the policy configs (dict) into config that is expected to be a dict""" + def shallow_merge_policies_into(config, default_config=None): + """ + shallow merge the :policy configs: (dict) into :config: that is expected to be a dict. + + the fields listed in :runtime_properties[POLICY_DEFAULTED_FIELDS]: + that where ever changed by policy-configs are initially reset to default values + found in :default_config: or :node.properties[APPLICATION_CONFIG]: + on merging the policies into config + """ if config is None: config = {} + policy_configs = Policies.get_policy_configs() - if not policy_configs or not isinstance(config, dict): + + if not config or not isinstance(config, dict): + ctx.logger.warn("unexpected config {0} to merge the policy_configs {1} into" \ + .format(json.dumps(config), json.dumps(policy_configs or []))) + return config + + defaulted_fields = ctx.instance.runtime_properties.get(POLICY_DEFAULTED_FIELDS) + if defaulted_fields: + if default_config is None or not isinstance(default_config, dict): + default_config = dict(ctx.node.properties.get(APPLICATION_CONFIG, {})) + ctx.logger.info("using default_config from node.properties[{0}]: {1}" + .format(APPLICATION_CONFIG, json.dumps(default_config))) + if default_config and isinstance(default_config, dict): + for defaulted_field in defaulted_fields: + if defaulted_field in default_config and defaulted_field in config: + config[defaulted_field] = deepcopy(default_config.get(defaulted_field)) + + ctx.logger.info("inited config {0} on {1} {2} from {3}" + .format(json.dumps(config), + POLICY_DEFAULTED_FIELDS, + json.dumps(defaulted_fields), + json.dumps(default_config))) + + if not policy_configs: + ctx.logger.warn("no policies to merge to config {0}".format(json.dumps(config))) return config - for policy_config in copy.deepcopy(policy_configs): + for policy_config in policy_configs: if not isinstance(policy_config, dict): + ctx.logger.warn("skipped unexpected format of policy_config {0} for config: {1}" \ + .format(json.dumps(policy_config), json.dumps(config))) continue - config.update(policy_config) - for cfg_item in policy_config: - if policy_config[cfg_item] is None: - config.pop(cfg_item, None) + ctx.logger.info("applying policy_config {0} to config {1}" \ + .format(json.dumps(policy_config), json.dumps(config))) + for (policy_key, policy_value) in policy_config.iteritems(): + if policy_key not in config or policy_value is None: + ctx.logger.warn("skipped unexpected policy({0}, {1}) for config: {2}" \ + .format(policy_key, json.dumps(policy_value), json.dumps(config))) + continue + config[policy_key] = deepcopy(policy_value) return config + + @staticmethod + def calc_latest_application_config(application_config_name=None): + """ + shallow merge the policy configs (dict) into config that is expected to be a dict + + if :application_config_name: is not provided, + the runtime property :application_config: on the node instance is used as initial config + """ + if not application_config_name: + application_config_name = APPLICATION_CONFIG + + config = deepcopy(dict(ctx.instance.runtime_properties.get(application_config_name, {}))) + if not config: + config = deepcopy(dict(ctx.node.properties.get(application_config_name, {}))) + + ctx.logger.info("going to merge policies over {0}: {1}" \ + .format(application_config_name, json.dumps(config))) + + return Policies.shallow_merge_policies_into(config) diff --git a/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/utils.py b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/utils.py new file mode 100644 index 0000000..c631c7d --- /dev/null +++ b/onap-dcae-dcaepolicy-lib/onap_dcae_dcaepolicy_lib/utils.py @@ -0,0 +1,121 @@ +# org.onap.dcae +# ================================================================================ +# Copyright (c) 2017 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. + +"""generic utils to be used by dcae_policy decorators for the policy lifecycle in cloudify""" + +from copy import deepcopy +from decimal import Decimal, DecimalException + +FIELD_NAME_DELIMITER = ":" +FIELD_TYPE_DELIMITER = "::" +FIELD_TYPE_NUMBER = "number" +KEYWORD_DESC = "desc" +KEYWORD_NULLS_LAST = "nulls-last" + +class Utils(object): + """generic static class used for policy operations""" + + @staticmethod + def remove_empties(any_list): + """returns the any_list without empty elements""" + return [element for element in any_list or [] if element] + + @staticmethod + def get_field_value(parent, field_path, field_type=None): + """ + Find and return the field :field_path: under :parent: + + Optionally, converts the field value to field_type. + + Parser of the :field_path: is using the delimiter ":" (semicolon) + + Example: + parent = ctx.node.properties + field_path = "docker_config:policy:apply_order" + + will return the value of the apply_order field under the ctx.node.properties in + + properties: + docker_config: + policy: + apply_order + """ + if not parent or not field_path or not isinstance(parent, dict): + return + + field_path = Utils.remove_empties([ + path.strip() for path in field_path.split(FIELD_NAME_DELIMITER) + ]) + + if not field_path: + return + + field_value = None + field_idx = len(field_path) - 1 + for (idx, field_name) in enumerate(field_path): + parent = parent.get(field_name) + if idx == field_idx: + field_value = deepcopy(parent) + if field_type in [FIELD_TYPE_NUMBER] and isinstance(field_value, (str, unicode)): + try: + field_value = Decimal(field_value) + except DecimalException: + pass + elif not parent or not isinstance(parent, dict): + return + return field_value + + @staticmethod + def parse_clause_item(clause_item): + """ + Parses: the :clause_item: in policy_apply_order_clause + and returns (field_path, field_type, reverse, nulls_last) + + delimiters: are whitespaces, "::" + + nulls-first is the default sorting order + + :clause_item: format is [:: ] [desc] [nulls-last] + + Examples: "config:db_client" versus "config:foo desc" versus + "matchingConditions:priority::number desc nulls-last" + """ + field_path = field_type = desc = nulls_last = None + + if not clause_item or not isinstance(clause_item, (str, unicode)): + return field_path, field_type, bool(desc), bool(nulls_last) + + for idx, token in enumerate(clause_item.split()): + if idx == 0: + split_for_type = token.split(FIELD_TYPE_DELIMITER) + field_path = split_for_type[0] + field_type = split_for_type[1] if len(split_for_type) > 1 else None + elif token == KEYWORD_DESC: + desc = True + elif token == KEYWORD_NULLS_LAST: + nulls_last = True + return field_path, field_type, bool(desc), bool(nulls_last) + + @staticmethod + def key_with_none_in_sort(reverse, nulls_last, value): + """ + constructs tuple for proper placement of None values (last versus first) + in the sorted list of values regardless of the :reverse: + """ + return reverse == nulls_last or value is None, value diff --git a/onap-dcae-dcaepolicy-lib/pom.xml b/onap-dcae-dcaepolicy-lib/pom.xml index d4a7a74..ef21159 100644 --- a/onap-dcae-dcaepolicy-lib/pom.xml +++ b/onap-dcae-dcaepolicy-lib/pom.xml @@ -28,7 +28,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. org.onap.dcaegen2.utils onap-dcae-dcaepolicy-lib dcaegen2-utils-onap-dcae-dcaepolicy-lib - 1.1.0-SNAPSHOT + 2.0.0-SNAPSHOT http://maven.apache.org diff --git a/onap-dcae-dcaepolicy-lib/setup.py b/onap-dcae-dcaepolicy-lib/setup.py index 7a4e71a..b0c12cd 100644 --- a/onap-dcae-dcaepolicy-lib/setup.py +++ b/onap-dcae-dcaepolicy-lib/setup.py @@ -1,5 +1,3 @@ -"""setup.py is used for package build and distribution""" - # org.onap.dcae # ================================================================================ # Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. @@ -19,12 +17,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.0.0", author='Alex Shatov', author_email="alexs@att.com", license='Apache 2', diff --git a/onap-dcae-dcaepolicy-lib/tests/__init__.py b/onap-dcae-dcaepolicy-lib/tests/__init__.py new file mode 100644 index 0000000..6c1012a --- /dev/null +++ b/onap-dcae-dcaepolicy-lib/tests/__init__.py @@ -0,0 +1,20 @@ +# org.onap.dcae +# ================================================================================ +# Copyright (c) 2017 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..9e8ef26 100644 --- a/onap-dcae-dcaepolicy-lib/tests/log_ctx.py +++ b/onap-dcae-dcaepolicy-lib/tests/log_ctx.py @@ -1,5 +1,3 @@ -""":@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. @@ -19,7 +17,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 +103,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 +121,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..f6504e5 100644 --- a/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py +++ b/onap-dcae-dcaepolicy-lib/tests/mock_cloudify_ctx.py @@ -1,4 +1,3 @@ - # ============LICENSE_START======================================================= # org.onap.dcae # ================================================================================ @@ -19,6 +18,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..7d4f5a8 100644 --- a/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py +++ b/onap-dcae-dcaepolicy-lib/tests/test_dcae_policy.py @@ -18,31 +18,44 @@ # # 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 - 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 POLICIES, dcae_policy +from onap_dcae_dcaepolicy_lib.dcae_policy import Policies +from onap_dcae_dcaepolicy_lib.utils import Utils +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' +DOCKER_CONFIG = "docker_config" +POLICY = "policy" +APPLY_ORDER = "apply_order" + 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 +79,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", + "ConfigName": "alex_config_name" + } + if priority is not None: + matching_conditions["priority"] = priority + return { "policyConfigMessage": "Config Retrieved! ", "policyConfigStatus": "CONFIG_RETRIEVED", @@ -87,22 +108,28 @@ 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""" @@ -111,23 +138,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,33 +171,89 @@ class MonkeyedNode(object): relationships=relationships, runtime_properties=runtime_properties ) - MonkeyedLogHandler.add_handler_to(self.ctx.logger) + +def operation_node_configure(**kwargs): + """do the node-configure operation""" + ctx.logger.info("operation_node_configure kwargs: {0}".format(kwargs)) + + app_config = Policies.calc_latest_application_config() + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config + ctx.logger.info("applied policy_configs to property 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))) + +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='exe_task') +@Policies.gather_policies_to_node() +def node_configure_default_order(**kwargs): + """default policy sorting because no param of policy_apply_order_path""" + operation_node_configure(**kwargs) @CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='exe_task') -@Policies.gather_policies_to_node +@Policies.gather_policies_to_node(policy_apply_order_path="docker_config:policy:apply_order") 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) + +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='exe_task') +@Policies.gather_policies_to_node(policy_apply_order_path="docker_config:junk:junk") +def node_configure_wrong_order_path(**kwargs): + """wrong data in param policy_apply_order_path""" + 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='exe_task') +@Policies.gather_policies_to_node(policy_apply_order_path=" ") +def node_configure_empty_order_path(**kwargs): + """wrong data in param policy_apply_order_path""" + operation_node_configure(**kwargs) + +@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, 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 + """ + # This is how to merge the policies into default app_config object + # (runtime_properties[APPLICATION_CONFIG] = application_config) + app_config = Policies.calc_latest_application_config() + + ctx.logger.info("merged updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) - 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))) - policy_configs = Policies.get_policy_configs() - if policy_configs: - ctx.logger.warn("TBD: apply policy_configs: {0}".format(json.dumps(policy_configs))) +@CtxLogger.log_ctx(pre_log=True, after_log=True, exe_task='execute_operation') +@Policies.update_policies_on_node(configs_only=False) +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 + """ + # This is how to merge the policies into default app_config object + # (runtime_properties[APPLICATION_CONFIG] = application_config) + app_config = Policies.calc_latest_application_config() + + ctx.logger.info("merged updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + + 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(configs_only=False) +def policy_update_many_calcs(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). @@ -176,87 +261,1169 @@ def policy_update(updated_policies, **kwargs): Use :Policies.shallow_merge_policies_into(): to merge the updated_policies into app_config """ + # This is how to merge the policies into default app_config object + # (runtime_properties[APPLICATION_CONFIG] = application_config) + app_config = Policies.calc_latest_application_config() + + ctx.logger.info("merged updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + + ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config + + app_config = Policies.calc_latest_application_config(APPLICATION_CONFIG) + ctx.logger.info("merged again updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + + app_config = Policies.calc_latest_application_config(APPLICATION_CONFIG) + ctx.logger.info("merged again updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + + app_config = Policies.shallow_merge_policies_into(None) + ctx.logger.info("merged to empty updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + + app_config = ctx.instance.runtime_properties[APPLICATION_CONFIG] + app_config = Policies.shallow_merge_policies_into(app_config, default_config={}) + ctx.logger.info( + "merged with empty defaults updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) + app_config = ctx.instance.runtime_properties[APPLICATION_CONFIG] + app_config = Policies.shallow_merge_policies_into(app_config, + default_config={"unexpected":"foo"}) + ctx.logger.info( + "merged with unexpected defaults updated_policies {0}, removed_policies {1} into config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) - # This is how to merge the policies into app_config object + app_config = ctx.instance.runtime_properties[APPLICATION_CONFIG] app_config = Policies.shallow_merge_policies_into(app_config) + ctx.logger.info("merged 3rd time updated_policies {0}, removed_policies {1} into app_config {2}" + .format(json.dumps(updated_policies), json.dumps(removed_policies), + json.dumps(app_config))) - ctx.logger.info("merged updated_policies {0} into app_config {1}" - .format(json.dumps(updated_policies), json.dumps(app_config))) +class CurrentCtx(object): + """cloudify context""" + _node_ms = None - ctx.instance.runtime_properties[APPLICATION_CONFIG] = app_config + @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 + ".*"}}, + 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", + {DOCKER_CONFIG: {POLICY: { + APPLY_ORDER: [ + "matchingConditions:priority::number desc", + "config:db_client nulls-last", + "config:policy_updated_to_ver"]}}, + 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 test_utils(): + """test utils""" + field_value = Utils.get_field_value( + { + "before":"bubu", + "pre":{"field":"hehe"}, + "hello":{"field":"haha"}, + "post":{"field":"hmmm"}, + "after":"bebe" + }, + "hello:field") + assert field_value == "haha" + + field_value = Utils.get_field_value(None, None) + assert field_value is None + + field_value = Utils.get_field_value({"hello":{"field":"haha"}}, "wrong_root:field") + assert field_value is None + + field_value = Utils.get_field_value({"hello":{"field":None}}, "hello:field") + assert field_value is None + + field_value = Utils.get_field_value({"hello":{"field":None}}, "hello:field::number") + assert field_value is None + +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 + +@cfy_ctx(include_bad=True) +def test_gather_policies_to_node(): + """test gather_policies_to_node""" + node_configure_default_order() + + 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 dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_2] + +@cfy_ctx(include_bad=True) +def test_policies_wrong_order(): + """test gather_policies_to_node""" + node_configure_wrong_order_path() + + 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 dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_2] + +@cfy_ctx(include_bad=True) +def test_policies_empty_order(): + """test gather_policies_to_node""" + node_configure_empty_order_path() + + 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 dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_2] + +@cfy_ctx(include_bad=True) +def test_policies_damaged_order(): + """test gather_policies_to_node""" + ctx.node.properties[DOCKER_CONFIG][POLICY][APPLY_ORDER] = " " + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + node_configure() + + 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 dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_2] + +@cfy_ctx(include_bad=True) +def test_policies_bad_order(): + """test gather_policies_to_node""" + ctx.node.properties[DOCKER_CONFIG][POLICY][APPLY_ORDER] = [" ", "", "ha he", "hu::mu dah"] + + runtime_properties = ctx.instance.runtime_properties + ctx.logger.info("runtime_properties: {0}".format(json.dumps(runtime_properties))) + + node_configure() + + 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 dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_2] + +@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)} + """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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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) + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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]: { + 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 Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID_M]) is None + + 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) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_M_2] + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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]: { + 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 Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID_M]) is None + + 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) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_M_2] + +@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 POLICIES in runtime_properties + assert APPLICATION_CONFIG in runtime_properties + policies = runtime_properties[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))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + expected_policies = copy.deepcopy(policies) + expected_app_config = copy.deepcopy(app_config) + expected_policy_apply_order = copy.deepcopy(policy_apply_order) + + 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 = "<<>>" + unexpected_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: { + POLICIES: {MONKEYED_POLICY_ID_M_2: added_policy}}, + policy_filter_ids[0]: { + 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) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == expected_policy_apply_order + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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]: { + 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 Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID_M]) is None + + 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) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID, MONKEYED_POLICY_ID_M_2] + +@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 POLICIES in runtime_properties + policies = runtime_properties[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 POLICIES in runtime_properties + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + assert runtime_properties[dcae_policy.POLICY_APPLY_ORDER] == [] + assert dcae_policy.POLICY_DEFAULTED_FIELDS in runtime_properties + assert Policies.get_policy_configs() == [] + + defaulted_fields = runtime_properties[dcae_policy.POLICY_DEFAULTED_FIELDS] + expected_defaulted_fields = dict( + (k, True) + for k in MonkeyedPolicyBody.create_policy_body(MONKEYED_POLICY_ID)[POLICY_CONFIG] ) - 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)} + assert MonkeyedPolicyBody.is_the_same_dict(defaulted_fields, expected_defaulted_fields) + assert MonkeyedPolicyBody.is_the_same_dict(expected_defaulted_fields, defaulted_fields) + + 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) + +@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 POLICIES in runtime_properties + policies = runtime_properties[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 POLICIES in runtime_properties + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + assert runtime_properties[dcae_policy.POLICY_APPLY_ORDER] == [] + assert dcae_policy.POLICY_DEFAULTED_FIELDS in runtime_properties + assert Policies.get_policy_configs() == [] + + defaulted_fields = runtime_properties[dcae_policy.POLICY_DEFAULTED_FIELDS] + expected_defaulted_fields = dict( + (k, True) + for k in MonkeyedPolicyBody.create_policy_body(MONKEYED_POLICY_ID)[POLICY_CONFIG] ) - 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} - ]) + assert MonkeyedPolicyBody.is_the_same_dict(defaulted_fields, expected_defaulted_fields) + assert MonkeyedPolicyBody.is_the_same_dict(expected_defaulted_fields, defaulted_fields) + + 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) + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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 ["--"] - try: - current_ctx.set(node_ms.ctx) + policy_update(updated_policies=[updated_policy], + added_policies={ + policy_filter_ids[0]: { + 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 Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID]) is None + + assert MONKEYED_POLICY_ID_B in policies + assert Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID_B]) is None + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_M_2] + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + remove_policy_ids = policies.keys() + policy_update_many_calcs(updated_policies=None, + added_policies=None, + removed_policies=remove_policy_ids) + + assert POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [] + + assert dcae_policy.POLICY_DEFAULTED_FIELDS in runtime_properties + assert Policies.get_policy_configs() == [] + + defaulted_fields = runtime_properties[dcae_policy.POLICY_DEFAULTED_FIELDS] + expected_defaulted_fields = dict( + (k, True) + for k in MonkeyedPolicyBody.create_policy_body(MONKEYED_POLICY_ID)[POLICY_CONFIG] + ) + assert MonkeyedPolicyBody.is_the_same_dict(defaulted_fields, expected_defaulted_fields) + assert MonkeyedPolicyBody.is_the_same_dict(expected_defaulted_fields, defaulted_fields) + + 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]: { + 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 Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID]) is None + + assert MONKEYED_POLICY_ID_B in policies + assert Policies._get_config_from_policy(policies[MONKEYED_POLICY_ID_B]) is None + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_M_2] + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_M, MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID] + + 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]: { + 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) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [ + MONKEYED_POLICY_ID_2, MONKEYED_POLICY_ID_B, MONKEYED_POLICY_ID_M_2, MONKEYED_POLICY_ID] + +@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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + assert policy_apply_order == [] + +@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() - 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() + 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" + +@cfy_ctx(include_bad=True, include_good=False) +def test_wrong_ctx_policy_update(): + """test wrong ctx""" + current_ctx.set(ctx.instance.relationships[0]) + ctx_type = ctx.type + no_policy_configs = Policies.get_policy_configs() + + 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_policy_configs == [] + 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() + + 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 POLICIES in runtime_properties + policies = runtime_properties[POLICIES] + ctx.logger.info("policies: {0}".format(json.dumps(policies))) + + assert dcae_policy.POLICY_APPLY_ORDER in runtime_properties + policy_apply_order = runtime_properties[dcae_policy.POLICY_APPLY_ORDER] + ctx.logger.info("policy_apply_order: {0}".format(json.dumps(policy_apply_order))) + + Policies._set_policies({}) + + assert POLICIES not in runtime_properties + assert dcae_policy.POLICY_APPLY_ORDER not in runtime_properties + + Policies._set_policies({}) + + assert POLICIES not in runtime_properties + assert dcae_policy.POLICY_APPLY_ORDER not in runtime_properties 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 - - -- cgit 1.2.3-korg