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.
22 from threading import Thread
23 from mdclogpy import Logger
24 from ricxappframe.xapp_sdl import SDLWrapper
25 from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, CantDeleteNonEmptyType
28 INSTANCE_DELETE_NO_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_NO_RESP_TTL", 5))
29 INSTANCE_DELETE_RESP_TTL = int(os.environ.get("INSTANCE_DELETE_RESP_TTL", 5))
31 TYPE_PREFIX = "a1.policy_type."
32 INSTANCE_PREFIX = "a1.policy_instance."
33 METADATA_PREFIX = "a1.policy_inst_metadata."
34 HANDLER_PREFIX = "a1.policy_handler."
37 mdc_logger = Logger(name=__name__)
43 def _generate_type_key(policy_type_id):
45 generate a key for a policy type
47 return "{0}{1}".format(TYPE_PREFIX, policy_type_id)
50 def _generate_instance_key(policy_type_id, policy_instance_id):
52 generate a key for a policy instance
54 return "{0}{1}.{2}".format(INSTANCE_PREFIX, policy_type_id, policy_instance_id)
57 def _generate_instance_metadata_key(policy_type_id, policy_instance_id):
59 generate a key for a policy instance metadata
61 return "{0}{1}.{2}".format(METADATA_PREFIX, policy_type_id, policy_instance_id)
64 def _generate_handler_prefix(policy_type_id, policy_instance_id):
66 generate the prefix to a handler key
68 return "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
71 def _generate_handler_key(policy_type_id, policy_instance_id, handler_id):
73 generate a key for a policy handler
75 return "{0}{1}".format(_generate_handler_prefix(policy_type_id, policy_instance_id), handler_id)
78 def _type_is_valid(policy_type_id):
80 check that a type is valid
82 if SDL.get(A1NS, _generate_type_key(policy_type_id)) is None:
83 raise PolicyTypeNotFound()
86 def _instance_is_valid(policy_type_id, policy_instance_id):
88 check that an instance is valid
90 _type_is_valid(policy_type_id)
91 if SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) is None:
92 raise PolicyInstanceNotFound
95 def _get_statuses(policy_type_id, policy_instance_id):
97 shared helper to get statuses for an instance
99 _instance_is_valid(policy_type_id, policy_instance_id)
100 prefixes_for_handler = "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
101 return list(SDL.find_and_get(A1NS, prefixes_for_handler).values())
104 def _get_instance_list(policy_type_id):
106 shared helper to get instance list for a type
108 _type_is_valid(policy_type_id)
109 prefixes_for_type = "{0}{1}.".format(INSTANCE_PREFIX, policy_type_id)
110 instancekeys = SDL.find_and_get(A1NS, prefixes_for_type).keys()
111 return [k.split(prefixes_for_type)[1] for k in instancekeys]
114 def _clear_handlers(policy_type_id, policy_instance_id):
116 delete all the handlers for a policy instance
118 all_handlers_pref = _generate_handler_prefix(policy_type_id, policy_instance_id)
119 keys = SDL.find_and_get(A1NS, all_handlers_pref)
124 def _get_metadata(policy_type_id, policy_instance_id):
126 get instance metadata
128 _instance_is_valid(policy_type_id, policy_instance_id)
129 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
130 return SDL.get(A1NS, metadata_key)
133 def _delete_after(policy_type_id, policy_instance_id, ttl):
135 this is a blocking function, must call this in a thread to not block!
136 waits ttl seconds, then deletes the instance
138 _instance_is_valid(policy_type_id, policy_instance_id)
143 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
144 SDL.delete(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) # delete instance
145 SDL.delete(A1NS, _generate_instance_metadata_key(policy_type_id, policy_instance_id)) # delete instance metadata
146 mdc_logger.debug("type {0} instance {1} deleted".format(policy_type_id, policy_instance_id))
154 retrieve all type ids
156 typekeys = SDL.find_and_get(A1NS, TYPE_PREFIX).keys()
157 # policy types are ints but they get butchered to strings in the KV
158 return [int(k.split(TYPE_PREFIX)[1]) for k in typekeys]
161 def store_policy_type(policy_type_id, body):
163 store a policy type if it doesn't already exist
165 key = _generate_type_key(policy_type_id)
166 if SDL.get(A1NS, key) is not None:
167 raise PolicyTypeAlreadyExists()
168 # overwrite ID in body to enforce consistency
169 body['policy_type_id'] = policy_type_id
170 SDL.set(A1NS, key, body)
173 def delete_policy_type(policy_type_id):
175 delete a policy type; can only be done if there are no instances (business logic)
177 pil = get_instance_list(policy_type_id)
178 if pil == []: # empty, can delete
179 SDL.delete(A1NS, _generate_type_key(policy_type_id))
181 raise CantDeleteNonEmptyType()
184 def get_policy_type(policy_type_id):
188 _type_is_valid(policy_type_id)
189 return SDL.get(A1NS, _generate_type_key(policy_type_id))
195 def store_policy_instance(policy_type_id, policy_instance_id, instance):
197 Store a policy instance
199 _type_is_valid(policy_type_id)
200 creation_timestamp = time.time()
203 key = _generate_instance_key(policy_type_id, policy_instance_id)
204 if SDL.get(A1NS, key) is not None:
205 # Reset the statuses because this is a new policy instance, even if it was overwritten
206 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
207 SDL.set(A1NS, key, instance)
209 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
210 SDL.set(A1NS, metadata_key, {"created_at": creation_timestamp, "has_been_deleted": False})
213 def get_policy_instance(policy_type_id, policy_instance_id):
215 Retrieve a policy instance
217 _instance_is_valid(policy_type_id, policy_instance_id)
218 return SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id))
221 def get_instance_list(policy_type_id):
223 retrieve all instance ids for a type
225 return _get_instance_list(policy_type_id)
228 def delete_policy_instance(policy_type_id, policy_instance_id):
230 initially sets has_been_deleted in the status
231 then launches a thread that waits until the relevent timer expires, and finally deletes the instance
233 _instance_is_valid(policy_type_id, policy_instance_id)
235 # set the metadata first
236 deleted_timestamp = time.time()
237 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
238 existing_metadata = _get_metadata(policy_type_id, policy_instance_id)
242 {"created_at": existing_metadata["created_at"], "has_been_deleted": True, "deleted_at": deleted_timestamp},
246 vector = _get_statuses(policy_type_id, policy_instance_id)
248 # handler is empty; we wait for t1 to expire then goodnight
249 clos = lambda: _delete_after(policy_type_id, policy_instance_id, INSTANCE_DELETE_NO_RESP_TTL)
251 # handler is not empty, we wait max t1,t2 to expire then goodnight
252 clos = lambda: _delete_after(
253 policy_type_id, policy_instance_id, max(INSTANCE_DELETE_RESP_TTL, INSTANCE_DELETE_NO_RESP_TTL)
255 Thread(target=clos).start()
261 def set_policy_instance_status(policy_type_id, policy_instance_id, handler_id, status):
263 update the database status for a handler
264 called from a1's rmr thread
266 _type_is_valid(policy_type_id)
267 _instance_is_valid(policy_type_id, policy_instance_id)
268 SDL.set(A1NS, _generate_handler_key(policy_type_id, policy_instance_id, handler_id), status)
271 def get_policy_instance_status(policy_type_id, policy_instance_id):
273 Gets the status of an instance
275 _instance_is_valid(policy_type_id, policy_instance_id)
276 metadata = _get_metadata(policy_type_id, policy_instance_id)
277 metadata["instance_status"] = "NOT IN EFFECT"
278 for i in _get_statuses(policy_type_id, policy_instance_id):
280 metadata["instance_status"] = "IN EFFECT"