NONRTRIC-955: Add uvicorn, fix for appl/json
[sim/a1-interface.git] / near-rt-ric-simulator / src / OSC_2.1.0 / controllers / a1_mediator_controller.py
1 #  ============LICENSE_START===============================================
2 #  Copyright (C) 2021-2023 Nordix Foundation. All rights reserved.
3 #  ========================================================================
4 #  Licensed under the Apache License, Version 2.0 (the "License");
5 #  you may not use this file except in compliance with the License.
6 #  You may obtain a copy of the License at
7 #
8 #       http://www.apache.org/licenses/LICENSE-2.0
9 #
10 #  Unless required by applicable law or agreed to in writing, software
11 #  distributed under the License is distributed on an "AS IS" BASIS,
12 #  WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 #  See the License for the specific language governing permissions and
14 #  limitations under the License.
15 #  ============LICENSE_END=================================================
16 #
17
18 import json
19 # import datetime
20 import time
21
22 from datetime import datetime
23 from flask import request, Response
24 from jsonschema import validate
25 from var_declaration import policy_instances, policy_types, policy_status, policy_fingerprint, callbacks, forced_settings, hosts_set, jobs, data_delivery
26 from models.enforceStatus import EnforceStatus
27 from utils import calcFingerprint
28 from maincommon import extract_host_name, is_duplicate_check
29 from payload_logging import is_payload_logging
30
31 # Constants
32 APPL_JSON = 'application/json'
33
34 #Helper funtion to log http reponse
35 def log_resp_text(msg):
36   global payload_log
37   if (is_payload_logging()):
38     print("-----Error description-----")
39     print(str(msg))
40
41 # API Function: Health check
42 def a1_controller_get_healthcheck():
43
44   extract_host_name(hosts_set, request)
45
46   if ((r := check_modified_response()) is not None):
47     return r
48
49   return (None, 200)
50
51 # API Function: Get all policy type ids
52 def a1_controller_get_all_policy_types():
53   extract_host_name(hosts_set, request)
54
55   if ((r := check_modified_response()) is not None):
56     return r
57
58   res = list(policy_instances.keys())
59   res = list(map(int, res))
60   return Response(json.dumps(res), 200, mimetype=APPL_JSON)
61
62 # API Function: Get a policy type
63 def a1_controller_get_policy_type(policy_type_id):
64   extract_host_name(hosts_set, request)
65
66   if ((r := check_modified_response()) is not None):
67     return r
68
69   policy_type_id=str(policy_type_id)
70
71   if (policy_type_id not in policy_types.keys()):
72     log_resp_text("Policy type id not found")
73     return (None, 404)
74
75   return Response(json.dumps(policy_types[policy_type_id]), 200, mimetype=APPL_JSON)
76
77 # API Function: Delete a policy type
78 def a1_controller_delete_policy_type(policy_type_id):
79
80   extract_host_name(hosts_set, request)
81
82   if ((r := check_modified_response()) is not None):
83     return r
84
85   policy_type_id=str(policy_type_id)
86
87   if (policy_type_id not in policy_instances.keys()):
88     log_resp_text("Policy type not found")
89     return (None, 404)
90
91   if (len(policy_instances[policy_type_id]) > 0):
92     log_resp_text("Policy type cannot be removed, instances exists")
93     return (None, 400)
94
95   del policy_instances[policy_type_id]
96   del policy_types[policy_type_id]
97
98   return (None, 204)
99
100
101 # API Function: Create a policy type
102 def a1_controller_create_policy_type(policy_type_id):
103
104   extract_host_name(hosts_set, request)
105
106   if ((r := check_modified_response()) is not None):
107     return r
108
109   try:
110     int(policy_type_id)
111   except Exception:
112     return Response("The policy type id is not an int", 400, mimetype='text/plain')
113
114   policy_type_id=str(policy_type_id)
115
116   if (policy_type_id in policy_instances.keys()):
117     if (len(policy_instances[policy_type_id]) > 0):
118       log_resp_text("Policy type id already exists")
119       return (None, 400)
120
121   try:
122     data = request.data
123     data = json.loads(data)
124   except Exception:
125     log_resp_text("Policy type validation failure")
126     return (None, 400)
127
128   if (('name' not in data.keys()) or ('description' not in data.keys()) or ('policy_type_id' not in data.keys()) or ('create_schema' not in data.keys())):
129     log_resp_text("Parameters missing in policy type")
130     return (None, 400)
131
132   if (policy_type_id not in policy_instances.keys()):
133     policy_instances[policy_type_id]={}
134
135   policy_types[policy_type_id]=data
136
137   return (None, 201)
138
139
140 # API Function: Get all policy ids for a type
141 def  a1_controller_get_all_instances_for_type(policy_type_id):
142
143   extract_host_name(hosts_set, request)
144
145   if ((r := check_modified_response()) is not None):
146     return r
147
148   policy_type_id=str(policy_type_id)
149
150   if (policy_type_id not in policy_instances.keys()):
151     log_resp_text("Policy type id not found")
152     return (None, 404)
153
154   res = list(policy_instances[policy_type_id].keys())
155   return Response(json.dumps(res), 200, mimetype=APPL_JSON)
156
157 # API Function: Get a policy instance
158 def a1_controller_get_policy_instance(policy_type_id, policy_instance_id):
159
160   extract_host_name(hosts_set, request)
161
162   if ((r := check_modified_response()) is not None):
163     return r
164
165   policy_type_id=str(policy_type_id)
166
167   if (policy_type_id not in policy_instances.keys()):
168     log_resp_text("Policy type id not found")
169     return (None, 404)
170
171   if (policy_instance_id not in policy_instances[policy_type_id].keys()):
172     log_resp_text("Policy instance id not found")
173     return (None, 404)
174
175   return Response(json.dumps(policy_instances[policy_type_id][policy_instance_id]), 200, mimetype=APPL_JSON)
176
177 # API function: Delete a policy
178 def a1_controller_delete_policy_instance(policy_type_id, policy_instance_id):
179
180   extract_host_name(hosts_set, request)
181
182   if ((r := check_modified_response()) is not None):
183     return r
184
185   policy_type_id=str(policy_type_id)
186
187   if (policy_type_id not in policy_instances.keys()):
188     log_resp_text("Policy type id not found")
189     return (None, 404)
190
191   if (policy_instance_id not in policy_instances[policy_type_id].keys()):
192     log_resp_text("Policy instance id not found")
193     return (None, 404)
194
195   if (is_duplicate_check()):
196     fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_instance_id], policy_type_id)
197   else:
198     fp_previous=policy_instance_id
199
200   del policy_fingerprint[fp_previous]
201   del policy_instances[policy_type_id][policy_instance_id]
202   del policy_status[policy_instance_id]
203   callbacks.pop(policy_instance_id)
204
205   return (None, 202)
206
207
208 # API function: Create/update a policy
209 def a1_controller_create_or_replace_policy_instance(policy_type_id, policy_instance_id):
210
211   extract_host_name(hosts_set, request)
212
213   if ((r := check_modified_response()) is not None):
214     return r
215
216   policy_type_id=str(policy_type_id)
217
218   if (policy_type_id not in policy_instances.keys()):
219     log_resp_text("Policy type id not found")
220     return (None, 404)
221
222   try:
223     data = request.data
224     data = json.loads(data)
225   except Exception:
226     log_resp_text("Policy json error")
227     return (None, 400)
228
229   try:
230     validate(instance=data, schema=policy_types[policy_type_id]['create_schema'])
231   except Exception:
232     log_resp_text("Policy validation error")
233     return (None, 400)
234
235   fp_previous=None
236   if policy_instance_id in policy_instances[policy_type_id].keys():
237     if (is_duplicate_check()):
238       fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_instance_id], policy_type_id)
239     else:
240       fp_previous=policy_instance_id
241
242   else:
243     if (policy_instance_id in policy_fingerprint.values()):
244       log_resp_text("Policy id already exist for other type")
245       return (None, 400)
246
247   if is_duplicate_check():
248     fp = calcFingerprint(data, policy_type_id)
249   else:
250     fp = policy_instance_id
251
252   if (fp in policy_fingerprint.keys()) and is_duplicate_check():
253     p_id = policy_fingerprint[fp]
254     if (p_id != policy_instance_id):
255       log_resp_text("Policy json duplicate of other instance")
256       return (None, 400)
257
258   if fp_previous is not None:
259     del policy_fingerprint[fp_previous]
260
261   policy_fingerprint[fp]=policy_instance_id
262
263   noti = request.args.get('notificationDestination')
264   callbacks[policy_instance_id] = noti
265
266   policy_instances[policy_type_id][policy_instance_id]=data
267
268   enforceStatus = EnforceStatus("NOT_ENFORCED", "OTHER_REASON")
269   policy_status[policy_instance_id] = enforceStatus.to_dict()
270
271   # return Response(json.dumps(data), 200, mimetype=APPL_JSON)
272   return (None, 202)
273
274 # API function: Get policy status
275 def a1_controller_get_policy_instance_status(policy_type_id, policy_instance_id):
276
277   extract_host_name(hosts_set, request)
278
279   if ((r := check_modified_response()) is not None):
280     return r
281
282   policy_type_id=str(policy_type_id)
283   if (policy_type_id not in policy_instances.keys()):
284     log_resp_text("Policy type id not found")
285     return (None, 404)
286
287   if (policy_instance_id not in policy_instances[policy_type_id].keys()):
288     log_resp_text("Policy instance id not found")
289     return (None, 404)
290
291   return Response(json.dumps(policy_status[policy_instance_id]), 200, mimetype=APPL_JSON)
292
293 # API function: Receive a data delivery package
294 def a1_controller_data_delivery():
295  
296   extract_host_name(hosts_set, request)
297   if ((r := check_modified_response()) is not None):
298     return r
299
300   try:
301     data = request.data
302     data = json.loads(data)
303     job = data['job']
304     jobs.index(job)
305   except ValueError:
306     log_resp_text("no job id defined for this data delivery")
307     return (None, 404)
308   except Exception:
309     log_resp_text("The data is corrupt or missing.")
310     return (None, 400)
311   data_delivery.append(data)
312   return (None, 200) # Should A1 and the A1 Simulator return 201 for creating a new resource?
313
314 # Helper: Create a response object if forced http response code is set
315 def get_forced_response():
316
317   response_code=forced_settings['code']
318   if (response_code is not None):
319     forced_settings['code'] = None
320     return (None, response_code)
321   return None
322
323 # Helper: Delay if delayed response code is set
324 def do_delay():
325
326   if (forced_settings['delay'] is not None):
327     try:
328       val=int(forced_settings['delay'])
329       time.sleep(val)
330     except Exception:
331       return
332
333 # Helper: Check if response shall be delayed or a forced response shall be sent
334 def check_modified_response():
335   do_delay()
336   return get_forced_response()