#! /usr/bin/python
#
# Copyright 2011-2014 Red Hat, Inc.
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
#
# Refer to the README and COPYING files for full details of the license
#
import logging
import logging.config

from vdsm.config import config
from vdsm import netinfo

# Ifcfg persistence restoration
from network.configurators import ifcfg

# Unified persistence restoration
from network.api import setupNetworks
from network import configurators
from vdsm.netconfpersistence import RunningConfig, PersistentConfig
import pkgutil


def ifcfg_restoration():
    configWriter = ifcfg.ConfigWriter()
    configWriter.restorePersistentBackup()


def unified_restoration():
    """
    Builds a setupNetworks command from the persistent configuration to set it
    as running configuration.
    """
    runningConfig = RunningConfig()
    removeNetworks = {}
    removeBonds = {}
    for network in runningConfig.networks:
        removeNetworks[network] = {'remove': True}
    for bond in runningConfig.bonds:
        removeBonds[bond] = {'remove': True}
    logging.debug('Removing all networks (%s) and bonds (%s) in running '
                  'config.', removeNetworks, removeBonds)
    setupNetworks(removeNetworks, removeBonds, connectivityCheck=False,
                  _inRollback=True)

    # Flush vdsm configurations left-overs from any configurator on the system
    # so that changes of configurator and persistence system are smooth.
    for configurator_cls in _get_all_configurators():
        configurator_cls().flush()

    persistentConfig = PersistentConfig()
    nets, bonds = _filter_nets_bonds(persistentConfig.networks,
                                     persistentConfig.bonds)
    logging.debug('Calling setupNetworks with networks (%s) and bond (%s).',
                  nets, bonds)
    setupNetworks(nets, bonds, connectivityCheck=False, _inRollback=True)


def _filter_nets_bonds(nets, bonds):
    """Returns only nets and bonds that can be configured with the devices
    present in the system"""
    available_nets, available_bonds = {}, {}
    available_nics = netinfo.nics()
    for bond, attrs in bonds.iteritems():
        available_bond_nics = [nic for nic in attrs['nics'] if
                               nic in available_nics]
        if available_bond_nics:
            available_bonds[bond] = attrs.copy()
            available_bonds[bond]['nics'] = available_bond_nics

    for net, attrs in nets.iteritems():
        bond = attrs.get('bonding')
        if bond is not None:
            if bond not in available_bonds:
                logging.error('Some of the nics required by bond "%s" (%s) '
                              'are missing. Network "%s" will not be '
                              'configured as a consequence', bond,
                              bonds[bond]['nics'], net)
            else:
                available_nets[net] = attrs
            continue  # Regardless of availability, the net is processed

        nic = attrs.get('nic')
        if nic is not None:
            if nic not in available_nics:
                logging.error('Nic "%s" required by network %s is missing. '
                              'The network will not be configured', nic, net)
            else:
                available_nets[net] = attrs
            continue  # Regardless of availability, the net is processed

        # Bridge-only nics
        available_nets[net] = attrs
    return available_nets, available_bonds


def _get_all_configurators():
    """Returns the class objects of all the configurators in the netconf pkg"""
    prefix = configurators.__name__ + '.'
    for importer, moduleName, isPackage in pkgutil.iter_modules(
            configurators.__path__, prefix):
        __import__(moduleName, fromlist="_")

    for cls in configurators.Configurator.__subclasses__():
        yield cls


def restore():
    if config.get('vars', 'net_persistence') == 'unified':
        unified_restoration()
    else:
        ifcfg_restoration()


if __name__ == '__main__':
    try:
        logging.config.fileConfig('/etc/vdsm/svdsm.logger.conf',
                                  disable_existing_loggers=False)
    except:
        logging.basicConfig(filename='/dev/stdout', filemode='w+',
                            level=logging.DEBUG)
        logging.error('Could not init proper logging', exc_info=True)

    restore()
