Return actually reachable IP as part of the Floating IP API
[osm/vim-emu.git] / src / emuvim / api / openstack / openstack_dummies / neutron_dummy_api.py
old mode 100644 (file)
new mode 100755 (executable)
index 38a5ad4..e094f0e
@@ -1,20 +1,52 @@
+# 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).
 from flask_restful import Resource
 from flask import request, Response
 from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
 from datetime import datetime
+import neutron_sfc_dummy_api as SFC
 import logging
 import json
 import uuid
 import copy
 
+LOG = logging.getLogger("api.openstack.neutron")
+
 
 class NeutronDummyApi(BaseOpenstackDummy):
     def __init__(self, ip, port, compute):
         super(NeutronDummyApi, self).__init__(ip, port)
         self.compute = compute
 
+        # create default networks (OSM usually assumes to have these
+        # pre-configured)
+        self.compute.create_network("mgmt")
+        self.compute.create_network("mgmtnet")
+
         self.api.add_resource(NeutronListAPIVersions, "/")
-        self.api.add_resource(Shutdown, "/shutdown")
         self.api.add_resource(NeutronShowAPIv2Details, "/v2.0")
         self.api.add_resource(NeutronListNetworks, "/v2.0/networks.json", "/v2.0/networks",
                               resource_class_kwargs={'api': self})
@@ -49,20 +81,62 @@ class NeutronDummyApi(BaseOpenstackDummy):
         self.api.add_resource(NeutronAddFloatingIp, "/v2.0/floatingips.json", "/v2.0/floatingips",
                               resource_class_kwargs={'api': self})
 
-    def _start_flask(self):
-        logging.info("Starting %s endpoint @ http://%s:%d" % (__name__, 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)
+        # Service Function Chaining (SFC) API
+        self.api.add_resource(SFC.PortPairsCreate, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairsUpdate, "/v2.0/sfc/port_pairs/<pair_id>.json",
+                              "/v2.0/sfc/port_pairs/<pair_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairsDelete, "/v2.0/sfc/port_pairs/<pair_id>.json",
+                              "/v2.0/sfc/port_pairs/<pair_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairsList, "/v2.0/sfc/port_pairs.json", "/v2.0/sfc/port_pairs",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairsShow, "/v2.0/sfc/port_pairs/<pair_id>.json",
+                              "/v2.0/sfc/port_pairs/<pair_id>",
+                              resource_class_kwargs={'api': self})
 
+        self.api.add_resource(SFC.PortPairGroupCreate, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairGroupUpdate, "/v2.0/sfc/port_pair_groups/<group_id>.json",
+                              "/v2.0/sfc/port_pair_groups/<group_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairGroupDelete, "/v2.0/sfc/port_pair_groups/<group_id>.json",
+                              "/v2.0/sfc/port_pair_groups/<group_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairGroupList, "/v2.0/sfc/port_pair_groups.json", "/v2.0/sfc/port_pair_groups",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortPairGroupShow, "/v2.0/sfc/port_pair_groups/<group_id>.json",
+                              "/v2.0/sfc/port_pair_groups/<group_id>",
+                              resource_class_kwargs={'api': self})
 
-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()
+        self.api.add_resource(SFC.FlowClassifierCreate, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.FlowClassifierUpdate, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
+                              "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.FlowClassifierDelete, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
+                              "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.FlowClassifierList, "/v2.0/sfc/flow_classifiers.json", "/v2.0/sfc/flow_classifiers",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.FlowClassifierShow, "/v2.0/sfc/flow_classifiers/<flow_classifier_id>.json",
+                              "/v2.0/sfc/flow_classifiers/<flow_classifier_id>",
+                              resource_class_kwargs={'api': self})
+
+        self.api.add_resource(SFC.PortChainCreate, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortChainUpdate, "/v2.0/sfc/port_chains/<chain_id>.json",
+                              "/v2.0/sfc/port_chains/<chain_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortChainDelete, "/v2.0/sfc/port_chains/<chain_id>.json",
+                              "/v2.0/sfc/port_chains/<chain_id>",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortChainList, "/v2.0/sfc/port_chains.json", "/v2.0/sfc/port_chains",
+                              resource_class_kwargs={'api': self})
+        self.api.add_resource(SFC.PortChainShow, "/v2.0/sfc/port_chains/<chain_id>.json",
+                              "/v2.0/sfc/port_chains/<chain_id>",
+                              resource_class_kwargs={'api': self})
 
 
 class NeutronListAPIVersions(Resource):
@@ -73,7 +147,7 @@ class NeutronListAPIVersions(Resource):
         :return: Returns a json with API versions.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: Neutron - List API Versions")
+        LOG.debug("API CALL: Neutron - List API Versions")
         resp = dict()
         resp['versions'] = dict()
 
@@ -89,7 +163,8 @@ class NeutronListAPIVersions(Resource):
         }]
         resp['versions'] = versions
 
