"""
Copyright (c) 2017 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 Response, request
from emuvim.api.openstack.openstack_dummies.base_openstack_dummy import BaseOpenstackDummy
from emuvim.api.openstack.helper import get_host
import logging
import json
import uuid
from mininet.link import Link


LOG = logging.getLogger("api.openstack.nova")


class NovaDummyApi(BaseOpenstackDummy):
    def __init__(self, in_ip, in_port, compute):
        super(NovaDummyApi, self).__init__(in_ip, in_port)
        self.compute = compute
        self.compute.add_flavor('m1.tiny', 1, 512, "MB", 1, "GB")
        self.compute.add_flavor('m1.nano', 1, 64, "MB", 0, "GB")
        self.compute.add_flavor('m1.micro', 1, 128, "MB", 0, "GB")
        self.compute.add_flavor('m1.small', 1, 1024, "MB", 2, "GB")

        self.api.add_resource(NovaVersionsList, "/",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaVersionShow, "/v2.1/<id>",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListServersApi, "/v2.1/<id>/servers",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListServersAndPortsApi, "/v2.1/<id>/servers/andPorts",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListServersDetailed, "/v2.1/<id>/servers/detail",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaShowServerDetails, "/v2.1/<id>/servers/<serverid>",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaInterfaceToServer, "/v2.1/<id>/servers/<serverid>/os-interface",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaShowAndDeleteInterfaceAtServer, "/v2.1/<id>/servers/<serverid>/os-interface/<port_id>",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListFlavors, "/v2.1/<id>/flavors", "/v2/<id>/flavors",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListFlavorsDetails, "/v2.1/<id>/flavors/detail", "/v2/<id>/flavors/detail",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListFlavorById, "/v2.1/<id>/flavors/<flavorid>", "/v2/<id>/flavors/<flavorid>",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListImages, "/v2.1/<id>/images",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListImagesDetails, "/v2.1/<id>/images/detail",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaListImageById, "/v2.1/<id>/images/<imageid>",
                              resource_class_kwargs={'api': self})
        self.api.add_resource(NovaLimits, "/v2.1/<id>/limits",
                              resource_class_kwargs={'api': self})


class NovaVersionsList(Resource):
    def __init__(self, api):
        self.api = api

    def get(self):
        """
        Lists API versions.

        :return: Returns a json with API versions.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = """
                {
                    "versions": [
                        {
                            "id": "v2.1",
                            "links": [
                                {
                                    "href": "http://%s:%d/v2.1/",
                                    "rel": "self"
                                }
                            ],
                            "status": "CURRENT",
                            "version": "2.38",
                            "min_version": "2.1",
                            "updated": "2013-07-23T11:33:21Z"
                        }
                    ]
                }
            """ % (get_host(request), self.api.port)

            response = Response(resp, status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not show list of versions." % __name__)
            return ex.message, 500


class NovaVersionShow(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Returns API details.

        :param id:
        :type id: ``str``
        :return: Returns a json with API details.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))

        try:
            resp = """
            {
                "version": {
                    "id": "v2.1",
                    "links": [
                        {
                            "href": "http://%s:%d/v2.1/",
                            "rel": "self"
                        },
                        {
                            "href": "http://docs.openstack.org/",
                            "rel": "describedby",
                            "type": "text/html"
                        }
                    ],
                    "media-types": [
                        {
                            "base": "application/json",
                            "type": "application/vnd.openstack.compute+json;version=2.1"
                        }
                    ],
                    "status": "CURRENT",
                    "version": "2.38",
                    "min_version": "2.1",
                    "updated": "2013-07-23T11:33:21Z"
                }
            }
            """ % (get_host(request), self.api.port)

            response = Response(resp, status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not show list of versions." % __name__)
            return ex.message, 500


class NovaListServersApi(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Creates a list with all running servers and their detailed information.

        :param id: Used to create a individual link to quarry further information.
        :type id: ``str``
        :return: Returns a json response with a dictionary that contains the server information.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))

        try:
            resp = dict()
            resp['servers'] = list()
            for server in self.api.compute.computeUnits.values():
                s = server.create_server_dict(self.api.compute)
                s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
                                                                            self.api.port,
                                                                            id,
                                                                            server.id)}]

                resp['servers'].append(s)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
            return ex.message, 500

    def post(self, id):
        """
        Creates a server instance.

        :param id: tenant id, we ignore this most of the time
        :type id: ``str``
        :return: Returns a flask response, with detailed information about the just created server.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
        try:
            server_dict = json.loads(request.data)['server']
            networks = server_dict.get('networks', None)
            name = str(self.api.compute.dc.label) + "_" + server_dict["name"]

            if self.api.compute.find_server_by_name_or_id(name) is not None:
                LOG.error("Server with name %s already exists. 409" % name)
                return Response("Server with name %s already exists." % name, status=409)
            # TODO: not finished!
            resp = dict()

            server = self.api.compute.create_server(name)
            server.full_name = str(self.api.compute.dc.label) + "_" + server_dict["name"]
            server.template_name = server_dict["name"]
            if "metadata" in server_dict:
                server.properties = server_dict["metadata"]

            for flavor in self.api.compute.flavors.values():
                if flavor.id == server_dict.get('flavorRef', ''):
                    server.flavor = flavor.name
            for image in self.api.compute.images.values():
                if image.id in server_dict['imageRef']:
                    server.image = image.name

            if networks is not None:
                for net in networks:
                    port = self.api.compute.find_port_by_name_or_id(net.get('port', ""))
                    if port is not None:
                        server.port_names.append(port.name)
                    else:
                        return Response("Currently only networking by port is supported.", status=400)

            self.api.compute._start_compute(server)

            response = NovaShowServerDetails(self.api).get(id, server.id)
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not create the server." % __name__)
            return ex.message, 500


class NovaListServersAndPortsApi(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Creates a list with all running servers and their detailed information. This function also presents all
        port information of each server.

        :param id: Used to create a individual link to quarry further information.
        :type id: ``str``
        :return: Returns a json response with a dictionary that contains the server information.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))

        try:
            resp = dict()
            resp['servers'] = list()
            for server in self.api.compute.computeUnits.values():
                s = server.create_server_dict(self.api.compute)
                s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
                                                                            self.api.port,
                                                                            id,
                                                                            server.id)}]

                s['ports'] = list()
                for port_name in server.port_names:
                    port = self.api.compute.find_port_by_name_or_id(port_name)
                    if port is None:
                        continue

                    tmp = port.create_port_dict(self.api.compute)
                    tmp['intf_name'] = port.intf_name
                    s['ports'].append(tmp)

                resp['servers'].append(s)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
            return ex.message, 500


class NovaListServersDetailed(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        As List Servers, it lists all running servers and their details but furthermore it also states the
        used flavor and the server image.

        :param id: tenant id, used for the 'href' link.
        :type id: ``str``
        :return: Returns a flask response, with detailed information aboit the servers and their flavor and image.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))

        try:
            resp = {"servers": list()}
            for server in self.api.compute.computeUnits.values():
                s = server.create_server_dict(self.api.compute)
                s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
                                                                            self.api.port,
                                                                            id,
                                                                            server.id)}]
                flavor = self.api.compute.flavors[server.flavor]
                s['flavor'] = {
                    "id": flavor.id,
                    "links": [
                        {
                            "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                         self.api.port,
                                                                         id,
                                                                         flavor.id),
                            "rel": "bookmark"
                        }
                    ]
                }
                image = self.api.compute.images[server.image]
                s['image'] = {
                    "id": image.id,
                    "links": [
                        {
                            "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
                                                                        self.api.port,
                                                                        id,
                                                                        image.id),
                            "rel": "bookmark"
                        }
                    ]
                }

                resp['servers'].append(s)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
            return ex.message, 500


class NovaListFlavors(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Lists all available flavors.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :return: Returns a flask response with a list of all flavors.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            resp['flavors'] = list()
            for flavor in self.api.compute.flavors.values():
                f = flavor.__dict__.copy()
                f['id'] = flavor.id
                f['name'] = flavor.name
                f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                            self.api.port,
                                                                            id,
                                                                            flavor.id)}]
                resp['flavors'].append(f)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
            return ex.message, 500

    def post(self, id):
        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
        data = json.loads(request.data).get("flavor")
        LOG.warning("Create Flavor: %s" % str(data))
        # add to internal dict
        f = self.api.compute.add_flavor(
            data.get("name"),
            data.get("vcpus"),
            data.get("ram"), "MB",
            data.get("disk"), "GB")
        # create response based on incoming data
        data["id"] = f.id
        data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                       self.api.port,
                                                                       id,
                                                                       f.id)}]
        resp = {"flavor": data}
        return Response(json.dumps(resp), status=200, mimetype="application/json")


class NovaListFlavorsDetails(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Lists all flavors with additional information like ram and disk space.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :return: Returns a flask response with a list of all flavors with additional information.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            resp['flavors'] = list()
            for flavor in self.api.compute.flavors.values():
                # use the class dict. it should work fine
                # but use a copy so we don't modifiy the original
                f = flavor.__dict__.copy()
                # add additional expected stuff stay openstack compatible
                f['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                            self.api.port,
                                                                            id,
                                                                            flavor.id)}]
                f['OS-FLV-DISABLED:disabled'] = False
                f['OS-FLV-EXT-DATA:ephemeral'] = 0
                f['os-flavor-access:is_public'] = True
                f['ram'] = flavor.memory
                f['vcpus'] = flavor.cpu
                f['swap'] = 0
                f['disk'] = flavor.storage
                f['rxtx_factor'] = 1.0
                resp['flavors'].append(f)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of servers." % __name__)
            return ex.message, 500

    def post(self, id):
        LOG.debug("API CALL: %s POST" % str(self.__class__.__name__))
        data = json.loads(request.data).get("flavor")
        LOG.warning("Create Flavor: %s" % str(data))
        # add to internal dict
        f = self.api.compute.add_flavor(
            data.get("name"),
            data.get("vcpus"),
            data.get("ram"), "MB",
            data.get("disk"), "GB")
        # create response based on incoming data
        data["id"] = f.id
        data["links"] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                       self.api.port,
                                                                       id,
                                                                       f.id)}]
        resp = {"flavor": data}
        return Response(json.dumps(resp), status=200, mimetype="application/json")


class NovaListFlavorById(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id, flavorid):
        """
        Returns details about one flavor.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :param flavorid: Represents the flavor.
        :type flavorid: ``str``
        :return: Returns a flask response with detailed information about the flavor.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            resp['flavor'] = dict()
            flavor = self.api.compute.flavors.get(flavorid, None)
            if flavor is None:
                for f in self.api.compute.flavors.values():
                    if f.id == flavorid:
                        flavor = f
                        break
            resp['flavor']['id'] = flavor.id
            resp['flavor']['name'] = flavor.name
            resp['flavor']['links'] = [{'href': "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                                     self.api.port,
                                                                                     id,
                                                                                     flavor.id)}]
            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve flavor with id %s" % (__name__, flavorid))
            return ex.message, 500

    def delete(self, id, flavorid):
        """
        Removes the given flavor.
        Does not really remove anything from the machine, just fakes an OK.
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        return Response("", status=204, mimetype="application/json")


class NovaListImages(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Creates a list of all usable images.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :return: Returns a flask response with a list of available images.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            resp['images'] = list()
            for image in self.api.compute.images.values():
                f = dict()
                f['id'] = image.id
                f['name'] = str(image.name).replace(":latest", "")
                f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
                                                                           self.api.port,
                                                                           id,
                                                                           image.id)}]
                resp['images'].append(f)
            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
            return ex.message, 500


class NovaListImagesDetails(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        As List Images but with additional metadata.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :return: Returns a flask response with a list of images and their metadata.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            resp['images'] = list()
            for image in self.api.compute.images.values():
                # use the class dict. it should work fine
                # but use a copy so we don't modifiy the original
                f = image.__dict__.copy()
                # add additional expected stuff stay openstack compatible
                f['name'] = str(image.name).replace(":latest", "")
                f['links'] = [{'href': "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
                                                                           self.api.port,
                                                                           id,
                                                                           image.id)}]
                f['metadata'] = {
                    "architecture": "x86_64",
                    "auto_disk_config": "True",
                    "kernel_id": "nokernel",
                    "ramdisk_id": "nokernel"
                }
                resp['images'].append(f)

            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
            return ex.message, 500


class NovaListImageById(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id, imageid):
        """
        Gets an image by id from the emulator with openstack nova compliant return values.

        :param id: tenantid, we ignore this most of the time
        :type id: ``str``
        :param imageid: id of the image. If it is 1 the dummy CREATE-IMAGE is returned
        :type imageid: ``str``
        :return: Returns a flask response with the information about one image.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = dict()
            i = resp['image'] = dict()
            for image in self.api.compute.images.values():
                if image.id == imageid or image.name == imageid:
                    i['id'] = image.id
                    i['name'] = image.name

                    return Response(json.dumps(resp), status=200, mimetype="application/json")

            response = Response("Image with id or name %s does not exists." % imageid, status=404)
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve image with id %s." % (__name__, imageid))
            return ex.message, 500

    def delete(self, id, imageid):
        """
        Removes the given image.
        Does not really remove anything from the machine, just fakes an OK.
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        return Response("", status=204, mimetype="application/json")


class NovaShowServerDetails(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id, serverid):
        """
        Returns detailed information about the specified server.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :param serverid: Specifies the requested server.
        :type serverid: ``str``
        :return: Returns a flask response with details about the server.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            server = self.api.compute.find_server_by_name_or_id(serverid)
            if server is None:
                return Response("Server with id or name %s does not exists." % serverid, status=404)
            s = server.create_server_dict()
            s['links'] = [{'href': "http://%s:%d/v2.1/%s/servers/%s" % (get_host(request),
                                                                        self.api.port,
                                                                        id,
                                                                        server.id)}]

            flavor = self.api.compute.flavors[server.flavor]
            s['flavor'] = {
                "id": flavor.id,
                "links": [
                    {
                        "href": "http://%s:%d/v2.1/%s/flavors/%s" % (get_host(request),
                                                                     self.api.port,
                                                                     id,
                                                                     flavor.id),
                        "rel": "bookmark"
                    }
                ]
            }
            image = self.api.compute.images[server.image]
            s['image'] = {
                "id": image.id,
                "links": [
                    {
                        "href": "http://%s:%d/v2.1/%s/images/%s" % (get_host(request),
                                                                    self.api.port,
                                                                    id,
                                                                    image.id),
                        "rel": "bookmark"
                    }
                ]
            }

            response = Response(json.dumps({'server': s}), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the server details." % __name__)
            return ex.message, 500

    def delete(self, id, serverid):
        """
        Delete a server instance.

        :param id: tenant id, we ignore this most of the time
        :type id: ``str``
        :param serverid: The UUID of the server
        :type serverid: ``str``
        :return: Returns 204 if everything is fine.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s DELETE" % str(self.__class__.__name__))
        try:
            server = self.api.compute.find_server_by_name_or_id(serverid)
            if server is None:
                return Response('Could not find server.', status=404, mimetype="application/json")

            self.api.compute.stop_compute(server)

            response = Response('', status=204, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not create the server." % __name__)
            return ex.message, 500


class NovaInterfaceToServer(Resource):
    def __init__(self, api):
        self.api = api

    def post(self, id, serverid):
        """
        Add an interface to the specified server.

        :param id: tenant id, we ignore this most of the time
        :type id: ``str``
        :param serverid: Specifies the server.
        :type serverid: ``str``
        :return: Returns a flask response with information about the attached interface.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            server = self.api.compute.find_server_by_name_or_id(serverid)
            if server is None:
                return Response("Server with id or name %s does not exists." % serverid, status=404)

            if server.emulator_compute is None:
                LOG.error("The targeted container does not exist.")
                return Response("The targeted container of %s does not exist." % serverid, status=404)
            data = json.loads(request.data).get("interfaceAttachment")
            resp = dict()
            port = data.get("port_id", None)
            net = data.get("net_id", None)
            dc = self.api.compute.dc
            network_dict = dict()
            network = None

            if net is not None and port is not None:
                port = self.api.compute.find_port_by_name_or_id(port)
                network = self.api.compute.find_network_by_name_or_id(net)
                network_dict['id'] = port.intf_name
                network_dict['ip'] = port.ip_address
                network_dict[network_dict['id']] = network.name
            elif net is not None:
                network = self.api.compute.find_network_by_name_or_id(net)
                if network is None:
                    return Response("Network with id or name %s does not exists." % net, status=404)
                port = self.api.compute.create_port("port:cp%s:fl:%s" %
                                                    (len(self.api.compute.ports), str(uuid.uuid4())))

                port.net_name = network.name
                port.ip_address = network.get_new_ip_address(port.name)
                network_dict['id'] = port.intf_name
                network_dict['ip'] = port.ip_address
                network_dict[network_dict['id']] = network.name
            elif port is not None:
                port = self.api.compute.find_port_by_name_or_id(port)
                network_dict['id'] = port.intf_name
                network_dict['ip'] = port.ip_address
                network = self.api.compute.find_network_by_name_or_id(port.net_name)
                network_dict[network_dict['id']] = network.name
            else:
                raise Exception("You can only attach interfaces by port or network at the moment")

            if network == self.api.manage.floating_network:
                dc.net.addLink(server.emulator_compute, self.api.manage.floating_switch,
                               params1=network_dict, cls=Link, intfName1=port.intf_name)
            else:
                dc.net.addLink(server.emulator_compute, dc.switch,
                               params1=network_dict, cls=Link, intfName1=port.intf_name)
            resp["port_state"] = "ACTIVE"
            resp["port_id"] = port.id
            resp["net_id"] = self.api.compute.find_network_by_name_or_id(port.net_name).id
            resp["mac_addr"] = port.mac_address
            resp["fixed_ips"] = list()
            fixed_ips = dict()
            fixed_ips["ip_address"] = port.ip_address
            fixed_ips["subnet_id"] = network.subnet_name
            resp["fixed_ips"].append(fixed_ips)
            response = Response(json.dumps({"interfaceAttachment": resp}), status=202, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not add interface to the server." % __name__)
            return ex.message, 500


class NovaShowAndDeleteInterfaceAtServer(Resource):
    def __init__(self, api):
        self.api = api

    def delete(self, id, serverid, port_id):
        """
        Deletes an existing interface.

        :param id: tenant id, we ignore this most of the time
        :type id: ``str``
        :param serverid: Specifies the server, where the interface will be deleted.
        :type serverid: ``str``
        :param port_id: Specifies the port of the interface.
        :type port_id: ``str``
        :return: Returns a flask response with 202 if everything worked out. Otherwise it will return 404 and an
         error message.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            server = self.api.compute.find_server_by_name_or_id(serverid)
            if server is None:
                return Response("Server with id or name %s does not exists." % serverid, status=404)
            port = self.api.compute.find_port_by_name_or_id(port_id)
            if port is None:
                return Response("Port with id or name %s does not exists." % port_id, status=404)

            for link in self.api.compute.dc.net.links:
                if str(link.intf1) == port.intf_name and \
                                str(link.intf1.ip) == port.ip_address.split('/')[0]:
                    self.api.compute.dc.net.removeLink(link)
                    break

            response = Response("", status=202, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not detach interface from the server." % __name__)
            return ex.message, 500


class NovaLimits(Resource):
    def __init__(self, api):
        self.api = api

    def get(self, id):
        """
        Returns the resource limits of the emulated cloud.
        https://developer.openstack.org/api-ref/compute/?expanded=show-rate-and-absolute-limits-detail#limits-limits

        TODO: For now we only return fixed limits, not based on the real deployment.

        :param id: tenant id, used for the 'href' link
        :type id: ``str``
        :return: Returns the resource limits.
        :rtype: :class:`flask.response`
        """
        LOG.debug("API CALL: %s GET" % str(self.__class__.__name__))
        try:
            resp = {
                "limits": {
                    "absolute": {
                        "maxImageMeta": 12800,
                        "maxPersonality": 500,
                        "maxPersonalitySize": 1024000,
                        "maxSecurityGroupRules": 2000,
                        "maxSecurityGroups": 1000,
                        "maxServerMeta": 12800,
                        "maxTotalCores": 2000,
                        "maxTotalFloatingIps": 1000,
                        "maxTotalInstances": 1000,
                        "maxTotalKeypairs": 1000,
                        "maxTotalRAMSize": 5120000,
                        "maxServerGroups": 1000,
                        "maxServerGroupMembers": 1000,
                        "totalCoresUsed": 0,
                        "totalInstancesUsed": 0,
                        "totalRAMUsed": 0,
                        "totalSecurityGroupsUsed": 0,
                        "totalFloatingIpsUsed": 0,
                        "totalServerGroupsUsed": 0
                    },
                    "rate": []
                }
            }
            response = Response(json.dumps(resp), status=200, mimetype="application/json")
            response.headers['Access-Control-Allow-Origin'] = '*'
            return response

        except Exception as ex:
            LOG.exception(u"%s: Could not retrieve the list of images." % __name__)
            return ex.message, 500
