diff --git a/neutron/db/ipam_backend_mixin.py b/neutron/db/ipam_backend_mixin.py index 88c22ae987b..dc189df1bc2 100644 --- a/neutron/db/ipam_backend_mixin.py +++ b/neutron/db/ipam_backend_mixin.py @@ -599,12 +599,18 @@ class IpamBackendMixin(db_base_plugin_common.DbBasePluginCommon): def _query_filter_service_subnets(self, query, service_type): # TODO(korzen) use SubnetServiceType OVO here + Subnet = models_v2.Subnet ServiceType = sst_model.SubnetServiceType query = query.add_entity(ServiceType) query = query.outerjoin(ServiceType) - query = query.filter(or_(ServiceType.service_type.is_(None), - ServiceType.service_type == service_type)) - return query.from_self(models_v2.Subnet) + query = query.filter(or_( + ServiceType.service_type.is_(None), + ServiceType.service_type == service_type, + # Allow DHCP ports to be created on subnets of any + # service type when DHCP is enabled on the subnet. + and_(Subnet.enable_dhcp.is_(True), + service_type == const.DEVICE_OWNER_DHCP))) + return query.from_self(Subnet) @staticmethod def _query_filter_by_segment_host_mapping(query, host): diff --git a/neutron/tests/unit/extensions/test_subnet_service_types.py b/neutron/tests/unit/extensions/test_subnet_service_types.py index 1a1f9e130fb..030ec3c0203 100644 --- a/neutron/tests/unit/extensions/test_subnet_service_types.py +++ b/neutron/tests/unit/extensions/test_subnet_service_types.py @@ -55,7 +55,7 @@ class SubnetServiceTypesExtensionTestCase( self).setUp(plugin=plugin, ext_mgr=ext_mgr) def _create_service_subnet(self, service_types=None, cidr=None, - network=None): + network=None, enable_dhcp=False): if not network: with self.network() as network: pass @@ -65,7 +65,8 @@ class SubnetServiceTypesExtensionTestCase( args = {'net_id': network['id'], 'tenant_id': network['tenant_id'], 'cidr': cidr, - 'ip_version': self.IP_VERSION} + 'ip_version': self.IP_VERSION, + 'enable_dhcp': enable_dhcp} if service_types: args['service_types'] = service_types return self._create_subnet(self.fmt, **args) @@ -274,6 +275,24 @@ class SubnetServiceTypesExtensionTestCase( def test_create_port_exhausted_subnet_no_fallback(self): self.test_create_port_exhausted_subnet(fallback=False) + def test_create_dhcp_port_compute_subnet(self, enable_dhcp=True): + with self.network() as network: + pass + res = self._create_service_subnet(['compute:nova'], + network=network, + enable_dhcp=enable_dhcp) + subnet = self.deserialize('json', res)['subnet'] + network = network['network'] + port = self._create_port(self.fmt, + net_id=network['id'], + tenant_id=network['tenant_id'], + fixed_ips=[{'subnet_id': subnet['id']}], + device_owner='network:dhcp') + self._assert_port_res(port, 'network:dhcp', subnet, enable_dhcp) + + def test_create_dhcp_port_compute_subnet_no_dhcp(self): + self.test_create_dhcp_port_compute_subnet(enable_dhcp=False) + class SubnetServiceTypesExtensionTestCasev6( SubnetServiceTypesExtensionTestCase): diff --git a/releasenotes/notes/dhcp-for-service-subnets-06f03d902f2fb738.yaml b/releasenotes/notes/dhcp-for-service-subnets-06f03d902f2fb738.yaml new file mode 100644 index 00000000000..704d549ace0 --- /dev/null +++ b/releasenotes/notes/dhcp-for-service-subnets-06f03d902f2fb738.yaml @@ -0,0 +1,10 @@ +--- +fixes: + - A special case has been added to allow the creation of DHCP ports + on Service Subnets that do not have the service type "network:dhcp", + provided that the subnet has 'enable_dhcp' set to 'True'. + This fixes the recurring error seen when neutron attempts to + automatically create a DHCP port on a dhcp-enabled subnet after the + subnet is created. See bug report + `1636963 `_ for + more details.