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