1 # ==================================================================================
2 # Copyright (c) 2019-2020 Nokia
3 # Copyright (c) 2018-2020 AT&T Intellectual Property.
5 # Licensed under the Apache License, Version 2.0 (the "License");
6 # you may not use this file except in compliance with the License.
7 # You may obtain a copy of the License at
9 # http://www.apache.org/licenses/LICENSE-2.0
11 # Unless required by applicable law or agreed to in writing, software
12 # distributed under the License is distributed on an "AS IS" BASIS,
13 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14 # See the License for the specific language governing permissions and
15 # limitations under the License.
16 # ==================================================================================
18 Represents A1s database and database access functions.
23 from threading import Thread
24 from mdclogpy import Logger
25 from ricxappframe.xapp_sdl import SDLWrapper
26 from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, PolicyTypeIdMismatch, CantDeleteNonEmptyType
29 INSTANCE_DELETE_NO_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_NO_RESP_TTL", 5))
30 INSTANCE_DELETE_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_RESP_TTL", 5))
31 USE_FAKE_SDL = bool(distutils.util.strtobool(os.environ.get("USE_FAKE_SDL", "False")))
33 TYPE_PREFIX = "a1.policy_type."
34 INSTANCE_PREFIX = "a1.policy_instance."
35 METADATA_PREFIX = "a1.policy_inst_metadata."
36 HANDLER_PREFIX = "a1.policy_handler."
39 mdc_logger = Logger(name=__name__)
41 mdc_logger.debug("Using fake SDL")
42 SDL = SDLWrapper(use_fake_sdl=USE_FAKE_SDL)
47 def _generate_type_key(policy_type_id):
49 generate a key for a policy type
51 return "{0}{1}".format(TYPE_PREFIX, policy_type_id)
54 def _generate_instance_key(policy_type_id, policy_instance_id):
56 generate a key for a policy instance
58 return "{0}{1}.{2}".format(INSTANCE_PREFIX, policy_type_id, policy_instance_id)
61 def _generate_instance_metadata_key(policy_type_id, policy_instance_id):
63 generate a key for a policy instance metadata
65 return "{0}{1}.{2}".format(METADATA_PREFIX, policy_type_id, policy_instance_id)
68 def _generate_handler_prefix(policy_type_id, policy_instance_id):
70 generate the prefix to a handler key
72 return "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
75 def _generate_handler_key(policy_type_id, policy_instance_id, handler_id):
77 generate a key for a policy handler
79 return "{0}{1}".format(_generate_handler_prefix(policy_type_id, policy_instance_id), handler_id)
82 def _type_is_valid(policy_type_id):
84 check that a type is valid
86 if SDL.get(A1NS, _generate_type_key(policy_type_id)) is None:
87 raise PolicyTypeNotFound(policy_type_id)
90 def _instance_is_valid(policy_type_id, policy_instance_id):
92 check that an instance is valid
94 _type_is_valid(policy_type_id)
95 if SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) is None:
96 raise PolicyInstanceNotFound(policy_type_id)
99 def _get_statuses(policy_type_id, policy_instance_id):
101 shared helper to get statuses for an instance
103 _instance_is_valid(policy_type_id, policy_instance_id)
104 prefixes_for_handler = "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
105 return list(SDL.find_and_get(A1NS, prefixes_for_handler).values())
108 def _get_instance_list(policy_type_id):
110 shared helper to get instance list for a type
112 _type_is_valid(policy_type_id)
113 prefixes_for_type = "{0}{1}.".format(INSTANCE_PREFIX, policy_type_id)
114 instancekeys = SDL.find_and_get(A1NS, prefixes_for_type).keys()
115 return [k.split(prefixes_for_type)[1] for k in instancekeys]
118 def _clear_handlers(policy_type_id, policy_instance_id):
120 delete all the handlers for a policy instance
122 all_handlers_pref = _generate_handler_prefix(policy_type_id, policy_instance_id)
123 keys = SDL.find_and_get(A1NS, all_handlers_pref)
128 def _get_metadata(policy_type_id, policy_instance_id):
130 get instance metadata
132 _instance_is_valid(policy_type_id, policy_instance_id)
133 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
134 return SDL.get(A1NS, metadata_key)
137 def _delete_after(policy_type_id, policy_instance_id, ttl):
139 this is a blocking function, must call this in a thread to not block!
140 waits ttl seconds, then deletes the instance
142 _instance_is_valid(policy_type_id, policy_instance_id)
147 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
148 SDL.delete(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) # delete instance
149 SDL.delete(A1NS, _generate_instance_metadata_key(policy_type_id, policy_instance_id)) # delete instance metadata
150 mdc_logger.debug("type {0} instance {1} deleted".format(policy_type_id, policy_instance_id))
158 retrieve all type ids
160 typekeys = SDL.find_and_get(A1NS, TYPE_PREFIX).keys()
161 # policy types are ints but they get butchered to strings in the KV
162 return [int(k.split(TYPE_PREFIX)[1]) for k in typekeys]
165 def store_policy_type(policy_type_id, body):
167 store a policy type if it doesn't already exist
169 if policy_type_id != body['policy_type_id']:
170 raise PolicyTypeIdMismatch("{0} vs. {1}".format(policy_type_id, body['policy_type_id']))
171 key = _generate_type_key(policy_type_id)
172 if SDL.get(A1NS, key) is not None:
173 raise PolicyTypeAlreadyExists(policy_type_id)
174 SDL.set(A1NS, key, body)
177 def delete_policy_type(policy_type_id):
179 delete a policy type; can only be done if there are no instances (business logic)
181 pil = get_instance_list(policy_type_id)
182 if pil == []: # empty, can delete
183 SDL.delete(A1NS, _generate_type_key(policy_type_id))
185 raise CantDeleteNonEmptyType(policy_type_id)
188 def get_policy_type(policy_type_id):
192 _type_is_valid(policy_type_id)
193 return SDL.get(A1NS, _generate_type_key(policy_type_id))
199 def store_policy_instance(policy_type_id, policy_instance_id, instance):
201 Store a policy instance
203 _type_is_valid(policy_type_id)
204 creation_timestamp = time.time()
208 key = _generate_instance_key(policy_type_id, policy_instance_id)
209 if SDL.get(A1NS, key) is not None:
211 # Reset the statuses because this is a new policy instance, even if it was overwritten
212 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
213 SDL.set(A1NS, key, instance)
215 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
216 SDL.set(A1NS, metadata_key, {"created_at": creation_timestamp, "has_been_deleted": False})
221 def get_policy_instance(policy_type_id, policy_instance_id):
223 Retrieve a policy instance
225 _instance_is_valid(policy_type_id, policy_instance_id)
226 return SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id))
229 def get_instance_list(policy_type_id):
231 retrieve all instance ids for a type
233 return _get_instance_list(policy_type_id)
236 def delete_policy_instance(policy_type_id, policy_instance_id):
238 initially sets has_been_deleted in the status
239 then launches a thread that waits until the relevent timer expires, and finally deletes the instance
241 _instance_is_valid(policy_type_id, policy_instance_id)
243 # set the metadata first
244 deleted_timestamp = time.time()
245 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
246 existing_metadata = _get_metadata(policy_type_id, policy_instance_id)
250 {"created_at": existing_metadata["created_at"], "has_been_deleted": True, "deleted_at": deleted_timestamp},
254 vector = _get_statuses(policy_type_id, policy_instance_id)
256 # handler is empty; we wait for t1 to expire then goodnight
257 clos = lambda: _delete_after(policy_type_id, policy_instance_id, INSTANCE_DELETE_NO_RESP_TTL)
259 # handler is not empty, we wait max t1,t2 to expire then goodnight
260 clos = lambda: _delete_after(
261 policy_type_id, policy_instance_id, max(INSTANCE_DELETE_RESP_TTL, INSTANCE_DELETE_NO_RESP_TTL)
263 Thread(target=clos).start()
269 def set_policy_instance_status(policy_type_id, policy_instance_id, handler_id, status):
271 update the database status for a handler
272 called from a1's rmr thread
274 _type_is_valid(policy_type_id)
275 _instance_is_valid(policy_type_id, policy_instance_id)
276 SDL.set(A1NS, _generate_handler_key(policy_type_id, policy_instance_id, handler_id), status)
279 def get_policy_instance_status(policy_type_id, policy_instance_id):
281 Gets the status of an instance
283 _instance_is_valid(policy_type_id, policy_instance_id)
284 metadata = _get_metadata(policy_type_id, policy_instance_id)
285 metadata["instance_status"] = "NOT IN EFFECT"
286 for i in _get_statuses(policy_type_id, policy_instance_id):
288 metadata["instance_status"] = "IN EFFECT"