Set "type=virtual" for OVN LSP with parent ports

This is a follow-up of [1]. Before this patch, any virtual logical
switch port that was updated and the "device_owner" was not and empty
string, had its type set to '' (empty string).

This maintenance task, that is executed only once, lists all logical
switch ports, checks the presence or not of virtual parents and
sets the type to "virtual" if needed.

Related-Bug: #1973276

[1]https://review.opendev.org/c/openstack/neutron/+/841711

Conflicts:
    neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/maintenance.py
    neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_maintenance.py
    neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py
    neutron/tests/unit/db/test_db_base_plugin_v2.py
    neutron/tests/unit/extensions/test_portsecurity.py
    neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py

Change-Id: I6cf1167d556f0c2c2aa2013f05c809648020b377
(cherry picked from commit 3c93da7bdf)
(cherry picked from commit 316b55440a)
(cherry picked from commit 8c79b631aa)
(cherry picked from commit d6f395378c)
This commit is contained in:
Rodolfo Alonso Hernandez 2022-05-14 23:54:10 +00:00
parent 2eb44d5985
commit 3b9f1be5e5
6 changed files with 77 additions and 14 deletions

View File

@ -358,6 +358,13 @@ def get_ovn_port_addresses(ovn_port):
return list(set(addresses + port_security))
def get_virtual_port_parents(nb_idl, virtual_ip, network_id, port_id):
ls = nb_idl.ls_get(ovn_name(network_id)).execute(check_error=True)
return [lsp.name for lsp in ls.ports
if lsp.name != port_id and
virtual_ip in get_ovn_port_addresses(lsp)]
def sort_ips_by_version(addresses):
ip_map = {'ip4': [], 'ip6': []}
for addr in addresses:

View File

@ -742,6 +742,39 @@ class DBInconsistenciesPeriodics(SchemaAwarePeriodicsBase):
txn.add(cmd)
raise periodics.NeverAgain()
# TODO(ralonsoh): Remove this in the Z+4 cycle
@periodics.periodic(spacing=600, run_immediately=True)
def update_port_virtual_type(self):
"""Set type=virtual to those ports with parents
Before LP#1973276, any virtual port with "device_owner" defined, lost
its type=virtual. This task restores the type for those ports updated
before the fix https://review.opendev.org/c/openstack/neutron/+/841711.
"""
if not self.has_lock:
return
context = n_context.get_admin_context()
cmds = []
for lsp in self._nb_idl.lsp_list().execute(check_error=True):
if lsp.type != '':
continue
port = self._ovn_client._plugin.get_port(context, lsp.name)
for ip in port.get('fixed_ips', []):
if utils.get_virtual_port_parents(
self._nb_idl, ip['ip_address'], port['network_id'],
port['id']):
cmds.append(self._nb_idl.db_set(
'Logical_Switch_Port', lsp.uuid,
('type', ovn_const.LSP_TYPE_VIRTUAL)))
break
if cmds:
with self._nb_idl.transaction(check_error=True) as txn:
for cmd in cmds:
txn.add(cmd)
raise periodics.NeverAgain()
class HashRingHealthCheckPeriodics(object):

View File

@ -207,13 +207,6 @@ class OVNClient(object):
external_ids=subnet_dhcp_options['external_ids'])
return {'cmd': add_dhcp_opts_cmd}
def get_virtual_port_parents(self, virtual_ip, port):
ls = self._nb_idl.ls_get(utils.ovn_name(port['network_id'])).execute(
check_error=True)
return [lsp.name for lsp in ls.ports
if lsp.name != port['id'] and
virtual_ip in utils.get_ovn_port_addresses(lsp)]
def _get_port_options(self, port):
context = n_context.get_admin_context()
binding_prof = utils.validate_and_get_data_from_binding_profile(port)
@ -248,7 +241,8 @@ class OVNClient(object):
subnet['cidr'].split('/')[1])
# Check if the port being created is a virtual port
parents = self.get_virtual_port_parents(ip_addr, port)
parents = utils.get_virtual_port_parents(
self._nb_idl, ip_addr, port['network_id'], port['id'])
if not parents:
continue

View File

@ -14,6 +14,7 @@
# limitations under the License.
import copy
from unittest import mock
from neutron_lib.api.definitions import port_security as psec
from neutron_lib.api import validators
@ -24,6 +25,7 @@ from neutron_lib.exceptions import port_security as psec_exc
from neutron_lib.plugins import directory
from webob import exc
from neutron.common.ovn import utils as ovn_utils
from neutron.db import db_base_plugin_v2
from neutron.db import portsecurity_db
from neutron.db import securitygroups_db
@ -177,6 +179,12 @@ class PortSecurityDBTestCase(PortSecurityTestCase):
class TestPortSecurity(PortSecurityDBTestCase):
def setUp(self, plugin=None, service_plugins=None):
super().setUp(plugin)
self.mock_vp_parents = mock.patch.object(
ovn_utils, 'get_virtual_port_parents', return_value=None).start()
def test_create_network_with_portsecurity_mac(self):
res = self._create_network('json', 'net1', True)
net = self.deserialize('json', res)

