From 34dd2c9a61528cb87ecf59d031bae23cd257210a Mon Sep 17 00:00:00 2001 From: Vignesh K Date: Wed, 27 Mar 2019 17:34:08 +0530 Subject: Initial commit for clamp-policy plugin Addressed review comments Issue-ID: DCAEGEN2-1129 Change-Id: Icdf95bdbf7c3ec2f201d5d57544ef6d206f3c5fa Signed-off-by: Vignesh K --- clamp-policy/.coveragerc | 25 +++++ clamp-policy/LICENSE.txt | 15 +++ clamp-policy/MANIFEST.in | 1 + clamp-policy/README.md | 27 +++++ clamp-policy/clamppolicy-node-type.yaml | 42 ++++++++ clamp-policy/clamppolicyplugin/__init__.py | 20 ++++ clamp-policy/clamppolicyplugin/tasks.py | 29 +++++ clamp-policy/pom.xml | 164 +++++++++++++++++++++++++++++ clamp-policy/requirements.txt | 1 + clamp-policy/setup.py | 37 +++++++ clamp-policy/tests/__init__.py | 16 +++ clamp-policy/tests/log_ctx.py | 137 ++++++++++++++++++++++++ clamp-policy/tests/mock_cloudify_ctx.py | 145 +++++++++++++++++++++++++ clamp-policy/tests/mock_setup.py | 155 +++++++++++++++++++++++++++ clamp-policy/tests/test_tasks.py | 46 ++++++++ clamp-policy/tox-local.ini | 15 +++ clamp-policy/tox.ini | 17 +++ mvn-phase-script.sh | 4 +- pom.xml | 1 + 19 files changed, 895 insertions(+), 2 deletions(-) create mode 100644 clamp-policy/.coveragerc create mode 100644 clamp-policy/LICENSE.txt create mode 100644 clamp-policy/MANIFEST.in create mode 100644 clamp-policy/README.md create mode 100644 clamp-policy/clamppolicy-node-type.yaml create mode 100644 clamp-policy/clamppolicyplugin/__init__.py create mode 100644 clamp-policy/clamppolicyplugin/tasks.py create mode 100644 clamp-policy/pom.xml create mode 100644 clamp-policy/requirements.txt create mode 100644 clamp-policy/setup.py create mode 100644 clamp-policy/tests/__init__.py create mode 100644 clamp-policy/tests/log_ctx.py create mode 100644 clamp-policy/tests/mock_cloudify_ctx.py create mode 100644 clamp-policy/tests/mock_setup.py create mode 100644 clamp-policy/tests/test_tasks.py create mode 100644 clamp-policy/tox-local.ini create mode 100644 clamp-policy/tox.ini diff --git a/clamp-policy/.coveragerc b/clamp-policy/.coveragerc new file mode 100644 index 0000000..22cdf0c --- /dev/null +++ b/clamp-policy/.coveragerc @@ -0,0 +1,25 @@ +# .coveragerc to control coverage.py +[run] +branch = True +cover_pylib = False +# include = */clamppolicyplugin/*.py + +[report] +# Regexes for lines to exclude from consideration +exclude_lines = + # Have to re-enable the standard pragma + pragma: no cover + + # Don't complain about missing debug-only code: + def __repr__ + if self\.debug + + # Don't complain if tests don't hit defensive assertion code: + raise AssertionError + raise NotImplementedError + + # Don't complain if non-runnable code isn't run: + if 0: + if __name__ == .__main__.: + +ignore_errors = True diff --git a/clamp-policy/LICENSE.txt b/clamp-policy/LICENSE.txt new file mode 100644 index 0000000..5bf12c1 --- /dev/null +++ b/clamp-policy/LICENSE.txt @@ -0,0 +1,15 @@ +================================================================================ +Copyright (c) 2019 Wipro Limited 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========================================================= diff --git a/clamp-policy/MANIFEST.in b/clamp-policy/MANIFEST.in new file mode 100644 index 0000000..f9bd145 --- /dev/null +++ b/clamp-policy/MANIFEST.in @@ -0,0 +1 @@ +include requirements.txt diff --git a/clamp-policy/README.md b/clamp-policy/README.md new file mode 100644 index 0000000..c26fb15 --- /dev/null +++ b/clamp-policy/README.md @@ -0,0 +1,27 @@ +#supporting policy_model_id for clamp + + - Adding a new node type extended from dcae.node.policy, so it is backward comatible with policy_id and + compatible with newly introduced policy_model_id + + ## Usage + +import the clamppolicy-node-type.yaml into your blueprint to use the clamp.nodes.type node + +```yaml +imports: + - https://YOUR_NEXUS_RAW_SERVER/type_files/clamppolicy/1.0.0/clamppolicy-node-type.yaml +``` + +provide the value for policy_model_id property + +```yaml +node_templates: +... + policy_model: + type: clamp.nodes.policy + properties: + policy_model_id: { get_input: policy_model_id } +``` + +Then the clamppolicyplugin will bring the latest policy model id to the clamp.nodes.policy node +during the install workflow of cloudify. diff --git a/clamp-policy/clamppolicy-node-type.yaml b/clamp-policy/clamppolicy-node-type.yaml new file mode 100644 index 0000000..feb4f20 --- /dev/null +++ b/clamp-policy/clamppolicy-node-type.yaml @@ -0,0 +1,42 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +tosca_definitions_version: cloudify_dsl_1_3 + +imports: + - http://www.getcloudify.org/spec/cloudify/3.4/types.yaml + - https://nexus.onap.org/service/local/repositories/raw/content/org.onap.dcaegen2.platform.plugins/R4/dcaepolicyplugin/2.3.0/dcaepolicyplugin_types.yaml + +plugins: + clamppolicy: + executor: 'central_deployment_agent' + package_name: clamppolicyplugin + package_version: 1.0.0 + +relationships: + # relationship that maintains mapping between micro-service and corresponding model id + clamp_node.relationships.gets_input_from: + derived_from: cloudify.relationships.depends_on + +node_types: + # node that maintains mapping between micro-service and corresponding model id + clamp.nodes.policy: + derived_from: dcae.nodes.policy + properties: + policy_model_id: + description: policy-model-id generated by SDC + type: string \ No newline at end of file diff --git a/clamp-policy/clamppolicyplugin/__init__.py b/clamp-policy/clamppolicyplugin/__init__.py new file mode 100644 index 0000000..9f32d4c --- /dev/null +++ b/clamp-policy/clamppolicyplugin/__init__.py @@ -0,0 +1,20 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +""":clamppolicyplugin: gets the policy model id and performs no operation""" + +from .tasks import policy_get diff --git a/clamp-policy/clamppolicyplugin/tasks.py b/clamp-policy/clamppolicyplugin/tasks.py new file mode 100644 index 0000000..40b1659 --- /dev/null +++ b/clamp-policy/clamppolicyplugin/tasks.py @@ -0,0 +1,29 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +"""tasks are the cloudify operations invoked on interfaces defined in the blueprint""" + +from cloudify import ctx +from cloudify.decorators import operation +from cloudify.exceptions import NonRecoverableError + +CLAMP_POLICY_TYPE = 'clamp.nodes.policy' + +@operation +def policy_get(**kwargs): + """clamppolicyplugin - Dummy Function returning no value""" + ctx.logger.info("clamppolicyplugin - Inside policy_get dummy function") diff --git a/clamp-policy/pom.xml b/clamp-policy/pom.xml new file mode 100644 index 0000000..87c2a26 --- /dev/null +++ b/clamp-policy/pom.xml @@ -0,0 +1,164 @@ + + + + 4.0.0 + + org.onap.dcaegen2.platform + plugins + 1.1.0-SNAPSHOT + + org.onap.dcaegen2.platform.plugins + clamp-policy + clamp-policy-plugin + 1.0.0 + http://maven.apache.org + + UTF-8 + . + xunit-results.xml + coverage.xml + py + Python + **/*.py + tests/*,setup.py + + + ${project.artifactId}-${project.version} + + + + + org.codehaus.mojo + exec-maven-plugin + 1.2.1 + + + clean phase script + clean + + exec + + + + ${project.artifactId} + clean + + + + + generate-sources script + generate-sources + + exec + + + + ${project.artifactId} + generate-sources + + + + + compile script + compile + + exec + + + + ${project.artifactId} + compile + + + + + package script + package + + exec + + + + ${project.artifactId} + package + + + + + test script + test + + exec + + + + ${project.artifactId} + test + + + + + install script + install + + exec + + + + ${project.artifactId} + install + + + + + deploy script + deploy + + exec + + + + ${project.artifactId} + deploy + + + + + + + + diff --git a/clamp-policy/requirements.txt b/clamp-policy/requirements.txt new file mode 100644 index 0000000..025de9d --- /dev/null +++ b/clamp-policy/requirements.txt @@ -0,0 +1 @@ +requests>=2.11.0,<3.0.0 \ No newline at end of file diff --git a/clamp-policy/setup.py b/clamp-policy/setup.py new file mode 100644 index 0000000..de6b119 --- /dev/null +++ b/clamp-policy/setup.py @@ -0,0 +1,37 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +"""package for clamppolicyplugin - getting policy model id from policy-engine through policy-handler""" + +from setuptools import setup + +setup( + name='clamppolicyplugin', + description='Cloudify plugin for clamp.nodes.policy node to retrieve the policy model id', + version="1.0.0", + author='Vignesh K', + packages=['clamppolicyplugin'], + install_requires=[ + "requests>=2.11.0,<3.0.0" + ], + keywords='clamp policy model cloudify plugin', + classifiers=[ + 'Development Status :: 4 - Beta', + 'Intended Audience :: Developers', + 'Programming Language :: Python :: 2.7' + ] +) diff --git a/clamp-policy/tests/__init__.py b/clamp-policy/tests/__init__.py new file mode 100644 index 0000000..20c17e4 --- /dev/null +++ b/clamp-policy/tests/__init__.py @@ -0,0 +1,16 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# diff --git a/clamp-policy/tests/log_ctx.py b/clamp-policy/tests/log_ctx.py new file mode 100644 index 0000000..89ed9ea --- /dev/null +++ b/clamp-policy/tests/log_ctx.py @@ -0,0 +1,137 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +""":@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 +from cloudify.context import NODE_INSTANCE, RELATIONSHIP_INSTANCE + + +class CtxLogger(object): + """static class for logging cloudify context ctx""" + @staticmethod + def _get_ctx_node_info(ctx_node): + if not ctx_node: + return {} + return {'id': ctx_node.id, 'name': ctx_node.name, 'type': ctx_node.type, + 'type_hierarchy': ctx_node.type_hierarchy, 'properties': ctx_node.properties} + + @staticmethod + def _get_ctx_instance_info(ctx_instance): + if not ctx_instance: + return {} + return {'id' : ctx_instance.id, 'runtime_properties' : ctx_instance.runtime_properties, + 'relationships' : CtxLogger._get_ctx_instance_relationships_info(ctx_instance)} + + @staticmethod + def _get_ctx_instance_relationships_info(ctx_instance): + if not ctx_instance or not ctx_instance.relationships: + return [] + return [{'target': CtxLogger._get_ctx_source_target_info(r.target), \ + 'type':r.type, 'type_hierarchy':r.type_hierarchy} \ + for r in ctx_instance.relationships] + + @staticmethod + def _get_ctx_source_target_info(ctx_source_target): + if not ctx_source_target: + return {} + return {'node': CtxLogger._get_ctx_node_info(ctx_source_target.node), + 'instance' : CtxLogger._get_ctx_instance_info(ctx_source_target.instance)} + + @staticmethod + def get_ctx_info(): + """collect the context data from ctx""" + context = { + 'type': ctx.type, + 'blueprint.id': ctx.blueprint.id, + 'deployment.id': ctx.deployment.id, + 'execution_id': ctx.execution_id, + 'workflow_id': ctx.workflow_id, + 'task_id': ctx.task_id, + 'task_name': ctx.task_name, + 'task_queue': ctx.task_queue, + 'task_target': ctx.task_target, + 'operation': { + 'name': ctx.operation.name, + 'retry_number': ctx.operation.retry_number, + 'max_retries': ctx.operation.max_retries + }, + 'plugin': { + 'name': ctx.plugin.name, + 'package_name': ctx.plugin.package_name, + 'package_version': ctx.plugin.package_version, + 'prefix': ctx.plugin.prefix, + 'workdir': ctx.plugin.workdir + } + } + if ctx.type == NODE_INSTANCE: + context['node'] = CtxLogger._get_ctx_node_info(ctx.node) + context['instance'] = CtxLogger._get_ctx_instance_info(ctx.instance) + elif ctx.type == RELATIONSHIP_INSTANCE: + context['source'] = CtxLogger._get_ctx_source_target_info(ctx.source) + context['target'] = CtxLogger._get_ctx_source_target_info(ctx.target) + + return context + + @staticmethod + def log_ctx_info(func_name): + ctx.logger.info("NODE_INSTANCE: {}",NODE_INSTANCE) + """shortcut for logging of the ctx of the function""" + try: + ctx.logger.info("NODE_INSTANCE: {}",NODE_INSTANCE) + if ctx.type == NODE_INSTANCE: + ctx.logger.info("{0} {1} context: {2}".format(\ + func_name, ctx.instance.id, json.dumps(CtxLogger.get_ctx_info()))) + elif ctx.type == RELATIONSHIP_INSTANCE: + 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}: {1}" \ + .format(str(ex), traceback.format_exc())) + + @staticmethod + def log_ctx(pre_log=True, after_log=False, exe_task=None): + """Decorate each operation on the node to log the context - before and after. + Optionally save the current function name into runtime_properties[exe_task] + """ + def log_ctx_info_decorator(func, **arguments): + """Decorate each operation on the node to log the context""" + if func is not None: + @wraps(func) + def wrapper(*args, **kwargs): + """the actual logger before and after""" + try: + 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}: {2}" \ + .format(exe_task, str(ex), traceback.format_exc())) + if pre_log: + CtxLogger.log_ctx_info('before ' + func.__name__) + + result = func(*args, **kwargs) + + if after_log: + CtxLogger.log_ctx_info('after ' + func.__name__) + + return result + return wrapper + return log_ctx_info_decorator diff --git a/clamp-policy/tests/mock_cloudify_ctx.py b/clamp-policy/tests/mock_cloudify_ctx.py new file mode 100644 index 0000000..cf40232 --- /dev/null +++ b/clamp-policy/tests/mock_cloudify_ctx.py @@ -0,0 +1,145 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +"""mock cloudify context with relationships and type_hierarchy""" + +from cloudify.mocks import (MockCloudifyContext, MockNodeContext, + MockNodeInstanceContext) + +TARGET_NODE_ID = "target_node_id" +TARGET_NODE_NAME = "target_node_name" + +class MockContextNode(MockNodeContext): + """ctx.node with type and type_hierarchy""" + + def __init__(self, id=None, properties=None, node_type=None, type_hierarchy=None): + super(MockContextNode, self).__init__(id, properties or {}) + self._type = node_type + self._type_hierarchy = type_hierarchy or [self._type] + MockCloudifyContextFull.nodes[id] = self + + @property + def type(self): + """node type""" + return self._type + + @property + def type_hierarchy(self): + """node type hierarchy is a list of types""" + return self._type_hierarchy + +class MockContextNodeInstance(MockNodeInstanceContext): + """ctx.instance with relationships""" + + def __init__(self, id=None, runtime_properties=None, relationships=None): + super(MockContextNodeInstance, self).__init__(id, runtime_properties or {}) + self._relationships = [] + self.add_relationships(relationships) + MockCloudifyContextFull.instances[id] = self + + def add_relationships(self, relationships): + """add more relationships to the node instance""" + if not relationships: + return + if not self._relationships: + self._relationships = [] + self._relationships.extend([ + MockContextRelationship(relationship) + for relationship in (relationships or []) if TARGET_NODE_ID in relationship + ]) + + @property + def relationships(self): + """list of relationships to other node instances""" + return self._relationships + +class MockContextRelationshipTarget(object): + """target of relationship""" + def __init__(self, relationship): + target_node_name = relationship[TARGET_NODE_NAME] + target_node_id = relationship[TARGET_NODE_ID] + + self.node = MockCloudifyContextFull.nodes.get(target_node_name) + self.instance = MockCloudifyContextFull.instances.get(target_node_id) + + if not self.node: + self.node = MockContextNode(target_node_name) + if not self.instance: + self.instance = MockContextNodeInstance(target_node_id) + +class MockContextRelationship(object): + """item of ctx.instance.relationships""" + + def __init__(self, relationship): + self.target = MockContextRelationshipTarget(relationship) + self.type = relationship.get("type", "cloudify.relationships.depends_on") + self.type_hierarchy = relationship.get("type_hierarchy") or [self.type] + +class MockCloudifyContextFull(MockCloudifyContext): + """ + ctx1 = MockCloudifyContextFull(node_id='node_1', + node_name='my_1', properties={'foo': 'bar'}) + ctx2 = MockCloudifyContextFull(node_id='node_2', + node_name='my_2', + relationships=[{'target_node_id': 'node_1', + 'target_node_name': 'my_1'}]) + """ + nodes = {} + instances = {} + + def __init__(self, + node_id=None, + node_name=None, + blueprint_id=None, + deployment_id=None, + execution_id=None, + properties=None, node_type=None, type_hierarchy=None, + runtime_properties=None, + capabilities=None, + related=None, + source=None, + target=None, + operation=None, + resources=None, + provider_context=None, + bootstrap_context=None, + relationships=None): + super(MockCloudifyContextFull, self).__init__( + node_id=node_id, + node_name=node_name, + blueprint_id=blueprint_id, + deployment_id=deployment_id, + execution_id=execution_id, + properties=properties, + capabilities=capabilities, + related=related, + source=source, + target=target, + operation=operation, + resources=resources, + provider_context=provider_context, + bootstrap_context=bootstrap_context, + runtime_properties=runtime_properties + ) + self._node = MockContextNode(node_name, properties, node_type, type_hierarchy) + self._instance = MockContextNodeInstance(node_id, runtime_properties, relationships) + + @staticmethod + def clear(): + """clean up the context links""" + MockCloudifyContextFull.instances.clear() + MockCloudifyContextFull.nodes.clear() diff --git a/clamp-policy/tests/mock_setup.py b/clamp-policy/tests/mock_setup.py new file mode 100644 index 0000000..147ba43 --- /dev/null +++ b/clamp-policy/tests/mock_setup.py @@ -0,0 +1,155 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +"""unit tests for tasks in clamppolicyplugin""" + +import json +import logging +from datetime import datetime, timedelta + +from tests.mock_cloudify_ctx import MockCloudifyContextFull + +LOG_FILE = 'logs/test_clamppolicyplugin.log' +POLICY_MODEL_ID = 'policy_model_id' +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' + +RUN_TS = datetime.utcnow() + + +class MonkeyedLogHandler(object): + """keep the shared logger handler here""" + _log_handler = None + + @staticmethod + def add_handler_to(logger): + """adds the local handler to the logger""" + if not MonkeyedLogHandler._log_handler: + MonkeyedLogHandler._log_handler = logging.FileHandler(LOG_FILE) + MonkeyedLogHandler._log_handler.setLevel(logging.DEBUG) + formatter = logging.Formatter( + fmt='%(asctime)s.%(msecs)03d %(levelname)+8s ' + + '%(threadName)s %(name)s.%(funcName)s: %(message)s', + datefmt='%Y%m%d_%H%M%S') + MonkeyedLogHandler._log_handler.setFormatter(formatter) + logger.addHandler(MonkeyedLogHandler._log_handler) + + +class MonkeyedPolicyBody(object): + """policy body that policy-engine returns""" + @staticmethod + def create_policy_body(policy_id, policy_version=1): + """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_to_ver": this_ver, + "policy_hello": "world!", + "policy_updated_ts": timestamp.isoformat()[:-3] + 'Z', + "updated_policy_id": policy_id + } + return { + "policyConfigMessage": "Config Retrieved! ", + "policyConfigStatus": "CONFIG_RETRIEVED", + "type": "JSON", + POLICY_NAME: "{0}.{1}.xml".format(policy_id, this_ver), + POLICY_VERSION: this_ver, + POLICY_CONFIG: config, + "matchingConditions": { + "ONAPName": "DCAE", + CONFIG_NAME: "VigneshK_config_name" + }, + "responseAttributes": {}, + "property": None + } + + @staticmethod + def create_policy(policy_id, policy_version=1): + """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) + } + + @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): + return False + for key in policy_body_1.keys(): + if key not in policy_body_2: + return False + + 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 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 + + +class MonkeyedNode(object): + """node in cloudify""" + BLUEPRINT_ID = 'test_clamp_policy_bp_id' + DEPLOYMENT_ID = 'test_clamp_policy_dpl_id' + EXECUTION_ID = 'test_clamp_policy_exe_id' + + def __init__(self, node_id, node_name, node_type, properties, relationships=None): + self.node_id = node_id + self.node_name = node_name + self.ctx = MockCloudifyContextFull( + node_id=self.node_id, + node_name=self.node_name, + node_type=node_type, + blueprint_id=MonkeyedNode.BLUEPRINT_ID, + deployment_id=MonkeyedNode.DEPLOYMENT_ID, + execution_id=MonkeyedNode.EXECUTION_ID, + properties=properties, + relationships=relationships + ) + MonkeyedLogHandler.add_handler_to(self.ctx.logger) diff --git a/clamp-policy/tests/test_tasks.py b/clamp-policy/tests/test_tasks.py new file mode 100644 index 0000000..f4c8d5a --- /dev/null +++ b/clamp-policy/tests/test_tasks.py @@ -0,0 +1,46 @@ +# ================================================================================ +# Copyright (c) 2019 Wipro Limited 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========================================================= +# + +"""unit tests for tasks in clamppolicyplugin""" + +import json + +import pytest +from cloudify.exceptions import NonRecoverableError +from cloudify.state import current_ctx + +from clamppolicyplugin import tasks +from tests.log_ctx import CtxLogger +from tests.mock_cloudify_ctx import (TARGET_NODE_ID, TARGET_NODE_NAME, + MockCloudifyContextFull) +from tests.mock_setup import (CONFIG_NAME, MONKEYED_POLICY_ID, POLICY_BODY, + POLICY_ID, POLICY_MODEL_ID, POLICY_NAME, MonkeyedNode, + MonkeyedPolicyBody, MonkeyedResponse) + +from cloudify.mocks import MockCloudifyContext +from cloudify.state import current_ctx + +def monkeyed_policy_handler_get(full_path, headers=None): + """monkeypatch for the GET to policy-engine""" + return MonkeyedResponse(full_path, headers, + MonkeyedPolicyBody.create_policy(MONKEYED_POLICY_ID)) + +def test_policy_get(): + """test policy_get operation on clamp.nodes.policy node""" + mock_ctx = MockCloudifyContext(node_id='policy_model_id',node_name='clamp.nodes.policy') + current_ctx.set(mock_ctx) + tasks.policy_get() \ No newline at end of file diff --git a/clamp-policy/tox-local.ini b/clamp-policy/tox-local.ini new file mode 100644 index 0000000..ad84024 --- /dev/null +++ b/clamp-policy/tox-local.ini @@ -0,0 +1,15 @@ +# tox -c tox-local.ini | tee -a logs/test_clamppolicyplugin.log 2>&1 +[tox] +envlist = py27 + +[testenv] +deps= + -rrequirements.txt + cloudify-plugins-common==3.4 + pytest + coverage + pytest-cov +setenv = + PYTHONPATH={toxinidir} +# recreate = True +commands=pytest -v --cov clamppolicyplugin --cov-report html diff --git a/clamp-policy/tox.ini b/clamp-policy/tox.ini new file mode 100644 index 0000000..3974a7e --- /dev/null +++ b/clamp-policy/tox.ini @@ -0,0 +1,17 @@ +# content of: tox.ini , put in same dir as setup.py +[tox] +envlist = py27 + +[testenv] +deps= + -rrequirements.txt + cloudify-plugins-common==3.4 + pytest + coverage + pytest-cov +setenv = + PYTHONPATH={toxinidir} +commands= + -mkdir logs + pytest --junitxml xunit-results.xml --cov clamppolicyplugin --cov-report xml + coverage xml diff --git a/mvn-phase-script.sh b/mvn-phase-script.sh index 234f73a..e404a08 100755 --- a/mvn-phase-script.sh +++ b/mvn-phase-script.sh @@ -63,7 +63,7 @@ test) package) echo "==> package phase script" case $MVN_PROJECT_MODULEID in - cdap|dcae-policy|docker|relationships|k8s) + cdap|dcae-policy|docker|relationships|k8s|clamp-policy) build_archives_for_wagons build_wagons ;; @@ -77,7 +77,7 @@ install) deploy) echo "==> deploy phase script" case $MVN_PROJECT_MODULEID in - cdap|dcae-policy|docker|relationships|k8s) + cdap|dcae-policy|docker|relationships|k8s|clamp-policy) upload_wagons_and_type_yamls upload_wagon_archives ;; diff --git a/pom.xml b/pom.xml index 266bdc1..888331f 100644 --- a/pom.xml +++ b/pom.xml @@ -37,6 +37,7 @@ ECOMP is a trademark and service mark of AT&T Intellectual Property. relationships dcae-policy k8s + clamp-policy https://nexus.onap.org -- cgit 1.2.3-korg