
from __future__ import absolute_import
import subprocess

from netaddr import IPAddress
from netaddr.core import AddrConversionError
from netaddr.core import AddrFormatError

from vdsm.network.api import network_caps
from vdsm.tool import expose, ExtraArgsError


OVN_SCRIPT_DIR = '/usr/libexec/ovirt-provider-ovn'
OVN_CONFIG_SCRIPT = '{}/setup_ovn_controller.sh'.format(OVN_SCRIPT_DIR)
OVN_UNCONFIGURE_SCRIPT = '{}/unconfigure_ovn_controller.sh'.format(
    OVN_SCRIPT_DIR
)


class NetworkNotFoundError(Exception):
    pass


class IpAddressNotFoundError(Exception):
    pass


@expose('ovn-config')
def ovn_config(*args):
    if len(args) != 3:
        raise ExtraArgsError(n=2)

    if is_ipaddress(args[2]):
        ip_address = args[2]
    else:
        net_name = args[2]
        ip_address = get_ip_addr(get_network(network_caps(), net_name))
        if not ip_address:
            raise IpAddressNotFoundError(net_name)

    cmd = [OVN_CONFIG_SCRIPT, format_literal_ipaddress(args[1]), ip_address]
    exec_command(cmd, 'Failed to configure OVN controller.')


@expose('ovn-unconfigure')
def ovn_unconfigure(*args):
    if len(args) != 1:
        raise ExtraArgsError(n=0)

    exec_command(
        [OVN_UNCONFIGURE_SCRIPT],
        'Failed to unconfigure OVN controller.'
    )


def is_ipaddress(candidate):
    try:
        IPAddress(candidate)
    except AddrFormatError:
        return False
    return True


def format_literal_ipaddress(ip_address):
    if is_ipv6(ip_address):
        return '[{ip_address}]'.format(ip_address=ip_address)
    return ip_address


def is_ipv6(candidate):
    try:
        IPAddress(candidate).ipv4()
        return False
    except AddrConversionError:
        return True


def get_network(net_caps, net_name):
    networks = net_caps['networks']
    try:
        return networks[net_name]
    except KeyError:
        raise NetworkNotFoundError(net_name)


def get_ip_addr(net):
    if net['addr']:
        ip = net['addr']
    elif net['ipv6addrs']:
        ip = net['ipv6addrs'][0].split('/')[0]
    else:
        return None
    return ip


def exec_command(cmd, error_msg):
    rc = subprocess.call(cmd)
    if rc != 0:
        raise EnvironmentError(error_msg)
