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

from charmhelpers.core.host import apt_install, filter_installed_packages
from charmhelpers.contrib.openstack import context, neutron, utils

from charmhelpers.contrib.hahelpers.cluster import (
    determine_api_port, determine_haproxy_port)


class ApacheSSLContext(context.ApacheSSLContext):

    interfaces = ['https']
    external_ports = []
    service_namespace = 'nova'

    def __call__(self):
        # late import to work around circular dependency
        from nova_cc_utils import determine_ports
        self.external_ports = determine_ports()
        return super(ApacheSSLContext, self).__call__()


class VolumeServiceContext(context.OSContextGenerator):
    interfaces = []

    def __call__(self):
        ctxt = {}

        if relation_ids('nova-volume-service'):
            if utils.os_release('nova-common') not in ['essex', 'folsom']:
                e = ('Attempting to relate a nova-volume service to an '
                     'Nova version (%s).  Use cinder.')
                log(e, level=ERROR)

                raise context.OSContextError(e)
            install_pkg = filter_installed_packages(['nova-api-os-volume'])
            if install_pkg:
                apt_install(install_pkg)
            ctxt['volume_service'] = 'nova-volume'
        elif relation_ids('cinder-volume-service'):
            ctxt['volume_service'] = 'cinder'
            # kick all compute nodes to know they should use cinder now.
            [relation_set(relation_id=rid, volume_service='cinder')
             for rid in relation_ids('cloud-compute')]
        return ctxt


class HAProxyContext(context.HAProxyContext):
    interfaces = ['ceph']

    def __call__(self):
        '''
        Extends the main charmhelpers HAProxyContext with a port mapping
        specific to this charm.
        Also used to extend nova.conf context with correct api_listening_ports
        '''
        from nova_cc_utils import api_port
        ctxt = super(HAProxyContext, self).__call__()

        # determine which port api processes should bind to, depending
        # on existence of haproxy + apache frontends
        compute_api = determine_api_port(api_port('nova-api-os-compute'))
        ec2_api = determine_api_port(api_port('nova-api-ec2'))
        s3_api = determine_api_port(api_port('nova-objectstore'))
        nvol_api = determine_api_port(api_port('nova-api-os-volume'))
        neutron_api = determine_api_port(api_port('neutron-server'))

        # to be set in nova.conf accordingly.
        listen_ports = {
            'osapi_compute_listen_port': compute_api,
            'ec2_listen_port': ec2_api,
            's3_listen_port': s3_api,
        }

        port_mapping = {
            'nova-api-os-compute': [
                determine_haproxy_port(api_port('nova-api-os-compute')),
                compute_api,
            ],
            'nova-api-ec2': [
                determine_haproxy_port(api_port('nova-api-ec2')),
                ec2_api,
            ],
            'nova-objectstore': [
                determine_haproxy_port(api_port('nova-objectstore')),
                s3_api,
            ],
        }

        if relation_ids('nova-volume-service'):
            port_mapping.update({
                'nova-api-ec2': [
                    determine_haproxy_port(api_port('nova-api-ec2')),
                    nvol_api],
            })
            listen_ports['osapi_volume_listen_port'] = nvol_api

        if neutron.network_manager() in ['neutron', 'quantum']:
            port_mapping.update({
                'neutron-server': [
                    determine_haproxy_port(api_port('neutron-server')),
                    neutron_api]
            })
            # quantum/neutron.conf listening port, set separte from nova's.
            ctxt['neutron_bind_port'] = neutron_api

        # for haproxy.conf
        ctxt['service_ports'] = port_mapping
        # for nova.conf
        ctxt['listen_ports'] = listen_ports
        return ctxt


class NeutronCCContext(context.NeutronContext):
    interfaces = []

    @property
    def plugin(self):
        from nova_cc_utils import neutron_plugin
        return neutron_plugin()

    @property
    def network_manager(self):
        return neutron.network_manager()

    @property
    def neutron_security_groups(self):
        sec_groups = (config('neutron-security-groups') or
                      config('quantum-security-groups'))
        return sec_groups.lower() == 'yes'

    def _ensure_packages(self):
        # Only compute nodes need to ensure packages here, to install
        # required agents.
        return


class IdentityServiceContext(context.IdentityServiceContext):
    def __call__(self):
        ctxt = super(IdentityServiceContext, self).__call__()
        if not ctxt:
            return

        # the ec2 api needs to know the location of the keystone ec2
        # tokens endpoint, set in nova.conf
        ec2_tokens = 'http://%s:%s/v2.0/ec2tokens' % (ctxt['service_host'],
                                                      ctxt['service_port'])
        ctxt['keystone_ec2_url'] = ec2_tokens
        return ctxt