Validate ids
[clamp.git] / src / test / resources / http-cache / third_party_proxy.py
1 #!/usr/bin/env python2
2 ###
3 # ============LICENSE_START=======================================================
4 # ONAP CLAMP
5 # ================================================================================
6 # Copyright (C) 2018 AT&T Intellectual Property. All rights
7 #                             reserved.
8 # ================================================================================
9 # Licensed under the Apache License, Version 2.0 (the "License");
10 # you may not use this file except in compliance with the License.
11 # You may obtain a copy of the License at
12 #
13 # http://www.apache.org/licenses/LICENSE-2.0
14 #
15 # Unless required by applicable law or agreed to in writing, software
16 # distributed under the License is distributed on an "AS IS" BASIS,
17 # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18 # See the License for the specific language governing permissions and
19 # limitations under the License.
20 # ============LICENSE_END============================================
21 # ===================================================================
22 #
23 ###
24
25 import json
26 import requests
27 import os
28 import errno
29 import sys
30 import SimpleHTTPServer
31 import SocketServer
32 import argparse
33 import tempfile
34 import signal
35 import uuid
36 import shutil
37
38 parser = argparse.ArgumentParser(description="3rd party Cache & Replay")
39 parser.add_argument("--username", "-u", type=str, help="Set the username for contacting 3rd party - only used for GET")
40 parser.add_argument("--password", "-p", type=str, help="Set the password for contacting 3rd party - only used for GET")
41 parser.add_argument("--root",     "-r", default=tempfile.mkdtemp(), type=str, help="Root folder for the proxy cache")
42 parser.add_argument("--temp",     "-t", default=tempfile.mkdtemp(), type=str, help="Temp folder for the generated content")
43 parser.add_argument("--proxy"         , type=str, help="Url of the  Act as a proxy. If not set, this script only uses the cache and will return a 404 if files aren't found")
44 parser.add_argument("--port",     "-P", type=int, default="8081", help="Port on which the proxy should listen to")
45 parser.add_argument("--verbose",  "-v", type=bool, help="Print more information in case of error")
46 parser.add_argument("--proxyaddress","-a", type=str, help="Address of this proxy, generally either third_party_proxy:8085 or localhost:8085 depending if started with docker-compose or not")
47 options = parser.parse_args()
48
49
50 PORT = options.port
51 HOST = options.proxy
52 AUTH = (options.username, options.password)
53 HEADERS = {'X-ECOMP-InstanceID':'CLAMP'}
54 CACHE_ROOT = str(options.root)
55 TMP_ROOT = str(options.temp)
56 PROXY_ADDRESS=str(options.proxyaddress)
57
58 def signal_handler(signal_sent, frame):
59     global httpd
60     if signal_sent == signal.SIGINT:
61         print('Got Ctrl-C (SIGINT)')
62         httpd.socket.close()
63         httpd.shutdown()
64         httpd.server_close()
65
66 class Proxy(SimpleHTTPServer.SimpleHTTPRequestHandler):
67     def print_headers(self):
68         for header,value in self.headers.items():
69             print("header: %s : %s" % (header, value))
70
71     def check_credentials(self):
72         pass
73
74     def _send_content(self, header_file, content_file):
75         self.send_response(200)
76         with open(header_file, 'rb') as f:
77             headers = json.load(f)
78             for key,value in headers.items():
79                 if key in ('Transfer-Encoding',):
80                     continue
81                 self.send_header(key, value)
82             self.end_headers()
83         with open(content_file,'rb') as f:
84             fc = f.read()
85             self.wfile.write(fc)
86
87     def _write_cache(self,cached_file_folder, header_file, content_file, response):
88         os.makedirs(cached_file_folder, 0777)
89         with open(content_file, 'w') as f:
90             f.write(response.raw.read())
91         with open(header_file, 'w') as f:
92             json.dump(dict(response.raw.headers), f)
93     # Entry point of the code
94     def _get_cached_file_folder_name(self,folder):
95         cached_file_folder = '%s/%s' % (folder, self.path,)
96         print("Cached file name before escaping : %s" % cached_file_folder)
97         cached_file_folder = cached_file_folder.replace('<','&#60;').replace('>','&#62;').replace('?','&#63;').replace('*','&#42;').replace('\\','&#42;').replace(':','&#58;').replace('|','&#124;')
98         print("Cached file name after escaping (used for cache storage) : %s" % cached_file_folder)
99         return cached_file_folder
100     
101     def _get_cached_content_file_name(self,cached_file_folder):
102         return "%s/.file" % (cached_file_folder,)
103     
104     def _get_cached_header_file_name(self,cached_file_folder):
105         return "%s/.header" % (cached_file_folder,)
106     
107     def _execute_content_generated_cases(self,http_type):
108      print("Testing special cases, cache files will be sent to :" +TMP_ROOT)
109      cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
110      cached_file_content = self._get_cached_content_file_name(cached_file_folder)
111      cached_file_header = self._get_cached_header_file_name(cached_file_folder)
112      _file_available = os.path.exists(cached_file_content)
113     
114      if self.path.startswith("/dcae-service-types?asdcResourceId=") and http_type == "GET":
115         if not _file_available:
116             print "self.path start with /dcae-service-types?asdcResourceId=, generating response json..."
117             uuidGenerated = str(uuid.uuid4())
118             typeId = "typeId-" + uuidGenerated
119             typeName = "typeName-" + uuidGenerated
120             print "typeId generated: " + typeName + " and typeName: "+ typeId
121             jsonGenerated = "{\"totalCount\":1, \"items\":[{\"typeId\":\"" + typeId + "\", \"typeName\":\"" + typeName +"\"}]}"
122             print "jsonGenerated: " + jsonGenerated
123     
124             os.makedirs(cached_file_folder, 0777)
125             with open(cached_file_header, 'w') as f:
126                 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
127             with open(cached_file_content, 'w') as f:
128                 f.write(jsonGenerated)
129         return True
130      elif self.path.startswith("/dcae-operationstatus") and http_type == "GET":
131         if not _file_available:
132             print "self.path start with /dcae-operationstatus, generating response json..."
133             jsonGenerated =  "{\"operationType\": \"operationType1\", \"status\": \"succeeded\"}"
134             print "jsonGenerated: " + jsonGenerated
135     
136             try:
137                 os.makedirs(cached_file_folder, 0777)
138             except OSError as e:
139                 if e.errno != errno.EEXIST:
140                     raise
141                 print(cached_file_folder+" already exists")
142     
143             with open(cached_file_header, 'w') as f:
144                 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
145             with open(cached_file_content, 'w') as f:
146                 f.write(jsonGenerated)
147         return True
148      elif self.path.startswith("/sdc/v1/catalog/services/") and http_type == "POST":
149         if not _file_available:
150             print "self.path start with /sdc/v1/catalog/services/, generating response json..."
151             jsondata = json.loads(self.data_string)
152             jsonGenerated = "{\"artifactName\":\"" + jsondata['artifactName'] + "\",\"artifactType\":\"" + jsondata['artifactType'] + "\",\"artifactURL\":\"" + self.path + "\",\"artifactDescription\":\"" + jsondata['description'] + "\",\"artifactChecksum\":\"ZjJlMjVmMWE2M2M1OTM2MDZlODlmNTVmZmYzNjViYzM=\",\"artifactUUID\":\"" + str(uuid.uuid4()) + "\",\"artifactVersion\":\"1\"}"
153             print "jsonGenerated: " + jsonGenerated
154     
155             os.makedirs(cached_file_folder, 0777)
156             with open(cached_file_header, 'w') as f:
157                 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
158             with open(cached_file_content, 'w') as f:
159                 f.write(jsonGenerated)
160         return True;
161      elif self.path.startswith("/dcae-deployments/") and (http_type == "PUT" or http_type == "DELETE"):
162         if not _file_available:
163             print "self.path start with /dcae-deployments/, generating response json..."
164             #jsondata = json.loads(self.data_string)
165             jsonGenerated = "{\"links\":{\"status\":\"http:\/\/" + PROXY_ADDRESS + "\/dcae-operationstatus\",\"test2\":\"test2\"}}"
166             print "jsonGenerated: " + jsonGenerated
167     
168             os.makedirs(cached_file_folder, 0777)
169             with open(cached_file_header, 'w') as f:
170                 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
171             with open(cached_file_content, 'w') as f:
172                 f.write(jsonGenerated)
173         return True
174      elif (self.path.startswith("/pdp/api/") and (http_type == "PUT" or http_type == "DELETE")) or (self.path.startswith("/pdp/api/policyEngineImport") and http_type == "POST"):
175         print "self.path start with /pdp/api/, copying body to response ..."
176         if not os.path.exists(cached_file_folder):
177             os.makedirs(cached_file_folder, 0777)
178         with open(cached_file_header, 'w+') as f:
179             f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
180         with open(cached_file_content, 'w+') as f:
181             f.write(self.data_string)
182         return True
183      elif self.path.startswith("/policy/api/v1/policyTypes/") and http_type == "POST":
184         print "self.path start with POST new policy API /pdp/api/, copying body to response ..."
185         if not os.path.exists(cached_file_folder):
186             os.makedirs(cached_file_folder, 0777)
187         with open(cached_file_header, 'w+') as f:
188             f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
189         with open(cached_file_content, 'w+') as f:
190             f.write(self.data_string)
191         return True
192      elif self.path.startswith("/policy/api/v1/policyTypes/") and http_type == "DELETE":
193         print "self.path start with DELETE new policy API /policy/api/v1/policyTypes/ ..."
194         if not os.path.exists(cached_file_folder):
195             os.makedirs(cached_file_folder, 0777)
196     
197         with open(cached_file_header, 'w+') as f:
198                 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
199         with open(cached_file_content, 'w+') as f:
200                 f.write(self.data_string)
201         return True
202      elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "GET":
203         print "self.path start with /policy/api/v1/policytypes/, generating response json..."
204         jsonGenerated =  "{\"policyTypeId\": \"onap.policies.controlloop.operational\",\"policyTypeVersion\": \"1.0.0\",\"policyId\": \"OPERATIONAL_z711F_v1_0_ResourceInstanceName1_tca\"}"
205         print "jsonGenerated: " + jsonGenerated
206         if not os.path.exists(cached_file_folder):
207             os.makedirs(cached_file_folder, 0777)
208
209         with open(cached_file_header, 'w') as f:
210             f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
211         with open(cached_file_content, 'w') as f:
212             f.write(jsonGenerated)
213         return True
214      else:
215         return False
216
217     
218     def do_GET(self):
219         cached_file_folder = ""
220         cached_file_content =""
221         cached_file_header=""
222         print("\n\n\nGot a GET request for %s " % self.path)
223
224         self.print_headers()
225         self.check_credentials()
226         # Verify if it's a special case
227         is_special = self._execute_content_generated_cases("GET")
228         if is_special:
229             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
230             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
231             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
232         else:
233             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
234             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
235             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
236
237         _file_available = os.path.exists(cached_file_content)
238
239         if not _file_available:
240             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
241
242             if not HOST:
243                 self.send_response(404)
244                 return "404 Not found"
245
246             url = '%s%s' % (HOST, self.path)
247             response = requests.get(url, auth=AUTH, headers=HEADERS, stream=True)
248
249             if response.status_code == 200:
250                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
251             else:
252                 print('Error when requesting file :')
253                 print('Requested url : %s' % (url,))
254                 print('Status code : %s' % (response.status_code,))
255                 print('Content : %s' % (response.content,))
256                 self.send_response(response.status_code)
257                 return response.content
258         else:
259             print("Request for data currently present in cache: %s" % (cached_file_folder,))
260
261         self._send_content(cached_file_header, cached_file_content)
262
263         if self.path.startswith("/dcae-service-types?asdcResourceId="):
264             print "DCAE case deleting folder created " + cached_file_folder
265             shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
266         else:
267             print "NOT in DCAE case deleting folder created " + cached_file_folder
268
269     def do_POST(self):
270         cached_file_folder = ""
271         cached_file_content =""
272         cached_file_header=""
273         print("\n\n\nGot a POST for %s" % self.path)
274         self.check_credentials()
275         self.data_string = self.rfile.read(int(self.headers['Content-Length']))
276         print("data-string:\n %s" % self.data_string)
277         print("self.headers:\n %s" % self.headers)
278
279         is_special = self._execute_content_generated_cases("POST")
280         if is_special:
281             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
282             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
283             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
284         else:
285             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
286             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
287             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
288
289         _file_available = os.path.exists(cached_file_content)
290
291         if not _file_available:
292         
293             if not HOST:
294                 self.send_response(404)
295                 return "404 Not found"
296
297             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
298
299             url = '%s%s' % (HOST, self.path)
300             print("url: %s" % (url,))
301             response = requests.post(url, data=self.data_string, headers=self.headers, stream=True)
302
303             if response.status_code == 200:
304                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
305             else:
306                 print('Error when requesting file :')
307                 print('Requested url : %s' % (url,))
308                 print('Status code : %s' % (response.status_code,))
309                 print('Content : %s' % (response.content,))
310                 self.send_response(response.status_code)
311                 return response.content
312         else:
313             print("Request for data present in cache: %s" % (cached_file_folder,))
314
315         self._send_content(cached_file_header, cached_file_content)
316
317     def do_PUT(self):
318         cached_file_folder = ""
319         cached_file_content =""
320         cached_file_header=""
321         print("\n\n\nGot a PUT for %s " % self.path)
322         self.check_credentials()
323         self.data_string = self.rfile.read(int(self.headers['Content-Length']))
324         print("data-string:\n %s" % self.data_string)
325         print("self.headers:\n %s" % self.headers)
326
327         is_special = self._execute_content_generated_cases("PUT")
328         if is_special:
329             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
330             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
331             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
332         else:
333             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
334             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
335             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
336
337         _file_available = os.path.exists(cached_file_content)
338
339         if not _file_available:
340             if not HOST:
341                 self.send_response(404)
342                 return "404 Not found"
343
344             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
345
346             url = '%s%s' % (HOST, self.path)
347             print("url: %s" % (url,))
348             response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
349
350             if response.status_code == 200:
351                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
352             else:
353                 print('Error when requesting file :')
354                 print('Requested url : %s' % (url,))
355                 print('Status code : %s' % (response.status_code,))
356                 print('Content : %s' % (response.content,))
357                 self.send_response(response.status_code)
358                 return response.content
359         else:
360             print("Request for data present in cache: %s" % (cached_file_folder,))
361
362         self._send_content(cached_file_header, cached_file_content)
363
364
365     def do_DELETE(self):
366         cached_file_folder = ""
367         cached_file_content =""
368         cached_file_header=""
369         print("\n\n\nGot a DELETE for %s " % self.path)
370         self.check_credentials()
371         if self.headers.get('Content-Length') is not None:
372             self.data_string = self.rfile.read(int(self.headers['Content-Length']))
373         else:
374             self.data_string = "empty generated"
375         print("self.headers:\n %s" % self.headers)
376
377         is_special = self._execute_content_generated_cases("DELETE")
378         if is_special:
379             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
380             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
381             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
382         else:
383             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
384             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
385             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
386
387         _file_available = os.path.exists(cached_file_content)
388
389         if not _file_available:
390             if not HOST:
391                 self.send_response(404)
392                 return "404 Not found"
393
394             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
395
396             url = '%s%s' % (HOST, self.path)
397             print("url: %s" % (url,))
398             response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
399
400             if response.status_code == 200:
401                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
402             else:
403                 print('Error when requesting file :')
404                 print('Requested url : %s' % (url,))
405                 print('Status code : %s' % (response.status_code,))
406                 print('Content : %s' % (response.content,))
407                 self.send_response(response.status_code)
408                 return response.content
409         else:
410             print("Request for data present in cache: %s" % (cached_file_folder,))
411
412         self._send_content(cached_file_header, cached_file_content)
413
414
415
416 # Main code that start the HTTP server
417 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
418 httpd.allow_reuse_address = True
419 print "Listening on port "+ str(PORT) + "(Press Ctrl+C/Ctrl+Z to stop HTTPD Caching script)"
420 print "Caching folder " + CACHE_ROOT + ", Tmp folder for generated files " + TMP_ROOT 
421 signal.signal(signal.SIGINT, signal_handler)
422 httpd.serve_forever()