Merge "Add methods to populate/free subport pools"
This commit is contained in:
commit
e2adbc70cc
|
@ -46,7 +46,7 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
|
||||||
return ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id)
|
return ovu.neutron_to_osvif_vif_nested_vlan(port, subnets, vlan_id)
|
||||||
|
|
||||||
def request_vifs(self, pod, project_id, subnets, security_groups,
|
def request_vifs(self, pod, project_id, subnets, security_groups,
|
||||||
num_ports):
|
num_ports, trunk_ip=None):
|
||||||
"""This method creates subports and returns a list with their vifs.
|
"""This method creates subports and returns a list with their vifs.
|
||||||
|
|
||||||
It creates up to num_ports subports and attaches them to the trunk
|
It creates up to num_ports subports and attaches them to the trunk
|
||||||
|
@ -61,6 +61,9 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
|
||||||
exception is raised.
|
exception is raised.
|
||||||
"""
|
"""
|
||||||
neutron = clients.get_neutron_client()
|
neutron = clients.get_neutron_client()
|
||||||
|
if trunk_ip:
|
||||||
|
parent_port = self._get_parent_port_by_host_ip(neutron, trunk_ip)
|
||||||
|
else:
|
||||||
parent_port = self._get_parent_port(neutron, pod)
|
parent_port = self._get_parent_port(neutron, pod)
|
||||||
trunk_id = self._get_trunk_id(parent_port)
|
trunk_id = self._get_trunk_id(parent_port)
|
||||||
|
|
||||||
|
@ -116,7 +119,6 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
|
||||||
def _get_port_request(self, pod, project_id, subnets, security_groups,
|
def _get_port_request(self, pod, project_id, subnets, security_groups,
|
||||||
unbound=False):
|
unbound=False):
|
||||||
port_req_body = {'project_id': project_id,
|
port_req_body = {'project_id': project_id,
|
||||||
'name': self._get_port_name(pod),
|
|
||||||
'network_id': self._get_network_id(subnets),
|
'network_id': self._get_network_id(subnets),
|
||||||
'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets),
|
'fixed_ips': ovu.osvif_to_neutron_fixed_ips(subnets),
|
||||||
'device_owner': kl_const.DEVICE_OWNER,
|
'device_owner': kl_const.DEVICE_OWNER,
|
||||||
|
@ -124,6 +126,8 @@ class NestedVlanPodVIFDriver(nested_vif.NestedPodVIFDriver):
|
||||||
|
|
||||||
if unbound:
|
if unbound:
|
||||||
port_req_body['name'] = 'available-port'
|
port_req_body['name'] = 'available-port'
|
||||||
|
else:
|
||||||
|
port_req_body['name'] = self._get_port_name(pod)
|
||||||
|
|
||||||
if security_groups:
|
if security_groups:
|
||||||
port_req_body['security_groups'] = security_groups
|
port_req_body['security_groups'] = security_groups
|
||||||
|
|
|
@ -348,6 +348,21 @@ class NestedVIFPool(BaseVIFPool):
|
||||||
return parent_port['fixed_ips'][0]['ip_address']
|
return parent_port['fixed_ips'][0]['ip_address']
|
||||||
|
|
||||||
def _recover_precreated_ports(self):
|
def _recover_precreated_ports(self):
|
||||||
|
self._precreated_ports(action='recover')
|
||||||
|
|
||||||
|
def _remove_precreated_ports(self, trunk_ips=None):
|
||||||
|
self._precreated_ports(action='free', trunk_ips=trunk_ips)
|
||||||
|
|
||||||
|
def _precreated_ports(self, action, trunk_ips=None):
|
||||||
|
"""Removes or recovers pre-created subports at given pools
|
||||||
|
|
||||||
|
This function handles the pre-created ports based on the given action:
|
||||||
|
- If action is `free` it will remove all the subport from the given
|
||||||
|
trunk ports, or from all the trunk ports if no trunk_ips are passed.
|
||||||
|
- If action is `recover` it will discover the existing subports in the
|
||||||
|
given trunk ports (or in all of them if none are passed) and will add
|
||||||
|
them (and the needed information) to the respective pools.
|
||||||
|
"""
|
||||||
neutron = clients.get_neutron_client()
|
neutron = clients.get_neutron_client()
|
||||||
# Note(ltomasbo): ML2/OVS changes the device_owner to trunk:subport
|
# Note(ltomasbo): ML2/OVS changes the device_owner to trunk:subport
|
||||||
# when a port is attached to a trunk. However, that is not the case
|
# when a port is attached to a trunk. However, that is not the case
|
||||||
|
@ -369,6 +384,9 @@ class NestedVIFPool(BaseVIFPool):
|
||||||
trunk['port_id'])
|
trunk['port_id'])
|
||||||
continue
|
continue
|
||||||
|
|
||||||
|
if trunk_ips and host_addr not in trunk_ips:
|
||||||
|
continue
|
||||||
|
|
||||||
for subport in trunk.get('sub_ports'):
|
for subport in trunk.get('sub_ports'):
|
||||||
kuryr_subport = None
|
kuryr_subport = None
|
||||||
for port in available_ports:
|
for port in available_ports:
|
||||||
|
@ -379,11 +397,67 @@ class NestedVIFPool(BaseVIFPool):
|
||||||
if kuryr_subport:
|
if kuryr_subport:
|
||||||
pool_key = (host_addr, kuryr_subport['project_id'],
|
pool_key = (host_addr, kuryr_subport['project_id'],
|
||||||
tuple(kuryr_subport['security_groups']))
|
tuple(kuryr_subport['security_groups']))
|
||||||
|
|
||||||
|
if action == 'recover':
|
||||||
subnet_id = kuryr_subport['fixed_ips'][0]['subnet_id']
|
subnet_id = kuryr_subport['fixed_ips'][0]['subnet_id']
|
||||||
subnet = {subnet_id: default_subnet._get_subnet(subnet_id)}
|
subnet = {
|
||||||
|
subnet_id: default_subnet._get_subnet(subnet_id)}
|
||||||
vif = ovu.neutron_to_osvif_vif_nested_vlan(
|
vif = ovu.neutron_to_osvif_vif_nested_vlan(
|
||||||
kuryr_subport, subnet, subport['segmentation_id'])
|
kuryr_subport, subnet, subport['segmentation_id'])
|
||||||
|
|
||||||
self._existing_vifs[subport['port_id']] = vif
|
self._existing_vifs[subport['port_id']] = vif
|
||||||
self._available_ports_pools.setdefault(
|
self._available_ports_pools.setdefault(
|
||||||
pool_key, []).append(subport['port_id'])
|
pool_key, []).append(subport['port_id'])
|
||||||
|
elif action == 'free':
|
||||||
|
try:
|
||||||
|
self._drv_vif._remove_subport(neutron, trunk['id'],
|
||||||
|
subport['port_id'])
|
||||||
|
neutron.delete_port(subport['port_id'])
|
||||||
|
self._drv_vif._release_vlan_id(
|
||||||
|
subport['segmentation_id'])
|
||||||
|
del self._existing_vifs[subport['port_id']]
|
||||||
|
self._available_ports_pools[pool_key].remove(
|
||||||
|
subport['port_id'])
|
||||||
|
except n_exc.PortNotFoundClient:
|
||||||
|
LOG.debug('Unable to release port %s as it no '
|
||||||
|
'longer exists.', subport['port_id'])
|
||||||
|
except KeyError:
|
||||||
|
LOG.debug('Port %s is not in the ports list.',
|
||||||
|
subport['port_id'])
|
||||||
|
except n_exc.NeutronClientException:
|
||||||
|
LOG.warning('Error removing the subport %s',
|
||||||
|
subport['port_id'])
|
||||||
|
except ValueError:
|
||||||
|
LOG.debug('Port %s is not in the available ports '
|
||||||
|
'pool.', subport['port_id'])
|
||||||
|
|
||||||
|
def force_populate_pool(self, trunk_ip, project_id, subnets,
|
||||||
|
security_groups, num_ports):
|
||||||
|
"""Create a given amount of subports at a given trunk port.
|
||||||
|
|
||||||
|
This function creates a given amount of subports and attaches them to
|
||||||
|
the specified trunk, adding them to the related subports pool
|
||||||
|
regardless of the amount of subports already available in the pool.
|
||||||
|
"""
|
||||||
|
vifs = self._drv_vif.request_vifs(
|
||||||
|
pod=[],
|
||||||
|
project_id=project_id,
|
||||||
|
subnets=subnets,
|
||||||
|
security_groups=security_groups,
|
||||||
|
num_ports=num_ports,
|
||||||
|
trunk_ip=trunk_ip)
|
||||||
|
|
||||||
|
pool_key = (trunk_ip, project_id, tuple(sorted(security_groups)))
|
||||||
|
for vif in vifs:
|
||||||
|
self._existing_vifs[vif.id] = vif
|
||||||
|
self._available_ports_pools.setdefault(pool_key,
|
||||||
|
[]).append(vif.id)
|
||||||
|
|
||||||
|
def free_pool(self, trunk_ips=None):
|
||||||
|
"""Removes subports from the pool and deletes neutron port resource.
|
||||||
|
|
||||||
|
This function empties the pool of available subports and removes the
|
||||||
|
neutron port resources of the specified trunk port (or all of them if
|
||||||
|
no trunk is specified).
|
||||||
|
"""
|
||||||
|
self._remove_precreated_ports(trunk_ips)
|
||||||
|
|
|
@ -356,6 +356,9 @@ class TestNestedVlanPodVIFDriver(test_base.TestCase):
|
||||||
security_groups, unbound)
|
security_groups, unbound)
|
||||||
|
|
||||||
self.assertEqual(expected, ret)
|
self.assertEqual(expected, ret)
|
||||||
|
if unbound:
|
||||||
|
m_driver._get_port_name.assert_not_called()
|
||||||
|
else:
|
||||||
m_driver._get_port_name.assert_called_once_with(pod)
|
m_driver._get_port_name.assert_called_once_with(pod)
|
||||||
m_driver._get_network_id.assert_called_once_with(subnets)
|
m_driver._get_network_id.assert_called_once_with(subnets)
|
||||||
m_to_fips.assert_called_once_with(subnets)
|
m_to_fips.assert_called_once_with(subnets)
|
||||||
|
|
|
@ -957,7 +957,7 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
'neutron_to_osvif_vif_nested_vlan')
|
'neutron_to_osvif_vif_nested_vlan')
|
||||||
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
||||||
'_get_subnet')
|
'_get_subnet')
|
||||||
def test__recover_precreated_ports(self, m_get_subnet, m_to_osvif):
|
def test__precreated_ports_recover(self, m_get_subnet, m_to_osvif):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||||
|
@ -975,15 +975,45 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
m_get_subnet.return_value = mock.sentinel.subnet
|
m_get_subnet.return_value = mock.sentinel.subnet
|
||||||
m_to_osvif.return_value = mock.sentinel.vif
|
m_to_osvif.return_value = mock.sentinel.vif
|
||||||
|
|
||||||
cls._recover_precreated_ports(m_driver)
|
cls._precreated_ports(m_driver, 'recover')
|
||||||
neutron.list_trunks.assert_called_once()
|
neutron.list_trunks.assert_called_once()
|
||||||
m_driver._get_parent_port_ip.assert_called_with(trunk_id)
|
m_driver._get_parent_port_ip.assert_called_with(trunk_id)
|
||||||
|
|
||||||
|
def test__precreated_ports_free(self):
|
||||||
|
cls = vif_pool.NestedVIFPool
|
||||||
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
|
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||||
|
cls_vif_driver = nested_vlan_vif.NestedVlanPodVIFDriver
|
||||||
|
vif_driver = mock.MagicMock(spec=cls_vif_driver)
|
||||||
|
m_driver._drv_vif = vif_driver
|
||||||
|
|
||||||
|
port_id = mock.sentinel.port_id
|
||||||
|
host_addr = mock.sentinel.host_addr
|
||||||
|
|
||||||
|
subport_obj = get_port_obj(port_id=port_id,
|
||||||
|
device_owner='trunk:subport')
|
||||||
|
m_driver._get_ports_by_attrs.side_effect = [[subport_obj], []]
|
||||||
|
trunk_id = mock.sentinel.trunk_id
|
||||||
|
trunk_obj = self._get_trunk_obj(port_id=trunk_id, subport_id=port_id)
|
||||||
|
pool_key = (host_addr, subport_obj['id'],
|
||||||
|
tuple(subport_obj['security_groups']))
|
||||||
|
m_driver._available_ports_pools = {pool_key: port_id}
|
||||||
|
|
||||||
|
neutron.list_trunks.return_value = {'trunks': [trunk_obj]}
|
||||||
|
m_driver._get_parent_port_ip.return_value = host_addr
|
||||||
|
|
||||||
|
cls._precreated_ports(m_driver, 'free')
|
||||||
|
neutron.list_trunks.assert_called_once()
|
||||||
|
m_driver._get_parent_port_ip.assert_called_with(trunk_id)
|
||||||
|
m_driver._drv_vif._remove_subport.assert_called_once()
|
||||||
|
neutron.delete_port.assert_called_once()
|
||||||
|
m_driver._drv_vif._release_vlan_id.assert_called_once()
|
||||||
|
|
||||||
@mock.patch('kuryr_kubernetes.os_vif_util.'
|
@mock.patch('kuryr_kubernetes.os_vif_util.'
|
||||||
'neutron_to_osvif_vif_nested_vlan')
|
'neutron_to_osvif_vif_nested_vlan')
|
||||||
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
||||||
'_get_subnet')
|
'_get_subnet')
|
||||||
def test__recover_precreated_ports_several_trunks(self, m_get_subnet,
|
def test__precreated_ports_recover_several_trunks(self, m_get_subnet,
|
||||||
m_to_osvif):
|
m_to_osvif):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
|
@ -1013,7 +1043,7 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
m_get_subnet.return_value = subnet
|
m_get_subnet.return_value = subnet
|
||||||
m_to_osvif.return_value = mock.sentinel.vif
|
m_to_osvif.return_value = mock.sentinel.vif
|
||||||
|
|
||||||
cls._recover_precreated_ports(m_driver)
|
cls._precreated_ports(m_driver, 'recover')
|
||||||
neutron.list_trunks.asser_called_once()
|
neutron.list_trunks.asser_called_once()
|
||||||
m_driver._get_parent_port_ip.assert_has_calls([mock.call(trunk_id1),
|
m_driver._get_parent_port_ip.assert_has_calls([mock.call(trunk_id1),
|
||||||
mock.call(trunk_id2)])
|
mock.call(trunk_id2)])
|
||||||
|
@ -1027,7 +1057,7 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
'neutron_to_osvif_vif_nested_vlan')
|
'neutron_to_osvif_vif_nested_vlan')
|
||||||
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
@mock.patch('kuryr_kubernetes.controller.drivers.default_subnet.'
|
||||||
'_get_subnet')
|
'_get_subnet')
|
||||||
def test__recover_precreated_ports_several_subports(self, m_get_subnet,
|
def test__precreated_ports_recover_several_subports(self, m_get_subnet,
|
||||||
m_to_osvif):
|
m_to_osvif):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
|
@ -1055,7 +1085,7 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
m_get_subnet.return_value = subnet
|
m_get_subnet.return_value = subnet
|
||||||
m_to_osvif.return_value = mock.sentinel.vif
|
m_to_osvif.return_value = mock.sentinel.vif
|
||||||
|
|
||||||
cls._recover_precreated_ports(m_driver)
|
cls._precreated_ports(m_driver, 'recover')
|
||||||
neutron.list_trunks.asser_called_once()
|
neutron.list_trunks.asser_called_once()
|
||||||
m_driver._get_parent_port_ip.assert_called_once_with(trunk_id)
|
m_driver._get_parent_port_ip.assert_called_once_with(trunk_id)
|
||||||
calls = [mock.call(port1, {port1['fixed_ips'][0]['subnet_id']: subnet},
|
calls = [mock.call(port1, {port1['fixed_ips'][0]['subnet_id']: subnet},
|
||||||
|
@ -1064,17 +1094,19 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
trunk_obj['sub_ports'][1]['segmentation_id'])]
|
trunk_obj['sub_ports'][1]['segmentation_id'])]
|
||||||
m_to_osvif.assert_has_calls(calls)
|
m_to_osvif.assert_has_calls(calls)
|
||||||
|
|
||||||
def test__recover_precreated_ports_no_ports_to_recover(self):
|
@ddt.data(('recover'), ('free'))
|
||||||
|
def test__precreated_ports_no_ports(self, m_action):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||||
|
|
||||||
m_driver._get_ports_by_attrs.return_value = []
|
m_driver._get_ports_by_attrs.return_value = []
|
||||||
|
|
||||||
cls._recover_precreated_ports(m_driver)
|
cls._precreated_ports(m_driver, m_action)
|
||||||
neutron.list_trunks.assert_not_called()
|
neutron.list_trunks.assert_not_called()
|
||||||
|
|
||||||
def test__recover_precreated_ports_no_trunks(self):
|
@ddt.data(('recover'), ('free'))
|
||||||
|
def test__precreated_ports_no_trunks(self, m_action):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||||
|
@ -1083,11 +1115,12 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
device_owner='trunk:subport')], []]
|
device_owner='trunk:subport')], []]
|
||||||
neutron.list_trunks.return_value = {'trunks': []}
|
neutron.list_trunks.return_value = {'trunks': []}
|
||||||
|
|
||||||
cls._recover_precreated_ports(m_driver)
|
cls._precreated_ports(m_driver, m_action)
|
||||||
neutron.list_trunks.assert_called()
|
neutron.list_trunks.assert_called()
|
||||||
m_driver._get_parent_port_ip.assert_not_called()
|
m_driver._get_parent_port_ip.assert_not_called()
|
||||||
|
|
||||||
def test__recover_precreated_ports_exception(self):
|
@ddt.data(('recover'), ('free'))
|
||||||
|
def test__precreated_ports_exception(self, m_action):
|
||||||
cls = vif_pool.NestedVIFPool
|
cls = vif_pool.NestedVIFPool
|
||||||
m_driver = mock.MagicMock(spec=cls)
|
m_driver = mock.MagicMock(spec=cls)
|
||||||
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
neutron = self.useFixture(k_fix.MockNeutronClient()).client
|
||||||
|
@ -1100,6 +1133,6 @@ class NestedVIFPool(test_base.TestCase):
|
||||||
neutron.list_trunks.return_value = {'trunks': [trunk_obj]}
|
neutron.list_trunks.return_value = {'trunks': [trunk_obj]}
|
||||||
m_driver._get_parent_port_ip.side_effect = n_exc.PortNotFoundClient
|
m_driver._get_parent_port_ip.side_effect = n_exc.PortNotFoundClient
|
||||||
|
|
||||||
self.assertIsNone(cls._recover_precreated_ports(m_driver))
|
self.assertIsNone(cls._precreated_ports(m_driver, m_action))
|
||||||
neutron.list_trunks.assert_called()
|
neutron.list_trunks.assert_called()
|
||||||
m_driver._get_parent_port_ip.assert_called_with(trunk_id)
|
m_driver._get_parent_port_ip.assert_called_with(trunk_id)
|
||||||
|
|
Loading…
Reference in New Issue