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