"""
# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2012, Cisco Systems, Inc.
#
#    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.
# @author: Sumit Naiksatam, Cisco Systems, Inc.
"""

import logging

from quantum.api.api_common import OperationalStatus
from quantum.common import exceptions as exc
from quantum.db import api as db
from quantum.plugins.linuxbridge import plugin_configuration as conf
from quantum.plugins.linuxbridge.common import constants as const
from quantum.plugins.linuxbridge.common import utils as cutil
from quantum.plugins.linuxbridge.db import l2network_db as cdb
from quantum.quantum_plugin_base import QuantumPluginBase


LOG = logging.getLogger(__name__)


class LinuxBridgePlugin(QuantumPluginBase):
    """
    LinuxBridgePlugin provides support for Quantum abstractions
    using LinuxBridge. A new VLAN is created for each network.
    It relies on an agent to perform the actual bridge configuration
    on each host.
    """

    def __init__(self, configfile=None):
        cdb.initialize()
        LOG.debug("Linux Bridge Plugin initialization done successfully")

    def _get_vlan_for_tenant(self, tenant_id, **kwargs):
        """Get an available VLAN ID"""
        try:
            return cdb.reserve_vlanid()
        except:
            raise Exception("Failed to reserve VLAN ID for network")

    def _release_vlan_for_tenant(self, tenant_id, net_id, **kwargs):
        """Release the ID"""
        vlan_binding = cdb.get_vlan_binding(net_id)
        return cdb.release_vlanid(vlan_binding[const.VLANID])

    def _validate_port_state(self, port_state):
        if port_state.upper() not in ('ACTIVE', 'DOWN'):
            raise exc.StateInvalid(port_state=port_state)
        return True

    def get_all_networks(self, tenant_id, **kwargs):
        """
        Returns a dictionary containing all
        <network_uuid, network_name> for
        the specified tenant.
        """
        LOG.debug("LinuxBridgePlugin.get_all_networks() called")
        networks_list = db.network_list(tenant_id)
        new_networks_list = []
        for network in networks_list:
            new_network_dict = cutil.make_net_dict(network[const.UUID],
                                                   network[const.NETWORKNAME],
                                                   [], network[const.OPSTATUS])
            new_networks_list.append(new_network_dict)

        # This plugin does not perform filtering at the moment
        return new_networks_list

    def get_network_details(self, tenant_id, net_id):
        """
        retrieved a list of all the remote vifs that
        are attached to the network
        """
        LOG.debug("LinuxBridgePlugin.get_network_details() called")
        db.validate_network_ownership(tenant_id, net_id)
        network = db.network_get(net_id)
        ports_list = db.port_list(net_id)
        ports_on_net = []
        for port in ports_list:
            new_port = cutil.make_port_dict(port)
            ports_on_net.append(new_port)

        new_network = cutil.make_net_dict(network[const.UUID],
                                          network[const.NETWORKNAME],
                                          ports_on_net,
                                          network[const.OPSTATUS])

        return new_network

    def create_network(self, tenant_id, net_name, **kwargs):
        """
        Creates a new Virtual Network, and assigns it
        a symbolic name.
        """
        LOG.debug("LinuxBridgePlugin.create_network() called")
        new_network = db.network_create(tenant_id, net_name,
                                        op_status=OperationalStatus.UP)
        new_net_id = new_network[const.UUID]
        vlan_id = self._get_vlan_for_tenant(tenant_id)
        cdb.add_vlan_binding(vlan_id, new_net_id)
        new_net_dict = {const.NET_ID: new_net_id,
                        const.NET_NAME: net_name,
                        const.NET_PORTS: [],
                        const.NET_OP_STATUS: new_network[const.OPSTATUS]}
        return new_net_dict

    def delete_network(self, tenant_id, net_id):
        """
        Deletes the network with the specified network identifier
        belonging to the specified tenant.
        """
        LOG.debug("LinuxBridgePlugin.delete_network() called")
        db.validate_network_ownership(tenant_id, net_id)
        net = db.network_get(net_id)
        if net:
            ports_on_net = db.port_list(net_id)
            if len(ports_on_net) > 0:
                for port in ports_on_net:
                    if port[const.INTERFACEID]:
                        raise exc.NetworkInUse(net_id=net_id)
                for port in ports_on_net:
                    self.delete_port(tenant_id, net_id, port[const.UUID])

            net_dict = cutil.make_net_dict(net[const.UUID],
                                           net[const.NETWORKNAME],
                                           [], net[const.OPSTATUS])
            try:
                self._release_vlan_for_tenant(tenant_id, net_id)
                cdb.remove_vlan_binding(net_id)
            except Exception as excp:
                LOG.warning("Exception: %s" % excp)
                db.network_update(net_id, tenant_id, {const.OPSTATUS:
                                                      OperationalStatus.DOWN})
            db.network_destroy(net_id)
            return net_dict
        # Network not found
        raise exc.NetworkNotFound(net_id=net_id)

    def update_network(self, tenant_id, net_id, **kwargs):
        """
        Updates the attributes of a particular Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.update_network() called")
        db.validate_network_ownership(tenant_id, net_id)
        network = db.network_update(net_id, tenant_id, **kwargs)
        net_dict = cutil.make_net_dict(network[const.UUID],
                                       network[const.NETWORKNAME],
                                       [], network[const.OPSTATUS])
        return net_dict

    def get_all_ports(self, tenant_id, net_id, **kwargs):
        """
        Retrieves all port identifiers belonging to the
        specified Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.get_all_ports() called")
        db.validate_network_ownership(tenant_id, net_id)
        network = db.network_get(net_id)
        ports_list = db.port_list(net_id)
        ports_on_net = []
        for port in ports_list:
            new_port = cutil.make_port_dict(port)
            ports_on_net.append(new_port)

        # This plugin does not perform filtering at the moment
        return ports_on_net

    def get_port_details(self, tenant_id, net_id, port_id):
        """
        This method allows the user to retrieve a remote interface
        that is attached to this particular port.
        """
        LOG.debug("LinuxBridgePlugin.get_port_details() called")
        db.validate_port_ownership(tenant_id, net_id, port_id)
        network = db.network_get(net_id)
        port = db.port_get(port_id, net_id)
        new_port_dict = cutil.make_port_dict(port)
        return new_port_dict

    def create_port(self, tenant_id, net_id, port_state=None, **kwargs):
        """
        Creates a port on the specified Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.create_port() called")
        db.validate_network_ownership(tenant_id, net_id)
        port = db.port_create(net_id, port_state,
                                op_status=OperationalStatus.DOWN)
        unique_port_id_string = port[const.UUID]
        new_port_dict = cutil.make_port_dict(port)
        return new_port_dict

    def update_port(self, tenant_id, net_id, port_id, **kwargs):
        """
        Updates the attributes of a port on the specified Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.update_port() called")
        db.validate_port_ownership(tenant_id, net_id, port_id)
        network = db.network_get(net_id)
        self._validate_port_state(kwargs["state"])
        port = db.port_update(port_id, net_id, **kwargs)

        new_port_dict = cutil.make_port_dict(port)
        return new_port_dict

    def delete_port(self, tenant_id, net_id, port_id):
        """
        Deletes a port on a specified Virtual Network,
        if the port contains a remote interface attachment,
        the remote interface is first un-plugged and then the port
        is deleted.
        """
        LOG.debug("LinuxBridgePlugin.delete_port() called")
        db.validate_port_ownership(tenant_id, net_id, port_id)
        network = db.network_get(net_id)
        port = db.port_get(port_id, net_id)
        attachment_id = port[const.INTERFACEID]
        if not attachment_id:
            db.port_destroy(port_id, net_id)
            new_port_dict = cutil.make_port_dict(port)
            return new_port_dict
        else:
            raise exc.PortInUse(port_id=port_id, net_id=net_id,
                                att_id=attachment_id)

    def plug_interface(self, tenant_id, net_id, port_id, remote_interface_id):
        """
        Attaches a remote interface to the specified port on the
        specified Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.plug_interface() called")
        db.validate_port_ownership(tenant_id, net_id, port_id)
        network = db.network_get(net_id)
        port = db.port_get(port_id, net_id)
        attachment_id = port[const.INTERFACEID]
        if attachment_id:
            raise exc.PortInUse(port_id=port_id, net_id=net_id,
                                att_id=attachment_id)
        db.port_set_attachment(port_id, net_id, remote_interface_id)

    def unplug_interface(self, tenant_id, net_id, port_id):
        """
        Detaches a remote interface from the specified port on the
        specified Virtual Network.
        """
        LOG.debug("LinuxBridgePlugin.unplug_interface() called")
        db.validate_port_ownership(tenant_id, net_id, port_id)
        network = db.network_get(net_id)
        port = db.port_get(port_id, net_id)
        attachment_id = port[const.INTERFACEID]
        if attachment_id == None:
            raise exc.InvalidDetach(port_id=port_id, net_id=net_id,
                                    att_id=remote_interface_id)
        db.port_unset_attachment(port_id, net_id)
        db.port_update(port_id, net_id, op_status=OperationalStatus.DOWN)
