Skip to content
5 changes: 3 additions & 2 deletions neutron/agent/linux/dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -1144,7 +1144,8 @@ def _get_ovn_metadata_port_ip(self, subnet):
m_ports = [port for port in self.network.ports if
self._is_ovn_metadata_port(port, self.network.id)]
if m_ports:
for fixed_ip in m_ports[0].fixed_ips:
port = self.device_manager.plugin.get_dhcp_port(m_ports[0].id)
for fixed_ip in port.fixed_ips:
if fixed_ip.subnet_id == subnet.id:
return fixed_ip.ip_address

Expand Down Expand Up @@ -1220,7 +1221,7 @@ def _generate_opts_per_subnet(self):
if subnet_dhcp_ip:
metadata_route_ip = subnet_dhcp_ip

if not isolated_subnets[subnet.id] and gateway:
elif not isolated_subnets[subnet.id] and gateway:
metadata_route_ip = gateway

if metadata_route_ip:
Expand Down
2 changes: 1 addition & 1 deletion neutron/conf/agent/metadata/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -94,7 +94,7 @@
cfg.IntOpt('metadata_workers',
sample_default='<num_of_cpus> / 2',
help=_('Number of separate worker processes for metadata '
'server (defaults to 2 when used with ML2/OVN and half '
'server (defaults to 0 when used with ML2/OVN and half '
'of the number of CPUs with other backend drivers)')),
cfg.IntOpt('metadata_backlog',
default=4096,
Expand Down
25 changes: 22 additions & 3 deletions neutron/privileged/agent/linux/ip_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -474,6 +474,10 @@ def delete_neigh_entry(ip_version, ip_address, mac_address, device, namespace,
if e.code == errno.ENOENT:
return
raise
except OSError as e:
if e.errno == errno.ENOENT:
raise NetworkNamespaceNotFound(netns_name=namespace)
raise


@tenacity.retry(
Expand Down Expand Up @@ -683,6 +687,11 @@ def delete_ip_rule(namespace, **kwargs):
try:
with get_iproute(namespace) as ip:
ip.rule('del', **kwargs)
except netlink_exceptions.NetlinkError as e:
# trying to delete a non-existent entry shouldn't raise an error
if e.code == errno.ENOENT:
return
raise
except OSError as e:
if e.errno == errno.ENOENT:
raise NetworkNamespaceNotFound(netns_name=namespace)
Expand Down Expand Up @@ -785,6 +794,11 @@ def delete_ip_route(namespace, cidr, ip_version, device=None, via=None,
try:
with get_iproute(namespace) as ip:
ip.route('del', **kwargs)
except netlink_exceptions.NetlinkError as e:
# trying to delete a non-existent entry shouldn't raise an error
if e.code == errno.ESRCH:
return
raise
except OSError as e:
if e.errno == errno.ENOENT:
raise NetworkNamespaceNotFound(netns_name=namespace)
Expand Down Expand Up @@ -819,6 +833,11 @@ def _command_bridge_fdb(command, mac, device, dst_ip=None, namespace=None,
kwargs['dst'] = dst_ip
with get_iproute(namespace) as ip:
return make_serializable(ip.fdb(command, **kwargs))
except netlink_exceptions.NetlinkError as e:
# trying to delete a non-existent entry shouldn't raise an error
if command == 'del' and e.code == errno.ENOENT:
return
raise
except OSError as e:
if e.errno == errno.ENOENT:
raise NetworkNamespaceNotFound(netns_name=namespace)
Expand All @@ -834,20 +853,20 @@ def add_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):

@privileged.default.entrypoint
def append_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
"""Add a FDB entry"""
"""Append a FDB entry"""
return _command_bridge_fdb('append', mac, device, dst_ip=dst_ip,
namespace=namespace, **kwargs)


@privileged.default.entrypoint
def replace_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
"""Add a FDB entry"""
"""Replace a FDB entry"""
return _command_bridge_fdb('replace', mac, device, dst_ip=dst_ip,
namespace=namespace, **kwargs)


@privileged.default.entrypoint
def delete_bridge_fdb(mac, device, dst_ip=None, namespace=None, **kwargs):
"""Add a FDB entry"""
"""Delete a FDB entry"""
return _command_bridge_fdb('del', mac, device, dst_ip=dst_ip,
namespace=namespace, **kwargs)
7 changes: 7 additions & 0 deletions neutron/tests/functional/agent/linux/test_bridge_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -211,6 +211,13 @@ def test_add_delete(self):
namespace=self.namespace)
self._assert_mac(self.MAC1, self.device, present=False)

try:
# This should not raise for a non-existent entry
bridge_lib.FdbInterface.delete(self.MAC1, self.device,
namespace=self.namespace)
except Exception:
self.fail('Delete FDB entry threw unexpected exception')

def test_add_delete_dst(self):
self._assert_mac(self.MAC1, self.device_vxlan, present=False)
bridge_lib.FdbInterface.add(
Expand Down
78 changes: 52 additions & 26 deletions neutron/tests/functional/privileged/agent/linux/test_ip_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -239,19 +239,34 @@ def test_get_devices_info_veth_same_namespaces(self):
self.assertEqual(veth1_2['index'], veth1_1_link)


class ListIpRulesTestCase(functional_base.BaseSudoTestCase):

RULE_TABLES = {'default': 253, 'main': 254, 'local': 255}
class BaseIpRuleTestCase(functional_base.BaseSudoTestCase):

def setUp(self):
super(ListIpRulesTestCase, self).setUp()
super().setUp()
self.namespace = 'ns_test-' + uuidutils.generate_uuid()
self.ns = priv_ip_lib.create_netns(self.namespace)
self.addCleanup(self._remove_ns)

def _remove_ns(self):
priv_ip_lib.remove_netns(self.namespace)

def _check_rules(self, rules, parameters, values, exception_string=None,
raise_exception=True):
for rule in rules:
if all(rule.get(parameter) == value
for parameter, value in zip(parameters, values)):
return True
else:
if raise_exception:
self.fail('Rule with %s was expected' % exception_string)
else:
return False


class ListIpRulesTestCase(BaseIpRuleTestCase):

RULE_TABLES = {'default': 253, 'main': 254, 'local': 255}

def test_list_default_rules_ipv4(self):
rules_ipv4 = priv_ip_lib.list_ip_rules(self.namespace, 4)
self.assertEqual(3, len(rules_ipv4))
Expand Down Expand Up @@ -291,28 +306,7 @@ def test_list_rules_ipv6(self):
self.fail('Rule added (2001:db8::1/64, table 20) not found')


class RuleTestCase(functional_base.BaseSudoTestCase):

def setUp(self):
super(RuleTestCase, self).setUp()
self.namespace = 'ns_test-' + uuidutils.generate_uuid()
self.ns = priv_ip_lib.create_netns(self.namespace)
self.addCleanup(self._remove_ns)

def _remove_ns(self):
priv_ip_lib.remove_netns(self.namespace)

def _check_rules(self, rules, parameters, values, exception_string=None,
raise_exception=True):
for rule in rules:
if all(rule.get(parameter) == value
for parameter, value in zip(parameters, values)):
return True
else:
if raise_exception:
self.fail('Rule with %s was expected' % exception_string)
else:
return False
class AddIpRulesTestCase(BaseIpRuleTestCase):

def test_add_rule_ip(self):
ip_addresses = ['192.168.200.250', '2001::250']
Expand Down Expand Up @@ -433,6 +427,23 @@ def test_add_rule_exists(self):
self.assertEqual(4, len(rules))


class DeleteIpRulesTestCase(BaseIpRuleTestCase):

def test_delete_rule_no_entry(self):
iif = 'iif_device'
priv_ip_lib.create_interface(iif, self.namespace, 'dummy')

try:
# This should not raise for a non-existent entry
priv_ip_lib.delete_ip_rule(self.namespace, iifname=iif)
except Exception:
self.fail('Delete IP rule threw unexpected exception')

rules = ip_lib.list_ip_rules(self.namespace, 4)
# There are always 3 rules by default
self.assertEqual(3, len(rules))


class GetIpAddressesTestCase(functional_base.BaseSudoTestCase):

def _remove_ns(self, namespace):
Expand Down Expand Up @@ -659,6 +670,21 @@ def test_add_multipath_route(self):
n_cons.IP_VERSION_4, via=multipath)
self._check_routes(['192.168.0.0/24'], gateway=multipath)

def test_delete_route_no_entry(self):
cidr = '192.168.0.0/24'
self.device.addr.add('10.1.0.1/24')
try:
# This should not raise for a non-existent entry
priv_ip_lib.delete_ip_route(self.namespace, cidr,
n_cons.IP_VERSION_4,
device=self.device_name)
except Exception:
self.fail('Delete IP route threw unexpected exception')

routes = ip_lib.list_ip_routes(self.namespace, n_cons.IP_VERSION_4)
# There will be a single interface route since we added an IP
self.assertEqual(1, len(routes))


class GetLinkAttributesTestCase(functional_base.BaseSudoTestCase):

Expand Down
2 changes: 2 additions & 0 deletions neutron/tests/unit/agent/linux/test_dhcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -3126,6 +3126,8 @@ def test__generate_opts_per_subnet_no_metadata(self):
def test__generate_opts_per_subnet_with_metadata_port(self):
config = {'enable_isolated_metadata': False,
'force_metadata': False}
self.mock_mgr.return_value.plugin.get_dhcp_port.return_value = \
FakeOvnMetadataPort()
self._test__generate_opts_per_subnet_helper(config, True,
network_class=FakeNetworkDhcpandOvnMetadataPort)

Expand Down
11 changes: 11 additions & 0 deletions neutron/tests/unit/privileged/agent/linux/test_ip_lib.py
Original file line number Diff line number Diff line change
Expand Up @@ -157,6 +157,17 @@ def test_run_iproute_neigh_namespace_not_exists(self):
priv_lib._run_iproute_neigh,
"test_cmd", "eth0", None, test_param="test_value")

def test_run_iproute_neigh_no_entry(self):
with mock.patch.object(pyroute2, "IPRoute") as iproute_mock:
iproute_mock.side_effect = netlink_exceptions.NetlinkError(
code=errno.ENOENT)
try:
priv_lib._run_iproute_neigh(
"test_cmd", "eth0", None, test_param="test_value")
self.fail("NetlinkError exception not raised")
except netlink_exceptions.NetlinkError as e:
self.assertEqual(errno.ENOENT, e.code)

def test_run_iproute_neigh_error(self):
with mock.patch.object(pyroute2, "IPRoute") as iproute_mock:
iproute_mock.side_effect = OSError(
Expand Down
2 changes: 0 additions & 2 deletions zuul.d/job-templates.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -100,8 +100,6 @@
- neutron-ovn-tempest-mariadb-full
- neutron-ovn-tempest-ovs-release
- neutron-ovn-tempest-ovs-release-ipv6-only
- neutron-ovs-tempest-fips
- neutron-ovn-tempest-ovs-release-fips
- devstack-tobiko-neutron:
voting: true
- ironic-tempest-ipa-wholedisk-bios-agent_ipmitool-tinyipa
Expand Down
36 changes: 0 additions & 36 deletions zuul.d/tempest-singlenode.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -491,39 +491,3 @@
name: neutron-ovn-tempest-ovs-release
description: Job testing for devstack/tempest testing Neutron with ovn driver and latest released OVN branch
parent: neutron-ovn-base

- job:
name: neutron-ovs-tempest-fips
parent: neutron-ovs-tempest-base
nodeset: devstack-single-node-centos-9-stream
description: |
Scenario testing for a FIPS enabled Centos 9 system
pre-run: playbooks/enable-fips.yaml
vars:
nslookup_target: 'opendev.org'
configure_swap_size: 4096
devstack_services:
br-ex-tcpdump: true
br-int-flows: true
devstack_local_conf:
test-config:
"$TEMPEST_CONFIG":
validation:
ssh_key_type: 'ecdsa'


- job:
name: neutron-ovn-tempest-ovs-release-fips
parent: neutron-ovn-tempest-ovs-release
nodeset: devstack-single-node-centos-9-stream
description: |
Scenario testing for a FIPS enabled Centos 9 system
pre-run: playbooks/enable-fips.yaml
vars:
nslookup_target: 'opendev.org'
configure_swap_size: 4096
devstack_local_conf:
test-config:
"$TEMPEST_CONFIG":
validation:
ssh_key_type: 'ecdsa'