[Bug 2085409] Re: [SRU] Heat wrongly passing "no_fixed_ips" to Neutron
James Page
2085409 at bugs.launchpad.net
Tue Dec 17 12:55:11 UTC 2024
I've reviewed and uploaded updates for all impacted series based on the
debdiff's provided above.
I should be able to push the fix into devel today (an eventlet issue was
blocking which is now worked-around).
Thanks Rodrigo
--
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:
[SRU] Heat wrongly passing "no_fixed_ips" to Neutron
Status in Ubuntu Cloud Archive:
New
Status in Ubuntu Cloud Archive antelope series:
New
Status in Ubuntu Cloud Archive bobcat series:
New
Status in Ubuntu Cloud Archive caracal series:
New
Status in Ubuntu Cloud Archive dalmation series:
New
Status in Ubuntu Cloud Archive epoxy series:
New
Status in heat package in Ubuntu:
Confirmed
Status in heat source package in Jammy:
New
Status in heat source package in Noble:
New
Status in heat source package in Oracular:
New
Status in heat source package in Plucky:
Confirmed
Bug description:
====== SRU TEMPLATE AT THE BOTTOM ======
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)
============
SRU TEMPLATE
============
===============
SRU DESCRIPTION
===============
[Impact]
A mishandling of port parameters in the Nova class when creating
instances is causing the issue where the user cannot specify a network
+ subnet + no specific IP combination to create a VM. Typically users
would specify only the network when creating VMs where the specific IP
or port doesn't matter, but if the network has multiple subnets, then
the user has to specify the desired subnet, triggering the issue
because the current code is incorrectly expecting an IP to be also
specified in such case, not considering the scenario where the IP
wouldn't be specified:
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]})
So if a subnet is specified, the logic is coded to use a fixed ip, not
allowing the no_fixed_ips scenario. The fix overrides the logic to not
use a fixed_ip in such case:
if net_data.get(neutron_port.Port.NO_FIXED_IPS):
kwargs.update({'fixed_ips': []})
[Test case]
1) Deploy OpenStack with Heat
2) Copy the following template adjusting the image, flavor, network
and subnet IDs accordingly:
heat_template_version: wallaby
resources:
server_test:
type: OS::Nova::Server
properties:
name: server_test
flavor: 6
image: "cirros-0.4.0"
networks:
- network: cdac5ad3-3084-4885-85a6-b1c547cd3136
port_extra_properties:
port_security_enabled: false
subnet: 68150f54-4548-4d65-a3ae-1714d0156f18
3) deploy the template using:
openstack stack create -t template.yaml stack1
4) Check the status using "openstack stack list". Stack creation
should fail, and upon running "openstack stack show" you should see
the following error message (except for a different req-ID):
| stack_status_reason | Resource CREATE failed: BadRequest: resources.server_test: Unrecognized attribute(s) 'no_fixed_ips' |
| | Neutron server returns request_ids: ['req-b3a2a896-2c0f-47f3-8fe5-68b15d7e6824']
5) Install fixed package
6) Retry step (4) and it should succeed creating the stack
[Regression Potential]
The code change is affecting the path of creating all VMs. This
default path is being tested upstream and it passed CI. The code
modifications introduces behavioral changes on the specific
situations, specifically when specifying a subnet. This code path is
being tested in the SRU. Given the current coverage, we can assume any
potential regression has its scope limited to the affected scenario
that causes the bug, or to the scenario where a subnet is specified
along with an IP/port. However, the code logic seems to be mutually
exclusive at [0], indicating that the parameters that lead to one
scenario shouldn't cause influence the behavior of the other,
therefore we can further assume that the scope of the change is
further limited to only this scenario.
[0]
https://github.com/openstack/heat/blob/dbc298b4ccc8f776791745496ff303387ad88f24/heat/engine/resources/openstack/neutron/port.py#L457
[Other Info]
None.
To manage notifications about this bug go to:
https://bugs.launchpad.net/cloud-archive/+bug/2085409/+subscriptions
More information about the Ubuntu-openstack-bugs
mailing list