#!/usr/bin/env python

import click
import sys

from solar.core import resource
from solar.core import signals
from solar.core import validation
from solar.core.resource import virtual_resource as vr
from solar import events as evapi
from solar.dblayer.model import ModelMeta

PROFILE = False
#PROFILE = True


if PROFILE:
    import StringIO
    import cProfile
    import pstats

    pr = cProfile.Profile()
    pr.enable()


# TODO
# Resource for repository OR puppet apt-module in run.pp
# add-apt-repository cloud-archive:juno
# To discuss: install stuff in Docker container

# NOTE
# No copy of manifests, pull from upstream (implemented in the librarian resource)
# Official puppet manifests, not fuel-library



@click.group()
def main():
    pass


def prepare_nodes(nodes_count):
    resources = vr.create('nodes', 'templates/nodes_with_transports.yaml', {"count": nodes_count})
    nodes = [x for x in resources if x.name.startswith('node')]
    resources = vr.create('nodes_network', 'templates/nodes_network.yaml', {"count": nodes_count})
    nodes_sdn = [x for x in resources if x.name.startswith('node')]
    r = {}

    for node, node_sdn in zip(nodes, nodes_sdn):
        r[node.name] = node
        r[node_sdn.name] = node_sdn

        # LIBRARIAN
        librarian = vr.create('librarian_{}'.format(node.name), 'resources/librarian', {})[0]
        r[librarian.name] = librarian

        node.connect(librarian, {})

        # NETWORKING
        # TODO(bogdando) node's IPs should be populated as br-mgmt IPs, but now are hardcoded in templates
        signals.connect(node, node_sdn)
        node_sdn.connect_with_events(librarian, {'module': 'modules'}, {})
        evapi.add_dep(librarian.name, node_sdn.name, actions=('run', 'update'))

        signals.connect(node, node_sdn)
        node_sdn.connect_with_events(librarian, {'module': 'modules'}, {})
        evapi.add_dep(librarian.name, node_sdn.name, actions=('run', 'update'))

    return r

def setup_base(node, librarian):
    # MARIADB
    mariadb_service = vr.create('mariadb_service1', 'resources/mariadb_service', {
        'image': 'mariadb',
        'port': 3306
    })[0]

    node.connect(mariadb_service)

    # RABBIT
    rabbitmq_service = vr.create('rabbitmq_service1', 'resources/rabbitmq_service/', {
        'management_port': 15672,
        'port': 5672,
    })[0]
    openstack_vhost = vr.create('openstack_vhost', 'resources/rabbitmq_vhost/', {
        'vhost_name': 'openstack'
    })[0]

    openstack_rabbitmq_user = vr.create('openstack_rabbitmq_user', 'resources/rabbitmq_user/', {
        'user_name': 'openstack',
        'password': 'openstack_password'
    })[0]

    node.connect(rabbitmq_service)
    rabbitmq_service.connect_with_events(librarian, {'module': 'modules'}, {})
    evapi.add_dep(librarian.name, rabbitmq_service.name, actions=('run', 'update'))
    rabbitmq_service.connect(openstack_vhost)
    rabbitmq_service.connect(openstack_rabbitmq_user)
    openstack_vhost.connect(openstack_rabbitmq_user, {
        'vhost_name',
    })
    return {'mariadb_service': mariadb_service,
            'rabbitmq_service1': rabbitmq_service,
            'openstack_vhost': openstack_vhost,
            'openstack_rabbitmq_user': openstack_rabbitmq_user}

