Public bug reported: Summary: --------
When creating an instance via the "OS::Nova::Server" resource type in a Heat template, specifying a network and a subnet to get a port created on the instance, it fails as below: 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource nic_info['port-id'] = self._create_internal_port( 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/heat/engine/resources/openstack/nova/server_network_mixin.py", line 103, in _create_internal_port 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource port = self.client('neutron').create_port({'port': kwargs})['port'] 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 822, in create_port 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource return self.post(self.ports_path, body=body) 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 361, in post 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource return self.do_request("POST", action, body=body, 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 297, in do_request 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource self._handle_fault_response(status_code, replybody, resp) 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 272, in _handle_fault_response 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource exception_handler_v20(status_code, error_body) 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource File "/usr/lib/python3/dist-packages/neutronclient/v2_0/client.py", line 90, in exception_handler_v20 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource raise client_exc(message=error_message, 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource neutronclient.common.exceptions.BadRequest: Unrecognized attribute(s) 'no_fixed_ips' 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource Neutron server returns request_ids: ['req-a5b98318-dd6d-468e-ac84-cd904060dd14'] 2024-10-21 15:11:27.924 5089 ERROR heat.engine.resource 2024-10-21 15:11:27.933 5089 INFO heat.engine.stack [req-e5c2d5d4-973c-4a69-bba5-3de9f62fc685 - - - - -] Stack CREATE FAILED (test-tests-zsdvasfbha-1-pfwwntfb5x5g-nodes-p4nciwpibd2g-0-l2t3ak6lgqsc): Resource CREATE failed: BadRequest: resources.host: Unrecognized attribute(s) 'no_fixed_ips' The reason why this fails is because for ports without fixed IPs, Neutron expects "fixed_ips = []" as it does not understand "no_fixed_ips", but when a subnet is specified, _create_internal_port() is called to do the API request to Neutron which internally calls _prepare_internal_port_kwargs(). Now, _prepare_internal_port_kwargs() builds the arguments to be passed to the request, including the extra port properties among which no_fixed_ips is included. First extra_props is checked and in our case is not None. Then port_extra_keys gets populated with neutron_port.Port.EXTRA_PROPERTIES which includes no_fixed_ips and that is never removed: from heat/engine/resources/openstack/neutron/port.py: ... EXTRA_PROPERTIES = ( VALUE_SPECS, ADMIN_STATE_UP, MAC_ADDRESS, ALLOWED_ADDRESS_PAIRS, VNIC_TYPE, QOS_POLICY, PORT_SECURITY_ENABLED, PROPAGATE_UPLINK_STATUS, NO_FIXED_IPS, ) = ( 'value_specs', 'admin_state_up', 'mac_address', 'allowed_address_pairs', 'binding:vnic_type', 'qos_policy', 'port_security_enabled', 'propagate_uplink_status', 'no_fixed_ips', ) ... from heat/engine/resources/openstack/nova/server_network_mixin.py: ... def _prepare_internal_port_kwargs(self, net_data, security_groups=None): kwargs = {'network_id': self._get_network_id(net_data)} fixed_ip = net_data.get(self.NETWORK_FIXED_IP) subnet = net_data.get(self.NETWORK_SUBNET) body = {} if fixed_ip: body['ip_address'] = fixed_ip if subnet: body['subnet_id'] = subnet # we should add fixed_ips only if subnet or ip were provided if body: kwargs.update({'fixed_ips': [body]}) if security_groups: sec_uuids = self.client_plugin( 'neutron').get_secgroup_uuids(security_groups) kwargs['security_groups'] = sec_uuids extra_props = net_data.get(self.NETWORK_PORT_EXTRA) if extra_props is not None: specs = extra_props.pop(neutron_port.Port.VALUE_SPECS) if specs: kwargs.update(specs) port_extra_keys = list(neutron_port.Port.EXTRA_PROPERTIES) port_extra_keys.remove(neutron_port.Port.ALLOWED_ADDRESS_PAIRS) for key in port_extra_keys: if extra_props.get(key) is not None: kwargs[key] = extra_props.get(key) allowed_address_pairs = extra_props.get( neutron_port.Port.ALLOWED_ADDRESS_PAIRS) if allowed_address_pairs is not None: for pair in allowed_address_pairs: if (neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS in pair and pair.get( neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS) is None): del pair[ neutron_port.Port.ALLOWED_ADDRESS_PAIR_MAC_ADDRESS] port_address_pairs = neutron_port.Port.ALLOWED_ADDRESS_PAIRS kwargs[port_address_pairs] = allowed_address_pairs ``` Again, the reason why this fails is because for ports without fixed IPs, Neutron expects "fixed_ips = []" as it does not understand "no_fixed_ips", so Heat should not be passing that. Commit [1] where the "no_fixed_ips" feature was introduced in Heat, added logic to avoid passing "no_fixed_ips" to Neutron, but that logic was never added for the case where the port is requested to be created via "OS::Nova::Server" (and there could be other edge cases missing this logic too) [1] https://opendev.org/openstack/heat/commit/9292264aa74d6d9e6e8f58045c7e3faf755ea725 Reproducer: ----------- Here's a template reproducing the issue: heat_template_version: wallaby resources: server_test: type: OS::Nova::Server properties: name: server_test config_drive: true flavor: m1.small image: "focal-raw" networks: - network: 0924ec50-c1b7-4ea9-bdd2-334772fd3398 port_extra_properties: port_security_enabled: false subnet: cb49c91d-62d0-4677-b121-0dcf0476ecf0 Openstack versions affected: --------------------------- >= Wallaby Potential diff for a fix: ------------------------- index 794406457..0e27e33b8 100644 --- a/heat/engine/resources/openstack/nova/server_network_mixin.py +++ b/heat/engine/resources/openstack/nova/server_network_mixin.py @@ -120,6 +120,8 @@ class ServerNetworkMixin(object): # we should add fixed_ips only if subnet or ip were provided if body: kwargs.update({'fixed_ips': [body]}) + if net_data.get(neutron_port.Port.NO_FIXED_IPS): + kwargs.update({'fixed_ips': []}) if security_groups: sec_uuids = self.client_plugin( @@ -133,6 +135,7 @@ class ServerNetworkMixin(object): kwargs.update(specs) port_extra_keys = list(neutron_port.Port.EXTRA_PROPERTIES) port_extra_keys.remove(neutron_port.Port.ALLOWED_ADDRESS_PAIRS) + port_extra_keys.remove(neutron_port.Port.NO_FIXED_IPS) for key in port_extra_keys: if extra_props.get(key) is not None: kwargs[key] = extra_props.get(key) ** Affects: heat (Ubuntu) Importance: Undecided Status: New -- You received this bug notification because you are a member of Ubuntu Bugs, which is subscribed to Ubuntu. https://bugs.launchpad.net/bugs/2085409 Title: Heat wrongly passing "no_fixed_ips" to Neutron To manage notifications about this bug go to: https://bugs.launchpad.net/ubuntu/+source/heat/+bug/2085409/+subscriptions -- ubuntu-bugs mailing list ubuntu-bugs@lists.ubuntu.com https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs