#!/usr/bin/python

import os
import sys
import traceback
import fcntl
from xml.dom import minidom
try:
    # 3.0 compat
    import libvirtconnection
    libvirtconnection
except ImportError:
    # 3.1 compat
    from vdsm import libvirtconnection

'''
Placed in before_vm_migrate_destination

vmfex hook on migration destination:

Set up a dynamic NIC pool for incoming migrations to use

      <network>
        <name>direct-pool</name>
        <forward mode="passthrough">
          <interface dev="eth3"/>
          <interface dev="eth4"/>
          <interface dev="eth5"/>
          <interface dev="eth6"/>
          <interface dev="eth7"/>
          <interface dev="eth8"/>
          <interface dev="eth9"/>
          <interface dev="eth10"/>
          <interface dev="eth11"/>
        </forward>
      </network>

Using libvirt, the network is defined like this:

   virsh net-define /tmp/direct-pool.xml
   virsh net-start direct-pool
   virsh net-autostart direct-pool

(where /tmp/direct-pool.xml contains the xml above)

(everything else is autogenerated, and shouldn't be specified
when defining a guest (but whatever is there after definition
should be left in place, e.g. the PCI address)). Note that these
interface definitions are completely static - you never need to modify
them due to migration, or starting up/shutting down the guest.

'''


def getUsableNics():
    # Scan localhost for physical NICs and return list of physical nics
    # that have all zeroes MAC. These NICs are the ones that can be used
    # with VMFEX.
    # Example ['eth0','eth1']
    nics = []
    for root, dirs, names in os.walk('/sys/devices/'):
        if 'address' in names and 'pci' in root:
            with open(root + '/address', 'r') as f:
                mac = f.readlines()[0].strip()
            if mac == '00:00:00:00:00:00':
                eth = root.split('/')[-1]
                nics.append(eth)
    return nics


def createDirectPool(conn):
    if 'direct-pool' in conn.listNetworks():
        dpool = conn.networkLookupByName('direct-pool')
        # destroy and undefine direct-pool
        dpool.destroy()
        dpool.undefine()
        sys.stderr.write('vmfex: removed direct-pool \n')

    # create a new direct-pool
    xmlstr = '''<network>
        <name>direct-pool</name>
        <forward mode="passthrough">
    '''
    for i in getUsableNics():
        xmlstr += '<interface dev="' + i + '"/> \n'
    xmlstr += ' </forward> \n </network> '
    conn.networkDefineXML(xmlstr)
    dpool = conn.networkLookupByName('direct-pool')
    dpool.setAutostart(1)
    dpool.create()
    sys.stderr.write('vmfex: created Direct-Pool Net \n')
    sys.stderr.write(xmlstr + '\n')


def qbhInUse(conn):
    for vm in conn.listDomainsID():
        domxml = minidom.parseString(conn.lookupByID(vm).XMLDesc(0))
        for vport in domxml.getElementsByTagName('virtualport'):
            if vport.getAttribute('type') == '802.1Qbh':
                return True
    return False


def validateDPool(conn):
    # Compare direct-pool to the list of available NICs
    dpool = conn.networkLookupByName('direct-pool')
    definedNics = []
    dpoolxml = minidom.parseString(dpool.XMLDesc(0))
    for iface in dpoolxml.getElementsByTagName('interface'):
        definedNics.append(iface.getAttribute('dev'))
    if set(definedNics) == set(getUsableNics()):
        return True
    else:
        return False


def handleDirectPool(conn):
    with open('/var/run/vdsm/hook-vmfex.lock', 'w') as f:
        fcntl.flock(f.fileno(), fcntl.LOCK_EX)
        try:
            if 'direct-pool' not in conn.listNetworks():
                createDirectPool(conn)

            elif not qbhInUse(conn) and not validateDPool(conn):
                createDirectPool(conn)
        finally:
            fcntl.flock(f.fileno(), fcntl.LOCK_UN)


if 'vmfex' in os.environ:
    try:
        # connect to libvirtd
        conn = libvirtconnection.get()
        handleDirectPool(conn)

    except:
        sys.stderr.write('vmfex: ERROR: %s\n' % traceback.format_exc())
        sys.exit(2)