View File

@ -519,3 +519,22 @@ class TestDBInconsistenciesPeriodics(testlib_api.SqlTestCaseLight,
mock.call('Logical_Router_Port', 'lrp-port1', ('options', opt))]
self.fake_ovn_client._nb_idl.db_set.assert_has_calls(
expected_calls)
@mock.patch.object(utils, 'get_virtual_port_parents',
return_value=[mock.ANY])
def test_update_port_virtual_type(self, *args):
nb_idl = self.fake_ovn_client._nb_idl
lsp0 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp0', 'type': ''})
lsp1 = fakes.FakeOvsdbRow.create_one_ovsdb_row(
attrs={'name': 'lsp1', 'type': constants.LSP_TYPE_VIRTUAL})
port0 = {'fixed_ips': [{'ip_address': mock.ANY}],
'network_id': mock.ANY, 'id': mock.ANY}
nb_idl.lsp_list.return_value.execute.return_value = (lsp0, lsp1)
self.fake_ovn_client._plugin.get_port.return_value = port0
self.assertRaises(
periodics.NeverAgain, self.periodic.update_port_virtual_type)
expected_calls = [mock.call('Logical_Switch_Port', lsp0.uuid,
('type', constants.LSP_TYPE_VIRTUAL))]
nb_idl.db_set.assert_has_calls(expected_calls)

View File

@ -121,7 +121,7 @@ class TestOVNMechanismDriver(test_plugin.Ml2PluginV2TestCase):
p.start()
self.addCleanup(p.stop)
self._virtual_port_parents = mock.patch.object(
ovn_client.OVNClient, 'get_virtual_port_parents', return_value=[])
ovn_utils, 'get_virtual_port_parents', return_value=[])
self.virtual_port_parents = self._virtual_port_parents.start()
def test_delete_mac_binding_entries(self):
@ -2147,7 +2147,7 @@ class OVNMechanismDriverTestCase(test_plugin.Ml2PluginV2TestCase):
p.start()
self.addCleanup(p.stop)
self._virtual_port_parents = mock.patch.object(
ovn_client.OVNClient, 'get_virtual_port_parents', return_value=[])
ovn_utils, 'get_virtual_port_parents', return_value=[])
self.virtual_port_parents = self._virtual_port_parents.start()
@ -2289,7 +2289,7 @@ class TestOVNMechanismDriverSegment(test_segment.HostSegmentMappingTestCase):
self.addCleanup(p.stop)
self.context = context.get_admin_context()
self._virtual_port_parents = mock.patch.object(
ovn_client.OVNClient, 'get_virtual_port_parents', return_value=[])
ovn_utils, 'get_virtual_port_parents', return_value=[])
self.virtual_port_parents = self._virtual_port_parents.start()
def _test_segment_host_mapping(self):
@ -3167,7 +3167,7 @@ class TestOVNMechanismDriverSecurityGroup(
self.ctx = context.get_admin_context()
revision_plugin.RevisionPlugin()
self._virtual_port_parents = mock.patch.object(
ovn_client.OVNClient, 'get_virtual_port_parents', return_value=[])
ovn_utils, 'get_virtual_port_parents', return_value=[])
self.virtual_port_parents = self._virtual_port_parents.start()
def _delete_default_sg_rules(self, security_group_id):
@ -3490,7 +3490,7 @@ class TestOVNMechanismDriverMetadataPort(test_plugin.Ml2PluginV2TestCase):
p.start()
self.addCleanup(p.stop)
self._virtual_port_parents = mock.patch.object(
ovn_client.OVNClient, 'get_virtual_port_parents', return_value=[])
ovn_utils, 'get_virtual_port_parents', return_value=[])
self.virtual_port_parents = self._virtual_port_parents.start()
def _create_fake_dhcp_port(self, device_id):
@ -3695,10 +3695,12 @@ class TestOVNVVirtualPort(OVNMechanismDriverTestCase):
self.subnet = self._make_subnet(
self.fmt, {'network': self.net},
'10.0.0.1', '10.0.0.0/24')['subnet']
self.mock_vp_parents = mock.patch.object(
ovn_utils, 'get_virtual_port_parents', return_value=None).start()
def test_create_port_with_virtual_type_and_options(self):
fake_parents = ['parent-0', 'parent-1']
self.virtual_port_parents.return_value = fake_parents
self.mock_vp_parents.return_value = fake_parents
for device_owner in ('', 'myVIPowner'):
port = {'id': 'virt-port',
'mac_address': '00:00:00:00:00:00',