# vim: tabstop=4 shiftwidth=4 softtabstop=4
#
#  Copyright (c) 2011 Openstack, LLC.
#  All Rights Reserved.
#
#     Licensed under the Apache License, Version 2.0 (the "License"); you may
#     not use this file except in compliance with the License. You may obtain
#     a copy of the License at
#
#          http://www.apache.org/licenses/LICENSE-2.0
#
#     Unless required by applicable law or agreed to in writing, software
#     distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
#     WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
#     License for the specific language governing permissions and limitations
#     under the License.
#

"""
arch linux network helper module
"""

# Arch has two different kinds of network configuration. More recently,
# there's 'netcfg' and previously (for lack of a better term) 'legacy'.
#
# legacy uses:
# - 1 shell-script-style global configuration (/etc/rc.conf)
# - one IP per interface
# - routes are per interface
# - gateways are global
# - DNS is per interface
#
# netcfg uses:
# - multiple shell-script-style network configurations, 1 per interface
# - one IP per configuration
# - routes are per interface
# - gateways are per interface
# - DNS is global (/etc/resolv.conf)
#
# netcfg is designed for one IP per configuration, but it's not tolerant
# of the older style colon interfaces for IP aliasing. So we have to use
# a hack to get IP aliasing working:
# https://bbs.archlinux.org/viewtopic.php?pid=951573#p951573
#
# Arch is a rolling release, meaning new features and updated packages
# roll out on a unpredictable schedule. It also means there is no such
# thing as v1.0 or v2.0. We check if the netcfg package is installed to
# determine which format should be used.

import os
import re
import time
import subprocess
import logging
from cStringIO import StringIO

import commands.network

CONF_FILE = "/etc/rc.conf"
NETWORK_DIR = "/etc/network.d"
NETCTL_DIR = "/etc/netctl/"


def _execute(command):
    logging.info('executing %s' % ' '.join(command))

    pipe = subprocess.PIPE
    p = subprocess.Popen(command, stdin=pipe, stdout=pipe, stderr=pipe, env={})

    # Wait for process to finish and get output
    stdout, stderr = p.communicate()

    logging.debug('status = %d' % p.returncode)
    if p.returncode:
        logging.info('stdout = %r' % stdout)
        logging.info('stderr = %r' % stderr)

    return p.returncode


def configure_network(hostname, interfaces):
    update_files = {}
    init=""

    # We need to figure out what style of network configuration is
    # currently being used by looking at /etc/rc.conf and then look
    # to see what style of network configuration we want to use by
    # looking to see if the netcfg package is installed
    if os.path.basename(os.path.realpath('/sbin/init')) == 'systemd':
        cur_netctl = True
        status = _execute(['/usr/bin/pacman', '-Q', 'netctl'])
        use_netctl = (status == 0)
        remove_files, netnames = process_interface_files_netctl(update_files, interfaces)
        get_hostname_file_systemd(hostname)
    else:
        cur_netctl = False
        use_netctl = False
        if os.path.exists(CONF_FILE):
            update_files[CONF_FILE] = open(CONF_FILE).read()

        infile = StringIO(update_files.get(CONF_FILE, ''))

        cur_netcfg = True    # Currently using netcfg
        lines, variables = _parse_config(infile)
        lineno = variables.get('DAEMONS')
        if lineno is not None:
            daemons = _parse_variable(lines[lineno])
            if 'network' in daemons:
                # Config uses legacy style networking
                cur_netcfg = False

        status = _execute(['/usr/bin/pacman', '-Q', 'netcfg'])
        use_netcfg = (status == 0)
        logging.info('using %s style configuration' %
                     (use_netcfg and 'netcfg' or 'legacy'))

        if use_netcfg:
            remove_files, netnames = process_interface_files_netcfg(
                    update_files, interfaces)
        else:
            process_interface_files_legacy(update_files, interfaces)
            remove_files = set()

            # Generate new /etc/resolv.conf file
            filepath, data = commands.network.get_resolv_conf(interfaces)
            if data:
                update_files[filepath] = data

        # Update config file with new hostname
        infile = StringIO(update_files.get(CONF_FILE, ''))

        data = get_hostname_file(infile, hostname)
        update_files[CONF_FILE] = data

        # Generate new /etc/hosts file
        filepath, data = commands.network.get_etc_hosts(interfaces, hostname)
        update_files[filepath] = data

    # Set hostname
    try:
        commands.network.sethostname(hostname)
    except Exception, e:
        logging.error("Couldn't sethostname(): %s" % str(e))
        return (500, "Couldn't set hostname: %s" % str(e))

    # Stage files
    commands.network.stage_files(update_files)

    errors = set()

    # Down network
    logging.info('configuring interfaces down')
    if cur_netctl:
        for netname in netnames:
            if not interfaces[netname]['up']:
                # Don't try to down an interface that isn't already up
                logging.info('  %s, skipped (already down)' %
                             netname)
                continue

            status = _execute(['/usr/bin/netctl', 'stop', ''.join(['ethernet-', netname])])
            if status != 0:
                logging.info('  %s, failed (status %d)' % (netname, status))
                # Treat down failures as soft failures
            else:
                logging.info('  %s, success' % netname)