736438ef21cf06045957e5529245ac92358baa8f
[sim/a1-interface.git] / near-rt-ric-simulator / src / OSC_2.1.0 / main.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 sys
20 import os
21 import requests
22
23 from connexion.resolver import RelativeResolver
24 from pathlib import Path
25 from flask import request, Response, jsonify
26 from var_declaration import policy_instances, policy_types, policy_status, callbacks, policy_fingerprint, forced_settings, hosts_set, app, data_delivery
27 from models.enforceStatus import EnforceStatus
28 from maincommon import check_apipath, get_supported_interfaces_response
29 from time import sleep
30
31
32 # Constants
33 TEXT_PLAIN = 'text/plain'
34 APPL_JSON  = 'application/json'
35
36 check_apipath()
37
38 # app is created in var_declarations
39
40 # Check alive function
41 @app.route('/', methods=['GET'])
42 def test():
43     return Response("OK", 200, mimetype=TEXT_PLAIN)
44
45 @app.route('/ip', methods=['GET'])
46 def get_ip():
47     if request.environ.get('HTTP_X_FORWARDED_FOR') is None:
48         return jsonify({'ip': request.environ['REMOTE_ADDR']}), 200
49     else:
50         return jsonify({'ip': request.environ['HTTP_X_FORWARDED_FOR']}), 200
51
52 #Return the current and all supported yamls for the this container
53 @app.route('/container_interfaces', methods=['GET'])
54 def container_interfaces():
55     return get_supported_interfaces_response()
56
57 #Delete all created instances and status
58 @app.route('/deleteinstances', methods=['POST'])
59 def deleteinstances():
60   for i in policy_instances.keys():
61     policy_instances[i]={}
62
63   policy_status.clear()
64   callbacks.clear()
65   forced_settings.clear()
66   forced_settings['code']=None
67   forced_settings['delay']=None
68   policy_fingerprint.clear()
69   return Response("All policy instances deleted", 200, mimetype=TEXT_PLAIN)
70
71 #Delete all - all reset
72 @app.route('/deleteall', methods=['POST'])
73 def deleteall():
74   policy_instances.clear()
75   policy_types.clear()
76   policy_status.clear()
77   callbacks.clear()
78   forced_settings['code']=None
79   forced_settings['delay']=None
80   policy_fingerprint.clear()
81   data_delivery.clear()
82   return Response("All policy instances and types deleted", 200, mimetype=TEXT_PLAIN)
83
84 #Load a policy type
85 @app.route('/policytype', methods=['PUT'])
86 def policytype():
87
88   policy_type_id=request.args.get('id')
89   if (policy_type_id is None):
90     return Response('Parameter <id> missing in request', status=400, mimetype=TEXT_PLAIN)
91   try:
92     int(policy_type_id)
93   except Exception:
94     return Response("The policy type id is not an int", 400, mimetype=TEXT_PLAIN)
95   try:
96     data = request.data
97     data = json.loads(data)
98   except Exception:
99     return Response("The policy type is corrupt or missing", 400, mimetype=TEXT_PLAIN)
100
101   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()):
102     return Response("The policy type missing atributes", 400, mimetype=TEXT_PLAIN)
103
104   retcode=201
105   if (policy_type_id in policy_types.keys()):
106     retcode=200
107     if (len(policy_instances[policy_type_id]) > 0):
108       return Response("The policy type already exists and instances exists", 400, mimetype=TEXT_PLAIN)
109
110   policy_types[policy_type_id]=data
111   policy_instances[policy_type_id]={}
112   return Response("Policy type " + policy_type_id + " is OK.", retcode, mimetype=TEXT_PLAIN)
113
114 #Delete a policy type
115 @app.route('/policytype', methods=['DELETE'])
116 def del_policytype():
117
118   policy_type_id=request.args.get('id')
119   if (policy_type_id is None):
120     return Response('Parameter <id> missing in request', status=400, mimetype=TEXT_PLAIN)
121   try:
122     int(policy_type_id)
123   except Exception:
124     return Response("The policy type id is not an int", 400, mimetype=TEXT_PLAIN)
125
126   if (policy_type_id in policy_types.keys()):
127     if (len(policy_instances[policy_type_id]) > 0):
128       return Response("The policy type already exists and instances exists", 400, mimetype=TEXT_PLAIN)
129
130     del policy_types[policy_type_id]
131     del policy_instances[policy_type_id]
132     return Response("Policy type " + policy_type_id + " is OK.", 204, mimetype=TEXT_PLAIN)
133
134   return Response("Policy type " + policy_type_id + " not found.", 204, mimetype=TEXT_PLAIN)
135
136
137 # Get all policy type ids
138 @app.route('/policytypes', methods=['GET'])
139 def get_policytype_ids():
140   return (json.dumps(list(policy_instances.keys())), 200)
141
142 #Set force response for one A1 response
143 #/forceresponse?code=<responsecode>
144 @app.route('/forceresponse', methods=['POST'])
145 def forceresponse():
146
147   try:
148     forced_settings['code']=int(request.args.get('code'))
149   except Exception:
150     forced_settings['code']=None
151   return Response("Force response code: " + str(forced_settings['code']) + " set for one single A1 response", 200, mimetype=TEXT_PLAIN)
152
153 #Set force delay response, in seconds, for all A1 responses
154 #/froceesponse?delay=<seconds>
155 @app.route('/forcedelay', methods=['POST'])
156 def forcedelay():
157
158   try:
159     forced_settings['delay']=int(request.args.get('delay'))
160   except Exception:
161     forced_settings['delay']=None
162   return Response("Force delay: " + str(forced_settings['delay']) + " sec set for all A1 responses", 200, mimetype=TEXT_PLAIN)
163
164
165 # Set status and reason
166 #/status?policyid=<policyid>&status=<status>[&reason=<reason>]
167 @app.route('/status', methods=['PUT'])
168 def setstatus():
169
170   policy_id=request.args.get('policyid')
171   if (policy_id is None):
172     return Response('Parameter <policyid> missing in request', status=400, mimetype=TEXT_PLAIN)
173   if policy_id not in policy_status.keys():
174     return Response('Policyid: '+policy_id+' not found.', status=404, mimetype=TEXT_PLAIN)
175
176   status=request.args.get('status')
177   if (status is None):
178     return Response('Parameter <status> missing in request', status=400, mimetype=TEXT_PLAIN)
179
180   enforceStatus = EnforceStatus()
181   try:
182     enforceStatus.enforce_status = status
183     msg = "Status set to " + status
184
185     reason = request.args.get('reason')
186     if (reason is not None):
187       enforceStatus.enforce_reason = reason
188       msg = msg + " and " + reason
189   
190     policy_status[policy_id] = enforceStatus.to_dict()
191     msg = msg + " for policy: " + policy_id
192   except ValueError as error:
193     return Response(str(error), status=400, mimetype=TEXT_PLAIN)
194
195   return Response(msg, 200, mimetype=TEXT_PLAIN)
196
197 # Metrics function
198 # Get a named counter
199 @app.route('/counter/<string:countername>', methods=['GET'])
200 def getcounter(countername):
201
202   if (countername == "num_instances"):
203     return Response(str(len(policy_fingerprint)), 200, mimetype=TEXT_PLAIN)
204   elif (countername == "num_types"):
205     return Response(str(len(policy_instances)),200, mimetype=TEXT_PLAIN)
206   elif (countername == "interface"):
207     p=Path(os.getcwd())
208     pp=p.parts
209     return Response(str(pp[len(pp)-1]),200, mimetype=TEXT_PLAIN)
210   elif (countername == "remote_hosts"):
211     hosts=",".join(hosts_set)
212     return str(hosts),200
213   elif (countername == "datadelivery"):
214     data_delivery_counter = str(len(data_delivery))
215     return Response(data_delivery_counter,200, mimetype=TEXT_PLAIN)
216   else:
217     return Response("Counter name: "+countername+" not found.",404, mimetype=TEXT_PLAIN)
218
219 port_number = 2222
220 if len(sys.argv) >= 2 :
221   if isinstance(sys.argv[1], int):
222     port_number = sys.argv[1]
223
224
225 # Send status
226 # /sendstatus?policyid=<policyid>
227 @app.route('/sendstatus', methods=['POST'])
228 def sendstatus():
229   policyid = request.args.get('policyid')
230   if policyid is None:
231     return Response('Parameter <policyid> missing in request', status=400, mimetype=TEXT_PLAIN)
232   if policyid not in policy_status.keys():
233     return Response('Policyid: '+policyid+' not found.', status=404, mimetype=TEXT_PLAIN)
234
235   ps = policy_status[ policyid ]
236   cb_url = callbacks[ policyid ]
237
238   try:
239     resp = requests.post(cb_url, json = json.dumps(ps), headers = { "Content-Type": APPL_JSON, "Accept": "*/*" })
240     resp.raise_for_status()
241     if (resp.status_code >= 200 and resp.status_code <= 300):
242       return Response("OK", resp.status_code, mimetype = TEXT_PLAIN)
243     return Response('Post status failed', status = resp.status_code, mimetype = TEXT_PLAIN)
244
245   except requests.ConnectionError as error:
246     return Response('Post status failed with Connection Error, could not send to ' + str(cb_url), status = 502, mimetype = TEXT_PLAIN)
247   except requests.Timeout as error:
248     return Response('Post status failed with Timeout, could not send to ' + str(cb_url), status = 504, mimetype = TEXT_PLAIN)
249   except requests.HTTPError as error:
250     return Response('Post status failed with HTTP Error, could not send to ' + str(cb_url), status = 502, mimetype = TEXT_PLAIN)
251   except requests.RequestException as error:
252     return Response('Post status failed with RequestException, could not send to ' + str(cb_url), status = 500, mimetype = TEXT_PLAIN)
253
254 # Receive status (only for testing callbacks)
255 # /statustest
256 @app.route('/statustest', methods=['POST', 'PUT'])
257 def statustest():
258   try:
259     data = request.data
260     data = json.loads(data)
261   except Exception:
262     return Response("The status data is corrupt or missing.", 400, mimetype=TEXT_PLAIN)
263
264   return Response("OK", 201, mimetype=TEXT_PLAIN)
265
266 app.add_api('openapi.yaml', resolver=RelativeResolver('controllers.a1_mediator_controller'))
267
268 if __name__ == '__main__':
269   app.run(port=port_number, host="127.0.0.1", threaded=True)