Update release notes
[sim/a1-interface.git] / near-rt-ric-simulator / src / STD_2.0.0 / a1.py
1 #  ============LICENSE_START===============================================
2 #  Copyright (C) 2021 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 os
19 import copy
20 import datetime
21 import json
22 import logging
23 import collections
24 import time
25 import requests
26
27 from connexion import NoContent
28 from flask import Flask, escape, request, Response, make_response
29 from jsonschema import validate
30 from var_declaration import policy_instances, policy_types, policy_status, callbacks, forced_settings, policy_fingerprint, hosts_set
31 from utils import calcFingerprint
32 from maincommon import check_apipath, apipath, get_supported_interfaces_response, extract_host_name, is_duplicate_check
33
34 #Constsants
35 APPL_JSON='application/json'
36 APPL_PROB_JSON='application/problem+json'
37
38 EXT_SRV_URL=os.getenv('EXT_SRV_URL')
39
40
41 # API Function: Get all policy type ids
42 def get_all_policy_types():
43
44   extract_host_name(hosts_set, request)
45
46   if ((r := check_modified_response()) is not None):
47     return r
48
49   res = list(policy_types.keys())
50   return (res, 200)
51
52 # API Function: Get a policy type
53 def get_policy_type(policyTypeId):
54
55   extract_host_name(hosts_set, request)
56
57   if ((r := check_modified_response()) is not None):
58     return r
59
60   policy_type_id=str(policyTypeId)
61
62   if (policy_type_id not in policy_types.keys()):
63     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_type_id)
64     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
65
66   return Response(json.dumps(policy_types[policy_type_id]), 200, mimetype=APPL_JSON)
67
68 # API Function: Get all policy ids
69 def get_all_policy_identities(policyTypeId):
70
71   extract_host_name(hosts_set, request)
72
73   if ((r := check_modified_response()) is not None):
74     return r
75
76   policy_type_id=str(policyTypeId)
77
78   if (policy_type_id not in policy_types.keys()):
79     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_type_id)
80     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
81
82   return (list(policy_instances[policy_type_id].keys()), 200)
83
84 # API Function: Create or update a policy
85 def put_policy(policyTypeId, policyId):
86
87   extract_host_name(hosts_set, request)
88
89   if ((r := check_modified_response()) is not None):
90     return r
91
92   policy_type_id=str(policyTypeId)
93   policy_id=str(policyId)
94
95   if (policy_type_id not in policy_types.keys()):
96     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
97     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
98
99   try:
100     data = request.data
101     data = json.loads(data)
102   except Exception:
103     pjson=create_problem_json(None, "The policy is corrupt or missing.", 400, None, policy_id)
104     return Response(json.dumps(pjson), 400, mimetype=APPL_PROB_JSON)
105
106   try:
107     validate(instance=data, schema=policy_types[policy_type_id]['policySchema'])
108   except Exception:
109     return (None, 400)
110
111   fp_previous=None
112   retcode=201
113   if policy_id in policy_instances[policy_type_id].keys():
114     retcode=200
115     if (is_duplicate_check()):
116       fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_id], policy_type_id)
117     else:
118       fp_previous=policy_id
119   else:
120     if (policy_id in policy_fingerprint.values()):
121       pjson=create_problem_json(None, "The policy id already exist for other policy type.", 400, None, policy_id)
122       return Response(json.dumps(pjson), 400, mimetype=APPL_PROB_JSON)
123
124   if (is_duplicate_check()):
125     fp=calcFingerprint(data, policy_type_id)
126   else:
127     fp=policy_id
128
129   if ((fp in policy_fingerprint.keys()) and is_duplicate_check()):
130     p_id=policy_fingerprint[fp]
131     if (p_id != policy_id):
132       pjson=create_problem_json(None, "Duplicate, the policy json already exists.", 400, None, policy_id)
133       return Response(json.dumps(pjson), 400, mimetype=APPL_PROB_JSON)
134
135   #Callout hooks for external server
136   #When it fails, break and return 419 HTTP status code
137   if (EXT_SRV_URL is not None):
138     resp = callout_external_server(policy_id, data, 'PUT')
139     if (resp != retcode):
140       pjson=create_error_response(resp)
141       return Response(json.dumps(pjson), 500, mimetype=APPL_PROB_JSON)
142
143   if (fp_previous is not None):
144     del policy_fingerprint[fp_previous]
145
146   policy_fingerprint[fp]=policy_id
147
148   noti=request.args.get('notificationDestination')
149   callbacks[policy_id]=noti
150
151   policy_instances[policy_type_id][policy_id]=data
152
153   if (policy_types[policy_type_id]['statusSchema'] is not None):
154     ps = {}
155     ps["enforceStatus"] = ""
156     ps["enforceReason"] = ""
157     policy_status[policy_id] = ps
158
159   if (retcode == 200):
160     return Response(json.dumps(data), 200, mimetype=APPL_JSON)
161   else:
162     headers={}
163     headers['Location']='/A1-P/v2/policytypes/' + policy_type_id + '/policies/' + policy_id
164     return Response(json.dumps(data), 201, headers=headers, mimetype=APPL_JSON)
165
166 # API Function: Get a policy
167 def get_policy(policyTypeId, policyId):
168
169   extract_host_name(hosts_set, request)
170
171   if ((r := check_modified_response()) is not None):
172     return r
173
174   policy_type_id=str(policyTypeId)
175   policy_id=str(policyId)
176
177   if (policy_type_id not in policy_types.keys()):
178     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
179     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
180
181   if (policy_id not in policy_instances[policy_type_id].keys()):
182     pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
183     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
184
185   return Response(json.dumps(policy_instances[policy_type_id][policy_id]), 200, mimetype=APPL_JSON)
186
187 # API Function: Delete a policy
188 def delete_policy(policyTypeId, policyId):
189
190   extract_host_name(hosts_set, request)
191
192   if ((r := check_modified_response()) is not None):
193     return r
194
195   policy_type_id=str(policyTypeId)
196   policy_id=str(policyId)
197
198   if (policy_type_id not in policy_types.keys()):
199     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
200     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
201
202   if (policy_id not in policy_instances[policy_type_id].keys()):
203     pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
204     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
205
206   #Callout hooks for external server
207   #When it fails, break and return 419 HTTP status code
208   if (EXT_SRV_URL is not None):
209     resp = callout_external_server(policy_id, None, 'DELETE')
210     if (resp != 204):
211       pjson=create_error_response(resp)
212       return Response(json.dumps(pjson), 500, mimetype=APPL_PROB_JSON)
213
214   if (is_duplicate_check()):
215     fp_previous=calcFingerprint(policy_instances[policy_type_id][policy_id], policy_type_id)
216   else:
217     fp_previous=policy_id
218
219   policy_fingerprint.pop(fp_previous)
220   policy_instances[policy_type_id].pop(policy_id)
221   policy_status.pop(policy_id)
222   callbacks.pop(policy_id)
223   return Response('', 204, mimetype=APPL_JSON)
224
225 # API Function: Get status for a policy
226 def get_policy_status(policyTypeId, policyId):
227
228   extract_host_name(hosts_set, request)
229
230   if ((r := check_modified_response()) is not None):
231     return r
232
233   policy_type_id=str(policyTypeId)
234   policy_id=str(policyId)
235
236   if (policy_type_id not in policy_types.keys()):
237     pjson=create_problem_json(None, "The policy type does not exist.", 404, None, policy_id)
238     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
239
240   if (policy_id not in policy_instances[policy_type_id].keys()):
241     pjson=create_problem_json(None, "The requested policy does not exist.", 404, None, policy_id)
242     return Response(json.dumps(pjson), 404, mimetype=APPL_PROB_JSON)
243
244   return Response(json.dumps(policy_status[policy_id]), status=200, mimetype=APPL_JSON)
245
246 # Helper: Callout external server to notify it for policy operations
247 # Returns 200, 201 and 204 for the success callout hooks, for the others returns 419
248 def callout_external_server(policy_id, payload, operation):
249
250   target_url=EXT_SRV_URL + policy_id
251   try:
252     if (operation == 'PUT'):
253       #Suppress error when self-signed certificate is being used with verify flag
254       resp=requests.put(target_url, json=payload, timeout=10, verify=False)
255       return resp.status_code
256     elif (operation == 'DELETE'):
257       resp=requests.delete(target_url, timeout=10, verify=False)
258       return resp.status_code
259   except Exception:
260     #Return a generic unassigned HTTP status code as per iana, for all exceptions (419:Callout failed)
261     #https://www.iana.org/assignments/http-status-codes/http-status-codes.xhtml
262     return 419
263
264 # Helper: Create a response object if forced http response code is set
265 def get_forced_response():
266
267   if (forced_settings['code'] is not None):
268     value=forced_settings['code']
269     pjson=create_error_response(int(value))
270     forced_settings['code']=None
271     return Response(json.dumps(pjson), pjson['status'], mimetype=APPL_PROB_JSON)
272   return None
273
274 # Helper: Delay if delayed response code is set
275 def do_delay():
276
277   if (forced_settings['delay'] is not None):
278     try:
279       val=int(forced_settings['delay'])
280       time.sleep(val)
281     except Exception:
282       return
283
284 # Helper: Check if response shall be delayed or a forced response shall be sent
285 def check_modified_response():
286
287   do_delay()
288   return get_forced_response()
289
290 # Helper: Create a problem json object
291 def create_problem_json(type_of, title, status, detail, instance):
292
293   error = {}
294   if type_of is not None:
295     error["type"] = type_of
296   if title is not None:
297     error["title"] = title
298   if status is not None:
299     error["status"] = status
300   if detail is not None:
301     error["detail"] = detail
302   if instance is not None:
303     error["instance"] = instance
304   return error
305
306 # Helper: Create a problem json based on a generic http response code
307 def create_error_response(code):
308
309     if (code == 400):
310       return(create_problem_json(None, "Bad request", 400, "Object in payload not properly formulated or not related to the method", None))
311     elif (code == 404):
312       return(create_problem_json(None, "Not found", 404, "No resource found at the URI", None))
313     elif (code == 405):
314       return(create_problem_json(None, "Method not allowed", 405, "Method not allowed for the URI", None))
315     elif (code == 409):
316       return(create_problem_json(None, "Conflict", 409, "Request could not be processed in the current state of the resource", None))
317     elif (code == 419):
318       return(create_problem_json(None, "Callout failed", 419, "Callout hooks could not be processed on the external server", None))
319     elif (code == 429):
320       return(create_problem_json(None, "Too many requests", 429, "Too many requests have been sent in a given amount of time", None))
321     elif (code == 507):
322       return(create_problem_json(None, "Insufficient storage", 507, "The method could not be performed on the resource because the provider is unable to store the representation needed to successfully complete the request", None))
323     elif (code == 503):
324       return(create_problem_json(None, "Service unavailable", 503, "The provider is currently unable to handle the request due to a temporary overload", None))
325     else:
326       return(create_problem_json(None, "Unknown", code, "Not implemented response code", None))