#!/usr/bin/python
import os
import sys
import hooking
import traceback
import fcntl
import ast
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_start
vmfex hook:
Add Cisco VM-FEX Port Profile to Virtual Machine
since the only unique parameter passed from the engine to the VM is the
MAC, a dictionary-like mapping of MAC to port profile will be used
Sample:
vmfex={'00:11:22:33:44:55:66':'Profile1',
'00:11:22:33:44:55:67':'Profile2',...} (one line)
Will add 2 virtual nics attached to profile1 and profile2 using
the vnic MAC addresses specified, replacing the actual NICs assigned
to the VM.
A VM NIC with a MAC that is not mentioned in this dictionary will not be
altered, and will remain attached to the bridge/logical network defined
for it in the engine.
Libvirt internals:
Replace the existing interface xml:
with the following interface xml:
Dynamic network with libvirt (define a NIC pool, so libvirt can assign
VMs to NICs dynamically):
direct-pool
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 = '''direct-pool
'''
for i in getUsableNics():
xmlstr += ' \n'
xmlstr += ' \n '
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:
sys.stderr.write('vmfex: starting to edit VM \n')
# connect to libvirtd and handle the direct-pool network
conn = libvirtconnection.get()
handleDirectPool(conn)
# Get the vmfex line
vmfex = os.environ['vmfex']
sys.stderr.write('vmfex: customProperty: ' + str(vmfex) + '\n')
# convert to dictionary
vmfexd = ast.literal_eval(vmfex)
# make sure the keys are lowercase
vmfexd = dict((k.lower(), v) for k, v in vmfexd.iteritems())
# Get the VM's xml definition
domxml = hooking.read_domxml()
for iface in domxml.getElementsByTagName('interface'):
mac = iface.getElementsByTagName('mac')[0]
macaddr = mac.getAttribute('address').lower()
if macaddr in vmfexd:
profile = vmfexd[macaddr]
iface.setAttribute('type', 'network')
source = iface.getElementsByTagName('source')[0]
source.removeAttribute('bridge')
source.setAttribute('network', 'direct-pool')
virtualport = domxml.createElement('virtualport')
virtualport.setAttribute('type', '802.1Qbh')
iface.appendChild(virtualport)
parameters = domxml.createElement('parameters')
parameters.setAttribute('profileid', profile)
virtualport.appendChild(parameters)
hooking.write_domxml(domxml)
except:
sys.stderr.write('vmfex: ERROR %s\n' % traceback.format_exc())
sys.exit(2)