from insalata.builder.xenBuild.xenHelper import getXenConnection
from insalata.builder.xenBuild.host import findVM
from insalata.builder.decorator import builderFor

def findInterface(interface, logger, xen, session):
    """
    Find this interface's reference in Xen (based on its MAC address). 

    :param interface: Interface to find a Xen reference for.
    :type interface: Interface
    
    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param xen: A xmlrpc reference to xen 
    :type xen: ServerProxy

    :param session: A session for the xen XML-RPC reference. 
    :type session: str

    :returns: A reference to the interface in Xen if it exists, otherwise None.
    :rtype: str
    """
    vm = findVM(interface.getHost(), logger, xen, session)

    if vm:
        request = xen.VM.get_VIFs(session, vm)
        if "Value" in request and len(request['Value']) > 0:
            ifacesWithMac = [i for i in request['Value'] if xen.VIF.get_MAC(session, i)['Value'].lower() == str(interface.getMAC()).lower()]

            if not ifacesWithMac == []:
                return ifacesWithMac[0]
        
        logger.debug("Interface " + interface.getID() + " of VM " + interface.getHost().getID() + " not found by its MAC address " + interface.getMAC())
        return None


@builderFor(action="createInterface", hypervisor="xen")
def create(logger, interface):
    """
    Create this interface in Xen. 

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """
    con = getXenConnection(interface.getHost().getLocation().getID(), logger)
    if not con:
        return
    xen, session, _ = con

    #get a reference to the network
    result = xen.network.get_by_name_label(session, interface.getNetwork().getID())
    if result['Status'] == 'Failure':
        logger.error("Error while fetching the network named '{0}' for interface creation. Error: {1}. Skipping creation.".format(interface.getNetwork().getID(), str(result['ErrorDescription'])))
        return

    networkUid = result['Value'][0]

    vm = findVM(interface.getHost(), logger, xen, session)

    if vm:
        if not findInterface(interface, logger, xen, session):
            #find highest device order
            order = 0
            request1 = xen.VM.get_VIFs(session, vm)
            if "Value" in request1:
                for i in request1['Value']:
                    request2 = xen.VIF.get_device(session, i)
                    if "Value" in request2 and int(request2['Value']) >= order:
                        order = int(request2['Value']) + 1

            #build vif record
            vifRecord = {
                'uuid': '',
                'allowed_operations': [],
                'current_operations': [],
                'device': str(order),
                'MAC': '' if interface.getMAC() is None else interface.getMAC(),
                'MTU': '1500' if interface.getMtu() is None else str(interface.getMtu()),
                "qos_algorithm_type": "ratelimit",
                "qos_algorithm_params": {},
                "other_config": {},
                "MAC_autogenerated": "True" if interface.getMAC() is None else "False",
                "currently_attached": False,
                "network": networkUid,
                "VM": vm
            }

            #set a limit if specified
            if interface.getRate():
                vifRecord["qos_algorithm_params"]["kbps"] = str(interface.getRate())

            #create the vif using the record above
            logger.info("Create interface " + str(interface.getID()) + " of VM " + interface.getHost().getID())
            result = xen.VIF.create(session, vifRecord)
            if result['Status'] == 'Failure':
                logger.error("Error creating interface " + str(interface.getID()) + " of VM " + interface.getHost().getID() + ". Error: " + str(result['ErrorDescription']))
            else:
                #get MAC if it was autogenerated
                if not interface.getMAC():
                    result2 = xen.get_mac(session, result['Value'])
                    if result2['Status'] == 'Success':
                        interface.setMAC(result2['Value'])
        else:
            logger.warning("An interface named '{0}' exists already. Skipping creation.".format(interface.getID()))
    else:
        logger.error("No interface created as the matching host was not found.")


@builderFor(action="removeInterface", hypervisor="xen")
def destroy(logger, interface):
    """
    Destorys the Xen VIF corresponding to the given interface.

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """
    con = getXenConnection(interface.getHost().getLocation().getID(), logger)
    if not con:
        return
    xen, session, _ = con

    reference = findInterface(interface, logger, xen, session)
    if reference:
        result = xen.VIF.destroy(session, reference)
        if result['Status'] == 'Failure':
            logger.error("Error deleting VIF #" + str(interface.getID()) + " of VM " + interface.getHost().getID() + " failed. Error: " + str(result['ErrorDescription']))
    else:
        logger.error("No interface found to delete")


@builderFor(action="configureNetwork", hypervisor="xen")
def configureNetwork(logger, interface):
    """
    Configure this interface to use its specified network.
    This destorys the Xen VIF and recreates it as networks cannot be changed otherwise.   

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """

    destroy(logger, interface)
    create(logger, interface)


@builderFor(action="configureMac", hypervisor="xen")
def configureMac(logger, interface):
    """
    Configure this interface to use its specified MAC address.  

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """
    con = getXenConnection(interface.getHost().getLocation().getID(), logger)
    if not con:
        return
    xen, session, _ = con

    reference = findInterface(interface, logger, xen, session)
    if reference:
        result = xen.VIF.set_MAC(session, reference, interface.getMAC())
        if result['Status'] == 'Failure':
            logger.error("Error configuring MAC of interface #" + str(interface.getID()) + " of VM " + interface.getHost().getID() + ". Error: " + str(result['ErrorDescription']))
    else:
        logger.error("No interface found for configureMac")


@builderFor(action="configureRate", hypervisor="xen")
def configureRate(logger, interface):
    """
    Configure the rate limit of this interface.

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """
    con = getXenConnection(interface.getHost().getLocation().getID(), logger)
    if not con:
        return
    xen, session, _ = con

    reference = findInterface(interface, logger, xen, session)
    if reference:
        result = xen.VIF.set_qos_algorithm_params(session, reference, { "kbps": str(interface.getRate()) })
        if result['Status'] == 'Failure':
            logger.error("Error configuring rate limit of interface #" + str(interface.getID()) + " of VM " + interface.getHost().getID() + ". Error: " + str(result['ErrorDescription']))
    else:
        logger.error("No interface found to configure the rate limit for.")


@builderFor(action="configureMtu", hypervisor="xen")
def configureMtu(logger, interface):
    """
    Configure the maximum transmission unit (MTU) of this interface.

    :param logger: A logger used for logging possible errors.
    :type logger: seealso:: :class:`logging:Logger`

    :param interface: Interface-instance to reconfigure.
    :type interface: Interface
    """
    con = getXenConnection(interface.getHost().getLocation().getID(), logger)
    if not con:
        return
    xen, session, _ = con

    reference = findInterface(interface, logger, xen, session)
    if reference:
        result = xen.VIF.set_MTU(session, reference, str(interface.getMtu()))
        if result['Status'] == 'Failure':
            logger.error("Error configuring MTU of interface #" + str(interface.getID()) + " of VM " + interface.getHost().getID() + ". Error: " + str(result['ErrorDescription']))
    else:
        logger.error("No interface found to configure MTU for.")
