#!/usr/bin/env python
#
#pylint: disable=line-too-long,invalid-name,missing-docstring,redefined-outer-name,too-many-arguments,too-many-locals,too-many-statements,logging-format-interpolation
#

import os
import sys
import time
import json
import subprocess

import logging as log


def setup_logging(loglevel=log.INFO):
    """initialize logging.
    Currently only logging to stdout and not directly to the journal to avoid double-logging
    since this script is intended to run from an ExecStartPost= of a systemd unit, its stdout
    will already be aggregated with that unit's journal logs, and it makes sense to combine this
    script's logs with the container unit's logs rather than making them separate.

    This uses the standard python logger instead of titan.pantheon.twistedLog for future
    portability and because we are not logging to the journal directly.
    """
    log.basicConfig(level=loglevel, format='%(levelname)-8s: %(message)s')


def is_ipv4(addr):
    """return true if addr looks like an ipv4 address, false otherwise"""
    if addr == '0/0' or '.' in addr:
        return True
    else:
        return False

def is_ipv6(addr):
    """return true if addr looks like an ipv6 address, false otherwise"""
    if addr == '0/0' or ':' in addr:
        return True
    else:
        return False


def docker_inspect(container_name, max_attempts=20):
    """Runs `docker inspect <container_name>` and parses its json output, returning a python dict.

    raises subprocess.CalledProcessError on non-zero exit status from docker
    """
    attempts = 0
    success = False
    while attempts < max_attempts and not success:
        try:
            result = subprocess.check_output('docker inspect {}'.format(container_name),
                                             stderr=subprocess.STDOUT,
                                             shell=True)
            success = True
        except subprocess.CalledProcessError:
            time.sleep(0.1)  # 100ms!!

        attempts += 1

    if success:
        return json.loads(result)[0]
    else:
        raise RuntimeError('Retries exhausted waiting for {} to become available'.format(container_name))


def wait_until_running(container_name, max_attempts=20):
    attempts = 0
    success = False
    while attempts < max_attempts and not success:
        container_data = docker_inspect(container_name)
        if container_data['State']['Running'] is True:
            return True
        time.sleep(0.1)
        attempts += 1
    raise RuntimeError('timed out waiting for {} to enter running state.'.format(container_name))


def create_ipv4_nat_rule(chain, bridge, proto, host_port, container_ip, container_port):
    """return a iptables v4 nat rule for forwarding a host port to a container IP:port"""
    return '-A {chain} ! -i {bridge} -p {proto} -m {proto}' \
           ' --dport {host_port} -j DNAT' \
           ' --to-destination {container_ip}:{container_port}'.format(chain=chain,
                                                                      bridge=bridge,
                                                                      proto=proto,
                                                                      host_port=host_port,
                                                                      container_ip=container_ip,
                                                                      container_port=container_port)

def create_ipv4_filter_rule(container_ip, bridge, proto, container_port):
    """return a iptables v4 filter rule for forwarding a host port to a container IP:port"""
    return '-A FORWARD -d {container_ip} ! -i {bridge} -o {bridge}' \
           ' -p {proto} -m {proto} --dport {container_port}'\
           ' -j ACCEPT\n'.format(container_ip=container_ip,
                                 bridge=bridge,
                                 proto=proto,