-        return Response(json.dumps(resp), status=200, mimetype='application/json')
+        return Response(json.dumps(resp), status=200,
+                        mimetype='application/json')
 
 
 class NeutronShowAPIv2Details(Resource):
@@ -100,7 +175,7 @@ class NeutronShowAPIv2Details(Resource):
         :return: Returns a json with API details.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         resp = dict()
 
         resp['resources'] = dict()
@@ -123,7 +198,7 @@ class NeutronShowAPIv2Details(Resource):
                 ],
                 "name": "network",
                 "collection": "networks"
-            },
+        },
             {
                 "links": [
                     {
@@ -133,10 +208,11 @@ class NeutronShowAPIv2Details(Resource):
                 ],
                 "name": "ports",
                 "collection": "ports"
-            }
+        }
         ]
 
-        return Response(json.dumps(resp), status=200, mimetype='application/json')
+        return Response(json.dumps(resp), status=200,
+                        mimetype='application/json')
 
 
 class NeutronListNetworks(Resource):
@@ -151,15 +227,24 @@ class NeutronListNetworks(Resource):
         :return: Returns a json response, starting with 'networks' as root node.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        # LOG.debug("ARGS: {}".format(request.args))
         try:
             if request.args.get('name'):
                 tmp_network = NeutronShowNetwork(self.api)
-                return tmp_network.get_network(request.args.get('name'), True)
+                response = tmp_network.get_network(
+                    request.args.get('name'), True)
+                LOG.debug("{} RESPONSE (1): {}".format(
+                    self.__class__.__name__, response))
+                return response
             id_list = request.args.getlist('id')
             if len(id_list) == 1:
                 tmp_network = NeutronShowNetwork(self.api)
-                return tmp_network.get_network(request.args.get('id'), True)
+                response = tmp_network.get_network(
+                    request.args.get('id'), True)
+                LOG.debug("{} RESPONSE (2): {}".format(
+                    self.__class__.__name__, response))
+                return response
 
             network_list = list()
             network_dict = dict()
@@ -177,12 +262,15 @@ class NeutronListNetworks(Resource):
                             network_list.append(tmp_network_dict)
 
             network_dict["networks"] = network_list
