XenAPI-Kolla: Gather domU's vifs in facts

This commit is to gather domU's vifs in facts. So that the compute
VM's interfaces' vif information will be included in the xenapi_facts.
When deploy OpenStack on XenServers, the vif information can be used
by deployment scripts (e.g. in Kolla-ansible, it can use the vif's
bridge to support provider networks by setting the bridge mappings).

Change-Id: I9a6bebe19ed488bb2173d5dc2daa14e236411243
This commit is contained in:
Jianghua Wang 2018-01-19 08:57:52 +00:00
parent 638faee5fe
commit b1bf53ccd6
4 changed files with 161 additions and 2 deletions

View File

@ -56,3 +56,65 @@ class CommonUtilFuncTestCase(base.TestCase):
self.assertEqual(ipv4s, expect)
mock_client.ssh.assert_called()
def test_get_vm_vifs(self):
mock_client = mock.Mock()
vm_uuid = '9eeeea9f-de18-f101-fcc2-ae7366b540f2'
vif_list_data = u'0\r\n\n1\r\n'
vif_0_data = u'vif-id = "0"\r\n\n'
vif_0_data += u'mac = "9a:77:18:20:cf:14"\r\n\n'
vif_0_data += u'bridge = "xapi1"\r\n'
vif_1_data = u'vif-id = "1"\r\n\n'
vif_1_data += u'mac = "02:e3:69:a6:7b:b8"\r\n\n'
vif_1_data += u'bridge = "xapi0"\r\n'
mock_client.ssh.side_effect = [
(0, vif_list_data, ''), # xenstore-list
(0, vif_0_data, ''), # xenstore-ls - vif 0
(0, vif_1_data, ''), # xenstore-ls - vif 1
]
expect = [{u'bridge': u'xapi1',
u'mac': u'9a:77:18:20:cf:14',
u'vif-id': u'0'},
{u'bridge': u'xapi0',
u'mac': u'02:e3:69:a6:7b:b8',
u'vif-id': u'1'}]
vifs = common_function.get_vm_vifs(mock_client, vm_uuid)
self.assertEqual(vifs, expect)
@mock.patch.object(common_function.netifaces, 'ifaddresses')
@mock.patch.object(common_function.netifaces, 'interfaces')
@mock.patch.object(common_function, 'execute')
@mock.patch.object(common_function, 'get_vm_vifs')
def test_get_domu_vifs_by_eth(self, mock_get, mock_exec,
mock_if, mock_ifaddr):
mock_client = mock.Mock()
vm_uuid = '9eeeea9f-de18-f101-fcc2-ae7366b540f2'
mock_exec.return_value = '/vm/%s' % vm_uuid
vif_0 = {u'vif-id': u'0',
u'bridge': u'xapi1',
u'mac': u'9a:77:18:20:cf:14'}
vif_1 = {u'vif-id': u'1',
u'bridge': u'xapi0',
u'mac': u'02:e3:69:a6:7b:b8'}
mock_get.return_value = [vif_0, vif_1]
mock_if.return_value = ['eth0', 'eth1']
AF_LINK = common_function.netifaces.AF_LINK
mock_ifaddr.side_effect = [
{AF_LINK: [{u'addr': u'9a:77:18:20:cf:14'}]},
{AF_LINK: [{u'addr': u'02:e3:69:a6:7b:b8'}]}]
vifs_by_eth = common_function.get_domu_vifs_by_eth(mock_client)
expect = {'eth0': vif_0,
'eth1': vif_1}
self.assertEqual(vifs_by_eth, expect)
mock_exec.assert_called_with('xenstore-read', 'vm')
mock_get.assert_called_with(mock_client, vm_uuid)
mock_if.assert_called_with()
self.assertEqual(mock_ifaddr.call_args_list,
[mock.call('eth0'), mock.call('eth1')])

View File

