# vim: tabstop=4 shiftwidth=4 softtabstop=4

# Copyright 2011 United States Government as represented by the
# Administrator of the National Aeronautics and Space Administration.
# All Rights Reserved.
# Copyright (c) 2011 Citrix 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.

from oslo.config import cfg

from nova.compute import utils as compute_utils
from nova import context
from nova.network import linux_net
from nova.objects import instance as instance_obj
from nova.objects import security_group as security_group_obj
from nova.objects import security_group_rule as security_group_rule_obj
from nova.openstack.common.gettextutils import _
from nova.openstack.common import importutils
from nova.openstack.common import log as logging
from nova import utils
from nova.virt import netutils

LOG = logging.getLogger(__name__)

firewall_opts = [
    cfg.StrOpt('firewall_driver',
               help='Firewall driver '
                    '(defaults to hypervisor specific iptables driver)'),
    cfg.BoolOpt('allow_same_net_traffic',
                default=True,
                help='Whether to allow network traffic from same network'),
]

CONF = cfg.CONF
CONF.register_opts(firewall_opts)
CONF.import_opt('use_ipv6', 'nova.netconf')


def load_driver(default, *args, **kwargs):
    fw_class = importutils.import_class(CONF.firewall_driver or default)
    return fw_class(*args, **kwargs)


class FirewallDriver(object):
    """Firewall Driver base class.

        Defines methods that any driver providing security groups
        and provider firewall functionality should implement.
    """
    def __init__(self, virtapi):
        self._virtapi = virtapi

    def prepare_instance_filter(self, instance, network_info):
        """Prepare filters for the instance.

        At this point, the instance isn't running yet.
        """
        raise NotImplementedError()

    def filter_defer_apply_on(self):
        """Defer application of IPTables rules."""
        pass

    def filter_defer_apply_off(self):
        """Turn off deferral of IPTables rules and apply the rules now."""
        pass

    def unfilter_instance(self, instance, network_info):
        """Stop filtering instance."""
        raise NotImplementedError()

    def apply_instance_filter(self, instance, network_info):
        """Apply instance filter.

        Once this method returns, the instance should be firewalled
        appropriately. This method should as far as possible be a
        no-op. It's vastly preferred to get everything set up in
        prepare_instance_filter.
        """
        raise NotImplementedError()

    def refresh_security_group_rules(self, security_group_id):
        """Refresh security group rules from data store

        Gets called when a rule has been added to or removed from
        the security group.
        """
        raise NotImplementedError()

    def refresh_security_group_members(self, security_group_id):
        """Refresh security group members from data store

        Gets called when an instance gets added to or removed from
        the security group.
        """
        raise NotImplementedError()

    def refresh_instance_security_rules(self, instance):
        """Refresh security group rules from data store

        Gets called when an instance gets added to or removed from
        the security group the instance is a member of or if the
        group gains or looses a rule.
        """
        raise NotImplementedError()

    def refresh_provider_fw_rules(self):
        """Refresh common rules for all hosts/instances from data store.

        Gets called when a rule has been added to or removed from
        the list of rules (via admin api).

        """
        raise NotImplementedError()

    def setup_basic_filtering(self, instance, network_info):
        """Create rules to block spoofing and allow dhcp.

        This gets called when spawning an instance, before
        :py:meth:`prepare_instance_filter`.

        """
        raise NotImplementedError()

    def instance_filter_exists(self, instance, network_info):
        """Check nova-instance-instance-xxx exists."""
        raise NotImplementedError()


class IptablesFirewallDriver(FirewallDriver):
    """Driver which enforces security groups through iptables rules."""

    def __init__(self, virtapi, **kwargs):
        super(IptablesFirewallDriver, self).__init__(virtapi)
        self.iptables = linux_net.iptables_manager
        self.instances = {}
        self.network_infos = {}
        self.basically_filtered = False

        # Flags for DHCP request rule
        self.dhcp_create = False
        self.dhcp_created = False

        self.iptables.ipv4['filter'].add_chain('sg-fallback')
        self.iptables.ipv4['filter'].add_rule('sg-fallback', '-j DROP')
        self.iptables.ipv6['filter'].add_chain('sg-fallback')
        self.iptables.ipv6['filter'].add_rule('sg-fallback', '-j DROP')

    def setup_basic_filtering(self, instance, network_info):
        pass

    def apply_instance_filter(self, instance, network_info):
        """No-op. Everything is done in prepare_instance_filter."""
        pass

    def filter_defer_apply_on(self):
        self.iptables.defer_apply_on()

    def filter_defer_apply_off(self):
        self.iptables.defer_apply_off()

    def unfilter_instance(self, instance, network_info):
        if self.instances.pop(instance['id'], None):
            # NOTE(vish): use the passed info instead of the stored info
            self.network_infos.pop(instance['id'])
            self.remove_filters_for_instance(instance)