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