5432ab8e563380873f6deab85e1ce68c6879ff87
[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/pap/v1/pdps/policies") and http_type == "POST":
203         print "self.path start with POST new policy API /policy/pap/v1/pdps/ ..."
204         if not os.path.exists(cached_file_folder):
205             os.makedirs(cached_file_folder, 0777)
206         with open(cached_file_header, 'w+') as f:
207                 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
208         with open(cached_file_content, 'w+') as f:
209                 f.write(self.data_string)
210         return True
211      elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "GET":
212         print "self.path start with /policy/api/v1/policytypes/, generating response json..."
213         jsonGenerated =  "{\"policyTypeId\": \"onap.policies.controlloop.operational\",\"policyTypeVersion\": \"1.0.0\",\"policyId\": \"OPERATIONAL_z711F_v1_0_ResourceInstanceName1_tca\"}"
214         print "jsonGenerated: " + jsonGenerated
215         if not os.path.exists(cached_file_folder):
216             os.makedirs(cached_file_folder, 0777)
217
218         with open(cached_file_header, 'w') as f:
219             f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
220         with open(cached_file_content, 'w') as f:
221             f.write(jsonGenerated)
222         return True
223      else:
224         return False
225
226     
227     def do_GET(self):
228         cached_file_folder = ""
229         cached_file_content =""
230         cached_file_header=""
231         print("\n\n\nGot a GET request for %s " % self.path)
232
233         self.print_headers()
234         self.check_credentials()
235         # Verify if it's a special case
236         is_special = self._execute_content_generated_cases("GET")
237         if is_special:
238             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
239             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
240             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
241         else:
242             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
243             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
244             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
245
246         _file_available = os.path.exists(cached_file_content)
247
248         if not _file_available:
249             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
250
251             if not HOST:
252                 self.send_response(404)
253                 self.end_headers()
254                 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
255                 return "404 Not found, no remote HOST specified on the emulator !!!"
256
257             url = '%s%s' % (HOST, self.path)
258             response = requests.get(url, auth=AUTH, headers=HEADERS, stream=True)
259
260             if response.status_code == 200:
261                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
262             else:
263                 print('Error when requesting file :')
264                 print('Requested url : %s' % (url,))
265                 print('Status code : %s' % (response.status_code,))
266                 print('Content : %s' % (response.content,))
267                 self.send_response(response.status_code)
268                 self.end_headers()
269                 self.wfile.write('404 Not found, nothing found on the remote server !!!')
270                 return response.content
271         else:
272             print("Request for data currently present in cache: %s" % (cached_file_folder,))
273
274         self._send_content(cached_file_header, cached_file_content)
275
276         if self.path.startswith("/dcae-service-types?asdcResourceId="):
277             print "DCAE case deleting folder created " + cached_file_folder
278             shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
279         else:
280             print "NOT in DCAE case deleting folder created " + cached_file_folder
281
282     def do_POST(self):
283         cached_file_folder = ""
284         cached_file_content =""
285         cached_file_header=""
286         print("\n\n\nGot a POST for %s" % self.path)
287         self.check_credentials()
288         self.data_string = self.rfile.read(int(self.headers['Content-Length']))
289         print("data-string:\n %s" % self.data_string)
290         print("self.headers:\n %s" % self.headers)
291
292         is_special = self._execute_content_generated_cases("POST")
293         if is_special:
294             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
295             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
296             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
297         else:
298             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
299             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
300             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
301
302         _file_available = os.path.exists(cached_file_content)
303
304         if not _file_available:
305         
306             if not HOST:
307                 self.send_response(404)
308                 self.end_headers()
309                 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
310                 return "404 Not found, no remote HOST specified on the emulator !!!"
311
312             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
313
314             url = '%s%s' % (HOST, self.path)
315             print("url: %s" % (url,))
316             response = requests.post(url, data=self.data_string, headers=self.headers, stream=True)
317
318             if response.status_code == 200:
319                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
320             else:
321                 print('Error when requesting file :')
322                 print('Requested url : %s' % (url,))
323                 print('Status code : %s' % (response.status_code,))
324                 print('Content : %s' % (response.content,))
325                 self.send_response(response.status_code)
326                 self.end_headers()
327                 self.wfile.write('404 Not found, nothing found on the remote server !!!')
328                 return response.content
329         else:
330             print("Request for data present in cache: %s" % (cached_file_folder,))
331
332         self._send_content(cached_file_header, cached_file_content)
333
334     def do_PUT(self):
335         cached_file_folder = ""
336         cached_file_content =""
337         cached_file_header=""
338         print("\n\n\nGot a PUT for %s " % self.path)
339         self.check_credentials()
340         self.data_string = self.rfile.read(int(self.headers['Content-Length']))
341         print("data-string:\n %s" % self.data_string)
342         print("self.headers:\n %s" % self.headers)
343
344         is_special = self._execute_content_generated_cases("PUT")
345         if is_special:
346             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
347             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
348             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
349         else:
350             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
351             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
352             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
353
354         _file_available = os.path.exists(cached_file_content)
355
356         if not _file_available:
357             if not HOST:
358                 self.send_response(404)
359                 self.end_headers()
360                 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
361                 return "404 Not found, no remote HOST specified on the emulator !!!"
362
363             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
364
365             url = '%s%s' % (HOST, self.path)
366             print("url: %s" % (url,))
367             response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
368
369             if response.status_code == 200:
370                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
371             else:
372                 print('Error when requesting file :')
373                 print('Requested url : %s' % (url,))
374                 print('Status code : %s' % (response.status_code,))
375                 print('Content : %s' % (response.content,))
376                 self.send_response(response.status_code)
377                 self.end_headers()
378                 self.wfile.write('404 Not found, nothing found on the remote server !!!')
379                 return response.content
380         else:
381             print("Request for data present in cache: %s" % (cached_file_folder,))
382
383         self._send_content(cached_file_header, cached_file_content)
384
385
386     def do_DELETE(self):
387         cached_file_folder = ""
388         cached_file_content =""
389         cached_file_header=""
390         print("\n\n\nGot a DELETE for %s " % self.path)
391         self.check_credentials()
392         if self.headers.get('Content-Length') is not None:
393             self.data_string = self.rfile.read(int(self.headers['Content-Length']))
394         else:
395             self.data_string = "empty generated"
396         print("self.headers:\n %s" % self.headers)
397
398         is_special = self._execute_content_generated_cases("DELETE")
399         if is_special:
400             cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
401             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
402             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
403         else:
404             cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
405             cached_file_content = self._get_cached_content_file_name(cached_file_folder)
406             cached_file_header = self._get_cached_header_file_name(cached_file_folder)
407
408         _file_available = os.path.exists(cached_file_content)
409
410         if not _file_available:
411             if not HOST:
412                 self.send_response(404)
413                 self.end_headers()
414                 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
415                 return "404 Not found, no remote HOST specified on the emulator !!!"
416
417             print("Request for data currently not present in cache: %s" % (cached_file_folder,))
418
419             url = '%s%s' % (HOST, self.path)
420             print("url: %s" % (url,))
421             response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
422
423             if response.status_code == 200:
424                 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
425             else:
426                 print('Error when requesting file :')
427                 print('Requested url : %s' % (url,))
428                 print('Status code : %s' % (response.status_code,))
429                 print('Content : %s' % (response.content,))
430                 self.send_response(response.status_code)
431                 self.end_headers()
432                 self.wfile.write('404 Not found, nothing found on the remote server !!!')
433                 return response.content
434         else:
435             print("Request for data present in cache: %s" % (cached_file_folder,))
436
437         self._send_content(cached_file_header, cached_file_content)
438
439
440
441 # Main code that start the HTTP server
442 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
443 httpd.allow_reuse_address = True
444 print "Listening on port "+ str(PORT) + "(Press Ctrl+C/Ctrl+Z to stop HTTPD Caching script)"
445 print "Caching folder " + CACHE_ROOT + ", Tmp folder for generated files " + TMP_ROOT 
446 signal.signal(signal.SIGINT, signal_handler)
447 httpd.serve_forever()