From b2b40b6a8caeb768239715f0103ab700e47b7019 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Maciej=20J=C3=B3zefczyk?= Date: Mon, 1 Jun 2020 10:17:02 +0000 Subject: [PATCH] [OVN] Use new distributed device_owner for OVN distributed services OVN distributed services like Metadata and DHCP uses now DEVICE_OWNER_DHCP device_owner which isn't distributed by its nature. To fully use benefits of OVN Distributed ports (localports) [1] and to not overlap with Neutron logic created for not-distributed ports we should use new device_owner. In this change we need also to bump minimum required neutron-lib to 2.4.0. [1] https://www.ovn.org/support/dist-docs/ovn-nb.5.txt Change-Id: I0a69f1bddaa7030c7287216e62ec1ac6dd381475 --- lower-constraints.txt | 2 +- neutron/common/_constants.py | 3 +- .../alembic_migrations/versions/EXPAND_HEAD | 2 +- ...6107509ccd_ovn_distributed_device_owner.py | 52 +++++++++++++++++++ neutron/ipam/requests.py | 3 +- .../ovn/mech_driver/ovsdb/ovn_client.py | 22 ++++++-- .../ovn/mech_driver/ovsdb/ovn_db_sync.py | 6 ++- .../ovn/mech_driver/ovsdb/test_ovn_db_sync.py | 4 +- neutron/tests/unit/ipam/test_requests.py | 7 +++ .../ovn/mech_driver/test_mech_driver.py | 31 ++++++++--- requirements.txt | 2 +- 11 files changed, 116 insertions(+), 18 deletions(-) create mode 100644 neutron/db/migration/alembic_migrations/versions/victoria/expand/fd6107509ccd_ovn_distributed_device_owner.py diff --git a/lower-constraints.txt b/lower-constraints.txt index 7eaa6257e7d..5b6d6d9da52 100644 --- a/lower-constraints.txt +++ b/lower-constraints.txt @@ -51,7 +51,7 @@ msgpack-python==0.4.0 munch==2.1.0 netaddr==0.7.18 netifaces==0.10.4 -neutron-lib==2.3.0 +neutron-lib==2.4.0 openstacksdk==0.31.2 os-client-config==1.28.0 os-ken==0.3.0 diff --git a/neutron/common/_constants.py b/neutron/common/_constants.py index 2260441c1e0..05dc54861ed 100644 --- a/neutron/common/_constants.py +++ b/neutron/common/_constants.py @@ -75,4 +75,5 @@ IDPOOL_SELECT_SIZE = 100 # finds out that all existing IP Allocations are associated with ports # with these owners, it will allow subnet deletion to proceed with the # IP allocations being cleaned up by cascade. -AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP] +AUTO_DELETE_PORT_OWNERS = [constants.DEVICE_OWNER_DHCP, + constants.DEVICE_OWNER_DISTRIBUTED] diff --git a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD index 6c4a8611f30..4410f68cd04 100644 --- a/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD +++ b/neutron/db/migration/alembic_migrations/versions/EXPAND_HEAD @@ -1 +1 @@ -dfe425060830 +fd6107509ccd diff --git a/neutron/db/migration/alembic_migrations/versions/victoria/expand/fd6107509ccd_ovn_distributed_device_owner.py b/neutron/db/migration/alembic_migrations/versions/victoria/expand/fd6107509ccd_ovn_distributed_device_owner.py new file mode 100644 index 00000000000..feaa5f44b80 --- /dev/null +++ b/neutron/db/migration/alembic_migrations/versions/victoria/expand/fd6107509ccd_ovn_distributed_device_owner.py @@ -0,0 +1,52 @@ +# Copyright 2020 OpenStack Foundation +# +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. +# + +from alembic import op +from neutron_lib import constants +import sqlalchemy as sa + +"""ovn_distributed_device_owner + +Revision ID: fd6107509ccd +Revises: 5c85685d616d +Create Date: 2020-06-01 11:16:58.312355 + +""" + +# revision identifiers, used by Alembic. +revision = 'fd6107509ccd' +down_revision = 'dfe425060830' + +PORTS_TABLE = 'ports' +OVN_METADATA_PREFIX = 'ovnmeta' + + +def upgrade(): + update_device_owner_ovn_distributed_ports() + + +def update_device_owner_ovn_distributed_ports(): + ports = sa.Table(PORTS_TABLE, + sa.MetaData(), + sa.Column('device_owner', sa.String(255)), + sa.Column('device_id', sa.String(255))) + + session = sa.orm.Session(bind=op.get_bind()) + with session.begin(subtransactions=True): + session.execute(ports.update().values( + device_owner=constants.DEVICE_OWNER_DISTRIBUTED).where( + ports.c.device_owner == constants.DEVICE_OWNER_DHCP).where( + ports.c.device_id.like('{}%'.format(OVN_METADATA_PREFIX)))) + session.commit() diff --git a/neutron/ipam/requests.py b/neutron/ipam/requests.py index 1a4fcf1967e..2b90da786d7 100644 --- a/neutron/ipam/requests.py +++ b/neutron/ipam/requests.py @@ -285,7 +285,8 @@ class AddressRequestFactory(object): elif ip_dict.get('eui64_address'): return AutomaticAddressRequest(prefix=ip_dict['subnet_cidr'], mac=ip_dict['mac']) - elif port['device_owner'] == constants.DEVICE_OWNER_DHCP: + elif (port['device_owner'] == constants.DEVICE_OWNER_DHCP or + port['device_owner'] == constants.DEVICE_OWNER_DISTRIBUTED): # preserve previous behavior of DHCP ports choosing start of pool return PreferNextAddressRequest() else: diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py index 453a432156c..22471580f49 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_client.py @@ -259,7 +259,11 @@ class OVNClient(object): # Only adjust the OVN type if the port is not owned by Neutron # DHCP agents. - if (port['device_owner'] == const.DEVICE_OWNER_DHCP and + # TODO(mjozefcz): Remove const.DEVICE_OWNER_DHCP + # from get_ports in W-release. + if (port['device_owner'] in [ + const.DEVICE_OWNER_DISTRIBUTED, + const.DEVICE_OWNER_DHCP] and not utils.is_neutron_dhcp_agent_port(port)): port_type = 'localport' @@ -2030,9 +2034,19 @@ class OVNClient(object): if not ovn_conf.is_ovn_metadata_enabled(): return + # TODO(mjozefcz): Remove const.DEVICE_OWNER_DHCP + # from get_ports in W-release. ports = self._plugin.get_ports(context, filters=dict( - network_id=[network_id], device_owner=[const.DEVICE_OWNER_DHCP])) - + network_id=[network_id], + device_owner=[ + const.DEVICE_OWNER_DHCP, + const.DEVICE_OWNER_DISTRIBUTED])) + # TODO(mjozefcz): Remove this compatibility code in W release. + # First look for const.DEVICE_OWNER_DISTRIBUTED and then for + # const.DEVICE_OWNER_DHCP. + for port in ports: + if port['device_owner'] == const.DEVICE_OWNER_DISTRIBUTED: + return port # Metadata ports are DHCP ports not belonging to the Neutron # DHCP agents for port in ports: @@ -2054,7 +2068,7 @@ class OVNClient(object): port = {'port': {'network_id': network['id'], 'tenant_id': network['project_id'], - 'device_owner': const.DEVICE_OWNER_DHCP, + 'device_owner': const.DEVICE_OWNER_DISTRIBUTED, 'device_id': 'ovnmeta-%s' % network['id']}} # TODO(boden): rehome create_port into neutron-lib p_utils.create_port(self._plugin, context, port) diff --git a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py index 38fb11eb686..9b39f8f0e11 100644 --- a/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py +++ b/neutron/plugins/ml2/drivers/ovn/mech_driver/ovsdb/ovn_db_sync.py @@ -742,10 +742,14 @@ class OvnNbSynchronizer(OvnDbSynchronizer): if not ovn_conf.is_ovn_metadata_enabled(): return LOG.debug('OVN sync metadata ports started') + # TODO(mjozefcz): Remove constants.DEVICE_OWNER_DHCP + # from get_ports in W-release. for net in self.core_plugin.get_networks(ctx): dhcp_ports = self.core_plugin.get_ports(ctx, filters=dict( network_id=[net['id']], - device_owner=[constants.DEVICE_OWNER_DHCP])) + device_owner=[ + constants.DEVICE_OWNER_DISTRIBUTED, + constants.DEVICE_OWNER_DHCP])) for port in dhcp_ports: # Do not touch the Neutron DHCP agents ports diff --git a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py index 88f66717916..e281da9f780 100644 --- a/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py +++ b/neutron/tests/functional/plugins/ml2/drivers/ovn/mech_driver/ovsdb/test_ovn_db_sync.py @@ -879,7 +879,7 @@ class TestOvnNbSync(base.TestOVNFunctionalBase): db_metadata_ports_ids = set() db_metadata_ports_nets = set() for port in db_ports['ports']: - if (port['device_owner'] == constants.DEVICE_OWNER_DHCP and + if (port['device_owner'] == constants.DEVICE_OWNER_DISTRIBUTED and port['device_id'].startswith('ovnmeta')): db_metadata_ports_ids.add(port['id']) db_metadata_ports_nets.add(port['network_id']) @@ -1341,7 +1341,7 @@ class TestOvnNbSync(base.TestOVNFunctionalBase): db_ports = self._list('ports') db_metadata_ports = [port for port in db_ports['ports'] if port['device_owner'] == - constants.DEVICE_OWNER_DHCP and + constants.DEVICE_OWNER_DISTRIBUTED and port['device_id'].startswith('ovnmeta')] lswitches = {} ports_to_delete = len(db_metadata_ports) / 2 diff --git a/neutron/tests/unit/ipam/test_requests.py b/neutron/tests/unit/ipam/test_requests.py index ad3d3203138..e4e5679209c 100644 --- a/neutron/tests/unit/ipam/test_requests.py +++ b/neutron/tests/unit/ipam/test_requests.py @@ -317,6 +317,13 @@ class TestAddressRequestFactory(base.BaseTestCase): ipam_req.AddressRequestFactory.get_request(None, port, ip), ipam_req.PreferNextAddressRequest) + def test_prefernext_address_request_on_distributed_port(self): + ip = {} + port = {'device_owner': constants.DEVICE_OWNER_DISTRIBUTED} + self.assertIsInstance( + ipam_req.AddressRequestFactory.get_request(None, port, ip), + ipam_req.PreferNextAddressRequest) + class TestSubnetRequestFactory(IpamSubnetRequestTestCase): diff --git a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py index b2b03a58c42..9e69853e97c 100644 --- a/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py +++ b/neutron/tests/unit/plugins/ml2/drivers/ovn/mech_driver/test_mech_driver.py @@ -2906,16 +2906,21 @@ class TestOVNMechanismDriverMetadataPort(test_plugin.Ml2PluginV2TestCase): p.start() self.addCleanup(p.stop) - def _create_fake_dhcp_port(self, device_id): - return {'network_id': 'fake', 'device_owner': const.DEVICE_OWNER_DHCP, + def _create_fake_dhcp_port(self, device_id, neutron_port=False): + port = {'network_id': 'fake', + 'device_owner': const.DEVICE_OWNER_DISTRIBUTED, 'device_id': device_id} + if neutron_port: + port['device_owner'] = const.DEVICE_OWNER_DHCP + return port @mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2, 'get_ports') def test__find_metadata_port(self, mock_get_ports): ports = [ - self._create_fake_dhcp_port('dhcp-0'), - self._create_fake_dhcp_port('dhcp-1'), - self._create_fake_dhcp_port(const.DEVICE_ID_RESERVED_DHCP_PORT), + self._create_fake_dhcp_port('dhcp-0', neutron_port=True), + self._create_fake_dhcp_port('dhcp-1', neutron_port=True), + self._create_fake_dhcp_port(const.DEVICE_ID_RESERVED_DHCP_PORT, + neutron_port=True), self._create_fake_dhcp_port('ovnmeta-0')] mock_get_ports.return_value = ports @@ -2923,6 +2928,20 @@ class TestOVNMechanismDriverMetadataPort(test_plugin.Ml2PluginV2TestCase): self.ctx, 'fake-net-id') self.assertEqual('ovnmeta-0', md_port['device_id']) + @mock.patch.object(db_base_plugin_v2.NeutronDbPluginV2, 'get_ports') + def test__find_metadata_port_compat(self, mock_get_ports): + ports = [ + self._create_fake_dhcp_port('dhcp-0', neutron_port=True), + self._create_fake_dhcp_port('dhcp-1', neutron_port=True), + self._create_fake_dhcp_port(const.DEVICE_ID_RESERVED_DHCP_PORT, + neutron_port=True), + self._create_fake_dhcp_port('ovnmeta-0', neutron_port=True)] + mock_get_ports.return_value = ports + + md_port = self.mech_driver._ovn_client._find_metadata_port( + self.ctx, 'fake-net-id') + self.assertEqual('ovnmeta-0', md_port['device_id']) + def test_metadata_port_on_network_create(self): """Check metadata port create. @@ -2958,7 +2977,7 @@ class TestOVNMechanismDriverMetadataPort(test_plugin.Ml2PluginV2TestCase): # Create a network:dhcp owner port just as how Neutron DHCP # agent would do. with self.port(subnet=subnet1, - device_owner=const.DEVICE_OWNER_DHCP, + device_owner=const.DEVICE_OWNER_DISTRIBUTED, device_id='dhcpxxxx', set_context=True, tenant_id='test'): with self.subnet(network=net1, cidr='20.0.0.0/24'): diff --git a/requirements.txt b/requirements.txt index 1e53423d35e..2a3f4e9e596 100644 --- a/requirements.txt +++ b/requirements.txt @@ -16,7 +16,7 @@ Jinja2>=2.10 # BSD License (3 clause) keystonemiddleware>=4.17.0 # Apache-2.0 netaddr>=0.7.18 # BSD netifaces>=0.10.4 # MIT -neutron-lib>=2.3.0 # Apache-2.0 +neutron-lib>=2.4.0 # Apache-2.0 python-neutronclient>=6.7.0 # Apache-2.0 tenacity>=4.4.0 # Apache-2.0 SQLAlchemy>=1.2.0 # MIT