# Copyright (c) 2013 The Chromium OS Authors. All rights reserved.
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.

"""
Encapsulate functionality of the Linux IPv6 Router Advertisement Daemon.
Support writing out a configuration file as well as starting and stopping
the service.
"""

import os
import signal

from autotest_lib.client.common_lib import error
from autotest_lib.client.common_lib import utils

# Filenames used for execution.
RADVD_EXECUTABLE = '/usr/local/sbin/radvd'
RADVD_CONFIG_FILE = '/tmp/radvd_test.conf'
RADVD_PID_FILE = '/tmp/radvd_test.pid'

# These are default configuration values.
RADVD_DEFAULT_ADV_ON_LINK = 'on'
RADVD_DEFAULT_ADV_AUTONOMOUS = 'on'
RADVD_DEFAULT_ADV_ROUTER_ADDR = 'on'
RADVD_DEFAULT_ADV_RDNSS_LIFETIME = 'infinity'
RADVD_DEFAULT_DNSSL_LIST = 'a.com b.com'
RADVD_DEFAULT_MAX_ADV_INTERVAL = 10
RADVD_DEFAULT_MIN_ADV_INTERVAL = 3
RADVD_DEFAULT_SEND_ADVERT = 'on'

# The addresses below are within the  2001:0db8/32 "documentation only" prefix
# (RFC3849), which is guaranteed never to be assigned to a real network.
RADVD_DEFAULT_SUFFIX = '/64'
RADVD_DEFAULT_PREFIX = '2001:db8:100:f101::/64'
RADVD_DEFAULT_RDNSS_SERVERS = ( '2001:db8:100:f101::1 '
                                '2001:db8:100:f101::2' )

# Option names.
OPTION_ADV_ON_LINK = 'adv_on_link'
OPTION_ADV_AUTONOMOUS = 'adv_autonomous'
OPTION_ADV_ROUTER_ADDR = 'adv_router_addr'
OPTION_ADV_RDNSS_LIFETIME = 'adv_rdnss_lifetime'
OPTION_DNSSL_LIST = 'dnssl_list'
OPTION_INTERFACE = 'interface'
OPTION_MAX_ADV_INTERVAL = 'max_adv_interval'
OPTION_MIN_ADV_INTERVAL = 'min_adv_interval'
OPTION_PREFIX = 'prefix'
OPTION_RDNSS_SERVERS = 'rdnss_servers'
OPTION_SEND_ADVERT = 'adv_send_advert'

class RadvdServer(object):
    """
    This is an embodiment of the radvd server process.  It converts an
    option dict into parameters for the radvd configuration file and
    manages startup and cleanup of the process.
    """

    def __init__(self, interface = None):
        if not os.path.exists(RADVD_EXECUTABLE):
            raise error.TestNAError('Could not find executable %s; '
                                    'this is likely an old version of '
                                    'ChromiumOS' %
                                    RADVD_EXECUTABLE)
        self._options = {
            OPTION_INTERFACE: interface,
            OPTION_ADV_ON_LINK: RADVD_DEFAULT_ADV_ON_LINK,
            OPTION_ADV_AUTONOMOUS: RADVD_DEFAULT_ADV_AUTONOMOUS,
            OPTION_ADV_ROUTER_ADDR: RADVD_DEFAULT_ADV_ROUTER_ADDR,
            OPTION_ADV_RDNSS_LIFETIME: RADVD_DEFAULT_ADV_RDNSS_LIFETIME,
            OPTION_DNSSL_LIST: RADVD_DEFAULT_DNSSL_LIST,
            OPTION_MAX_ADV_INTERVAL: RADVD_DEFAULT_MAX_ADV_INTERVAL,
            OPTION_MIN_ADV_INTERVAL: RADVD_DEFAULT_MIN_ADV_INTERVAL,
            OPTION_PREFIX: RADVD_DEFAULT_PREFIX,
            OPTION_RDNSS_SERVERS: RADVD_DEFAULT_RDNSS_SERVERS,
            OPTION_SEND_ADVERT: RADVD_DEFAULT_SEND_ADVERT
        }

    @property
    def options(self):
        """
        Property dict used to generate configuration file.
        """
        return self._options

    def _write_config_file(self):
        """
        Write out a configuration file for radvd to use.
        """
        config = '\n'.join([
                     'interface %(interface)s {',
                     '  AdvSendAdvert %(adv_send_advert)s;',
                     '  MinRtrAdvInterval %(min_adv_interval)d;',
                     '  MaxRtrAdvInterval %(max_adv_interval)d;',
                     '  prefix %(prefix)s {',
                     '    AdvOnLink %(adv_on_link)s;',
                     '    AdvAutonomous %(adv_autonomous)s;',
                     '    AdvRouterAddr %(adv_router_addr)s;',
                     '  };',
                     '  RDNSS %(rdnss_servers)s {',
                     '    AdvRDNSSLifetime %(adv_rdnss_lifetime)s;',
                     '  };',
                     '  DNSSL %(dnssl_list)s {',
                     '  };',
                     '};',
                     '']) % self.options
        with open(RADVD_CONFIG_FILE, 'w') as f:
            f.write(config)

    def _cleanup(self):
        """
        Cleanup temporary files.  If PID file exists, also kill the
        associated process.
        """
        if os.path.exists(RADVD_PID_FILE):
            pid = int(file(RADVD_PID_FILE).read())
            os.remove(RADVD_PID_FILE)
            try:
                os.kill(pid, signal.SIGTERM)
            except OSError:
                pass
        if os.path.exists(RADVD_CONFIG_FILE):
            os.remove(RADVD_CONFIG_FILE)

    def start_server(self):
        """
        Start the radvd server.  The server will daemonize itself and
        run in the background.
        """
        self._cleanup()
        self._write_config_file()
        utils.system('%s -p %s -C %s' %
                     (RADVD_EXECUTABLE, RADVD_PID_FILE, RADVD_CONFIG_FILE))

    def stop_server(self):
        """
        Halt the radvd server.
        """
        self._cleanup()