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