#!/usr/bin/env python3
#
# Copyright (C) 2019 VyOS maintainers and contributors
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License version 2 or later as
# published by the Free Software Foundation.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program.  If not, see <http://www.gnu.org/licenses/>.

import os

from sys import exit
from copy import deepcopy
from netifaces import interfaces

from vyos.config import Config
from vyos.ifconfig import GeneveIf
from vyos.validate import is_member
from vyos import ConfigError

from vyos import airbag
airbag.enable()

default_config_data = {
    'address': [],
    'deleted': False,
    'description': '',
    'disable': False,
    'intf': '',
    'ip_arp_cache_tmo': 30,
    'ip_proxy_arp': 0,
    'is_bridge_member': False,
    'mtu': 1500,
    'remote': '',
    'vni': ''
}

def get_config():
    geneve = deepcopy(default_config_data)
    conf = Config()

    # determine tagNode instance
    if 'VYOS_TAGNODE_VALUE' not in os.environ:
        raise ConfigError('Interface (VYOS_TAGNODE_VALUE) not specified')

    geneve['intf'] = os.environ['VYOS_TAGNODE_VALUE']

    # check if interface is member if a bridge
    geneve['is_bridge_member'] = is_member(conf, geneve['intf'], 'bridge')

    # Check if interface has been removed
    if not conf.exists('interfaces geneve ' + geneve['intf']):
        geneve['deleted'] = True
        return geneve

    # set new configuration level
    conf.set_level('interfaces geneve ' + geneve['intf'])

    # retrieve configured interface addresses
    if conf.exists('address'):
        geneve['address'] = conf.return_values('address')

    # retrieve interface description
    if conf.exists('description'):
        geneve['description'] = conf.return_value('description')

    # Disable this interface
    if conf.exists('disable'):
        geneve['disable'] = True

    # ARP cache entry timeout in seconds
    if conf.exists('ip arp-cache-timeout'):
        geneve['ip_arp_cache_tmo'] = int(conf.return_value('ip arp-cache-timeout'))

    # Enable proxy-arp on this interface
    if conf.exists('ip enable-proxy-arp'):
        geneve['ip_proxy_arp'] = 1

    # Maximum Transmission Unit (MTU)
    if conf.exists('mtu'):
        geneve['mtu'] = int(conf.return_value('mtu'))

    # Remote address of GENEVE tunnel
    if conf.exists('remote'):
        geneve['remote'] = conf.return_value('remote')

    # Virtual Network Identifier
    if conf.exists('vni'):
        geneve['vni'] = conf.return_value('vni')

    return geneve


def verify(geneve):
    if geneve['deleted']:
        if geneve['is_bridge_member']:
            raise ConfigError((
                f'Cannot delete interface "{geneve["intf"]}" as it is a '
                f'member of bridge "{geneve["is_bridge_member"]}"!'))

        return None

    if geneve['is_bridge_member'] and geneve['address']:
        raise ConfigError((
            f'Cannot assign address to interface "{geneve["intf"]}" '
            f'as it is a member of bridge "{geneve["is_bridge_member"]}"!'))

    if not geneve['remote']:
        raise ConfigError('GENEVE remote must be configured')

    if not geneve['vni']:
        raise ConfigError('GENEVE VNI must be configured')

    return None


def generate(geneve):
    return None


def apply(geneve):
    # Check if GENEVE interface already exists
    if geneve['intf'] in interfaces():
        g = GeneveIf(geneve['intf'])
        # GENEVE is super picky and the tunnel always needs to be recreated,
        # thus we can simply always delete it first.
        g.remove()

    if not geneve['deleted']:
        # GENEVE interface needs to be created on-block
        # instead of passing a ton of arguments, I just use a dict
        # that is managed by vyos.ifconfig
        conf = deepcopy(GeneveIf.get_config())

        # Assign GENEVE instance configuration parameters to config dict
        conf['vni'] = geneve['vni']
        conf['remote'] = geneve['remote']

        # Finally create the new interface
        g = GeneveIf(geneve['intf'], **conf)
        # update interface description used e.g. by SNMP
        g.set_alias(geneve['description'])
        # Maximum Transfer Unit (MTU)
        g.set_mtu(geneve['mtu'])

        # configure ARP cache timeout in milliseconds
        g.set_arp_cache_tmo(geneve['ip_arp_cache_tmo'])
        # Enable proxy-arp on this interface
        g.set_proxy_arp(geneve['ip_proxy_arp'])

        # Configure interface address(es) - no need to implicitly delete the
        # old addresses as they have already been removed by deleting the
        # interface above
        for addr in geneve['address']:
            g.add_addr(addr)

        # As the GENEVE interface is always disabled first when changing
        # parameters we will only re-enable the interface if it is not
        # administratively disabled
        if not geneve['disable']:
            g.set_admin_state('up')

        # re-add ourselves to any bridge we might have fallen out of
        if geneve['is_bridge_member']:
            g.add_to_bridge(geneve['is_bridge_member'])

    return None


if __name__ == '__main__':
    try:
        c = get_config()
        verify(c)
        generate(c)
        apply(c)
    except ConfigError as e:
        print(e)
        exit(1)