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:
parent
638faee5fe
commit
b1bf53ccd6
@ -56,3 +56,65 @@ class CommonUtilFuncTestCase(base.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(ipv4s, expect)
|
self.assertEqual(ipv4s, expect)
|
||||||
mock_client.ssh.assert_called()
|
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')])
|
||||||
|
@ -20,11 +20,13 @@ from os_xenapi.utils import xenapi_facts
|
|||||||
|
|
||||||
|
|
||||||
class XenapiFactsTestCase(base.TestCase):
|
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_host_ipv4s')
|
||||||
@mock.patch.object(common_function, 'get_remote_hostname')
|
@mock.patch.object(common_function, 'get_remote_hostname')
|
||||||
@mock.patch.object(himn, 'get_local_himn_eth')
|
@mock.patch.object(himn, 'get_local_himn_eth')
|
||||||
@mock.patch.object(netifaces, 'ifaddresses')
|
@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 = mock.Mock()
|
||||||
mock_client.ip = mock.sentinel.dom0_himn_ip
|
mock_client.ip = mock.sentinel.dom0_himn_ip
|
||||||
mock_eth.return_value = 'eth3'
|
mock_eth.return_value = 'eth3'
|
||||||
@ -48,12 +50,46 @@ class XenapiFactsTestCase(base.TestCase):
|
|||||||
}
|
}
|
||||||
]
|
]
|
||||||
mock_ip.return_value = fake_ipv4s
|
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)
|
ret_facts = xenapi_facts.get_xenapi_facts(mock_client)
|
||||||
|
|
||||||
expect_facts = {"domu_himn_ip": "169.254.0.2",
|
expect_facts = {"domu_himn_ip": "169.254.0.2",
|
||||||
"domu_himn_eth": "eth3",
|
"domu_himn_eth": "eth3",
|
||||||
"dom0_hostname": "traya",
|
"dom0_hostname": "traya",
|
||||||
"dom0_ipv4s": fake_ipv4s}
|
"dom0_ipv4s": fake_ipv4s,
|
||||||
|
"domu_vifs": fake_vifs_by_eth}
|
||||||
self.assertEqual(ret_facts, expect_facts)
|
self.assertEqual(ret_facts, expect_facts)
|
||||||
mock_eth.assert_called_with(mock.sentinel.dom0_himn_ip)
|
mock_eth.assert_called_with(mock.sentinel.dom0_himn_ip)
|
||||||
|
@ -27,6 +27,9 @@ import sys
|
|||||||
from os_xenapi.client import exception
|
from os_xenapi.client import exception
|
||||||
|
|
||||||
|
|
||||||
|
PATTERN_XENSTORE_VIFS_IN_VM = '/xapi/%s/private/vif'
|
||||||
|
|
||||||
|
|
||||||
LOG = logging.getLogger('XenAPI_utils')
|
LOG = logging.getLogger('XenAPI_utils')
|
||||||
|
|
||||||
|
|
||||||
@ -119,6 +122,61 @@ def get_host_ipv4s(host_client):
|
|||||||
return ipv4s
|
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):
|
def scp_and_execute(dom0_client, script_name):
|
||||||
# copy script to remote host and execute it
|
# copy script to remote host and execute it
|
||||||
TMP_SH_DIR = dom0_client.ssh("mktemp -d /tmp/domu_sh.XXXXXX", output=True)
|
TMP_SH_DIR = dom0_client.ssh("mktemp -d /tmp/domu_sh.XXXXXX", output=True)
|
||||||
|
@ -50,6 +50,9 @@ def get_xenapi_facts(dom0_client):
|
|||||||
facts['domu_himn_eth'] = eth
|
facts['domu_himn_eth'] = eth
|
||||||
facts['domu_himn_ip'] = ip_addr
|
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
|
return facts
|
||||||
|
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user