from interface.vm.instance import InstanceInterface
from interface.vm.resources import Instance
from openstack.exceptions import SDKException
from novaclient import client
import logging
from keystoneauth1.identity import v3
from keystoneauth1 import session

auth = v3.Password(auth_url="http://10.34.0.113/identity/v3",
                   username="admin",
                   password="64c7ee341d03844c548c",
                   user_domain_id='default',
                   project_id="2db5309f541c4466bc80bc534cf579d7")

sess = session.Session(auth=auth)


def openstackError(func):
    def wrap_OpenStackError(*args, **kw):
        """ Decorator to wrap openstack error in simple Exception.
        Return decorated function

        """
        try:
            return func(*args, **kw)
        except SDKException as e:
                logging.error(e.get_error_message())
                new_e = Exception(e.get_error_message())
                new_e.OpenStackError = True
                raise new_e
    return wrap_OpenStackError


class OSVirtualMachineManager(InstanceInterface):

    def __init__(self, cloud):
        super().__init__()
        self.openstack = cloud

    @openstackError
    def create_base_vm(self, name, resource, networks, block_dev_map):
        flavor = self.get_flavor(resource)
        new_server = self.compute.create_server(name=name,
                                                flavorRef=flavor.id,
                                                networks=networks,
                                                block_device_mapping=block_dev_map
                                                )
        return new_server

    @openstackError
    def create_vm_from_template(self, name, image, resource, networks):
        self.create_multiple_vm_from_template(name, image, resource, networks, 1)

    @openstackError
    def create_multiple_vm_from_template(self, name, image, resource, networks,
                                         number, **args):
        compute = self.openstack.compute
        flav = compute.find_flavor(resource)

        image = compute.find_image(image)
        if not image:
            raise ValueError("The template not found")

        new_server = compute.create_server(name=name,
                                           flavorRef=flav.id,
                                           imageRef=image.id,
                                           networks=networks,
                                           min_count=number,
                                           )

        new_server = self.openstack.compute.wait_for_server(new_server)

        return new_server

    @openstackError
    def get_vm(self, name_or_id=None):
        if not name_or_id:
            raise ValueError("Name or id doesn't given")
        server_instance = self.openstack.get_server(name_or_id)
        if not server_instance:
            raise ValueError("Could not get the vm")
        return server_instance

    @openstackError
    def start_vm(self, name_or_id=None):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.start_server(instance)

    @openstackError
    def stop_vm(self, name_or_id=None):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.stop_server(instance)

    @openstackError
    def suspend_vm(self, name_or_id=None):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.suspend_server(instance)

    @openstackError
    def wake_up_vm(self, name_or_id=None):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.resume_server(instance)

    @openstackError
    def reboot_vm(self, name_or_id):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.reboot_server(instance, reboot_type='SOFT')

    @openstackError
    def reset_vm(self, name_or_id):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.reboot_server(instance, reboot_type='HARD')

    @openstackError
    def destroy_vm(self, name_or_id):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.delete_server(instance)

    @openstackError
    def get_status(self, name_or_id):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        return instance.status

    @openstackError
    def list_all_vm(self):
        return self.openstack.compute.servers()

    @openstackError
    def resize_vm(self, name_or_id, resource):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        flavor = self.openstack.compute.find_flavor(resource['name'])

        self.openstack.compute.resize_server(instance, flavor)

    @openstackError
    def create_template(self, name_or_id, template_name, metadata=None):
        if name_or_id:
            instance = self.get_vm(name_or_id)
        self.openstack.compute.create_server_image(instance, template_name, metadata)

    def get_vnc_console(self, name_or_id):
        with client.Client("2", session=sess) as nova:
            if name_or_id:
                instance = nova.servers.get(name_or_id)
                return instance.get_vnc_console("novnc")

    def attach_volume(self, name_or_id, amount):
        raise NotImplementedError

    def convert_server_to_instance(self, server):
        if not server.image:
            image_id = None
        else:
            image_id = server.image.id
        return Instance(id=server.id,
                        resource=server.flavor.id,
                        name=server.name,
                        image_id=image_id,
                        disks=server.volumes,
                        status=server.status,
                        launched_at=server.launched_at,
                        terminated_at=server.terminated_at,
                        addresses=server.addresses)