diff options
author | Alex Shatov <alexs@att.com> | 2017-09-12 11:46:25 -0400 |
---|---|---|
committer | Alex Shatov <alexs@att.com> | 2017-09-12 11:46:25 -0400 |
commit | 0c209c0c2b6e4bbadf413c048d9c535d3c2d1a0b (patch) | |
tree | 0d4b4db47b5d7cc931bb63363fb168bc62c41f4a | |
parent | eee6ffd3ab306b38487746ec5f883fee4dde72e9 (diff) |
1.0.0 and removed dcae.node.type and policy_get
* moved dcae.node.type and policy_get from lib
to dcaepolicy plugin and dcae.node.type
* removed dependency on cloudify-plugins-common and requests packages
* removed discovery logic
since the lib does not talk to policy-handler anymore
- happens in the new dcaepolicy plugin now
* no more policy_apply_mode per policy node
- it is up to component's blueprint to decide how to notify
about policy change
Change-Id: Ieefc3b5af1166a5c201574144fbf8c399e5ddc15
Issue-Id: DCAEGEN2-62
Signed-off-by: Alex Shatov <alexs@att.com>
-rw-r--r-- | python-dcae-policy/README.md | 78 | ||||
-rw-r--r-- | python-dcae-policy/dcaepolicy/dcae_consul_client.py | 45 | ||||
-rw-r--r-- | python-dcae-policy/dcaepolicy/dcae_policy.py | 91 | ||||
-rw-r--r-- | python-dcae-policy/requirements.txt | 3 | ||||
-rw-r--r-- | python-dcae-policy/setup.py | 6 |
5 files changed, 36 insertions, 187 deletions
diff --git a/python-dcae-policy/README.md b/python-dcae-policy/README.md index eb26087..78047a5 100644 --- a/python-dcae-policy/README.md +++ b/python-dcae-policy/README.md @@ -33,38 +33,28 @@ from dcaepolicy import Policies # examples of **@operation** with **@Policies.<>** decorator -## **dcae.nodes.policy** cloudify.interfaces.lifecycle.**create** +## Usage + +import the dcaepolicy-node-type.yaml into your blueprint to use the dcae.nodes.type node -- retrieve the latest policy data on dcae.nodes.policy node ```yaml - dcae.nodes.policy: - derived_from: cloudify.nodes.Root - properties: - policy_id: - description: PK to policy - type: string - default: DCAE_alex.Config_empty-policy - policy_apply_mode: - description: choice of how to apply the policy update (none|script) - type: string - default: none - interfaces: - cloudify.interfaces.lifecycle: - create: - implementation: dcae_policy_plugin.dcaepolicy.policy_get +imports: + - https://YOUR_NEXUS_RAW_SERVER/type_files/dcaepolicy/1.0.0/node-type.yaml ``` -```python -@operation -@Policies.populate_policy_on_node -def policy_get(**kwargs): - """decorate with @Policies.populate_policy_on_node on dcae.nodes.policy node to - retrieve the latest policy_body for policy_id - property and save it in runtime_properties - """ - pass +provide the value for policy_id property + +```yaml +node_templates: +... + host_capacity_policy: + type: dcae.nodes.policy + properties: + policy_id: { get_input: host_capacity_policy_id } ``` +Then the dcaepolicyplugin will bring the latest policy to the dcae.nodes.policy node during the install workflow of cloudify. + ------ ## cloudify.interfaces.lifecycle.**configure** - gather policy data into runtime_properties of policy consumer node @@ -72,11 +62,9 @@ def policy_get(**kwargs): cloudify.interfaces.lifecycle: configure: implementation: dcae_policy_plugin.dcaepolicy.node_configure - ``` ```python - from dcaepolicy import Policies, POLICIES from .discovery import DiscoveryClient from .demo_app import DemoApp @@ -145,41 +133,39 @@ SERVICE_COMPONENT_NAME = "service_component_name" @operation @Policies.update_policies_on_node(configs_only=True) -def policy_update(updated_policies, notify_app_through_script=False, **kwargs): +def policy_update(updated_policies, **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:. - - :notify_app_through_script: in kwargs is set to True/False to indicate whether to invoke - the script based on policy_apply_mode property in the blueprint """ - - if not updated_policies or POLICIES not in ctx.instance.runtime_properties: - return - 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 - ctx.logger.info("example: updated app_config {0} with updated_policies: {1}" \ - .format(json.dumps(app_config), json.dumps(updated_policies))) + 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: + ctx.logger.info("notify dockerized app about updated_policies {0} and app_config {1}" + .format(json.dumps(updated_policies), json.dumps(app_config))) demo_app = DemoApp(ctx.node.id) demo_app.notify_app_through_script( POLICY_MESSAGE_TYPE, updated_policies=updated_policies, application_config=app_config ) - - # alternative 1 - use the list of updated_policies on your own - if updated_policies: - ctx.logger.warn("TBD: apply updated_policies: {0}".format(json.dumps(updated_policies))) ``` -example of the **changed\_policies** with **configs_only=True** -- list of config objects (preparsed from json string) +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 [{ @@ -224,7 +210,7 @@ example of **policies** in runtime_properties **before policy-update** "policyName": "DCAE_alex.Config_db_client_policy_id_value.2.xml", "policyConfigMessage": "Config Retrieved! ", "responseAttributes": { - + }, "policyConfigStatus": "CONFIG_RETRIEVED", "matchingConditions": { @@ -280,7 +266,7 @@ example of **policies** in runtime_properties **after policy-update** "policyName": "DCAE_alex.Config_db_client_policy_id_value.3.xml", "policyConfigMessage": "Config Retrieved! ", "responseAttributes": { - + }, "policyConfigStatus": "CONFIG_RETRIEVED", "matchingConditions": { diff --git a/python-dcae-policy/dcaepolicy/dcae_consul_client.py b/python-dcae-policy/dcaepolicy/dcae_consul_client.py deleted file mode 100644 index 9856eeb..0000000 --- a/python-dcae-policy/dcaepolicy/dcae_consul_client.py +++ /dev/null @@ -1,45 +0,0 @@ -"""client to talk to consul on standard port 8500""" - -# org.onap.dcae -# ================================================================================ -# Copyright (c) 2017 AT&T Intellectual Property. All rights reserved. -# ================================================================================ -# Licensed under the Apache License, Version 2.0 (the "License"); -# you may not use this file except in compliance with the License. -# You may obtain a copy of the License at -# -# http://www.apache.org/licenses/LICENSE-2.0 -# -# Unless required by applicable law or agreed to in writing, software -# distributed under the License is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# See the License for the specific language governing permissions and -# limitations under the License. -# ============LICENSE_END========================================================= -# -# ECOMP is a trademark and service mark of AT&T Intellectual Property. - -import requests - -CONSUL_HOST = "localhost" -CONSUL_PORT = 8500 - -class ConsulClient(object): - """talking to the local Consul agent for interfacing with Consul from the plugin. - Safe to assume that the Consul agent is always at localhost. - """ - CONSUL_SERVICE_MASK = "{0}/v1/catalog/service/{1}" - SERVICE_MASK = "http://{0}:{1}" - _consul_url = "http://{0}:{1}".format(CONSUL_HOST, CONSUL_PORT) - - @staticmethod - def get_service_url(service_name): - """find the service record in consul""" - response = requests.get(ConsulClient.CONSUL_SERVICE_MASK.format( \ - ConsulClient._consul_url, service_name)) - response.raise_for_status() - resp_json = response.json() - if resp_json: - service = resp_json[0] - return ConsulClient.SERVICE_MASK.format( \ - service["ServiceAddress"], service["ServicePort"]) diff --git a/python-dcae-policy/dcaepolicy/dcae_policy.py b/python-dcae-policy/dcaepolicy/dcae_policy.py index 1ae9b8f..d64d50f 100644 --- a/python-dcae-policy/dcaepolicy/dcae_policy.py +++ b/python-dcae-policy/dcaepolicy/dcae_policy.py @@ -19,23 +19,15 @@ # # ECOMP is a trademark and service mark of AT&T Intellectual Property. - import json -import uuid import copy from functools import wraps -import requests - from cloudify import ctx from cloudify.context import NODE_INSTANCE from cloudify.exceptions import NonRecoverableError -from .dcae_consul_client import ConsulClient - POLICIES = 'policies' -SERVICE_NAME_POLICY_HANDLER = "policy_handler" -X_ECOMP_REQUESTID = 'X-ECOMP-RequestID' POLICY_ID = 'policy_id' POLICY_APPLY_MODE = 'policy_apply_mode' @@ -44,69 +36,9 @@ POLICY_VERSION = "policyVersion" POLICY_CONFIG = 'config' DCAE_POLICY_TYPE = 'dcae.nodes.policy' POLICY_MESSAGE_TYPE = 'policy' -POLICY_NOTIFICATION_SCRIPT = 'script' class Policies(object): """static class for policy operations""" - _policy_handler_url = None - - @staticmethod - def _get_latest_policy(policy_id): - """retrieve the latest policy for policy_id from policy-handler""" - if not Policies._policy_handler_url: - Policies._policy_handler_url = ConsulClient.get_service_url(SERVICE_NAME_POLICY_HANDLER) - - ph_path = "{0}/policy_latest/{1}".format(Policies._policy_handler_url, policy_id) - headers = {X_ECOMP_REQUESTID: str(uuid.uuid4())} - - ctx.logger.info("getting latest policy from {0} headers={1}".format( \ - ph_path, json.dumps(headers))) - res = requests.get(ph_path, headers=headers) - res.raise_for_status() - - if res.status_code == requests.codes.ok: - return res.json() - return {} - - @staticmethod - def populate_policy_on_node(func): - """dcae.nodes.policy node retrieves the policy_body identified by policy_id property - from policy-handler that gets it from policy-engine. - - Places the found policy into runtime_properties["policy_body"]. - """ - if not func: - return - - @wraps(func) - def wrapper(*args, **kwargs): - """retrieve and save the latest policy body per policy_id""" - try: - if ctx.type != NODE_INSTANCE: - return func(*args, **kwargs) - - if POLICY_ID not in ctx.node.properties: - ctx.logger.error("no {0} found in ctx.node.properties".format(POLICY_ID)) - return func(*args, **kwargs) - - policy_id = ctx.node.properties[POLICY_ID] - policy = Policies._get_latest_policy(policy_id) - if policy: - ctx.logger.info("found policy {0}".format(json.dumps(policy))) - if POLICY_BODY in policy: - ctx.instance.runtime_properties[POLICY_BODY] = policy[POLICY_BODY] - else: - error = "policy not found for policy_id {0}".format(policy_id) - ctx.logger.error(error) - raise NonRecoverableError(error) - - except Exception as ex: - error = "Failed to get the policy {0}".format(str(ex)) - ctx.logger.error(error) - raise NonRecoverableError(error) - - return func(*args, **kwargs) - return wrapper @staticmethod def gather_policies_to_node(func): @@ -206,9 +138,6 @@ class Policies(object): 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. - - :notify_app_through_script: in kwargs is set to True/False to indicate whether to invoke - the script based on policy_apply_mode property in the blueprint """ def update_policies_decorator(func): """actual decorator""" @@ -223,34 +152,16 @@ class Policies(object): updated_policies = Policies._update_policies_on_ctx(updated_policies) if updated_policies: - notify_app_through_script = max( - updated_policies, - key=lambda pol: pol.get(POLICY_APPLY_MODE) == POLICY_NOTIFICATION_SCRIPT - ) - 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, - notify_app_through_script=notify_app_through_script, **kwargs) + return func(updated_policies, **kwargs) return wrapper return update_policies_decorator - @staticmethod - def get_notify_app_through_script(): - """returns True if any of the policy has property policy_apply_mode==script""" - if ctx.type != NODE_INSTANCE \ - or POLICIES not in ctx.instance.runtime_properties: - return - policies = ctx.instance.runtime_properties[POLICIES] - if not policies: - return - for policy_id in policies: - if policies[policy_id].get(POLICY_APPLY_MODE) == POLICY_NOTIFICATION_SCRIPT: - return True @staticmethod def get_policy_configs(): diff --git a/python-dcae-policy/requirements.txt b/python-dcae-policy/requirements.txt index cdcf98d..2f98303 100644 --- a/python-dcae-policy/requirements.txt +++ b/python-dcae-policy/requirements.txt @@ -1,2 +1 @@ -cloudify-plugins-common==3.4 -requests>=2.11.0,<3.0.0 +# - not here because cloudify provides it: cloudify-plugins-common==3.4 diff --git a/python-dcae-policy/setup.py b/python-dcae-policy/setup.py index 1fd4550..5f0c765 100644 --- a/python-dcae-policy/setup.py +++ b/python-dcae-policy/setup.py @@ -24,17 +24,15 @@ from setuptools import setup setup( name='dcaepolicy', description='lib of policy decorators to be used by cloudify plugins of dcae controller', - version="0.0.2", + version="1.0.0", author='Alex Shatov', email="dcae@lists.openecomp.org", packages=['dcaepolicy'], install_requires=[ - "cloudify-plugins-common==3.4", - "requests>=2.11.0,<3.0.0" ], keywords='policy dcae controller cloudify plugin', classifiers=[ - 'Development Status :: 3 - Alpha', + 'Development Status :: 4 - Beta', 'Intended Audience :: Developers', 'Programming Language :: Python :: 2.7' ] |