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