X-Git-Url: https://osm.etsi.org/gitweb/?p=osm%2Fvim-emu.git;a=blobdiff_plain;f=src%2Femuvim%2Fapi%2Fopenstack%2Fchain_api.py;h=fde3a423f18508b66e65a25e4e22c03b8f90c2f8;hp=7c65e562d880a91b5ef6fac62c2fd2de6a54b36b;hb=68d042113554a310c357ef1b614c7a9e84cd7a72;hpb=00199782de7042b51927bbbac5540d6f671597bb diff --git a/src/emuvim/api/openstack/chain_api.py b/src/emuvim/api/openstack/chain_api.py old mode 100644 new mode 100755 index 7c65e56..fde3a42 --- a/src/emuvim/api/openstack/chain_api.py +++ b/src/emuvim/api/openstack/chain_api.py @@ -1,14 +1,42 @@ +# Copyright (c) 2015 SONATA-NFV and Paderborn University +# ALL RIGHTS RESERVED. +# +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# See the License for the specific language governing permissions and +# limitations under the License. +# +# Neither the name of the SONATA-NFV, Paderborn University +# nor the names of its contributors may be used to endorse or promote +# products derived from this software without specific prior written +# permission. +# +# This work has been performed in the framework of the SONATA project, +# funded by the European Commission under Grant number 671517 through +# the Horizon 2020 and 5G-PPP programmes. The authors would like to +# acknowledge the contributions of their colleagues of the SONATA +# partner consortium (www.sonata-nfv.eu). import json import logging import copy +from gevent import monkey +from gevent.pywsgi import WSGIServer + from mininet.node import OVSSwitch from flask import Flask from flask import Response, request from flask_restful import Api, Resource -from mininet.link import Link -import uuid + +monkey.patch_all() class ChainApi(Resource): @@ -42,7 +70,6 @@ class ChainApi(Resource): resource_class_kwargs={'api': self}) self.api.add_resource(QueryTopology, "/v1/topo", resource_class_kwargs={'api': self}) - self.api.add_resource(Shutdown, "/shutdown") @self.app.after_request def add_access_control_header(response): @@ -50,10 +77,20 @@ class ChainApi(Resource): return response def _start_flask(self): - logging.info("Starting %s endpoint @ http://%s:%d" % ("ChainDummyApi", self.ip, self.port)) - if self.app is not None: - self.app.before_request(self.dump_playbook) - self.app.run(self.ip, self.port, debug=True, use_reloader=False) + logging.info("Starting %s endpoint @ http://%s:%d" % + ("ChainDummyApi", self.ip, self.port)) + self.http_server = WSGIServer( + (self.ip, self.port), + self.app, + log=open("/dev/null", "w") # don't show http logs + ) + self.http_server.serve_forever(stop_timeout=1) + logging.info('Stopped %s' % self.__class__.__name__) + + def stop(self): + if self.http_server: + logging.info('Stopping %s' % self.__class__.__name__) + self.http_server.stop(timeout=1) def dump_playbook(self): with self.manage.lock: @@ -61,20 +98,11 @@ class ChainApi(Resource): if len(request.data) > 0: data = "# CHAIN API\n" data += "curl -X {type} -H \"Content-type: application/json\" -d '{data}' {url}".format(type=request.method, - data=request.data, - url=request.url) + data=request.data, + url=request.url) logfile.write(data + "\n") -class Shutdown(Resource): - def get(self): - logging.debug(("%s is beeing shut down") % (__name__)) - func = request.environ.get('werkzeug.server.shutdown') - if func is None: - raise RuntimeError('Not running with the Werkzeug Server') - func() - - class ChainVersionsList(Resource): ''' Entrypoint to find versions of the chain api. @@ -112,7 +140,8 @@ class ChainVersionsList(Resource): return Response(resp, status=200, mimetype="application/json") except Exception as ex: - logging.exception(u"%s: Could not show list of versions." % __name__) + logging.exception( + u"%s: Could not show list of versions." % __name__) return ex.message, 500 @@ -135,10 +164,12 @@ class ChainList(Resource): for chain in self.api.manage.full_chain_data.values(): resp["chains"].append(chain) - return Response(json.dumps(resp), status=200, mimetype="application/json") + return Response(json.dumps(resp), status=200, + mimetype="application/json") except Exception as ex: - logging.exception(u"%s: Could not list all network chains." % __name__) + logging.exception( + u"%s: Could not list all network chains." % __name__) return ex.message, 500 @@ -161,10 +192,12 @@ class BalanceHostList(Resource): for lb in self.api.manage.full_lb_data.values(): resp["loadbalancers"].append(lb) - return Response(json.dumps(resp), status=200, mimetype="application/json") + return Response(json.dumps(resp), status=200, + mimetype="application/json") except Exception as ex: - logging.exception(u"%s: Could not list all live loadbalancers." % __name__) + logging.exception( + u"%s: Could not list all live loadbalancers." % __name__) return ex.message, 500 @@ -244,11 +277,14 @@ class ChainVnfInterfaces(Resource): vnf_dst_interface=dst_intfs, bidirectional=True, path=path, layer2=layer2) resp = {'cookie': cookie} - return Response(json.dumps(resp), status=200, mimetype="application/json") + return Response(json.dumps(resp), status=200, + mimetype="application/json") except Exception as e: - logging.exception(u"%s: Error setting up the chain.\n %s" % (__name__, e)) - return Response(u"Error setting up the chain", status=500, mimetype="application/json") + logging.exception( + u"%s: Error setting up the chain.\n %s" % (__name__, e)) + return Response(u"Error setting up the chain", + status=500, mimetype="application/json") def delete(self, src_vnf, src_intfs, dst_vnf, dst_intfs): """ @@ -280,10 +316,13 @@ class ChainVnfInterfaces(Resource): try: cookie = self.api.manage.network_action_stop(src_vnf, dst_vnf, vnf_src_interface=src_intfs, vnf_dst_interface=dst_intfs, bidirectional=True) - return Response(json.dumps(cookie), status=200, mimetype="application/json") + return Response(json.dumps(cookie), status=200, + mimetype="application/json") except Exception as e: - logging.exception(u"%s: Error deleting the chain.\n %s" % (__name__, e)) - return Response(u"Error deleting the chain", status=500, mimetype="application/json") + logging.exception( + u"%s: Error deleting the chain.\n %s" % (__name__, e)) + return Response(u"Error deleting the chain", + status=500, mimetype="application/json") class ChainVnfDcStackInterfaces(Resource): @@ -295,7 +334,8 @@ class ChainVnfDcStackInterfaces(Resource): def __init__(self, api): self.api = api - def put(self, src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs): + def put(self, src_dc, src_stack, src_vnf, src_intfs, + dst_dc, dst_stack, dst_vnf, dst_intfs): """ A PUT request to "/v1/chain////////" will set up chain. @@ -325,18 +365,21 @@ class ChainVnfDcStackInterfaces(Resource): """ # search for real names - real_names = self._findNames(src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) - if type(real_names) is not tuple: + real_names = self._findNames( + src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) + if not isinstance(real_names, tuple): # something went wrong return real_names container_src, container_dst, interface_src, interface_dst = real_names # check if both VNFs exist - if not self.api.manage.check_vnf_intf_pair(container_src, interface_src): + if not self.api.manage.check_vnf_intf_pair( + container_src, interface_src): return Response(u"VNF %s or intfs %s does not exist" % (container_src, interface_src), status=501, mimetype="application/json") - if not self.api.manage.check_vnf_intf_pair(container_dst, interface_dst): + if not self.api.manage.check_vnf_intf_pair( + container_dst, interface_dst): return Response(u"VNF %s or intfs %s does not exist" % (container_dst, interface_dst), status=501, mimetype="application/json") @@ -345,13 +388,17 @@ class ChainVnfDcStackInterfaces(Resource): vnf_dst_interface=interface_dst, bidirectional=True, layer2=True) resp = {'cookie': cookie} - return Response(json.dumps(resp), status=200, mimetype="application/json") + return Response(json.dumps(resp), status=200, + mimetype="application/json") except Exception as e: - logging.exception(u"%s: Error setting up the chain.\n %s" % (__name__, e)) - return Response(u"Error setting up the chain", status=500, mimetype="application/json") + logging.exception( + u"%s: Error setting up the chain.\n %s" % (__name__, e)) + return Response(u"Error setting up the chain", + status=500, mimetype="application/json") - def post(self, src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs): + def post(self, src_dc, src_stack, src_vnf, src_intfs, + dst_dc, dst_stack, dst_vnf, dst_intfs): """ A post request to "/v1/chain////////" will create a chain between two interfaces at the specified vnfs. @@ -383,8 +430,9 @@ class ChainVnfDcStackInterfaces(Resource): layer2 = True # search for real names - real_names = self._findNames(src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) - if type(real_names) is not tuple: + real_names = self._findNames( + src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) + if not isinstance(real_names, tuple): # something went wrong return real_names @@ -395,13 +443,17 @@ class ChainVnfDcStackInterfaces(Resource): vnf_dst_interface=interface_dst, bidirectional=True, path=path, layer2=layer2) resp = {'cookie': cookie} - return Response(json.dumps(resp), status=200, mimetype="application/json") + return Response(json.dumps(resp), status=200, + mimetype="application/json") except Exception as e: - logging.exception(u"%s: Error setting up the chain.\n %s" % (__name__, e)) - return Response(u"Error setting up the chain", status=500, mimetype="application/json") + logging.exception( + u"%s: Error setting up the chain.\n %s" % (__name__, e)) + return Response(u"Error setting up the chain", + status=500, mimetype="application/json") - def delete(self, src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs): + def delete(self, src_dc, src_stack, src_vnf, src_intfs, + dst_dc, dst_stack, dst_vnf, dst_intfs): """ A DELETE request to "/v1/chain////////" will delete a previously created chain. @@ -429,8 +481,9 @@ class ChainVnfDcStackInterfaces(Resource): """ # search for real names - real_names = self._findNames(src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) - if type(real_names) is not tuple: + real_names = self._findNames( + src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs) + if not isinstance(real_names, tuple): # something went wrong, real_names is a Response object return real_names @@ -439,17 +492,22 @@ class ChainVnfDcStackInterfaces(Resource): try: cookie = self.api.manage.network_action_stop(container_src, container_dst, vnf_src_interface=interface_src, vnf_dst_interface=interface_dst, bidirectional=True) - return Response(json.dumps(cookie), status=200, mimetype="application/json") + return Response(json.dumps(cookie), status=200, + mimetype="application/json") except Exception as e: - logging.exception(u"%s: Error deleting the chain.\n %s" % (__name__, e)) - return Response(u"Error deleting the chain", status=500, mimetype="application/json") + logging.exception( + u"%s: Error deleting the chain.\n %s" % (__name__, e)) + return Response(u"Error deleting the chain", + status=500, mimetype="application/json") # Tries to find real container and interface names according to heat template names # Returns a tuple of 4 or a Response object - def _findNames(self, src_dc, src_stack, src_vnf, src_intfs, dst_dc, dst_stack, dst_vnf, dst_intfs): + def _findNames(self, src_dc, src_stack, src_vnf, src_intfs, + dst_dc, dst_stack, dst_vnf, dst_intfs): # search for datacenters if src_dc not in self.api.manage.net.dcs or dst_dc not in self.api.manage.net.dcs: - return Response(u"At least one DC does not exist", status=500, mimetype="application/json") + return Response(u"At least one DC does not exist", + status=500, mimetype="application/json") dc_src = self.api.manage.net.dcs[src_dc] dc_dst = self.api.manage.net.dcs[dst_dc] # search for related OpenStackAPIs @@ -462,7 +520,8 @@ class ChainVnfDcStackInterfaces(Resource): if api.compute.dc == dc_dst: api_dst = api if api_src is None or api_dst is None: - return Response(u"At least one OpenStackAPI does not exist", status=500, mimetype="application/json") + return Response(u"At least one OpenStackAPI does not exist", + status=500, mimetype="application/json") # search for stacks stack_src = None stack_dst = None @@ -473,7 +532,8 @@ class ChainVnfDcStackInterfaces(Resource): if stack.stack_name == dst_stack: stack_dst = stack if stack_src is None or stack_dst is None: - return Response(u"At least one Stack does not exist", status=500, mimetype="application/json") + return Response(u"At least one Stack does not exist", + status=500, mimetype="application/json") # search for servers server_src = None server_dst = None @@ -486,7 +546,8 @@ class ChainVnfDcStackInterfaces(Resource): server_dst = server break if server_src is None or server_dst is None: - return Response(u"At least one VNF does not exist", status=500, mimetype="application/json") + return Response(u"At least one VNF does not exist", + status=500, mimetype="application/json") container_src = server_src.name container_dst = server_dst.name @@ -499,7 +560,8 @@ class ChainVnfDcStackInterfaces(Resource): if dst_intfs in server_dst.port_names: port_dst = stack_dst.ports[dst_intfs] if port_src is None or port_dst is None: - return Response(u"At least one Port does not exist", status=500, mimetype="application/json") + return Response(u"At least one Port does not exist", + status=500, mimetype="application/json") interface_src = port_src.intf_name interface_dst = port_dst.intf_name @@ -549,8 +611,9 @@ class BalanceHostDcStack(Resource): # check src vnf/port if src_stack != "floating": - real_src = self._findName(src_dc, src_stack, vnf_src_name, vnf_src_interface) - if type(real_src) is not tuple: + real_src = self._findName( + src_dc, src_stack, vnf_src_name, vnf_src_interface) + if not isinstance(real_src, tuple): # something went wrong, real_src is a Response object return real_src @@ -563,20 +626,24 @@ class BalanceHostDcStack(Resource): dst_server = dst_vnf.get('server', None) dst_port = dst_vnf.get('port', None) if dst_dc is not None and dst_stack is not None and dst_server is not None and dst_port is not None: - real_dst = self._findName(dst_dc, dst_stack, dst_server, dst_port) - if type(real_dst) is not tuple: + real_dst = self._findName( + dst_dc, dst_stack, dst_server, dst_port) + if not isinstance(real_dst, tuple): # something went wrong, real_dst is a Response object return real_dst real_dst_dict[real_dst[0]] = real_dst[1] - input_object = {"dst_vnf_interfaces": real_dst_dict, "path": req.get("path", None)} + input_object = {"dst_vnf_interfaces": real_dst_dict, + "path": req.get("path", None)} if src_stack != "floating": - self.api.manage.add_loadbalancer(container_src, interface_src, lb_data=input_object) + self.api.manage.add_loadbalancer( + container_src, interface_src, lb_data=input_object) return Response(u"Loadbalancer set up at %s:%s" % (container_src, interface_src), status=200, mimetype="application/json") else: - cookie, floating_ip = self.api.manage.add_floating_lb(src_dc, lb_data=input_object) + cookie, floating_ip = self.api.manage.add_floating_lb( + src_dc, lb_data=input_object) return Response(json.dumps({"cookie": "%d" % cookie, "floating_ip": "%s" % floating_ip}), status=200, mimetype="application/json") @@ -585,7 +652,8 @@ class BalanceHostDcStack(Resource): logging.exception(u"%s: Error setting up the loadbalancer at %s %s %s:%s.\n %s" % (__name__, src_dc, src_stack, vnf_src_name, vnf_src_interface, e)) return Response(u"%s: Error setting up the loadbalancer at %s %s %s:%s.\n %s" % - (__name__, src_dc, src_stack, vnf_src_name, vnf_src_interface, e), status=500, + (__name__, src_dc, src_stack, vnf_src_name, + vnf_src_interface, e), status=500, mimetype="application/json") def delete(self, src_dc, src_stack, vnf_src_name, vnf_src_interface): @@ -607,14 +675,16 @@ class BalanceHostDcStack(Resource): try: # check src vnf/port if src_stack != "floating": - real_src = self._findName(src_dc, src_stack, vnf_src_name, vnf_src_interface) - if type(real_src) is not tuple: + real_src = self._findName( + src_dc, src_stack, vnf_src_name, vnf_src_interface) + if not isinstance(real_src, tuple): # something went wrong, real_src is a Response object return real_src container_src, interface_src = real_src - self.api.manage.delete_loadbalancer(container_src, interface_src) + self.api.manage.delete_loadbalancer( + container_src, interface_src) return Response(u"Loadbalancer deleted at %s:%s" % (vnf_src_name, vnf_src_interface), status=200, mimetype="application/json") else: @@ -627,7 +697,8 @@ class BalanceHostDcStack(Resource): logging.exception(u"%s: Error deleting the loadbalancer at %s %s %s%s.\n %s" % (__name__, src_dc, src_stack, vnf_src_name, vnf_src_interface, e)) return Response(u"%s: Error deleting the loadbalancer at %s %s %s%s." % - (__name__, src_dc, src_stack, vnf_src_name, vnf_src_interface), status=500, + (__name__, src_dc, src_stack, vnf_src_name, + vnf_src_interface), status=500, mimetype="application/json") # Tries to find real container and port name according to heat template names @@ -635,7 +706,8 @@ class BalanceHostDcStack(Resource): def _findName(self, dc, stack, vnf, port): # search for datacenters if dc not in self.api.manage.net.dcs: - return Response(u"DC does not exist", status=500, mimetype="application/json") + return Response(u"DC does not exist", status=500, + mimetype="application/json") dc_real = self.api.manage.net.dcs[dc] # search for related OpenStackAPIs api_real = None @@ -644,14 +716,16 @@ class BalanceHostDcStack(Resource): if api.compute.dc == dc_real: api_real = api if api_real is None: - return Response(u"OpenStackAPI does not exist", status=500, mimetype="application/json") + return Response(u"OpenStackAPI does not exist", + status=500, mimetype="application/json") # search for stacks stack_real = None for stackObj in api_real.compute.stacks.values(): if stackObj.stack_name == stack: stack_real = stackObj if stack_real is None: - return Response(u"Stack does not exist", status=500, mimetype="application/json") + return Response(u"Stack does not exist", status=500, + mimetype="application/json") # search for servers server_real = None for server in stack_real.servers.values(): @@ -659,7 +733,8 @@ class BalanceHostDcStack(Resource): server_real = server break if server_real is None: - return Response(u"VNF does not exist", status=500, mimetype="application/json") + return Response(u"VNF does not exist", status=500, + mimetype="application/json") container_real = server_real.name @@ -668,7 +743,8 @@ class BalanceHostDcStack(Resource): if port in server_real.port_names: port_real = stack_real.ports[port] if port_real is None: - return Response(u"At least one Port does not exist", status=500, mimetype="application/json") + return Response(u"At least one Port does not exist", + status=500, mimetype="application/json") interface_real = port_real.intf_name @@ -706,16 +782,19 @@ class BalanceHost(Resource): if vnf_src_name != "floating": # check if VNF exist - if not self.api.manage.check_vnf_intf_pair(vnf_src_name, vnf_src_interface): + if not self.api.manage.check_vnf_intf_pair( + vnf_src_name, vnf_src_interface): return Response(u"VNF %s or intfs %s does not exist" % (vnf_src_name, vnf_src_interface), status=501, mimetype="application/json") - self.api.manage.add_loadbalancer(vnf_src_name, vnf_src_interface, lb_data=req) + self.api.manage.add_loadbalancer( + vnf_src_name, vnf_src_interface, lb_data=req) return Response(u"Loadbalancer set up at %s:%s" % (vnf_src_name, vnf_src_interface), status=200, mimetype="application/json") else: - cookie, floating_ip = self.api.manage.add_floating_lb(vnf_src_interface, lb_data=req) + cookie, floating_ip = self.api.manage.add_floating_lb( + vnf_src_interface, lb_data=req) return Response(json.dumps({"cookie": "%d" % cookie, "floating_ip": "%s" % floating_ip}), status=200, mimetype="application/json") @@ -739,11 +818,13 @@ class BalanceHost(Resource): """ # check if VNF exist - if not self.api.manage.check_vnf_intf_pair(vnf_src_name, vnf_src_interface): + if not self.api.manage.check_vnf_intf_pair( + vnf_src_name, vnf_src_interface): return Response(u"VNF %s or intfs %s does not exist" % (vnf_src_name, vnf_src_interface), status=501, mimetype="application/json") try: - logging.debug("Deleting loadbalancer at %s: interface: %s" % (vnf_src_name, vnf_src_interface)) + logging.debug("Deleting loadbalancer at %s: interface: %s" % + (vnf_src_name, vnf_src_interface)) net = self.api.manage.net if vnf_src_name != "floating": @@ -752,7 +833,8 @@ class BalanceHost(Resource): return Response(u"Source VNF or interface can not be found." % vnf_src_name, status=404, mimetype="application/json") - self.api.manage.delete_loadbalancer(vnf_src_name, vnf_src_interface) + self.api.manage.delete_loadbalancer( + vnf_src_name, vnf_src_interface) return Response(u"Loadbalancer deleted at %s:%s" % (vnf_src_name, vnf_src_interface), status=200, mimetype="application/json") @@ -821,7 +903,8 @@ class QueryTopology(Resource): # with their unique keys link = copy.copy(data) for edge in link: - # do not add any links to the floating switch to the topology! + # do not add any links to the floating switch + # to the topology! if graph_node == "fs1": continue # the translator wants everything as a string!