# -*- coding: utf-8 -*-

module Dcmgr
  module VNet
    # This file stores the factories. It is their job to read config files etc. to decide which implementation to use.
    V = Dcmgr::VNet
    class ControllerFactory
      def self.create_controller(node)
        V::Netfilter::NetfilterController.new(node)
      end
    end
    
    class IsolatorFactory
      def self.create_isolator
        V::Isolators::BySecurityGroup.new
        #V::Isolators::DummyIsolator.new
      end
    end
    
    class TaskManagerFactory
      def self.create_task_manager(node)
        manager = V::Netfilter::VNicProtocolTaskManager.new
        manager.enable_ebtables = node.manifest.config.enable_ebtables
        manager.enable_iptables = node.manifest.config.enable_iptables
        manager.verbose_commands = node.manifest.config.verbose_netfilter
        
        manager
      end
    end
    
    class TaskFactory
      extend Dcmgr::Helpers::NicHelper
      include V::Tasks

      def self.create_tasks_for_isolation(vnic,friends,node)
        tasks = []
        enable_logging = node.manifest.config.packet_drop_log
        ipset_enabled = node.manifest.config.use_ipset
        friend_ips = friends.map {|vnic_map| vnic_map[:ipv4][:address]}
        
        tasks << AcceptARPFromFriends.new(vnic[:ipv4][:address],friend_ips,enable_logging,"A arp friend #{vnic[:uuid]}")
        #tasks << AcceptIpFromFriends(friend_ips)
        
        if is_natted? vnic          
          # Friends don't use NAT, friends talk to each other with their REAL ip addresses.
          # It's a heart warming scene, really
          if ipset_enabled
            # Not implemented yet
            #tasks << ExcludeFromNatIpSet.new(friend_ips,vnic[:ipv4][:address])
          else
            tasks << ExcludeFromNat.new(friend_ips,vnic[:ipv4][:address])
          end
        end
        
        tasks
      end

      # Returns the tasks required for applying this security group
      def self.create_tasks_for_secgroup(secgroup)
        [SecurityGroup.new(secgroup)]
      end

      # Returns the tasks that drop all traffic
      def self.create_drop_tasks_for_vnic(vnic,node)
        enable_logging = node.manifest.config.packet_drop_log

        #TODO: Add logging to ip drops
        [DropIpFromAnywhere.new, DropArpForwarding.new(enable_logging,"D arp #{vnic[:uuid]}: ")]
      end
      
      # Creates tasks related to network address translation
      def self.create_nat_tasks_for_vnic(vnic,node)
        #friend_ips = friends.map {|vnic_map| vnic_map[:ipv4][:address]}
        #ipset_enabled = node.manifest.config.use_ipset
        tasks = []
        
        # Nat tasks
        if is_natted? vnic          
          # Exclude instances in the same security group form using nat
          #if ipset_enabled
            # Not implemented yet
            #tasks << ExcludeFromNatIpSet.new(friend_ips,vnic[:ipv4][:address])
          #else
            #tasks << ExcludeFromNat.new(friend_ips,vnic[:ipv4][:address])
          #end
          
          tasks << StaticNatLog.new(vnic[:ipv4][:address], vnic[:ipv4][:nat_address], "SNAT #{vnic[:uuid]}", "DNAT #{vnic[:uuid]}") if node.manifest.config.packet_drop_log
          tasks << StaticNat.new(vnic[:ipv4][:address], vnic[:ipv4][:nat_address], clean_mac(vnic[:mac_addr]))
        end
        
        tasks << TranslateMetadataAddress.new(vnic[:uuid],vnic[:ipv4][:network][:metadata_server],vnic[:ipv4][:network][:metadata_server_port] || 80) unless vnic[:ipv4][:network][:metadata_server].nil?
        
        tasks
      end

      #Returns the netfilter tasks required for this vnic
      # The _friends_ parameter is an array of vnic_maps that should not be isolated from _vnic_
      def self.create_tasks_for_vnic(vnic,friends,security_groups,node)
        tasks = []

        host_addr = Isono::Util.default_gw_ipaddr
        enable_logging = node.manifest.config.packet_drop_log
        ipset_enabled = node.manifest.config.use_ipset
        
        # Drop all traffic that isn't explicitely accepted
        tasks += self.create_drop_tasks_for_vnic(vnic,node)
        
        # General data link layer tasks
        tasks << AcceptARPToHost.new(host_addr,vnic[:ipv4][:address],enable_logging,"A arp to_host #{vnic[:uuid]}: ")
        tasks << AcceptARPFromGateway.new(vnic[:ipv4][:network][:ipv4_gw],enable_logging,"A arp from_gw #{vnic[:uuid]}: ") unless vnic[:ipv4][:network][:ipv4_gw].nil?
        tasks << DropIpSpoofing.new(vnic[:ipv4][:address],enable_logging,"D arp sp #{vnic[:uuid]}: ")
        tasks << DropMacSpoofing.new(clean_mac(vnic[:mac_addr]),enable_logging,"D ip sp #{vnic[:uuid]}: ")
        tasks << AcceptArpBroadcast.new(host_addr,enable_logging,"A arp bc #{vnic[:uuid]}: ")
        
        # General ip layer tasks
        tasks << AcceptIcmpRelatedEstablished.new
        tasks << AcceptTcpRelatedEstablished.new
        tasks << AcceptUdpEstablished.new
        tasks << AcceptAllDNS.new
        tasks << AcceptWakameDHCPOnly.new(vnic[:ipv4][:network][:dhcp_server]) unless vnic[:ipv4][:network][:dhcp_server].nil?
        # Accept OUTGOING traffic from instances to anywhere in the network
        #tasks << AcceptIpToAnywhere.new
        
        # VM isolation based
        tasks += self.create_tasks_for_isolation(vnic,friends,node)
        tasks += self.create_nat_tasks_for_vnic(vnic,node)
        
        # Accept ip traffic from the gateway that isn't blocked by other tasks
        tasks << AcceptIpFromGateway.new(vnic[:ipv4][:network][:ipv4_gw]) unless vnic[:ipv4][:network][:ipv4_gw].nil?
        
        # Security group tasks
        security_groups.each { |secgroup|
          tasks += self.create_tasks_for_secgroup(secgroup)
        }
        
        tasks
      end
    end
      
  end
end