[Bug 2114984] [NEW] Incorrect handling of value_specs upon port update via stack update
Alejandro Santoyo Gonzalez
2114984 at bugs.launchpad.net
Thu Jun 19 13:08:07 UTC 2025
Public bug reported:
When a port's value_specs are updated via a stack update action, the
current value_specs content is wiped and replaced with the new values.
This causes issues in various use cases. For example:
- An SRIOV port has 'binding:profile' data such as {pci_slot='0000:17:02.1', pci_vendor_info='8086:1889', pf_mac_address='b4:96:91:f7:f1:d6'...}
- A stack update is triggered with the below template:
port2:
type: OS::Neutron::Port
properties:
network: { get_param: network2 }
binding:vnic_type: direct
value_specs: {"binding:profile": {"trusted": "on"}}
- Given that value_specs is updatable as per the schema definition, one would expect the above update to return a port with {pci_slot='0000:17:02.1', pci_vendor_info='8086:1889', pf_mac_address='b4:96:91:f7:f1:d6'..., "trusted": "on"}
- The issue is that the above update returns a port with binding:profile reduced to "binding:profile": {"trusted": "on"}
I guess one could argue that you need to pass the current value_specs +
new key/value(s) in the template, but I'd argue that is not the best
approach since values such as pf_mac_address change per port making it
difficult to use more generic templates for complex deployments.
Creating a new stack, defining this parameter doesn't cause the issue,
but this is expected as the remaining 'binding:profile' properties are
added during the creation process. However, setting the property via
"openstack port set --binding-profile trusted=true <port-id>" doesn't
cause the issue, meaning that the CLI client is correctly handling the
request.
Reproducer:
-----------
1. Create a simple stack by adding the below content to port.yaml and
running the stack creation
heat_template_version: 2015-10-15
resources:
my_port:
type: OS::Neutron::Port
properties:
network: e08dd8fd-b484-4d3c-864a-b96a2dad61e3
value_specs: {"binding:profile": {"trusted": "off"}}
then run:
openstack stack create -t ./port.yaml test-port-val-spec --wait
2. Change port.yaml to:
heat_template_version: 2015-10-15
resources:
my_port:
type: OS::Neutron::Port
properties:
network: e08dd8fd-b484-4d3c-864a-b96a2dad61e3
value_specs: {"binding:profile": {"spoof": "true"}}
3. Run the stack update:
openstack stack update -t ./port.yaml test-port-val-spec --wait
4. Check the 'Request body' passed to Neutron:
2025-06-19 11:36:33.216 4191656 DEBUG neutron.api.v2.base [req-e6dd17f9-b306-4add-b6fa-8cd81e364443 ...] Request body: {'port': {'device_id': '', 'device_owner': '', 'binding:host_id': None, 'binding:profile': {'spoof': 'true'}, 'dns_name': ''}} prepare_request_body /usr/lib/python3/dist-packages/neutron/api/v2/base.py:730
Notice how the 'binding:profile' data is wiped and only contains the new
value {'spoof': 'true'}, instead of {'trusted': 'off', 'spoof': 'true'}
5. Delete the stack and change port.yaml to how it looked in step 1
6. Recreate the stack
7. Update the port via openstack client:
openstack port set --binding-profile spoof=true --debug <port-id>
8. Check the 'Request body' passed to Neutron:
2025-06-19 12:46:42.961 3785 DEBUG neutron.api.v2.base
[req-2604229e-e09f-4e65-98a2-3cd682837d5f ...] Request body: {'port':
{'binding:profile': {'trusted': 'off', 'spoof': 'true'}}}
prepare_request_body /usr/lib/python3/dist-
packages/neutron/api/v2/base.py:730
Notice how the 'binding:profile' data is now updated with the old + new
key/value(s): 'binding:profile': {'trusted': 'off', 'spoof': 'true'}
Now, I believe the issue is in merge_value_specs(). The call tree is:
handle_update()
\_ prepare_update_properties()
\_ NeutronResource.merge_value_specs()
and merge_value_specs() seems not to be merging the values as it should. See the example behavior below with the corresponding values in each step:
def merge_value_specs(props, before_value_specs=None):
props: {'value_specs': {'binding:profile': {'trusted': 'off'}}}
value_spec_props = props.pop('value_specs')
props: {}
before_value_specs: {'binding:profile': {'spoof': 'off'}}
value_spec_props: {'binding:profile': {'trusted': 'off'}}
if value_spec_props is not None:
if before_value_specs:
for k in list(value_spec_props):
if value_spec_props[k] == before_value_specs.get(k, None):
value_spec_props.pop(k)
props: {}
props.update(value_spec_props)
props: {'binding:profile': {'trusted': 'off'}}
I will submit a patch shortly.
** 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/2114984
Title:
Incorrect handling of value_specs upon port update via stack update
Status in heat package in Ubuntu:
New
Bug description:
When a port's value_specs are updated via a stack update action, the
current value_specs content is wiped and replaced with the new values.
This causes issues in various use cases. For example:
- An SRIOV port has 'binding:profile' data such as {pci_slot='0000:17:02.1', pci_vendor_info='8086:1889', pf_mac_address='b4:96:91:f7:f1:d6'...}
- A stack update is triggered with the below template:
port2:
type: OS::Neutron::Port
properties:
network: { get_param: network2 }
binding:vnic_type: direct
value_specs: {"binding:profile": {"trusted": "on"}}
- Given that value_specs is updatable as per the schema definition, one would expect the above update to return a port with {pci_slot='0000:17:02.1', pci_vendor_info='8086:1889', pf_mac_address='b4:96:91:f7:f1:d6'..., "trusted": "on"}
- The issue is that the above update returns a port with binding:profile reduced to "binding:profile": {"trusted": "on"}
I guess one could argue that you need to pass the current value_specs
+ new key/value(s) in the template, but I'd argue that is not the best
approach since values such as pf_mac_address change per port making it
difficult to use more generic templates for complex deployments.
Creating a new stack, defining this parameter doesn't cause the issue,
but this is expected as the remaining 'binding:profile' properties are
added during the creation process. However, setting the property via
"openstack port set --binding-profile trusted=true <port-id>" doesn't
cause the issue, meaning that the CLI client is correctly handling the
request.
Reproducer:
-----------
1. Create a simple stack by adding the below content to port.yaml and
running the stack creation
heat_template_version: 2015-10-15
resources:
my_port:
type: OS::Neutron::Port
properties:
network: e08dd8fd-b484-4d3c-864a-b96a2dad61e3
value_specs: {"binding:profile": {"trusted": "off"}}
then run:
openstack stack create -t ./port.yaml test-port-val-spec --wait
2. Change port.yaml to:
heat_template_version: 2015-10-15
resources:
my_port:
type: OS::Neutron::Port
properties:
network: e08dd8fd-b484-4d3c-864a-b96a2dad61e3
value_specs: {"binding:profile": {"spoof": "true"}}
3. Run the stack update:
openstack stack update -t ./port.yaml test-port-val-spec --wait
4. Check the 'Request body' passed to Neutron:
2025-06-19 11:36:33.216 4191656 DEBUG neutron.api.v2.base [req-e6dd17f9-b306-4add-b6fa-8cd81e364443 ...] Request body: {'port': {'device_id': '', 'device_owner': '', 'binding:host_id': None, 'binding:profile': {'spoof': 'true'}, 'dns_name': ''}} prepare_request_body /usr/lib/python3/dist-packages/neutron/api/v2/base.py:730
Notice how the 'binding:profile' data is wiped and only contains the
new value {'spoof': 'true'}, instead of {'trusted': 'off', 'spoof':
'true'}
5. Delete the stack and change port.yaml to how it looked in step 1
6. Recreate the stack
7. Update the port via openstack client:
openstack port set --binding-profile spoof=true --debug <port-id>
8. Check the 'Request body' passed to Neutron:
2025-06-19 12:46:42.961 3785 DEBUG neutron.api.v2.base
[req-2604229e-e09f-4e65-98a2-3cd682837d5f ...] Request body: {'port':
{'binding:profile': {'trusted': 'off', 'spoof': 'true'}}}
prepare_request_body /usr/lib/python3/dist-
packages/neutron/api/v2/base.py:730
Notice how the 'binding:profile' data is now updated with the old +
new key/value(s): 'binding:profile': {'trusted': 'off', 'spoof':
'true'}
Now, I believe the issue is in merge_value_specs(). The call tree is:
handle_update()
\_ prepare_update_properties()
\_ NeutronResource.merge_value_specs()
and merge_value_specs() seems not to be merging the values as it should. See the example behavior below with the corresponding values in each step:
def merge_value_specs(props, before_value_specs=None):
props: {'value_specs': {'binding:profile': {'trusted':
'off'}}}
value_spec_props = props.pop('value_specs')
props: {}
before_value_specs: {'binding:profile': {'spoof': 'off'}}
value_spec_props: {'binding:profile': {'trusted': 'off'}}
if value_spec_props is not None:
if before_value_specs:
for k in list(value_spec_props):
if value_spec_props[k] == before_value_specs.get(k, None):
value_spec_props.pop(k)
props: {}
props.update(value_spec_props)
props: {'binding:profile': {'trusted': 'off'}}
I will submit a patch shortly.
To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/heat/+bug/2114984/+subscriptions
More information about the Ubuntu-openstack-bugs
mailing list