[Bug 2085409] [NEW] Heat wrongly passing "no_fixed_ips" to Neutron
Alejandro Santoyo Gonzalez
2085409 at bugs.launchpad.net
Wed Oct 23 07:19:15 UTC 2024
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
OpenStack, which is subscribed to heat in Ubuntu.
https://bugs.launchpad.net/bugs/2085409
Title:
Heat wrongly passing "no_fixed_ips" to Neutron
Status in heat package in Ubuntu:
New
Bug description:
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)
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/heat/+bug/2085409/+subscriptions
More information about the Ubuntu-openstack-bugs
mailing list