#!/usr/bin/env python3
# vim:ts=4 sw=4 expandtab
"""
Determine and set preferred repository component
"""
import os
import sys
import json
import tempfile
import time
import syslog
import http.client as httplib

source_list_file = "/etc/apt/sources.list.d/substrate.list"
preferences_file = '/etc/apt/preferences.d/substrate'

syslog.openlog('set-repository-reference')

def get_domains():
    """
    Get platformUpdateDomain and platformFaultDomain of this instance, return as tuple
    """
    con = httplib.HTTPConnection("169.254.169.254")
    headers = {'Metadata': 'true'}
    try:
        con.request('GET', "/metadata/instance?api-version=2017-08-01", headers=headers)
        response = con.getresponse()
        if response.status == 200:
            data = response.read()
            result = json.loads(data.decode("utf-8"))
        else:
            result = json.loads("{'compute': {'platformUpdateDomain': '-1', 'platformFaultDomain': '-1'}}")
        con.close()
    # pylint: disable=broad-except
    except Exception as e:
        syslog.syslog(syslog.LOG_ALERT, "Error happened while trying to get instance metadata: {}.".format(e))
        sys.exit(1)

    return result["compute"]["platformUpdateDomain"], result["compute"]["platformFaultDomain"]


def get_value_by_key(key, filename='/etc/default/gcp'):
    """
    Read and return first key from file with format: key = value\n
    """
    value = False
    try:
        with open(filename, 'r') as f:
            for l in f.readlines():
                if l.find(key) != -1:
                    value = l.rsplit('=', 1)[1].strip()
                    break
    # pylint: disable=broad-except
    except Exception as e:
        syslog.syslog(syslog.LOG_ALERT, "Error finding {} from {}: {}, ".format(key, filename, e))
        return False
    return value


def write_preferences(ring):
    """
    Increase Pin-Priority of the ring chosen.
    """
    oldring = get_value_by_key("Pin: release c", preferences_file)
    if oldring == ring:
        syslog.syslog(syslog.LOG_DEBUG, "Host is already receiving upates from {}".format(ring))
        return
    try:
        newf = tempfile.NamedTemporaryFile(mode='w', prefix=os.path.basename(preferences_file),
                                           dir=os.path.dirname(preferences_file), delete=False)
        newf.write('Package: *\nPin: release o=kevlar, c={}\nPin-Priority: 2001\n'.format(ring))
        newf.flush()
        name = newf.name
        newf.close()
        os.rename(name, preferences_file)
        syslog.syslog(syslog.LOG_INFO, "Host is now receiving upates from {}".format(ring))
    # pylint: disable=broad-except
    except Exception as e:
        syslog.syslog(syslog.LOG_ALERT, "Writing a new preferences file failed with an exception: {}".format(e))
        sys.exit(1)


def select_update_ring(ring_assignment=-1):
    """
    There are 4 options: 1 - user enforcerd ring, 2 - no assingment (-1) - defaults to ring0
    3 - Azure platformFaultDomain (fd), 4 - Azure platformUpdateDomain (ud)
    """
    forced_ring = get_userset_ring()
    if forced_ring:
        syslog.syslog(syslog.LOG_INFO, "User enforced update ring to {}...".format(forced_ring))
        write_preferences(forced_ring)
        return
    if ring_assignment == -1:
        syslog.syslog(syslog.LOG_INFO, "No ring assingment, defaulting to ring0...")
        write_preferences('ring0')
        return
    if ring_assignment == 'fd':
        _, fault_domain = get_domains()
        syslog.syslog(syslog.LOG_INFO, "Azure FaultDomain based ring assingment configured...")
        write_preferences('ring{}'.format(int(fault_domain) % 3))
        return
    if ring_assignment == 'ud':
        update_domain, _ = get_domains()
        syslog.syslog(syslog.LOG_INFO, "Azure UpdateDomain based ring assingment configured...")
        write_preferences('ring{}'.format(int(update_domain) % 3))


def get_userset_ring():
    """
    Check if user has enforced a specific ring
    """
    if os.path.isfile('/etc/apt/kevlarring'):
        try:
            time_set = os.stat('/etc/apt/kevlarring').st_mtime
            cur = time.time()
            # more than 1 week old
            if (cur - time_set) > (60 * 60 * 24 * 7):
                # remove old file
                os.remove('/etc/apt/kevlarring')
                syslog.syslog(syslog.LOG_INFO, "The time for user enforced update ring is over, "
                                               "switching to system default...")
                return False
            return get_value_by_key('RING', filename='/etc/apt/kevlarring')
        # pylint: disable=broad-except
        except Exception as e:
            syslog.syslog(syslog.LOG_ALERT, "Getting user set update ring failed with an exception: {}".format(e))
            return False
    return False


if not os.path.isfile('/etc/default/gcp') or not os.path.isfile(source_list_file):
    sys.exit(0)

RepoComponent = get_value_by_key('RepoComponent')
if not RepoComponent or RepoComponent != 'stable':
    # update rings only exist for stable
    sys.exit(0)

update_ring_assignment = get_value_by_key('UPDATE_RING_ASIGNMENT')


if not update_ring_assignment:
    update_ring_assignment = -1

select_update_ring(update_ring_assignment)
