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 # ==================================================================================
18 from flask import Response
19 from jsonschema import validate
21 from jsonschema.exceptions import ValidationError
22 from a1 import get_module_logger
23 from a1 import a1rmr, exceptions, data
26 logger = get_module_logger(__name__)
29 def _try_func_return(func):
31 generic caller that returns the apporp http response if exceptions are raised
35 except ValidationError as exc:
38 except exceptions.PolicyTypeAlreadyExists as exc:
41 except exceptions.PolicyTypeNotFound as exc:
44 except exceptions.PolicyInstanceNotFound as exc:
47 except exceptions.MissingManifest as exc:
49 return "A1 was unable to find the required RIC manifest. report this!", 500
50 except exceptions.MissingRmrString as exc:
52 return "A1 does not have a mapping for the desired rmr string. report this!", 500
53 except BaseException as exc:
54 # catch all, should never happen...
56 return Response(status=500)
62 def get_healthcheck():
64 Handles healthcheck GET
65 Currently, this basically checks the server is alive.a1rmr
73 def get_all_policy_types():
75 Handles GET /a1-p/policytypes
80 def create_policy_type(policy_type_id):
82 Handles PUT /a1-p/policytypes/policy_type_id
85 def _put_type_handler(policy_type_id, body):
86 data.store_policy_type(policy_type_id, body)
89 body = connexion.request.json
90 return _try_func_return(lambda: _put_type_handler(policy_type_id, body))
93 def get_policy_type(policy_type_id):
95 Handles GET /a1-p/policytypes/policy_type_id
97 return _try_func_return(lambda: data.get_policy_type(policy_type_id))
100 def delete_policy_type(policy_type_id):
102 Handles DELETE /a1-p/policytypes/policy_type_id
110 def get_all_instances_for_type(policy_type_id):
112 Handles GET /a1-p/policytypes/policy_type_id/policies
117 def get_policy_instance(policy_type_id, policy_instance_id):
119 Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id
121 # 200 is automatic here
122 return _try_func_return(lambda: data.get_policy_instance(policy_type_id, policy_instance_id))
125 def get_policy_instance_status(policy_type_id, policy_instance_id):
127 Handles GET /a1-p/policytypes/polidyid/policies/policy_instance_id/status
130 def _get_status_handler(policy_type_id, policy_instance_id):
132 Pop trough A1s mailbox, insert the latest status updates into the database, and then return the status vector
134 NOTE: this is done lazily. Meaning, when someone performs a GET on this API, we pop through a1s mailbox.
135 THis may not work in the future if there are "thousands" of policy acknowledgements that hit a1 before this is called,
136 because the rmr mailbox may fill. However, in the near term, we do not expect this to happen.
138 # check validity to 404 first:
139 data.type_is_valid(policy_type_id)
140 data.instance_is_valid(policy_type_id, policy_instance_id)
142 # pop a1s mailbox, looking for policy notifications
143 new_messages = a1rmr.dequeue_all_waiting_messages(21024)
145 # try to parse the messages as responses. Drop those that are malformed
146 for msg in new_messages:
147 # note, we don't use the parameters "policy_type_id, policy_instance" from above here,
148 # because we are popping the whole mailbox, which might include other statuses
149 pay = json.loads(msg["payload"])
150 if "policy_type_id" in pay and "policy_instance_id" in pay and "handler_id" in pay and "status" in pay:
151 data.set_policy_instance_status(
152 pay["policy_type_id"], pay["policy_instance_id"], pay["handler_id"], pay["status"]
155 logger.debug("Dropping message")
158 # return the status vector
159 return data.get_policy_instance_statuses(policy_type_id, policy_instance_id)
161 return _try_func_return(lambda: _get_status_handler(policy_type_id, policy_instance_id))
164 def create_or_replace_policy_instance(policy_type_id, policy_instance_id):
166 Handles PUT /a1-p/policytypes/polidyid/policies/policy_instance_id
169 def _put_instance_handler(policy_type_id, policy_instance_id, instance):
171 Handles policy instance put
173 For now, policy_type_id is used as the message type
175 # validate the PUT against the schema
176 schema = data.get_policy_type(policy_type_id)["create_schema"]
177 validate(instance=instance, schema=schema)
180 data.store_policy_instance(policy_type_id, policy_instance_id, instance)
183 "operation": "CREATE",
184 "policy_type_id": policy_type_id,
185 "policy_instance_id": policy_instance_id,
189 # send rmr (best effort)
190 a1rmr.send(json.dumps(body), message_type=policy_type_id)
194 instance = connexion.request.json
195 return _try_func_return(lambda: _put_instance_handler(policy_type_id, policy_instance_id, instance))
198 def delete_policy_instance(policy_type_id, policy_instance_id):
200 Handles DELETE /a1-p/policytypes/polidyid/policies/policy_instance_id