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__)
40 mdc_logger.mdclog_format_init(configmap_monitor=True)
42 mdc_logger.debug("Using fake SDL")
43 SDL = SDLWrapper(use_fake_sdl=USE_FAKE_SDL)
48 def _generate_type_key(policy_type_id):
50 generate a key for a policy type
52 return "{0}{1}".format(TYPE_PREFIX, policy_type_id)
55 def _generate_instance_key(policy_type_id, policy_instance_id):
57 generate a key for a policy instance
59 return "{0}{1}.{2}".format(INSTANCE_PREFIX, policy_type_id, policy_instance_id)
62 def _generate_instance_metadata_key(policy_type_id, policy_instance_id):
64 generate a key for a policy instance metadata
66 return "{0}{1}.{2}".format(METADATA_PREFIX, policy_type_id, policy_instance_id)
69 def _generate_handler_prefix(policy_type_id, policy_instance_id):
71 generate the prefix to a handler key
73 return "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
76 def _generate_handler_key(policy_type_id, policy_instance_id, handler_id):
78 generate a key for a policy handler
80 return "{0}{1}".format(_generate_handler_prefix(policy_type_id, policy_instance_id), handler_id)
83 def _type_is_valid(policy_type_id):
85 check that a type is valid
87 if SDL.get(A1NS, _generate_type_key(policy_type_id)) is None:
88 raise PolicyTypeNotFound(policy_type_id)
91 def _instance_is_valid(policy_type_id, policy_instance_id):
93 check that an instance is valid
95 _type_is_valid(policy_type_id)
96 if SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) is None:
97 raise PolicyInstanceNotFound(policy_type_id)
100 def _get_statuses(policy_type_id, policy_instance_id):
102 shared helper to get statuses for an instance
104 _instance_is_valid(policy_type_id, policy_instance_id)
105 prefixes_for_handler = "{0}{1}.{2}.".format(HANDLER_PREFIX, policy_type_id, policy_instance_id)
106 return list(SDL.find_and_get(A1NS, prefixes_for_handler).values())
109 def _get_instance_list(policy_type_id):
111 shared helper to get instance list for a type
113 _type_is_valid(policy_type_id)
114 prefixes_for_type = "{0}{1}.".format(INSTANCE_PREFIX, policy_type_id)
115 instancekeys = SDL.find_and_get(A1NS, prefixes_for_type).keys()
116 return [k.split(prefixes_for_type)[1] for k in instancekeys]
119 def _clear_handlers(policy_type_id, policy_instance_id):
121 delete all the handlers for a policy instance
123 all_handlers_pref = _generate_handler_prefix(policy_type_id, policy_instance_id)
124 keys = SDL.find_and_get(A1NS, all_handlers_pref)
129 def _get_metadata(policy_type_id, policy_instance_id):
131 get instance metadata
133 _instance_is_valid(policy_type_id, policy_instance_id)
134 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
135 return SDL.get(A1NS, metadata_key)
138 def _delete_after(policy_type_id, policy_instance_id, ttl):
140 this is a blocking function, must call this in a thread to not block!
141 waits ttl seconds, then deletes the instance
143 _instance_is_valid(policy_type_id, policy_instance_id)
148 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
149 SDL.delete(A1NS, _generate_instance_key(policy_type_id, policy_instance_id)) # delete instance
150 SDL.delete(A1NS, _generate_instance_metadata_key(policy_type_id, policy_instance_id)) # delete instance metadata
151 mdc_logger.debug("type {0} instance {1} deleted".format(policy_type_id, policy_instance_id))
159 retrieve all type ids
161 typekeys = SDL.find_and_get(A1NS, TYPE_PREFIX).keys()
162 # policy types are ints but they get butchered to strings in the KV
163 return [int(k.split(TYPE_PREFIX)[1]) for k in typekeys]
166 def store_policy_type(policy_type_id, body):
168 store a policy type if it doesn't already exist
170 if policy_type_id != body['policy_type_id']:
171 raise PolicyTypeIdMismatch("{0} vs. {1}".format(policy_type_id, body['policy_type_id']))
172 key = _generate_type_key(policy_type_id)
173 if SDL.get(A1NS, key) is not None:
174 raise PolicyTypeAlreadyExists(policy_type_id)
175 SDL.set(A1NS, key, body)
178 def delete_policy_type(policy_type_id):
180 delete a policy type; can only be done if there are no instances (business logic)
182 pil = get_instance_list(policy_type_id)
183 if pil == []: # empty, can delete
184 SDL.delete(A1NS, _generate_type_key(policy_type_id))
186 raise CantDeleteNonEmptyType(policy_type_id)
189 def get_policy_type(policy_type_id):
193 _type_is_valid(policy_type_id)
194 return SDL.get(A1NS, _generate_type_key(policy_type_id))
200 def store_policy_instance(policy_type_id, policy_instance_id, instance):
202 Store a policy instance
204 _type_is_valid(policy_type_id)
205 creation_timestamp = time.time()
209 key = _generate_instance_key(policy_type_id, policy_instance_id)
210 if SDL.get(A1NS, key) is not None:
212 # Reset the statuses because this is a new policy instance, even if it was overwritten
213 _clear_handlers(policy_type_id, policy_instance_id) # delete all the handlers
214 SDL.set(A1NS, key, instance)
216 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
217 SDL.set(A1NS, metadata_key, {"created_at": creation_timestamp, "has_been_deleted": False})
222 def get_policy_instance(policy_type_id, policy_instance_id):
224 Retrieve a policy instance
226 _instance_is_valid(policy_type_id, policy_instance_id)
227 return SDL.get(A1NS, _generate_instance_key(policy_type_id, policy_instance_id))
230 def get_instance_list(policy_type_id):
232 retrieve all instance ids for a type
234 return _get_instance_list(policy_type_id)
237 def delete_policy_instance(policy_type_id, policy_instance_id):
239 initially sets has_been_deleted in the status
240 then launches a thread that waits until the relevent timer expires, and finally deletes the instance
242 _instance_is_valid(policy_type_id, policy_instance_id)
244 # set the metadata first
245 deleted_timestamp = time.time()
246 metadata_key = _generate_instance_metadata_key(policy_type_id, policy_instance_id)
247 existing_metadata = _get_metadata(policy_type_id, policy_instance_id)
251 {"created_at": existing_metadata["created_at"], "has_been_deleted": True, "deleted_at": deleted_timestamp},
255 vector = _get_statuses(policy_type_id, policy_instance_id)
257 # handler is empty; we wait for t1 to expire then goodnight
258 clos = lambda: _delete_after(policy_type_id, policy_instance_id, INSTANCE_DELETE_NO_RESP_TTL)
260 # handler is not empty, we wait max t1,t2 to expire then goodnight
261 clos = lambda: _delete_after(
262 policy_type_id, policy_instance_id, max(INSTANCE_DELETE_RESP_TTL, INSTANCE_DELETE_NO_RESP_TTL)
264 Thread(target=clos).start()
270 def set_policy_instance_status(policy_type_id, policy_instance_id, handler_id, status):
272 update the database status for a handler
273 called from a1's rmr thread
275 _type_is_valid(policy_type_id)
276 _instance_is_valid(policy_type_id, policy_instance_id)
277 SDL.set(A1NS, _generate_handler_key(policy_type_id, policy_instance_id, handler_id), status)
280 def get_policy_instance_status(policy_type_id, policy_instance_id):
282 Gets the status of an instance
284 _instance_is_valid(policy_type_id, policy_instance_id)
285 metadata = _get_metadata(policy_type_id, policy_instance_id)
286 metadata["instance_status"] = "NOT IN EFFECT"
287 for i in _get_statuses(policy_type_id, policy_instance_id):
289 metadata["instance_status"] = "IN EFFECT"