from copy import copy, deepcopy
import os
import pwd

from charmhelpers.core.hookenv import (
    config,
    log,
    related_units,
    relation_ids,
    relation_get,
    ERROR,
)

BASE_PACKAGES = [
    'nova-compute',
    'genisoimage',  # was missing as a package dependency until raring.
]

BASE_RESOURCE_MAP = {
    '/etc/libvirt/qemu.conf': {
        'services': ['libvirt-bin'],
        'contexts': [],
    },
    '/etc/default/libvirt-bin': {
        'services': ['libvirt-bin'],
        'contexts': [],
    },
    '/etc/nova/nova.conf': {
        'services': ['nova-compute'],
        'contexts': [],
    },
    '/etc/nova/nova-compute.conf': {
        'services': ['nova-compute'],
        'contexts': [],
    },
}


QUANTUM_PLUGINS = {
    'ovs': {
        'config': '/etc/quantum/plugins/openvswitch/ovs_quantum_plugin.ini',
        'services': ['quantum-plugin-openvswitch-agent'],
        'packages': ['quantum-plugin-openvswitch-agent',
                     'openvswitch-datapath-dkms'],
    },
    'nvp': {
        'config': '/etc/quantum/plugins/nicira/nvp.ini',
        'services': [],
        'packages': ['quantum-plugin-nicira'],
    }
}

# Maps virt-type config to a compute package(s).
VIRT_TYPES = {
    'kvm': ['nova-compute-kvm'],
    'qemu': ['nova-compute-qemu'],
    'xen': ['nova-compute-xen'],
    'uml': ['nova-compute-uml'],
    'lxc': ['nova-compute-lxc'],
}

# This is just a label and it must be consistent across
# nova-compute nodes to support live migration.
CEPH_SECRET_UUID = '514c9fca-8cbe-11e2-9c52-3bc8c7819472'


def resource_map():
    '''
    Dynamically generate a map of resources that will be managed for a single
    hook execution.
    '''
    # TODO: Cache this on first call?
    resource_map = deepcopy(BASE_RESOURCE_MAP)
    net_manager = network_manager()

    if (net_manager in ['FlatManager', 'FlatDHCPManager'] and
            config('multi-host').lower() == 'yes'):
        resource_map['/etc/nova/nova.conf']['services'].extend(
            ['nova-api', 'nova-network']
        )
    elif net_manager == 'Quantum':
        plugin = quantum_plugin()
        if plugin:
            conf = quantum_attribute(plugin, 'config')
            svcs = quantum_attribute(plugin, 'services')
            ctxts = quantum_attribute(plugin, 'contexts') or []
            resource_map[conf] = {}
            resource_map[conf]['services'] = svcs
            resource_map[conf]['contexts'] = ctxts
            resource_map['/etc/quantum/quantum.conf'] = {
                'services': svcs,
                'contexts': ctxts
            }
    return resource_map

def restart_map():
    '''
    Constructs a restart map based on charm config settings and relation
    state.
    '''
    return {k: v['services'] for k, v in resource_map().iteritems()}

def register_configs():
    '''
    Registers config files with their correpsonding context generators.
    '''
    pass


def determine_packages():
    packages = [] + BASE_PACKAGES

    net_manager = network_manager()
    if (net_manager in ['FlatManager', 'FlatDHCPManager'] and
            config('multi-host').lower() == 'yes'):
        packages.extend(['nova-api', 'nova-network'])
    elif net_manager == 'Quantum':
        plugin = quantum_plugin()
        packages.extend(quantum_attribute(plugin, 'packages'))

    if relation_ids('ceph'):
        packages.append('ceph-common')

    virt_type = config('virt-type')
    try:
        packages.extend(VIRT_TYPES[virt_type])
    except KeyError:
        log('Unsupported virt-type configured: %s' % virt_type)

        raise
    return packages


def migration_enabled():
    return config('enable-live-migration').lower() == 'true'


def quantum_enabled():
    return config('network-manager').lower() == 'quantum'


def _network_config():
    '''
    Obtain all relevant network configuration settings from nova-c-c via
    cloud-compute interface.
    '''
    settings = ['network_manager', 'quantum_plugin']
    net_config = {}
    for rid in relation_ids('cloud-compute'):
        for unit in related_units(rid):
            for setting in settings:
                value = relation_get(setting, rid=rid, unit=unit)
                if value:
                    net_config[setting] = value
    return net_config


def quantum_plugin():
    return _network_config().get('quantum_plugin')


def network_manager():
    return _network_config().get('network_manager')


def quantum_attribute(plugin, attr):
    try:
        _plugin = QUANTUM_PLUGINS[plugin]
    except KeyError:
        log('Unrecognised plugin for quantum: %s' % plugin, level=ERROR)
        raise
    try:
        return _plugin[attr]
    except KeyError:
        return None

def public_ssh_key(user='root'):
    home = pwd.getpwnam(user).pw_dir
    try:
        with open(os.path.join(home, '.ssh', 'id_rsa')) as key:
            return key.read().strip()
    except:
        return None


def initialize_ssh_keys():
    pass


def import_authorized_keys():
    pass


def configure_live_migration(configs=None):
    """
    Ensure libvirt live migration is properly configured or disabled,
    depending on current config setting.
    """
    configs = configs or register_configs()
    configs.write('/etc/libvirt/libvirtd.conf')
    configs.write('/etc/default/libvirt-bin')
    configs.write('/etc/nova/nova.conf')

    if not migration_enabled():
        return

    if config('migration-auth-type') == 'ssh':
        initialize_ssh_keys()


def do_openstack_upgrade():
    pass


def import_keystone_ca_cert():
    pass


def configure_network_service():
    pass


def configure_volume_service():
    pass