def setup_keystone(node, librarian, mariadb_service, openstack_rabbitmq_user):
    keystone_puppet = vr.create('keystone_puppet', 'resources/keystone_puppet', {})[0]

    keystone_puppet.connect_with_events(librarian, {'module': 'modules'}, {})
    evapi.add_dep(librarian.name, keystone_puppet.name, actions=('run', 'update'))

    evapi.add_dep(openstack_rabbitmq_user.name, keystone_puppet.name, actions=('run', 'update'))
    keystone_db = vr.create('keystone_db', 'resources/mariadb_db/', {
        'db_name': 'keystone_db',
        'login_user': 'root'
    })[0]
    keystone_db_user = vr.create('keystone_db_user', 'resources/mariadb_user/', {
        'user_name': 'keystone',
        'user_password': 'keystone',
    })[0]
    keystone_service_endpoint = vr.create('keystone_service_endpoint', 'resources/keystone_service_endpoint', {
        'endpoint_name': 'keystone',
        'adminurl': 'http://{{admin_ip}}:{{admin_port}}/v2.0',
        'internalurl': 'http://{{internal_ip}}:{{internal_port}}/v2.0',
        'publicurl': 'http://{{public_ip}}:{{public_port}}/v2.0',
        'description': 'OpenStack Identity Service',
        'type': 'identity'
    })[0]

    admin_tenant = vr.create('admin_tenant', 'resources/keystone_tenant', {
        'tenant_name': 'admin'
    })[0]
    admin_user = vr.create('admin_user', 'resources/keystone_user', {
        'user_name': 'admin',
        'user_password': 'admin'
    })[0]
    admin_role = vr.create('admin_role', 'resources/keystone_role', {
        'role_name': 'admin'
    })[0]
    services_tenant = vr.create('services_tenant', 'resources/keystone_tenant', {
        'tenant_name': 'services'
    })[0]
    admin_role_services = vr.create('admin_role_services', 'resources/keystone_role', {
        'role_name': 'admin'
    })[0]

    node.connect(keystone_db)
    node.connect(keystone_db_user)
    node.connect(keystone_puppet)
    mariadb_service.connect(keystone_db, {
        'port': 'login_port',
        'root_user': 'login_user',
        'root_password': 'login_password',
        'ip' : 'db_host',
    })
    keystone_db.connect(keystone_db_user, {
        'db_name',
        'login_port',
        'login_user',
        'login_password',
        'db_host'
    })

    node.connect(keystone_service_endpoint)
    keystone_puppet.connect(keystone_service_endpoint, {
        'admin_token': 'admin_token',
        'admin_port': ['admin_port', 'keystone_admin_port'],
        'ip': ['keystone_host', 'admin_ip', 'internal_ip', 'public_ip'],
        'port': ['internal_port', 'public_port'],
    })

    keystone_puppet.connect(admin_tenant)
    keystone_puppet.connect(admin_tenant, {
        'admin_port': 'keystone_port',
        'ip': 'keystone_host'
    })
    admin_tenant.connect(admin_user)
    admin_user.connect(admin_role)
    admin_tenant.connect(admin_role, { 'tenant_name' })

    admin_user.connect(admin_role_services)
    services_tenant.connect(admin_role_services, { 'tenant_name' })

    keystone_puppet.connect(services_tenant)
    keystone_puppet.connect(services_tenant, {
        'admin_port': 'keystone_port',
        'ip': 'keystone_host'
    })

    keystone_db.connect(keystone_puppet, {
        'db_name',
    })
    keystone_db_user.connect(keystone_puppet, {
        'user_name': 'db_user',
        'user_password': 'db_password',
    })
    mariadb_service.connect(keystone_puppet, {
        'ip': 'db_host',
        'port': 'db_port',
    })
    return {'keystone_puppet': keystone_puppet,
            'keystone_db': keystone_db,
            'keystone_db_user': keystone_db_user,
            'keystone_service_endpoint': keystone_service_endpoint,
            'admin_tenant': admin_tenant,
            'admin_user': admin_user,
            'admin_role': admin_role,
            'services_tenant': services_tenant,
            'admin_role_services': admin_role_services,
            }

def setup_openrc(node, keystone_puppet, admin_user):
    # OPENRC
    openrc = vr.create('openrc_file', 'resources/openrc_file', {})[0]

    node.connect(openrc)
    keystone_puppet.connect(openrc, {'ip': 'keystone_host', 'admin_port':'keystone_port'})
    admin_user.connect(openrc, {'user_name': 'user_name','user_password':'password', 'tenant_name': 'tenant'})
    return {'openrc_file' : openrc}

