1 # ==================================================================================
2 # Copyright (c) 2019 Nokia
3 # Copyright (c) 2018-2019 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 # ==================================================================================
19 Represents A1s database and database access functions.
20 In the future, this may change to use a different backend, possibly dramatically.
21 Hopefully, the access functions are a good api so nothing else has to change when this happens
23 For now, the database is in memory.
24 We use dict data structures (KV) with the expectation of having to move this into Redis
27 from a1.exceptions import PolicyTypeNotFound, PolicyInstanceNotFound, PolicyTypeAlreadyExists, CantDeleteNonEmptyType
28 from a1 import get_module_logger
31 logger = get_module_logger(__name__)
33 # This is essentially mockouts for future KV
34 # Note that the D subkey won't be needed when in redis, since you can store data at x anx x_y
44 def _get_statuses(policy_type_id, policy_instance_id):
46 shared helper to get statuses for an instance
48 instance_is_valid(policy_type_id, policy_instance_id)
49 return [v for _, v in POLICY_DATA[policy_type_id][I][policy_instance_id][H].items()]
52 def _get_instance_list(policy_type_id):
54 shared helper to get instance list for a type
56 type_is_valid(policy_type_id)
57 return list(POLICY_DATA[policy_type_id][I].keys())
60 def _clean_up_type(policy_type_id):
62 pop through a1s mailbox, updating a1s db of all policy statuses
63 for all instances of type, see if it can be deleted
65 type_is_valid(policy_type_id)
66 for msg in a1rmr.dequeue_all_waiting_messages([21024]):
67 # try to parse the messages as responses. Drop those that are malformed
68 pay = json.loads(msg["payload"])
69 if "policy_type_id" in pay and "policy_instance_id" in pay and "handler_id" in pay and "status" in pay:
71 NOTE: can't raise an exception here e.g.:
72 instance_is_valid(pti, pii)
73 because this is called on many functions; just drop bad status messages.
74 We def don't want bad messages that happen to hit a1s mailbox to blow up anything
76 NOTE2: we don't use the parameters "policy_type_id, policy_instance" from above here,
77 # because we are popping the whole mailbox, which might include other statuses
79 pti = pay["policy_type_id"]
80 pii = pay["policy_instance_id"]
81 if pti in POLICY_DATA and pii in POLICY_DATA[pti][I]: # manual check per comment above
82 POLICY_DATA[pti][I][pii][H][pay["handler_id"]] = pay["status"]
84 logger.debug("Dropping message")
87 for policy_instance_id in _get_instance_list(policy_type_id):
88 # see if we can delete
89 vector = _get_statuses(policy_type_id, policy_instance_id)
92 TODO: not being able to delete if the list is [] is prolematic.
93 There are cases, such as a bad routing file, where this type will never be able to be deleted because it never went to any xapps
94 However, A1 cannot distinguish between the case where [] was never going to work, and the case where it hasn't worked *yet*
96 However, removing this constraint also leads to problems.
97 Deleting the instance when the vector is empty, for example doing so “shortly after” the PUT, can lead to a worse race condition where the xapps get the policy after that, implement it, but because the DELETE triggered “too soon”, you can never get the status or do the delete on it again, so the xapps are all implementing the instance roguely.
99 This requires some thought to address.
100 For now we stick with the "less bad problem".
107 break # have at least one not DELETED, do nothing
109 # blow away from a1 db
111 del POLICY_DATA[policy_type_id][I][policy_instance_id]
119 retrieve all type ids
121 return list(POLICY_DATA.keys())
124 def type_is_valid(policy_type_id):
126 check that a type is valid
128 if policy_type_id not in POLICY_DATA:
129 logger.error("%s not found", policy_type_id)
130 raise PolicyTypeNotFound()
133 def store_policy_type(policy_type_id, body):
135 store a policy type if it doesn't already exist
137 if policy_type_id in POLICY_DATA:
138 raise PolicyTypeAlreadyExists()
140 POLICY_DATA[policy_type_id] = {}
141 POLICY_DATA[policy_type_id][D] = body
142 POLICY_DATA[policy_type_id][I] = {}
145 def delete_policy_type(policy_type_id):
147 delete a policy type; can only be done if there are no instances (business logic)
149 pil = get_instance_list(policy_type_id)
150 if pil == []: # empty, can delete
151 del POLICY_DATA[policy_type_id]
153 raise CantDeleteNonEmptyType()
156 def get_policy_type(policy_type_id):
160 type_is_valid(policy_type_id)
161 return POLICY_DATA[policy_type_id][D]
167 def instance_is_valid(policy_type_id, policy_instance_id):
169 check that an instance is valid
171 type_is_valid(policy_type_id)
172 if policy_instance_id not in POLICY_DATA[policy_type_id][I]:
173 raise PolicyInstanceNotFound
176 def store_policy_instance(policy_type_id, policy_instance_id, instance):
178 Store a policy instance
180 type_is_valid(policy_type_id)
183 # Reset the statuses because this is a new policy instance, even if it was overwritten
184 POLICY_DATA[policy_type_id][I][policy_instance_id] = {}
185 POLICY_DATA[policy_type_id][I][policy_instance_id][D] = instance
186 POLICY_DATA[policy_type_id][I][policy_instance_id][H] = {}
189 def get_policy_instance(policy_type_id, policy_instance_id):
191 Retrieve a policy instance
193 _clean_up_type(policy_type_id)
194 instance_is_valid(policy_type_id, policy_instance_id)
195 return POLICY_DATA[policy_type_id][I][policy_instance_id][D]
198 def get_policy_instance_statuses(policy_type_id, policy_instance_id):
200 Retrieve the status vector for a policy instance
202 _clean_up_type(policy_type_id)
203 return _get_statuses(policy_type_id, policy_instance_id)
206 def get_instance_list(policy_type_id):
208 retrieve all instance ids for a type
210 _clean_up_type(policy_type_id)
211 return _get_instance_list(policy_type_id)