@ -20,11 +20,13 @@ from os_xenapi.utils import xenapi_facts
class XenapiFactsTestCase(base.TestCase):
@mock.patch.object(common_function, 'get_domu_vifs_by_eth')
@mock.patch.object(common_function, 'get_host_ipv4s')
@mock.patch.object(common_function, 'get_remote_hostname')
@mock.patch.object(himn, 'get_local_himn_eth')
@mock.patch.object(netifaces, 'ifaddresses')
def test_get_facts(self, mock_ifaddr, mock_eth, mock_hostname, mock_ip):
def test_get_facts(self, mock_ifaddr, mock_eth, mock_hostname, mock_ip,
mock_vifs):
mock_client = mock.Mock()
mock_client.ip = mock.sentinel.dom0_himn_ip
mock_eth.return_value = 'eth3'
@ -48,12 +50,46 @@ class XenapiFactsTestCase(base.TestCase):
}
]
mock_ip.return_value = fake_ipv4s
fake_vifs_by_eth = {
"eth0": {
"MTU": "1500",
"backend-id": "0",
"backend-kind": "vif",
"bridge": "xapi1",
"bridge-MAC": "fe:ff:ff:ff:ff:ff",
"locking-mode": "unlocked",
"mac": "9a:77:18:20:cf:14",
"network-uuid": "f06300db-6b6c-006c-0ea1-9d1eb1e97350",
"setup-pvs-proxy-rules": "/usr/libexec/xenopsd/rules",
"setup-vif-rules": "/usr/libexec/xenopsd/setup-vif-rules",
"vif-id": "0",
"vif-uuid": "9e1f78d1-956c-cb2d-0327-652d96302f9d",
"xenopsd-backend": "classic"
},
"eth1": {
"MTU": "1500",
"backend-id": "0",
"backend-kind": "vif",
"bridge": "xapi0",
"bridge-MAC": "fe:ff:ff:ff:ff:ff",
"locking-mode": "unlocked",
"mac": "02:e3:69:a6:7b:b8",
"network-uuid": "41968989-1d86-383b-114e-3d1ccae02157",
"setup-pvs-proxy-rules": "/usr/libexec/xenopsd/rules",
"setup-vif-rules": "/usr/libexec/xenopsd/setup-vif-rules",
"vif-id": "1",
"vif-uuid": "80eea22b-a778-afec-0eac-3891cfd5faf9",
"xenopsd-backend": "classic"
}
}
mock_vifs.return_value = fake_vifs_by_eth
ret_facts = xenapi_facts.get_xenapi_facts(mock_client)
expect_facts = {"domu_himn_ip": "169.254.0.2",
"domu_himn_eth": "eth3",
"dom0_hostname": "traya",
"dom0_ipv4s": fake_ipv4s}
"dom0_ipv4s": fake_ipv4s,
"domu_vifs": fake_vifs_by_eth}
self.assertEqual(ret_facts, expect_facts)
mock_eth.assert_called_with(mock.sentinel.dom0_himn_ip)

View File

@ -27,6 +27,9 @@ import sys
from os_xenapi.client import exception
PATTERN_XENSTORE_VIFS_IN_VM = '/xapi/%s/private/vif'
LOG = logging.getLogger('XenAPI_utils')
@ -119,6 +122,61 @@ def get_host_ipv4s(host_client):
return ipv4s
def get_vm_vifs(xenserver_client, vm_uuid):
"""Get a specific VM's vifs
This function can be used to get vif list for a specific VM.
:param xenserver_client: the ssh client connected to XenServer where
the domU belongs to.
:param vm_uuid: the VM's uuid
:returns: list -- list the VM's vif data.
"""
vm_vifs = PATTERN_XENSTORE_VIFS_IN_VM % vm_uuid
_, out, _ = xenserver_client.ssh('xenstore-list %s' % vm_vifs)
vif_ids = [x.strip() for x in out.split('\n') if x.strip()]
vifs = []
for id in vif_ids:
vif_ent = '/'.join([vm_vifs, id])
_, out, _ = xenserver_client.ssh('xenstore-ls %s' % vif_ent)
key_values = [x.strip().split(' = ') for x in out.split('\n')
if ' = ' in x]
vif_dict = {x[0]: x[1].replace('\"', '') for x in key_values}
vifs.append(vif_dict)
return vifs
def get_domu_vifs_by_eth(xenserver_client):
"""Get domU's vifs
This function can be used to get a domU's vifs.
:param xenserver_client: the ssh client connected to XenServer where
the domU belongs to.
:returns: dict -- The domU's vifs with ethernet interfaces as the keys.
"""
# Get domU VM's uuid
out = execute('xenstore-read', 'vm')
vm_uuid = out.split('/')[-1]
vifs = get_vm_vifs(xenserver_client, vm_uuid)
vifs_by_mac = {vif['mac']: vif for vif in vifs}
# Get all ethernet interfaces and mapping them into vifs basing on
# mac address
vifs_by_eth = {}
for eth in netifaces.interfaces():
mac_addrs = [x['addr'] for x in
netifaces.ifaddresses(eth)[netifaces.AF_LINK]]
for mac in vifs_by_mac:
if mac in mac_addrs:
vifs_by_eth[eth] = vifs_by_mac[mac]
break
return vifs_by_eth
def scp_and_execute(dom0_client, script_name):
# copy script to remote host and execute it
TMP_SH_DIR = dom0_client.ssh("mktemp -d /tmp/domu_sh.XXXXXX", output=True)

View File

@ -50,6 +50,9 @@ def get_xenapi_facts(dom0_client):
facts['domu_himn_eth'] = eth
facts['domu_himn_ip'] = ip_addr
# get domU eths' vif data
facts['domu_vifs'] = common_function.get_domu_vifs_by_eth(dom0_client)
return facts