blob: c96d4f63a3fc6911170ee2eef85d499010227a4a [file] [log] [blame]
Alex Shatov1369bea2018-01-10 11:00:50 -05001# ================================================================================
Lusheng Ji967cc9a2018-02-12 10:45:42 -05002# Copyright (c) 2018 AT&T Intellectual Property. All rights reserved.
Alex Shatov1369bea2018-01-10 11:00:50 -05003# ================================================================================
4# Licensed under the Apache License, Version 2.0 (the "License");
5# you may not use this file except in compliance with the License.
6# You may obtain a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS,
12# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13# See the License for the specific language governing permissions and
14# limitations under the License.
15# ============LICENSE_END=========================================================
16#
17# ECOMP is a trademark and service mark of AT&T Intellectual Property.
18
Alex Shatovf53e5e72018-01-11 11:15:56 -050019"""policy-client communicates with policy-engine thru REST API"""
20
Alex Shatov1369bea2018-01-10 11:00:50 -050021import json
Alex Shatova2b26292018-03-13 17:50:51 -040022import logging
Alex Shatov1369bea2018-01-10 11:00:50 -050023import re
24
Alex Shatova2b26292018-03-13 17:50:51 -040025from .policy_consts import (POLICY_BODY, POLICY_CONFIG, POLICY_ID, POLICY_NAME,
26 POLICY_VERSION)
Alex Shatov1369bea2018-01-10 11:00:50 -050027
28class PolicyUtils(object):
29 """policy-client utils"""
30 _logger = logging.getLogger("policy_handler.policy_utils")
31 _policy_name_ext = re.compile('[.][0-9]+[.][a-zA-Z]+$')
32
33 @staticmethod
Alex Shatov1369bea2018-01-10 11:00:50 -050034 def extract_policy_id(policy_name):
35 """ policy_name = policy_id + "." + <version> + "." + <extension>
36 For instance,
37 policy_name = DCAE_alex.Config_alex_policy_number_1.3.xml
38 policy_id = DCAE_alex.Config_alex_policy_number_1
39 policy_scope = DCAE_alex
40 policy_class = Config
41 policy_version = 3
42 type = extension = xml
43 delimiter = "."
44 policy_class_delimiter = "_"
45 policy_name in PAP = DCAE_alex.alex_policy_number_1
46 """
47 if not policy_name:
48 return
49 return PolicyUtils._policy_name_ext.sub('', policy_name)
50
51 @staticmethod
52 def parse_policy_config(policy):
53 """try parsing the config in policy."""
54 if not policy:
55 return policy
56 config = policy.get(POLICY_BODY, {}).get(POLICY_CONFIG)
57 if config:
Alex Shatova2b26292018-03-13 17:50:51 -040058 policy[POLICY_BODY][POLICY_CONFIG] = Utils.safe_json_parse(config)
Alex Shatov1369bea2018-01-10 11:00:50 -050059 return policy
60
61 @staticmethod
62 def convert_to_policy(policy_config):
63 """wrap policy_config received from policy-engine with policy_id."""
64 if not policy_config:
65 return
66 policy_name = policy_config.get(POLICY_NAME)
67 policy_version = policy_config.get(POLICY_VERSION)
68 if not policy_name or not policy_version:
69 return
70 policy_id = PolicyUtils.extract_policy_id(policy_name)
71 if not policy_id:
72 return
73 return {POLICY_ID:policy_id, POLICY_BODY:policy_config}
74
75 @staticmethod
76 def select_latest_policy(policy_configs, min_version_expected=None, ignore_policy_names=None):
77 """For some reason, the policy-engine returns all version of the policy_configs.
78 DCAE-Controller is only interested in the latest version
79 """
80 if not policy_configs:
81 return
82 latest_policy_config = {}
83 for policy_config in policy_configs:
84 policy_name = policy_config.get(POLICY_NAME)
85 policy_version = policy_config.get(POLICY_VERSION)
86 if not policy_name or not policy_version or not policy_version.isdigit():
87 continue
88 policy_version = int(policy_version)
89 if min_version_expected and policy_version < min_version_expected:
90 continue
91 if ignore_policy_names and policy_name in ignore_policy_names:
92 continue
93
94 if not latest_policy_config \
95 or int(latest_policy_config[POLICY_VERSION]) < policy_version:
96 latest_policy_config = policy_config
97
98 return PolicyUtils.parse_policy_config(PolicyUtils.convert_to_policy(latest_policy_config))
99
100 @staticmethod
101 def select_latest_policies(policy_configs):
102 """For some reason, the policy-engine returns all version of the policy_configs.
103 DCAE-Controller is only interested in the latest versions
104 """
105 if not policy_configs:
106 return {}
107 policies = {}
108 for policy_config in policy_configs:
109 policy = PolicyUtils.convert_to_policy(policy_config)
110 if not policy:
111 continue
112 policy_id = policy.get(POLICY_ID)
113 policy_version = policy.get(POLICY_BODY, {}).get(POLICY_VERSION)
114 if not policy_id or not policy_version or not policy_version.isdigit():
115 continue
116 if policy_id not in policies \
117 or int(policy_version) > int(policies[policy_id][POLICY_BODY][POLICY_VERSION]):
118 policies[policy_id] = policy
119
120 for policy_id in policies:
121 policies[policy_id] = PolicyUtils.parse_policy_config(policies[policy_id])
122
123 return policies
Alex Shatova2b26292018-03-13 17:50:51 -0400124
125class Utils(object):
126 """general purpose utils"""
127 _logger = logging.getLogger("policy_handler.utils")
128
129 @staticmethod
130 def safe_json_parse(json_str):
131 """try parsing json without exception - returns the json_str back if fails"""
132 if not json_str:
133 return json_str
134 try:
135 return json.loads(json_str)
136 except (ValueError, TypeError) as err:
Alex Shatov51052582018-05-18 15:13:40 -0400137 Utils._logger.warn("unexpected json error(%s): len(%s) str[:100]: (%s)",
138 str(err), len(json_str), str(json_str)[:100])
Alex Shatova2b26292018-03-13 17:50:51 -0400139 return json_str
140
141 @staticmethod
142 def are_the_same(body_1, body_2):
143 """check whether both objects are the same"""
144 if (body_1 and not body_2) or (not body_1 and body_2):
145 Utils._logger.debug("only one is empty %s != %s", body_1, body_2)
146 return False
147
148 if body_1 is None and body_2 is None:
149 return True
150
151 if isinstance(body_1, list) and isinstance(body_2, list):
152 if len(body_1) != len(body_2):
153 Utils._logger.debug("len %s != %s", json.dumps(body_1), json.dumps(body_2))
154 return False
155
156 for val_1, val_2 in zip(body_1, body_2):
157 if not Utils.are_the_same(val_1, val_2):
158 return False
159 return True
160
161 if isinstance(body_1, dict) and isinstance(body_2, dict):
162 if body_1.viewkeys() ^ body_2.viewkeys():
163 Utils._logger.debug("keys %s != %s", json.dumps(body_1), json.dumps(body_2))
164 return False
165
166 for key, val_1 in body_1.iteritems():
167 if not Utils.are_the_same(val_1, body_2[key]):
168 return False
169 return True
170
171 # ... here when primitive values or mismatched types ...
172 the_same_values = (body_1 == body_2)
173 if not the_same_values:
174 Utils._logger.debug("values %s != %s", body_1, body_2)
175 return the_same_values