diff options
author | Zuul <zuul@review.opendev.org> | 2022-02-08 22:56:47 +0000 |
---|---|---|
committer | Gerrit Code Review <review@openstack.org> | 2022-02-08 22:56:47 +0000 |
commit | f7fa3bf5fcfc39f8ac9dcfa126747e376d801eb5 (patch) | |
tree | 9133dfefc3888ecc5bb54508c20c2224192f2b66 /nova/network | |
parent | b6fe7521afa8d42febc68f5f79782f7bcc3b568f (diff) | |
parent | 0f7f95b917b5a48779a327c45bd4a8558efcc7a3 (diff) | |
download | nova-f7fa3bf5fcfc39f8ac9dcfa126747e376d801eb5.tar.gz |
Merge "neutron: Rework how we check for extensions"
Diffstat (limited to 'nova/network')
-rw-r--r-- | nova/network/constants.py | 47 | ||||
-rw-r--r-- | nova/network/neutron.py | 178 |
2 files changed, 152 insertions, 73 deletions
diff --git a/nova/network/constants.py b/nova/network/constants.py index 4a08c870f6..eff49022c1 100644 --- a/nova/network/constants.py +++ b/nova/network/constants.py @@ -13,23 +13,40 @@ # License for the specific language governing permissions and limitations # under the License. -QOS_QUEUE = 'QoS Queue' -NET_EXTERNAL = 'router:external' -VNIC_INDEX_EXT = 'VNIC Index' -DNS_INTEGRATION = 'DNS Integration' -MULTI_NET_EXT = 'Multi Provider Network' -FIP_PORT_DETAILS = 'Floating IP Port Details Extension' -SUBSTR_PORT_FILTERING = 'IP address substring filtering' -PORT_BINDING = 'Port Binding' -PORT_BINDING_EXTENDED = 'Port Bindings Extended' -DEFAULT_SECGROUP = 'default' +# Port fields + BINDING_PROFILE = 'binding:profile' BINDING_HOST_ID = 'binding:host_id' -MIGRATING_ATTR = 'migrating_to' -L3_NETWORK_TYPES = ['vxlan', 'gre', 'geneve'] -ALLOCATION = 'allocation' RESOURCE_REQUEST = 'resource_request' REQUEST_GROUPS = 'request_groups' -SEGMENT = 'Segment' NUMA_POLICY = 'numa_affinity_policy' -RESOURCE_REQUEST_GROUPS_EXTENSION = "Port Resource Request Groups" + +# Binding profile fields + +MIGRATING_ATTR = 'migrating_to' +ALLOCATION = 'allocation' + +# Core extensions + +DNS_INTEGRATION = 'dns-integration' +MULTI_PROVIDER = 'multi-provider' +FIP_PORT_DETAILS = 'fip-port-details' +PORT_BINDING = 'binding' +PORT_BINDING_EXTENDED = 'binding-extended' +SUBSTR_PORT_FILTERING = 'ip-substring-filtering' +SEGMENT = 'segment' +RESOURCE_REQUEST_GROUPS = 'port-resource-request-groups' + +# Third-party extensions + +VNIC_INDEX = 'vnic-index' # this is provided by the vmware_nsx project +QOS_QUEUE = 'qos-queue' # TODO(stephenfin): what defines this? Xen? + +# Search fields + +NET_EXTERNAL = 'router:external' + +# Misc + +DEFAULT_SECGROUP = 'default' +L3_NETWORK_TYPES = ['vxlan', 'gre', 'geneve'] diff --git a/nova/network/neutron.py b/nova/network/neutron.py index 294ccaebbe..a3c7afdb7c 100644 --- a/nova/network/neutron.py +++ b/nova/network/neutron.py @@ -382,7 +382,8 @@ class API: # If a host was provided, delete any bindings between that # host and the ports as long as the host isn't the same as # the current instance.host. - has_binding_ext = self.supports_port_binding_extension(context) + has_binding_ext = self.has_port_binding_extension( + client=admin_client) if port_migrating and has_binding_ext: self._delete_port_bindings(context, ports, host) elif port_migrating: @@ -676,8 +677,12 @@ class API: # NOTE: For internal DNS integration (network does not have a # dns_domain), or if we cannot retrieve network info, we use the # admin client to reset dns_name. - if self._has_dns_extension() and not network.get('dns_domain'): + if ( + self.has_dns_extension(client=port_client) and + not network.get('dns_domain') + ): port_req_body['port']['dns_name'] = '' + try: port_client.update_port(port_id, port_req_body) except neutron_client_exc.PortNotFoundClient: @@ -1334,62 +1339,112 @@ class API: return (nets_in_requested_order, ports_in_requested_order, preexisting_port_ids, created_port_ids) - def _refresh_neutron_extensions_cache(self, context, neutron=None): + def _refresh_neutron_extensions_cache(self, client): """Refresh the neutron extensions cache when necessary.""" if (not self.last_neutron_extension_sync or ((time.time() - self.last_neutron_extension_sync) >= CONF.neutron.extension_sync_interval)): - if neutron is None: - neutron = get_client(context) - extensions_list = neutron.list_extensions()['extensions'] + extensions_list = client.list_extensions()['extensions'] self.last_neutron_extension_sync = time.time() self.extensions.clear() - self.extensions = {ext['name']: ext for ext in extensions_list} + self.extensions = {ext['alias']: ext for ext in extensions_list} + + def _has_extension(self, extension, context=None, client=None): + """Check if the provided neutron extension is enabled. - def _has_multi_provider_extension(self, context, neutron=None): - self._refresh_neutron_extensions_cache(context, neutron=neutron) - return constants.MULTI_NET_EXT in self.extensions + :param extension: The alias of the extension to check + :param client: keystoneauth1.adapter.Adapter + :param context: nova.context.RequestContext + :returns: True if the neutron extension is available, else False + """ + if client is None: + client = get_client(context) - def _has_dns_extension(self): - return constants.DNS_INTEGRATION in self.extensions + self._refresh_neutron_extensions_cache(client) + return extension in self.extensions - def _has_qos_queue_extension(self, context, neutron=None): - self._refresh_neutron_extensions_cache(context, neutron=neutron) - return constants.QOS_QUEUE in self.extensions + def has_multi_provider_extension(self, context=None, client=None): + """Check if the 'multi-provider' extension is enabled. - def _has_fip_port_details_extension(self, context, neutron=None): - self._refresh_neutron_extensions_cache(context, neutron=neutron) - return constants.FIP_PORT_DETAILS in self.extensions + This extension allows administrative users to define multiple physical + bindings for a logical network. + """ + return self._has_extension(constants.MULTI_PROVIDER, context, client) - def has_substr_port_filtering_extension(self, context): - self._refresh_neutron_extensions_cache(context) - return constants.SUBSTR_PORT_FILTERING in self.extensions + def has_dns_extension(self, context=None, client=None): + """Check if the 'dns-integration' extension is enabled. - def _has_segment_extension(self, context, neutron=None): - self._refresh_neutron_extensions_cache(context, neutron=neutron) - return constants.SEGMENT in self.extensions + This extension adds the 'dns_name' and 'dns_assignment' attributes to + port resources. + """ + return self._has_extension(constants.DNS_INTEGRATION, context, client) # TODO(gibi): Remove all branches where this is False after Neutron made # the this extension mandatory. In Xena this extension will be optional to # support the scenario where Neutron upgraded first. So Neutron can mark # this mandatory earliest in Yoga. - def has_extended_resource_request_extension(self, context, neutron=None): - self._refresh_neutron_extensions_cache(context, neutron=neutron) - return constants.RESOURCE_REQUEST_GROUPS_EXTENSION in self.extensions + def has_extended_resource_request_extension( + self, context=None, client=None, + ): + return self._has_extension( + constants.RESOURCE_REQUEST_GROUPS, context, client, + ) + + # TODO(stephenfin): This is optionally used by the XenAPI virt driver, but + # I can't find what defines it and suspect it's dead code. Consider + # removing the functionality + def has_qos_queue_extension(self, context=None, client=None): + """Check if the 'qos-queue' extension is enabled. - def supports_port_binding_extension(self, context): - """This is a simple check to see if the neutron "binding-extended" - extension exists and is enabled. + This extension is provided by a XenServer neutron plugin...we think. + """ + return self._has_extension(constants.QOS_QUEUE, context, client) - The "binding-extended" extension allows nova to bind a port to multiple - hosts at the same time, like during live migration. + def has_vnic_index_extension(self, context=None, client=None): + """Check if the 'vnic-index' extension is enabled. - :param context: the user request context - :returns: True if the binding-extended API extension is available, - False otherwise + This extension is provided by the VMWare NSX neutron plugin. + """ + return self._has_extension(constants.VNIC_INDEX, context, client) + + def has_fip_port_details_extension(self, context=None, client=None): + """Check if the 'fip-port-details' extension is enabled. + + This extension adds the 'port_details' attribute to floating IPs. """ - self._refresh_neutron_extensions_cache(context) - return constants.PORT_BINDING_EXTENDED in self.extensions + return self._has_extension(constants.FIP_PORT_DETAILS, context, client) + + def has_substr_port_filtering_extension(self, context=None, client=None): + """Check if the 'ip-substring-filtering' extension is enabled. + + This extension adds support for filtering ports by using part of an IP + address. + """ + return self._has_extension( + constants.SUBSTR_PORT_FILTERING, context, client + ) + + def has_segment_extension(self, context=None, client=None): + """Check if the neutron 'segment' extension is enabled. + + This extension exposes information about L2 segments of a network. + """ + return self._has_extension( + constants.SEGMENT, context, client, + ) + + def has_port_binding_extension(self, context=None, client=None): + """Check if the neutron 'binding-extended' extension is enabled. + + This extensions exposes port bindings of a virtual port to external + application. + + This extension allows nova to bind a port to multiple hosts at the same + time, like during live migration. + """ + return self._has_extension( + constants.PORT_BINDING_EXTENDED, context, client + ) def bind_ports_to_host(self, context, instance, host, vnic_types=None, port_profiles=None): @@ -1403,7 +1458,7 @@ class API: In the event of an error, any ports which were successfully bound to the host should have those host bindings removed from the ports. - This method should not be used if "supports_port_binding_extension" + This method should not be used if "has_port_binding_extension" returns False. :param context: the user request context @@ -1482,7 +1537,7 @@ class API: def delete_port_binding(self, context, port_id, host): """Delete the port binding for the given port ID and host - This method should not be used if "supports_port_binding_extension" + This method should not be used if "has_port_binding_extension" returns False. :param context: The request context for the operation. @@ -1602,7 +1657,10 @@ class API: If the extensions loaded contain QOS_QUEUE then pass the rxtx_factor. """ - if self._has_qos_queue_extension(context, neutron=neutron): + if neutron is None: + neutron = get_client(context) + + if self.has_qos_queue_extension(client=neutron): flavor = instance.get_flavor() rxtx_factor = flavor.get('rxtx_factor') port_req_body['port']['rxtx_factor'] = rxtx_factor @@ -1612,7 +1670,7 @@ class API: port_req_body, port_arq) - if self._has_dns_extension(): + if self.has_dns_extension(client=neutron): # If the DNS integration extension is enabled in Neutron, most # ports will get their dns_name attribute set in the port create or # update requests in allocate_for_instance. So we just add the @@ -1638,7 +1696,8 @@ class API: an additional update request. Only a very small fraction of ports will require this additional update request. """ - if self._has_dns_extension() and network.get('dns_domain'): + if self.has_dns_extension(client=neutron) and network.get( + 'dns_domain'): try: port_req_body = {'port': {'dns_name': instance.hostname}} neutron.update_port(port_id, port_req_body) @@ -1650,7 +1709,7 @@ class API: 'name') % {'hostname': instance.hostname}) raise exception.InvalidInput(reason=msg) - def _reset_port_dns_name(self, network, port_id, neutron_client): + def _reset_port_dns_name(self, network, port_id, client): """Reset an instance port dns_name attribute to empty when using external DNS service. @@ -1660,10 +1719,11 @@ class API: request with a Neutron client using user's context, so that the DNS record can be found under user's zone and domain. """ - if self._has_dns_extension() and network.get('dns_domain'): + if self.has_dns_extension(client=client) and network.get( + 'dns_domain'): try: port_req_body = {'port': {'dns_name': ''}} - neutron_client.update_port(port_id, port_req_body) + client.update_port(port_id, port_req_body) except neutron_client_exc.NeutronClientException: LOG.exception("Failed to reset dns_name for port %s", port_id) @@ -2037,7 +2097,7 @@ class API: segments, the first segment that defines a physnet value will be used for the physnet name. """ - if self._has_multi_provider_extension(context, neutron=neutron): + if self.has_multi_provider_extension(client=neutron): network = neutron.show_network(net_id, fields='segments').get('network') segments = network.get('segments', {}) @@ -2712,7 +2772,7 @@ class API: # ...and retrieve the port details for the same reason, but only if # they're not already there because the fip-port-details extension is # present - if not self._has_fip_port_details_extension(context, client): + if not self.has_fip_port_details_extension(client=client): port_id = fip['port_id'] try: fip['port_details'] = client.show_port( @@ -2740,7 +2800,7 @@ class API: # ...and retrieve the port details for the same reason, but only if # they're not already there because the fip-port-details extension is # present - if not self._has_fip_port_details_extension(context, client): + if not self.has_fip_port_details_extension(client=client): port_id = fip['port_id'] try: fip['port_details'] = client.show_port( @@ -2779,7 +2839,7 @@ class API: # ...and retrieve the port details for the same reason, but only if # they're not already there because the fip-port-details extension is # present - if not self._has_fip_port_details_extension(context, client): + if not self.has_fip_port_details_extension(client=client): ports = {port['id']: port for port in client.list_ports( **{'tenant_id': project_id})['ports']} for fip in fips: @@ -2975,7 +3035,7 @@ class API: :raises: nova.exception.PortBindingActivationFailed if any port binding activation fails """ - if not self.supports_port_binding_extension(context): + if not self.has_port_binding_extension(context): # If neutron isn't new enough yet for the port "binding-extended" # API extension, we just no-op. The port binding host will be # be updated in migrate_instance_finish, which is functionally OK, @@ -3501,7 +3561,8 @@ class API: :raises: PortBindingDeletionFailed if port binding deletion fails. """ # First check to see if the port binding extension is supported. - if not self.supports_port_binding_extension(context): + client = get_client(context) + if not self.has_port_binding_extension(client=client): LOG.info("Neutron extension '%s' is not supported; not cleaning " "up port bindings for host %s.", constants.PORT_BINDING_EXTENDED, host, instance=instance) @@ -3692,9 +3753,8 @@ class API: :param vif: The VIF in question. :param index: The index on the instance for the VIF. """ - self._refresh_neutron_extensions_cache(context) - if constants.VNIC_INDEX_EXT in self.extensions: - neutron = get_client(context) + neutron = get_client(context) + if self.has_vnic_index_extension(client=neutron): port_req_body = {'port': {'vnic_index': index}} try: neutron.update_port(vif['id'], port_req_body) @@ -3717,10 +3777,11 @@ class API: either Segment extension isn't enabled in Neutron or if the network isn't configured for routing. """ - if not self._has_segment_extension(context): + client = get_client(context) + + if not self.has_segment_extension(client=client): return [] - client = get_client(context) try: # NOTE(sbauza): We can't use list_segments() directly because the # API is borked and returns both segments but also segmentation IDs @@ -3747,10 +3808,11 @@ class API: extension isn't enabled in Neutron or the provided subnet doesn't have segments (if the related network isn't configured for routing) """ - if not self._has_segment_extension(context): + client = get_client(context) + + if not self.has_segment_extension(client=client): return None - client = get_client(context) try: subnet = client.show_subnet(subnet_id)['subnet'] except neutron_client_exc.NeutronClientException as e: |