def setup_neutron(node, librarian, rabbitmq_service, openstack_rabbitmq_user, openstack_vhost):
    # NEUTRON
    # Deploy chain neutron -> (plugins) -> neutron_server -> ( agents )
    neutron_puppet = vr.create('neutron_puppet', 'resources/neutron_puppet', {
        'core_plugin': 'neutron.plugins.ml2.plugin.Ml2Plugin'
        })[0]

    node.connect(neutron_puppet)

    neutron_puppet.connect_with_events(librarian, {'module': 'modules'}, {})
    evapi.add_dep(librarian.name, neutron_puppet.name, actions=('run', 'update'))

    rabbitmq_service.connect(neutron_puppet, {
        'ip': 'rabbit_host',
        'port': 'rabbit_port'
    })
    openstack_rabbitmq_user.connect(neutron_puppet, {
        'user_name': 'rabbit_user',
        'password': 'rabbit_password'})
    openstack_vhost.connect(neutron_puppet, {
        'vhost_name': 'rabbit_virtual_host'})
    return {'neutron_puppet': neutron_puppet}

def setup_neutron_api(node, mariadb_service, admin_user, keystone_puppet, services_tenant, neutron_puppet):
    # NEUTRON PLUGIN AND  NEUTRON API (SERVER)
    neutron_plugins_ml2 = vr.create('neutron_plugins_ml2', 'resources/neutron_plugins_ml2_puppet', {})[0]
    node.connect(neutron_plugins_ml2)

    neutron_server_puppet = vr.create('neutron_server_puppet', 'resources/neutron_server_puppet', {
        'sync_db': True,
    })[0]
    evapi.add_dep(neutron_puppet.name, neutron_server_puppet.name, actions=('run',))
    evapi.add_dep(neutron_plugins_ml2.name, neutron_server_puppet.name, actions=('run',))
    evapi.add_dep(neutron_puppet.name, neutron_plugins_ml2.name, actions=('run',))

    neutron_db = vr.create('neutron_db', 'resources/mariadb_db/', {
        'db_name': 'neutron_db', 'login_user': 'root'})[0]
    neutron_db_user = vr.create('neutron_db_user', 'resources/mariadb_user/', {
        'user_name': 'neutron', 'user_password': 'neutron', 'login_user': 'root'})[0]
    neutron_keystone_user = vr.create('neutron_keystone_user', 'resources/keystone_user', {
        'user_name': 'neutron',
        'user_password': 'neutron'
    })[0]
    neutron_keystone_role = vr.create('neutron_keystone_role', 'resources/keystone_role', {
        'role_name': 'admin'
    })[0]
    evapi.add_dep(neutron_keystone_role.name, neutron_server_puppet.name, actions=('run',))
    neutron_keystone_service_endpoint = vr.create('neutron_keystone_service_endpoint', 'resources/keystone_service_endpoint', {
        'endpoint_name': 'neutron',
        'adminurl': 'http://{{admin_ip}}:{{admin_port}}',
        'internalurl': 'http://{{internal_ip}}:{{internal_port}}',
        'publicurl': 'http://{{public_ip}}:{{public_port}}',
        'description': 'OpenStack Network Service',
        'type': 'network'
    })[0]

    node.connect(neutron_db)
    node.connect(neutron_db_user)
    mariadb_service.connect(neutron_db, {
        'port': 'login_port',
        'root_password': 'login_password',
        'root_user': 'login_user',
        'ip' : 'db_host'})
    mariadb_service.connect(neutron_db_user, {'port': 'login_port', 'root_password': 'login_password'})
    neutron_db.connect(neutron_db_user, {'db_name', 'db_host'})
    neutron_db_user.connect(neutron_server_puppet, {
        'user_name':'db_user',
        'db_name':'db_name',
        'user_password':'db_password',
        'db_host' : 'db_host'})
    mariadb_service.connect(neutron_server_puppet, {
        'port': 'db_port',
        'ip' : 'db_host'})
    node.connect(neutron_server_puppet)
    admin_user.connect(neutron_server_puppet, {
        'user_name': 'auth_user',
        'user_password': 'auth_password',