# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
# Copyright 2013, Nachi Ueno, NTT I3, Inc.
# 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.
from oslo.config import cfg

from neutron.agent import l3_agent
from neutron.extensions import vpnaas
from neutron.openstack.common import importutils

vpn_agent_opts = [
    cfg.MultiStrOpt(
        'vpn_device_driver',
        default=['neutron.services.vpn.device_drivers.'
                 'ipsec.OpenSwanDriver'],
        help=_("The vpn device drivers Neutron will use")),
]
cfg.CONF.register_opts(vpn_agent_opts, 'vpnagent')


class VPNAgent(l3_agent.L3NATAgentWithStateReport):
    """VPNAgent class which can handle vpn service drivers."""
    def __init__(self, host, conf=None):
        super(VPNAgent, self).__init__(host=host, conf=conf)
        self.setup_device_drivers(host)

    def setup_device_drivers(self, host):
        """Setting up device drivers.

        :param host: hostname. This is needed for rpc
        Each devices will stays as processes.
        They will communiate with
        server side service plugin using rpc with
        device specific rpc topic.
        :returns: None
        """
        device_drivers = cfg.CONF.vpnagent.vpn_device_driver
        self.devices = []
        for device_driver in device_drivers:
            try:
                self.devices.append(
                    importutils.import_object(device_driver, self, host))
            except ImportError:
                raise vpnaas.DeviceDriverImportError(
                    device_driver=device_driver)

    def get_namespace(self, router_id):
        """Get namespace of router.

        :router_id: router_id
        :returns: namespace string.
            Note if the router is not exist, this function
            returns None
        """
        router_info = self.router_info.get(router_id)
        if not router_info:
            return
        return router_info.ns_name

    def add_nat_rule(self, router_id, chain, rule, top=False):
        """Add nat rule in namespace.

        :param router_id: router_id
        :param chain: a string of chain name
        :param rule: a string of rule
        :param top: if top is true, the rule
            will be placed on the top of chain
            Note if there is no rotuer, this method do nothing
        """
        router_info = self.router_info.get(router_id)
        if not router_info:
            return
        router_info.iptables_manager.ipv4['nat'].add_rule(
            chain, rule, top=top)

    def remove_nat_rule(self, router_id, chain, rule, top=False):
        """Remove nat rule in namespace.

        :param router_id: router_id
        :param chain: a string of chain name
        :param rule: a string of rule
        :param top: unused
            needed to have same argument with add_nat_rule
        """
        router_info = self.router_info.get(router_id)
        if not router_info:
            return
        router_info.iptables_manager.ipv4['nat'].remove_rule(
            chain, rule)

    def iptables_apply(self, router_id):
        """Apply IPtables.

        :param router_id: router_id
        This method do nothing if there is no router
        """
        router_info = self.router_info.get(router_id)
        if not router_info:
            return
        router_info.iptables_manager.apply()

    def _router_added(self, router_id, router):
        """Router added event.

        This method overwrites parent class method.
        :param router_id: id of added router
        :param router: dict of rotuer
        """
        super(VPNAgent, self)._router_added(router_id, router)
        for device in self.devices:
            device.create_router(router_id)

    def _router_removed(self, router_id):
        """Router removed event.

        This method overwrites parent class method.
        :param router_id: id of removed router
        """
        super(VPNAgent, self)._router_removed(router_id)
        for device in self.devices:
            device.destroy_router(router_id)

    def _process_routers(self, routers, all_routers=False):
        """Router sync event.

        This method overwrites parent class method.
        :param routers: list of routers
        """
        super(VPNAgent, self)._process_routers(routers, all_routers)
        for device in self.devices:
            device.sync(self.context, routers)


def main():
    l3_agent.main(
        manager='neutron.services.vpn.agent.VPNAgent')
