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