3 # ============LICENSE_START=======================================================
5 # ================================================================================
6 # Copyright (C) 2018 AT&T Intellectual Property. All rights
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
13 # http://www.apache.org/licenses/LICENSE-2.0
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 # ===================================================================
30 import SimpleHTTPServer
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()
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)
58 def signal_handler(signal_sent, frame):
60 if signal_sent == signal.SIGINT:
61 print('Got Ctrl-C (SIGINT)')
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))
71 def check_credentials(self):
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',):
81 self.send_header(key, value)
83 with open(content_file,'rb') as f:
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('<','<').replace('>','>').replace('?','?').replace('*','*').replace('\\','*').replace(':',':').replace('|','|')
98 print("Cached file name after escaping (used for cache storage) : %s" % cached_file_folder)
99 return cached_file_folder
101 def _get_cached_content_file_name(self,cached_file_folder):
102 return "%s/.file" % (cached_file_folder,)
104 def _get_cached_header_file_name(self,cached_file_folder):
105 return "%s/.header" % (cached_file_folder,)
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)
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
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)
130 elif self.path.startswith("/dcae-operationstatus/install") and http_type == "GET":
131 if not _file_available:
132 print "self.path start with /dcae-operationstatus/install, generating response json..."
133 jsonGenerated = "{\"operationType\": \"install\", \"status\": \"succeeded\"}"
134 print "jsonGenerated: " + jsonGenerated
137 os.makedirs(cached_file_folder, 0777)
139 if e.errno != errno.EEXIST:
141 print(cached_file_folder+" already exists")
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)
148 elif self.path.startswith("/dcae-operationstatus/uninstall") and http_type == "GET":
149 if not _file_available:
150 print "self.path start with /dcae-operationstatus/uninstall, generating response json..."
151 jsonGenerated = "{\"operationType\": \"uninstall\", \"status\": \"succeeded\"}"
152 print "jsonGenerated: " + jsonGenerated
155 os.makedirs(cached_file_folder, 0777)
157 if e.errno != errno.EEXIST:
159 print(cached_file_folder+" already exists")
161 with open(cached_file_header, 'w') as f:
162 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
163 with open(cached_file_content, 'w') as f:
164 f.write(jsonGenerated)
166 elif self.path.startswith("/sdc/v1/catalog/services/") and http_type == "POST":
167 if not _file_available:
168 print "self.path start with /sdc/v1/catalog/services/, generating response json..."
169 jsondata = json.loads(self.data_string)
170 jsonGenerated = "{\"artifactName\":\"" + jsondata['artifactName'] + "\",\"artifactType\":\"" + jsondata['artifactType'] + "\",\"artifactURL\":\"" + self.path + "\",\"artifactDescription\":\"" + jsondata['description'] + "\",\"artifactChecksum\":\"ZjJlMjVmMWE2M2M1OTM2MDZlODlmNTVmZmYzNjViYzM=\",\"artifactUUID\":\"" + str(uuid.uuid4()) + "\",\"artifactVersion\":\"1\"}"
171 print "jsonGenerated: " + jsonGenerated
173 os.makedirs(cached_file_folder, 0777)
174 with open(cached_file_header, 'w') as f:
175 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
176 with open(cached_file_content, 'w') as f:
177 f.write(jsonGenerated)
179 elif self.path.startswith("/dcae-deployments/") and http_type == "PUT":
180 print "self.path start with /dcae-deployments/ DEPLOY, generating response json..."
181 #jsondata = json.loads(self.data_string)
182 jsonGenerated = "{\"operationType\":\"install\",\"status\":\"processing\",\"links\":{\"status\":\"http:\/\/" + PROXY_ADDRESS + "\/dcae-operationstatus/install\"}}"
183 print "jsonGenerated: " + jsonGenerated
184 if not os.path.exists(cached_file_folder):
185 os.makedirs(cached_file_folder, 0777)
186 with open(cached_file_header, 'w+') as f:
187 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
188 with open(cached_file_content, 'w+') as f:
189 f.write(jsonGenerated)
191 elif self.path.startswith("/dcae-deployments/") and http_type == "DELETE":
192 print "self.path start with /dcae-deployments/ UNDEPLOY, generating response json..."
193 #jsondata = json.loads(self.data_string)
194 jsonGenerated = "{\"operationType\":\"uninstall\",\"status\":\"processing\",\"links\":{\"status\":\"http:\/\/" + PROXY_ADDRESS + "\/dcae-operationstatus/uninstall\"}}"
195 print "jsonGenerated: " + jsonGenerated
196 if not os.path.exists(cached_file_folder):
197 os.makedirs(cached_file_folder, 0777)
198 with open(cached_file_header, 'w+') as f:
199 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
200 with open(cached_file_content, 'w+') as f:
201 f.write(jsonGenerated)
203 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"):
204 print "self.path start with /pdp/api/, copying body to response ..."
205 if not os.path.exists(cached_file_folder):
206 os.makedirs(cached_file_folder, 0777)
207 with open(cached_file_header, 'w+') as f:
208 f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
209 with open(cached_file_content, 'w+') as f:
210 f.write(self.data_string)
212 elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "POST":
213 print "self.path start with POST new policy API /pdp/api/, copying body to response ..."
214 if not os.path.exists(cached_file_folder):
215 os.makedirs(cached_file_folder, 0777)
216 with open(cached_file_header, 'w+') as f:
217 f.write("{\"Content-Length\": \"" + str(len(self.data_string)) + "\", \"Content-Type\": \""+str(self.headers['Content-Type'])+"\"}")
218 with open(cached_file_content, 'w+') as f:
219 f.write(self.data_string)
221 elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "DELETE":
222 print "self.path start with DELETE new policy API /policy/api/v1/policytypes/ ..."
223 if not os.path.exists(cached_file_folder):
224 os.makedirs(cached_file_folder, 0777)
226 with open(cached_file_header, 'w+') as f:
227 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
228 with open(cached_file_content, 'w+') as f:
229 f.write(self.data_string)
231 elif self.path.startswith("/policy/pap/v1/pdps/policies") and http_type == "POST":
232 print "self.path start with POST new policy API /policy/pap/v1/pdps/ ..."
233 if not os.path.exists(cached_file_folder):
234 os.makedirs(cached_file_folder, 0777)
235 with open(cached_file_header, 'w+') as f:
236 f.write("{\"Content-Length\": \"" + str(len("")) + "\", \"Content-Type\": \""+str("")+"\"}")
237 with open(cached_file_content, 'w+') as f:
238 f.write(self.data_string)
240 elif self.path.startswith("/policy/api/v1/policytypes/") and http_type == "GET":
241 print "self.path start with /policy/api/v1/policytypes/, generating response json..."
242 jsonGenerated = "{\"policyTypeId\": \"onap.policies.controlloop.operational\",\"policyTypeVersion\": \"1.0.0\",\"policyId\": \"OPERATIONAL_z711F_v1_0_ResourceInstanceName1_tca\"}"
243 print "jsonGenerated: " + jsonGenerated
244 if not os.path.exists(cached_file_folder):
245 os.makedirs(cached_file_folder, 0777)
247 with open(cached_file_header, 'w') as f:
248 f.write("{\"Content-Length\": \"" + str(len(jsonGenerated)) + "\", \"Content-Type\": \"application/json\"}")
249 with open(cached_file_content, 'w') as f:
250 f.write(jsonGenerated)
257 cached_file_folder = ""
258 cached_file_content =""
259 cached_file_header=""
260 print("\n\n\nGot a GET request for %s " % self.path)
263 self.check_credentials()
264 # Verify if it's a special case
265 is_special = self._execute_content_generated_cases("GET")
267 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
268 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
269 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
271 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
272 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
273 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
275 _file_available = os.path.exists(cached_file_content)
277 if not _file_available:
278 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
281 self.send_response(404)
283 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
284 return "404 Not found, no remote HOST specified on the emulator !!!"
286 url = '%s%s' % (HOST, self.path)
287 response = requests.get(url, auth=AUTH, headers=HEADERS, stream=True)
289 if response.status_code == 200:
290 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
292 print('Error when requesting file :')
293 print('Requested url : %s' % (url,))
294 print('Status code : %s' % (response.status_code,))
295 print('Content : %s' % (response.content,))
296 self.send_response(response.status_code)
298 self.wfile.write('404 Not found, nothing found on the remote server !!!')
299 return response.content
301 print("Request for data currently present in cache: %s" % (cached_file_folder,))
303 self._send_content(cached_file_header, cached_file_content)
305 if self.path.startswith("/dcae-service-types?asdcResourceId="):
306 print "DCAE case deleting folder created " + cached_file_folder
307 shutil.rmtree(cached_file_folder, ignore_errors=False, onerror=None)
309 print "NOT in DCAE case deleting folder created " + cached_file_folder
312 cached_file_folder = ""
313 cached_file_content =""
314 cached_file_header=""
315 print("\n\n\nGot a POST for %s" % self.path)
316 self.check_credentials()
317 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
318 print("data-string:\n %s" % self.data_string)
319 print("self.headers:\n %s" % self.headers)
321 is_special = self._execute_content_generated_cases("POST")
323 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
324 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
325 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
327 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
328 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
329 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
331 _file_available = os.path.exists(cached_file_content)
333 if not _file_available:
336 self.send_response(404)
338 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
339 return "404 Not found, no remote HOST specified on the emulator !!!"
341 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
343 url = '%s%s' % (HOST, self.path)
344 print("url: %s" % (url,))
345 response = requests.post(url, data=self.data_string, headers=self.headers, stream=True)
347 if response.status_code == 200:
348 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
350 print('Error when requesting file :')
351 print('Requested url : %s' % (url,))
352 print('Status code : %s' % (response.status_code,))
353 print('Content : %s' % (response.content,))
354 self.send_response(response.status_code)
356 self.wfile.write('404 Not found, nothing found on the remote server !!!')
357 return response.content
359 print("Request for data present in cache: %s" % (cached_file_folder,))
361 self._send_content(cached_file_header, cached_file_content)
364 cached_file_folder = ""
365 cached_file_content =""
366 cached_file_header=""
367 print("\n\n\nGot a PUT for %s " % self.path)
368 self.check_credentials()
369 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
370 print("data-string:\n %s" % self.data_string)
371 print("self.headers:\n %s" % self.headers)
373 is_special = self._execute_content_generated_cases("PUT")
375 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
376 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
377 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
379 cached_file_folder = self._get_cached_file_folder_name(CACHE_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)
383 _file_available = os.path.exists(cached_file_content)
385 if not _file_available:
387 self.send_response(404)
389 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
390 return "404 Not found, no remote HOST specified on the emulator !!!"
392 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
394 url = '%s%s' % (HOST, self.path)
395 print("url: %s" % (url,))
396 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
398 if response.status_code == 200:
399 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
401 print('Error when requesting file :')
402 print('Requested url : %s' % (url,))
403 print('Status code : %s' % (response.status_code,))
404 print('Content : %s' % (response.content,))
405 self.send_response(response.status_code)
407 self.wfile.write('404 Not found, nothing found on the remote server !!!')
408 return response.content
410 print("Request for data present in cache: %s" % (cached_file_folder,))
412 self._send_content(cached_file_header, cached_file_content)
416 cached_file_folder = ""
417 cached_file_content =""
418 cached_file_header=""
419 print("\n\n\nGot a DELETE for %s " % self.path)
420 self.check_credentials()
421 if self.headers.get('Content-Length') is not None:
422 self.data_string = self.rfile.read(int(self.headers['Content-Length']))
424 self.data_string = "empty generated"
425 print("self.headers:\n %s" % self.headers)
427 is_special = self._execute_content_generated_cases("DELETE")
429 cached_file_folder = self._get_cached_file_folder_name(TMP_ROOT)
430 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
431 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
433 cached_file_folder = self._get_cached_file_folder_name(CACHE_ROOT)
434 cached_file_content = self._get_cached_content_file_name(cached_file_folder)
435 cached_file_header = self._get_cached_header_file_name(cached_file_folder)
437 _file_available = os.path.exists(cached_file_content)
439 if not _file_available:
441 self.send_response(404)
443 self.wfile.write('404 Not found, no remote HOST specified on the emulator !!!')
444 return "404 Not found, no remote HOST specified on the emulator !!!"
446 print("Request for data currently not present in cache: %s" % (cached_file_folder,))
448 url = '%s%s' % (HOST, self.path)
449 print("url: %s" % (url,))
450 response = requests.put(url, data=self.data_string, headers=self.headers, stream=True)
452 if response.status_code == 200:
453 self._write_cache(cached_file_folder, cached_file_header, cached_file_content, response)
455 print('Error when requesting file :')
456 print('Requested url : %s' % (url,))
457 print('Status code : %s' % (response.status_code,))
458 print('Content : %s' % (response.content,))
459 self.send_response(response.status_code)
461 self.wfile.write('404 Not found, nothing found on the remote server !!!')
462 return response.content
464 print("Request for data present in cache: %s" % (cached_file_folder,))
466 self._send_content(cached_file_header, cached_file_content)
470 # Main code that start the HTTP server
471 httpd = SocketServer.ForkingTCPServer(('', PORT), Proxy)
472 httpd.allow_reuse_address = True
473 print "Listening on port "+ str(PORT) + "(Press Ctrl+C/Ctrl+Z to stop HTTPD Caching script)"
474 print "Caching folder " + CACHE_ROOT + ", Tmp folder for generated files " + TMP_ROOT
475 signal.signal(signal.SIGINT, signal_handler)
476 httpd.serve_forever()