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 commit3c93da7bdf
) (cherry picked from commit316b55440a
) (cherry picked from commit8c79b631aa
) (cherry picked from commitd6f395378c
)
This commit is contained in:
parent
2eb44d5985
commit
3b9f1be5e5
|
@ -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:
|
||||
|
|
|
@ -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):
|
||||
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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',
|
||||
|
|
Loading…
Reference in New Issue