Merge "Do not fail deleting namespace if it does not exist" into stable/rocky
This commit is contained in:
commit
9532cac4ea
|
@ -262,9 +262,6 @@ class DhcpLocalProcess(DhcpBase):
|
||||||
LOG.warning('Failed trying to delete interface: %s',
|
LOG.warning('Failed trying to delete interface: %s',
|
||||||
self.interface_name)
|
self.interface_name)
|
||||||
|
|
||||||
if not ip_lib.network_namespace_exists(self.network.namespace):
|
|
||||||
LOG.debug("Namespace already deleted: %s", self.network.namespace)
|
|
||||||
return
|
|
||||||
try:
|
try:
|
||||||
ip_lib.delete_network_namespace(self.network.namespace)
|
ip_lib.delete_network_namespace(self.network.namespace)
|
||||||
except RuntimeError:
|
except RuntimeError:
|
||||||
|
|
|
@ -93,6 +93,13 @@ class IpAddressAlreadyExists(RuntimeError):
|
||||||
super(IpAddressAlreadyExists, self).__init__(message)
|
super(IpAddressAlreadyExists, self).__init__(message)
|
||||||
|
|
||||||
|
|
||||||
|
def _make_route_dict(destination, nexthop, device, scope):
|
||||||
|
return {'destination': destination,
|
||||||
|
'nexthop': nexthop,
|
||||||
|
'device': device,
|
||||||
|
'scope': scope}
|
||||||
|
|
||||||
|
|
||||||
@privileged.default.entrypoint
|
@privileged.default.entrypoint
|
||||||
def get_routing_table(ip_version, namespace=None):
|
def get_routing_table(ip_version, namespace=None):
|
||||||
"""Return a list of dictionaries, each representing a route.
|
"""Return a list of dictionaries, each representing a route.
|
||||||
|
@ -112,14 +119,32 @@ def get_routing_table(ip_version, namespace=None):
|
||||||
if e.errno == errno.ENOENT:
|
if e.errno == errno.ENOENT:
|
||||||
raise NetworkNamespaceNotFound(netns_name=namespace)
|
raise NetworkNamespaceNotFound(netns_name=namespace)
|
||||||
raise
|
raise
|
||||||
|
routes = []
|
||||||
with pyroute2.IPDB(nl=netns) as ipdb:
|
with pyroute2.IPDB(nl=netns) as ipdb:
|
||||||
ipdb_routes = ipdb.routes
|
ipdb_routes = ipdb.routes
|
||||||
ipdb_interfaces = ipdb.interfaces
|
ipdb_interfaces = ipdb.interfaces
|
||||||
routes = [{'destination': route['dst'],
|
for route in ipdb_routes:
|
||||||
'nexthop': route.get('gateway'),
|
if route['family'] != family:
|
||||||
'device': ipdb_interfaces[route['oif']]['ifname'],
|
continue
|
||||||
'scope': _get_scope_name(route['scope'])}
|
dst = route['dst']
|
||||||
for route in ipdb_routes if route['family'] == family]
|
nexthop = route.get('gateway')
|
||||||
|
oif = route.get('oif')
|
||||||
|
scope = _get_scope_name(route['scope'])
|
||||||
|
|
||||||
|
# If there is not a valid outgoing interface id, check if
|
||||||
|
# this is a multipath route (i.e. same destination with
|
||||||
|
# multiple outgoing interfaces)
|
||||||
|
if oif:
|
||||||
|
device = ipdb_interfaces[oif]['ifname']
|
||||||
|
rt = _make_route_dict(dst, nexthop, device, scope)
|
||||||
|
routes.append(rt)
|
||||||
|
elif route.get('multipath'):
|
||||||
|
for mpr in route['multipath']:
|
||||||
|
oif = mpr['oif']
|
||||||
|
device = ipdb_interfaces[oif]['ifname']
|
||||||
|
rt = _make_route_dict(dst, nexthop, device, scope)
|
||||||
|
routes.append(rt)
|
||||||
|
|
||||||
return routes
|
return routes
|
||||||
|
|
||||||
|
|
||||||
|
@ -410,7 +435,11 @@ def remove_netns(name, **kwargs):
|
||||||
|
|
||||||
:param name: The name of the namespace to remove
|
:param name: The name of the namespace to remove
|
||||||
"""
|
"""
|
||||||
netns.remove(name, **kwargs)
|
try:
|
||||||
|
netns.remove(name, **kwargs)
|
||||||
|
except OSError as e:
|
||||||
|
if e.errno != errno.ENOENT:
|
||||||
|
raise
|
||||||
|
|
||||||
|
|
||||||
@privileged.default.entrypoint
|
@privileged.default.entrypoint
|
||||||
|
|
|
@ -1134,9 +1134,7 @@ class TestDhcpLocalProcess(TestBase):
|
||||||
self.assertTrue(lp.process_monitor.unregister.called)
|
self.assertTrue(lp.process_monitor.unregister.called)
|
||||||
self.assertTrue(self.external_process().disable.called)
|
self.assertTrue(self.external_process().disable.called)
|
||||||
|
|
||||||
@mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists')
|
def test_disable_not_active(self):
|
||||||
def test_disable_not_active(self, namespace_exists):
|
|
||||||
namespace_exists.return_value = False
|
|
||||||
attrs_to_mock = dict([(a, mock.DEFAULT) for a in
|
attrs_to_mock = dict([(a, mock.DEFAULT) for a in
|
||||||
['active', 'interface_name']])
|
['active', 'interface_name']])
|
||||||
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
|
||||||
|
@ -1145,11 +1143,15 @@ class TestDhcpLocalProcess(TestBase):
|
||||||
network = FakeDualNetwork()
|
network = FakeDualNetwork()
|
||||||
lp = LocalChild(self.conf, network)
|
lp = LocalChild(self.conf, network)
|
||||||
lp.device_manager = mock.Mock()
|
lp.device_manager = mock.Mock()
|
||||||
lp.disable()
|
with mock.patch('neutron.agent.linux.ip_lib.'
|
||||||
|
'delete_network_namespace') as delete_ns:
|
||||||
|
lp.disable()
|
||||||
lp.device_manager.destroy.assert_called_once_with(
|
lp.device_manager.destroy.assert_called_once_with(
|
||||||
network, 'tap0')
|
network, 'tap0')
|
||||||
self._assert_disabled(lp)
|
self._assert_disabled(lp)
|
||||||
|
|
||||||
|
delete_ns.assert_called_with('qdhcp-ns')
|
||||||
|
|
||||||
def test_disable_retain_port(self):
|
def test_disable_retain_port(self):
|
||||||
attrs_to_mock = dict([(a, mock.DEFAULT) for a in
|
attrs_to_mock = dict([(a, mock.DEFAULT) for a in
|
||||||
['active', 'interface_name']])
|
['active', 'interface_name']])
|
||||||
|
@ -1161,9 +1163,7 @@ class TestDhcpLocalProcess(TestBase):
|
||||||
lp.disable(retain_port=True)
|
lp.disable(retain_port=True)
|
||||||
self._assert_disabled(lp)
|
self._assert_disabled(lp)
|
||||||
|
|
||||||
@mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists')
|
def test_disable(self):
|
||||||
def test_disable(self, namespace_exists):
|
|
||||||
namespace_exists.return_value = True
|
|
||||||
attrs_to_mock = {'active': mock.DEFAULT}
|
attrs_to_mock = {'active': mock.DEFAULT}
|
||||||
|
|
||||||
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
|
with mock.patch.multiple(LocalChild, **attrs_to_mock) as mocks:
|
||||||
|
@ -1177,9 +1177,7 @@ class TestDhcpLocalProcess(TestBase):
|
||||||
|
|
||||||
delete_ns.assert_called_with('qdhcp-ns')
|
delete_ns.assert_called_with('qdhcp-ns')
|
||||||
|
|
||||||
@mock.patch('neutron.agent.linux.ip_lib.network_namespace_exists')
|
def test_disable_config_dir_removed_after_destroy(self):
|
||||||
def test_disable_config_dir_removed_after_destroy(self, namespace_exists):
|
|
||||||
namespace_exists.return_value = True
|
|
||||||
parent = mock.MagicMock()
|
parent = mock.MagicMock()
|
||||||
parent.attach_mock(self.rmtree, 'rmtree')
|
parent.attach_mock(self.rmtree, 'rmtree')
|
||||||
parent.attach_mock(self.mock_mgr, 'DeviceManager')
|
parent.attach_mock(self.mock_mgr, 'DeviceManager')
|
||||||
|
|
|
@ -1559,6 +1559,55 @@ class TestGetRoutingTable(base.BaseTestCase):
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
|
||||||
|
ip_db_multipath_routes = [
|
||||||
|
{
|
||||||
|
'dst_len': 24,
|
||||||
|
'family': socket.AF_INET,
|
||||||
|
'proto': 3,
|
||||||
|
'tos': 0,
|
||||||
|
'dst': '10.0.1.0/24',
|
||||||
|
'flags': 16,
|
||||||
|
'ipdb_priority': 0,
|
||||||
|
'metrics': {},
|
||||||
|
'scope': 0,
|
||||||
|
'encap': {},
|
||||||
|
'src_len': 0,
|
||||||
|
'table': 254,
|
||||||
|
'multipath': ({'oif': 1, 'family': socket.AF_INET},
|
||||||
|
{'oif': 2, 'dst_len': 24, 'family': socket.AF_INET,
|
||||||
|
'proto': 2, 'tos': 0, 'pref': '00',
|
||||||
|
'priority': 256, 'flags': 0, 'encap': {},
|
||||||
|
'src_len': 0, 'table': 254, 'type': 1,
|
||||||
|
'scope': 0}),
|
||||||
|
'type': 1,
|
||||||
|
'gateway': '10.0.0.1',
|
||||||
|
'ipdb_scope': 'system'
|
||||||
|
}, {
|
||||||
|
'metrics': {},
|
||||||
|
'dst_len': 64,
|
||||||
|
'family': socket.AF_INET6,
|
||||||
|
'proto': 2,
|
||||||
|
'tos': 0,
|
||||||
|
'dst': '1111:1111:1111:1111::/64',
|
||||||
|
'pref': '00',
|
||||||
|
'ipdb_priority': 0,
|
||||||
|
'priority': 256,
|
||||||
|
'flags': 0,
|
||||||
|
'encap': {},
|
||||||
|
'src_len': 0,
|
||||||
|
'table': 254,
|
||||||
|
'multipath': ({'oif': 1, 'family': socket.AF_INET6},
|
||||||
|
{'oif': 2, 'dst_len': 64, 'family': socket.AF_INET6,
|
||||||
|
'proto': 2, 'tos': 0, 'pref': '00',
|
||||||
|
'priority': 256, 'flags': 0, 'encap': {},
|
||||||
|
'src_len': 0, 'table': 254, 'type': 1,
|
||||||
|
'scope': 0}),
|
||||||
|
'type': 1,
|
||||||
|
'scope': 0,
|
||||||
|
'ipdb_scope': 'system'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
super(TestGetRoutingTable, self).setUp()
|
super(TestGetRoutingTable, self).setUp()
|
||||||
self.addCleanup(privileged.default.set_client_mode, True)
|
self.addCleanup(privileged.default.set_client_mode, True)
|
||||||
|
@ -1616,6 +1665,28 @@ class TestGetRoutingTable(base.BaseTestCase):
|
||||||
'scope': 'universe'}]
|
'scope': 'universe'}]
|
||||||
self._test_get_routing_table(6, self.ip_db_routes, expected)
|
self._test_get_routing_table(6, self.ip_db_routes, expected)
|
||||||
|
|
||||||
|
def test_get_routing_table_multipath_4(self):
|
||||||
|
expected = [{'destination': '10.0.1.0/24',
|
||||||
|
'nexthop': '10.0.0.1',
|
||||||
|
'device': 'lo',
|
||||||
|
'scope': 'universe'},
|
||||||
|
{'destination': '10.0.1.0/24',
|
||||||
|
'nexthop': '10.0.0.1',
|
||||||
|
'device': 'tap-1',
|
||||||
|
'scope': 'universe'}]
|
||||||
|
self._test_get_routing_table(4, self.ip_db_multipath_routes, expected)
|
||||||
|
|
||||||
|
def test_get_routing_table_multipath_6(self):
|
||||||
|
expected = [{'destination': '1111:1111:1111:1111::/64',
|
||||||
|
'nexthop': None,
|
||||||
|
'device': 'lo',
|
||||||
|
'scope': 'universe'},
|
||||||
|
{'destination': '1111:1111:1111:1111::/64',
|
||||||
|
'nexthop': None,
|
||||||
|
'device': 'tap-1',
|
||||||
|
'scope': 'universe'}]
|
||||||
|
self._test_get_routing_table(6, self.ip_db_multipath_routes, expected)
|
||||||
|
|
||||||
|
|
||||||
class TestIpNeighCommand(TestIPCmdBase):
|
class TestIpNeighCommand(TestIPCmdBase):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
|
|
Loading…
Reference in New Issue