-
-            return Response(json.dumps(network_dict), status=200, mimetype='application/json')
+            LOG.debug("{} RESPONSE (3): {}".format(
+                self.__class__.__name__, network_dict))
+            return Response(json.dumps(network_dict),
+                            status=200, mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: List networks exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: List networks exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronShowNetwork(Resource):
@@ -198,7 +286,7 @@ class NeutronShowNetwork(Resource):
         :return: Returns a json response, starting with 'network' as root node and one network description.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         return self.get_network(network_id, False)
 
     def get_network(self, network_name_or_id, as_list):
@@ -213,9 +301,11 @@ class NeutronShowNetwork(Resource):
         :rtype: :class:`flask.response`
         """
         try:
-            net = self.api.compute.find_network_by_name_or_id(network_name_or_id)
+            net = self.api.compute.find_network_by_name_or_id(
+                network_name_or_id)
             if net is None:
-                return Response(u'Network not found.\n', status=404, mimetype='application/json')
+                return Response(u'Network not found.\n',
+                                status=404, mimetype='application/json')
 
             tmp_network_dict = net.create_network_dict()
             tmp_dict = dict()
@@ -224,12 +314,13 @@ class NeutronShowNetwork(Resource):
             else:
                 tmp_dict["network"] = tmp_network_dict
 
-            return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
-
+            return Response(json.dumps(tmp_dict), status=200,
+                            mimetype='application/json')
 
         except Exception as ex:
             logging.exception("Neutron: Show network exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronCreateNetwork(Resource):
@@ -245,19 +336,22 @@ class NeutronCreateNetwork(Resource):
             * 201, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s POST" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
         try:
             network_dict = json.loads(request.data)
             name = network_dict['network']['name']
             net = self.api.compute.find_network_by_name_or_id(name)
             if net is not None:
-                return Response('Network already exists.\n', status=400, mimetype='application/json')
+                return Response('Network already exists.\n',
+                                status=400, mimetype='application/json')
 
             net = self.api.compute.create_network(name)
-            return Response(json.dumps({"network": net.create_network_dict()}), status=201, mimetype='application/json')
+            return Response(json.dumps(
+                {"network": net.create_network_dict()}), status=201, mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Create network excepiton.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Create network excepiton.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronUpdateNetwork(Resource):
@@ -275,12 +369,11 @@ class NeutronUpdateNetwork(Resource):
             * 200, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s PUT" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
         try:
             if network_id in self.api.compute.nets:
                 net = self.api.compute.nets[network_id]
                 network_dict = json.loads(request.data)
-                old_net = copy.copy(net)
 
                 if "status" in network_dict["network"]:
                     net.status = network_dict["network"]["status"]
@@ -291,17 +384,21 @@ class NeutronUpdateNetwork(Resource):
                 if "admin_state_up" in network_dict["network"]:
                     pass  # tmp_network_dict["admin_state_up"] = True
                 if "tenant_id" in network_dict["network"]:
-                    pass  # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
+                    # tmp_network_dict["tenant_id"] = "c1210485b2424d48804aad5d39c61b8f"
+                    pass
                 if "shared" in network_dict["network"]:
                     pass  # tmp_network_dict["shared"] = False
 
-                return Response(json.dumps(network_dict), status=200, mimetype='application/json')
+                return Response(json.dumps(network_dict),
+                                status=200, mimetype='application/json')
 
-            return Response('Network not found.\n', status=404, mimetype='application/json')
+            return Response('Network not found.\n', status=404,
+                            mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: Show networks exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Show networks exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronDeleteNetwork(Resource):
@@ -319,7 +416,7 @@ class NeutronDeleteNetwork(Resource):
             * 204, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
         try:
             if network_id not in self.api.compute.nets:
                 return Response('Could not find network. (' + network_id + ')\n',
@@ -329,15 +426,16 @@ class NeutronDeleteNetwork(Resource):
             delete_subnet = NeutronDeleteSubnet(self.api)
             resp = delete_subnet.delete(net.subnet_id)
 
-            if not '204' in resp.status and not '404' in resp.status:
+            if '204' not in resp.status and '404' not in resp.status:
                 return resp
 
             self.api.compute.delete_network(network_id)
 
-            return Response('Network ' + str(network_id) + ' deleted.\n', status=204, mimetype='application/json')
+            return Response('', status=204, mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Delete network exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Delete network exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronListSubnets(Resource):
@@ -352,7 +450,7 @@ class NeutronListSubnets(Resource):
         :return: Returns a json response, starting with 'subnets' as root node.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         try:
             if request.args.get('name'):
                 show_subnet = NeutronShowSubnet(self.api)
@@ -378,11 +476,13 @@ class NeutronListSubnets(Resource):
 
             subnet_dict["subnets"] = subnet_list
 
-            return Response(json.dumps(subnet_dict), status=200, mimetype='application/json')
+            return Response(json.dumps(subnet_dict), status=200,
+                            mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: List subnets exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: List subnets exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronShowSubnet(Resource):
@@ -398,7 +498,7 @@ class NeutronShowSubnet(Resource):
         :return: Returns a json response, starting with 'subnet' as root node and one subnet description.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         return self.get_subnet(subnet_id, False)
 
     def get_subnet(self, subnet_name_or_id, as_list):
@@ -421,13 +521,16 @@ class NeutronShowSubnet(Resource):
                         tmp_dict["subnets"] = [tmp_subnet_dict]
                     else:
                         tmp_dict["subnet"] = tmp_subnet_dict
-                    return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
+                    return Response(json.dumps(tmp_dict),
+                                    status=200, mimetype='application/json')
 
-            return Response('Subnet not found. (' + subnet_name_or_id + ')\n', status=404, mimetype='application/json')
+            return Response('Subnet not found. (' + subnet_name_or_id +
+                            ')\n', status=404, mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: Show subnet exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Show subnet exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronCreateSubnet(Resource):
@@ -445,17 +548,23 @@ class NeutronCreateSubnet(Resource):
             * 201, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s POST" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
         try:
             subnet_dict = json.loads(request.data)
-            net = self.api.compute.find_network_by_name_or_id(subnet_dict['subnet']['network_id'])
+            net = self.api.compute.find_network_by_name_or_id(
+                subnet_dict['subnet']['network_id'])
 
             if net is None:
-                return Response('Could not find network.\n', status=404, mimetype='application/json')
+                return Response('Could not find network.\n',
+                                status=404, mimetype='application/json')
 
-            net.subnet_name = subnet_dict["subnet"].get('name', str(net.name) + '-sub')
+            net.subnet_name = subnet_dict["subnet"].get(
+                'name', str(net.name) + '-sub')
             if net.subnet_id is not None:
-                return Response('Only one subnet per network is supported\n', status=409, mimetype='application/json')
+                LOG.error(
+                    "Only one subnet per network is supported: {}".format(net.subnet_id))
+                return Response('Only one subnet per network is supported\n',
+                                status=409, mimetype='application/json')
 
             if "id" in subnet_dict["subnet"]:
                 net.subnet_id = subnet_dict["subnet"]["id"]
@@ -475,11 +584,13 @@ class NeutronCreateSubnet(Resource):
             if "enable_dhcp" in subnet_dict["subnet"]:
                 pass
 
-            return Response(json.dumps({'subnet': net.create_subnet_dict()}), status=201, mimetype='application/json')
+            return Response(json.dumps(
+                {'subnet': net.create_subnet_dict()}), status=201, mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: Create network excepiton.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Create network excepiton.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronUpdateSubnet(Resource):
@@ -497,7 +608,7 @@ class NeutronUpdateSubnet(Resource):
             * 200, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s PUT" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
         try:
             for net in self.api.compute.nets.values():
                 if net.subnet_id == subnet_id:
@@ -524,13 +635,16 @@ class NeutronUpdateSubnet(Resource):
 
                     net.subnet_update_time = str(datetime.now())
                     tmp_dict = {'subnet': net.create_subnet_dict()}
-                    return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
+                    return Response(json.dumps(tmp_dict),
+                                    status=200, mimetype='application/json')
 
-            return Response('Network not found.\n', status=404, mimetype='application/json')
+            return Response('Network not found.\n', status=404,
+                            mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: Show networks exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Show networks exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronDeleteSubnet(Resource):
@@ -548,13 +662,18 @@ class NeutronDeleteSubnet(Resource):
             * 204, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
         try:
             for net in self.api.compute.nets.values():
                 if net.subnet_id == subnet_id:
                     for server in self.api.compute.computeUnits.values():
                         for port_name in server.port_names:
-                            port = self.api.compute.find_port_by_name_or_id(port_name)
+                            port = self.api.compute.find_port_by_name_or_id(
+                                port_name)
+                            if port is None:
+                                LOG.warning(
+                                    "Port search for {} returned None.".format(port_name))
+                                continue
                             if port.net_name == net.name:
                                 port.ip_address = None
                                 self.api.compute.dc.net.removeLink(
@@ -565,13 +684,15 @@ class NeutronDeleteSubnet(Resource):
 
                     net.delete_subnet()
 
-                    return Response('Subnet ' + str(subnet_id) + ' deleted.\n',
-                                    status=204, mimetype='application/json')
+                    return Response(
+                        '', status=204, mimetype='application/json')
 
-            return Response('Could not find subnet.', status=404, mimetype='application/json')
+            return Response('Could not find subnet.',
+                            status=404, mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Delete subnet exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Delete subnet exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronListPorts(Resource):
@@ -586,7 +707,7 @@ class NeutronListPorts(Resource):
         :return: Returns a json response, starting with 'ports' as root node.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         try:
             if request.args.get('name'):
                 show_port = NeutronShowPort(self.api)
@@ -611,11 +732,13 @@ class NeutronListPorts(Resource):
 
             port_dict["ports"] = port_list
 
-            return Response(json.dumps(port_dict), status=200, mimetype='application/json')
+            return Response(json.dumps(port_dict), status=200,
+                            mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: List ports exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: List ports exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronShowPort(Resource):
@@ -631,7 +754,7 @@ class NeutronShowPort(Resource):
         :return: Returns a json response, starting with 'port' as root node and one network description.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s GET" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
         return self.get_port(port_id, False)
 
     def get_port(self, port_name_or_id, as_list):
@@ -648,17 +771,20 @@ class NeutronShowPort(Resource):
         try:
             port = self.api.compute.find_port_by_name_or_id(port_name_or_id)
             if port is None:
-                return Response('Port not found. (' + port_name_or_id + ')\n', status=404, mimetype='application/json')
+                return Response('Port not found. (' + port_name_or_id + ')\n',
+                                status=404, mimetype='application/json')
             tmp_port_dict = port.create_port_dict(self.api.compute)
             tmp_dict = dict()
             if as_list:
                 tmp_dict["ports"] = [tmp_port_dict]
             else:
                 tmp_dict["port"] = tmp_port_dict
-            return Response(json.dumps(tmp_dict), status=200, mimetype='application/json')
+            return Response(json.dumps(tmp_dict), status=200,
+                            mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Show port exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Show port exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronCreatePort(Resource):
@@ -674,13 +800,14 @@ class NeutronCreatePort(Resource):
             * 201, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s POST" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
         try:
             port_dict = json.loads(request.data)
             net_id = port_dict['port']['network_id']
 
             if net_id not in self.api.compute.nets:
-                return Response('Could not find network.\n', status=404, mimetype='application/json')
+                return Response('Could not find network.\n',
+                                status=404, mimetype='application/json')
 
             net = self.api.compute.nets[net_id]
             if 'name' in port_dict['port']:
@@ -690,7 +817,8 @@ class NeutronCreatePort(Resource):
                 name = "port:cp%s:man:%s" % (num_ports, str(uuid.uuid4()))
 
             if self.api.compute.find_port_by_name_or_id(name):
-                return Response("Port with name %s already exists.\n" % name, status=500, mimetype='application/json')
+                return Response("Port with name %s already exists.\n" %
+                                name, status=500, mimetype='application/json')
 
             port = self.api.compute.create_port(name)
 
@@ -712,7 +840,8 @@ class NeutronCreatePort(Resource):
             if "tenant_id" in port_dict["port"]:
                 pass
 
-            # add the port to a stack if the specified network is a stack network
+            # add the port to a stack if the specified network is a stack
+            # network
             for stack in self.api.compute.stacks.values():
                 for net in stack.nets.values():
                     if net.id == net_id:
@@ -721,8 +850,9 @@ class NeutronCreatePort(Resource):
             return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=201,
                             mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Show port exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Show port exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronUpdatePort(Resource):
@@ -740,12 +870,13 @@ class NeutronUpdatePort(Resource):
             * 200, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s PUT" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s PUT" % str(self.__class__.__name__))
         try:
             port_dict = json.loads(request.data)
             port = self.api.compute.find_port_by_name_or_id(port_id)
             if port is None:
-                return Response("Port with id %s does not exists.\n" % port_id, status=404, mimetype='application/json')
+                return Response("Port with id %s does not exists.\n" %
+                                port_id, status=404, mimetype='application/json')
             old_port = copy.copy(port)
 
             stack = None
@@ -769,7 +900,8 @@ class NeutronUpdatePort(Resource):
                 port.set_name(port_dict["port"]["name"])
                 if stack is not None:
                     if port.net_name in stack.nets:
-                        stack.nets[port.net_name].update_port_name_for_ip_address(port.ip_address, port.name)
+                        stack.nets[port.net_name].update_port_name_for_ip_address(
+                            port.ip_address, port.name)
                     stack.ports[port.name] = stack.ports[old_port.name]
                     del stack.ports[old_port.name]
             if "network_id" in port_dict["port"]:
@@ -782,8 +914,9 @@ class NeutronUpdatePort(Resource):
             return Response(json.dumps({'port': port.create_port_dict(self.api.compute)}), status=200,
                             mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Update port exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Update port exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronDeletePort(Resource):
@@ -801,11 +934,12 @@ class NeutronDeletePort(Resource):
             * 204, if everything worked out.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
         try:
             port = self.api.compute.find_port_by_name_or_id(port_id)
             if port is None:
-                return Response("Port with id %s does not exists.\n" % port_id, status=404)
+                return Response("Port with id %s does not exists.\n" %
+                                port_id, status=404)
             stack = None
             for s in self.api.compute.stacks.values():
                 for p in s.ports.values():
@@ -813,7 +947,8 @@ class NeutronDeletePort(Resource):
                         stack = s
             if stack is not None:
                 if port.net_name in stack.nets:
-                    stack.nets[port.net_name].withdraw_ip_address(port.ip_address)
+                    stack.nets[port.net_name].withdraw_ip_address(
+                        port.ip_address)
                 for server in stack.servers.values():
                     try:
                         server.port_names.remove(port.name)
@@ -823,11 +958,12 @@ class NeutronDeletePort(Resource):
             # delete the port
             self.api.compute.delete_port(port.id)
 
-            return Response('Port ' + port_id + ' deleted.\n', status=204, mimetype='application/json')
+            return Response('', status=204, mimetype='application/json')
 
         except Exception as ex:
-            logging.exception("Neutron: Delete port exception.")
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Delete port exception.")
+            return Response(ex.message, status=500,
+                            mimetype='application/json')
 
 
 class NeutronAddFloatingIp(Resource):
@@ -836,32 +972,25 @@ class NeutronAddFloatingIp(Resource):
 
     def get(self):
         """
-        Added a quick and dirty fake for the OSM integration. Returns a list of
-        floating IPs. Has nothing to do with the setup inside the emulator.
-        But its enough to make the OSM driver happy.
-        @PG Sandman: Feel free to improve this and let it do something meaningful.
+        Returns a Floating IP for a port.
+
+        Currently ports are not mapped to individual IPs, but the
+        (potentially shared) Docker external IP is returned.
         """
+        port_id = request.args.get("port_id")
+        if not port_id:
+            message = "Neutron: List API for FloatingIPs is not implemented"
+            LOG.exception(message)
+            return Response(message, status=500,
+                            mimetype='application/json')
+        port = self.api.compute.find_port_by_name_or_id(port_id)
+        ip = port.assigned_container.dcinfo["NetworkSettings"]["IPAddress"]
         resp = dict()
-        resp["floatingips"] = list()
-        # create a list of floting IP definitions and return it
-        for i in range(100, 110):
-            ip=dict()
-            ip["router_id"] = "router_id"
-            ip["description"] = "hardcoded in api"
-            ip["created_at"] = "router_id"
-            ip["updated_at"] = "router_id"
-            ip["revision_number"] = 1
-            ip["tenant_id"] = "tenant_id"
-            ip["project_id"] = "project_id"
-            ip["floating_network_id"] = str(i)
-            ip["status"] = "ACTIVE"
-            ip["id"] = str(i)
-            ip["port_id"] = "port_id"
-            ip["floating_ip_address"] = "172.0.0.%d" % i
-            ip["fixed_ip_address"] = "10.0.0.%d" % i
-            resp["floatingips"].append(ip)
-        return Response(json.dumps(resp), status=200, mimetype='application/json')
-
+        resp["floatingips"] = [
+            {'floating_ip_address': ip}
+        ]
+        return Response(json.dumps(resp), status=200,
+                        mimetype='application/json')
 
     def post(self):
         """
@@ -870,7 +999,7 @@ class NeutronAddFloatingIp(Resource):
         :return: Returns a floating network description.
         :rtype: :class:`flask.response`
         """
-        logging.debug("API CALL: %s POST" % str(self.__class__.__name__))
+        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
         try:
             # Fiddle with floating_network !
             req = json.loads(request.data)
@@ -889,7 +1018,8 @@ class NeutronAddFloatingIp(Resource):
                                     status=400, mimetype='application/json')
 
                 if port.floating_ip is not None:
-                    return Response("We allow only one floating ip per port\n", status=400, mimetype='application/json')
+                    return Response("We allow only one floating ip per port\n",
+                                    status=400, mimetype='application/json')
             else:
                 num_ports = len(self.api.compute.ports)
                 name = "port:cp%s:fl:%s" % (num_ports, str(uuid.uuid4()))
@@ -909,7 +1039,9 @@ class NeutronAddFloatingIp(Resource):
             resp["floating_ip_address"] = port.floating_ip
             resp["fixed_ip_address"] = port.floating_ip
 
-            return Response(json.dumps(response), status=200, mimetype='application/json')
+            return Response(json.dumps(response), status=200,
+                            mimetype='application/json')
         except Exception as ex:
-            logging.exception("Neutron: Create FloatingIP exception %s.", ex)
-            return Response(ex.message, status=500, mimetype='application/json')
+            LOG.exception("Neutron: Create FloatingIP exception %s.", ex)
+            return Response(ex.message, status=500,
+                            mimetype='application/json')