[Bug 2114984] Re: [SRU] Preserve auto-populated binding:profile fields during port update
Ubuntu Foundations Team Bug Bot
2114984 at bugs.launchpad.net
Thu Apr 30 04:19:34 UTC 2026
The attachment "stonking.debdiff" seems to be a debdiff. The ubuntu-
sponsors team has been subscribed to the bug report so that they can
review and hopefully sponsor the debdiff. If the attachment isn't a
patch, please remove the "patch" flag from the attachment, remove the
"patch" tag, and if you are member of the ~ubuntu-sponsors, unsubscribe
the team.
[This is an automated message performed by a Launchpad user owned by
~brian-murray, for any issue please contact him.]
** Tags added: patch
--
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:
[SRU] Preserve auto-populated binding:profile fields during port
update
Status in Ubuntu Cloud Archive:
New
Status in Ubuntu Cloud Archive caracal series:
New
Status in Ubuntu Cloud Archive dalmatian series:
Won't Fix
Status in Ubuntu Cloud Archive epoxy series:
New
Status in Ubuntu Cloud Archive flamingo series:
New
Status in Ubuntu Cloud Archive gazpacho series:
New
Status in Ubuntu Cloud Archive yoga series:
New
Status in heat package in Ubuntu:
New
Status in heat source package in Jammy:
New
Status in heat source package in Noble:
New
Status in heat source package in Questing:
New
Status in heat source package in Resolute:
New
Status in heat source package in Stonking:
New
Bug description:
[Impact]
When updating Neutron ports with `binding:profile` in `value_specs`
through Heat stack updates, auto-populated SR-IOV fields (such as
`pci_slot`, `pci_vendor_info`, `pf_mac_address`) are being lost. This
causes port configurations to become corrupted during stack updates.
Problem Scenario:
- An SR-IOV port is created with auto-populated `binding:profile` containing fields like `pci_slot='0000:17:02.1'`, `pci_vendor_info='8086:1889'`, etc.
- When the stack is updated with a new template that modifies only one field in `binding:profile` (e.g., adding `"trusted": "on"`), the entire `binding:profile` is replaced with only the new value
- This results in loss of critical SR-IOV descriptor fields, breaking port functionality
Root Cause:
The `merge_value_specs()` function was using only the template's old state as the "before" snapshot for merging. It didn't account for fields that Neutron automatically populates after port creation, causing these auto-populated fields to be treated as "removed from template" and deleted from the update request.
How the Fix Addresses This:
This patch implements a 3-way merge strategy for `binding:profile` updates:
1. Fetches the current live `binding:profile` state from Neutron API
2. Compares old vs new templates to identify which keys were explicitly changed
3. Applies only the changed keys to the current API state
4. Preserves all auto-populated and unchanged fields
5. Allows explicit removal of fields by setting them to `None` in the template
The fix ensures that Heat templates can safely update port
configurations without losing Neutron-managed fields.
[Test Plan]
Detailed test procedures and verification steps are documented in this OpenStack paste:
https://paste.openstack.org/show/bUeIQ1FTQgev9U3HEbA9/
The test covers:
- Initial port state with auto-populated binding:profile fields
- Adding new fields while preserving auto-populated ones
- Modifying existing fields
- Deleting fields using `null` values
- Complex scenarios with multiple field changes
- Edge cases with Neutron API availability
To execute the tests, follow the steps in the paste above. The fix
should preserve all auto-populated SR-IOV fields (e.g., `pci_slot`,
`pci_vendor_info`, `pf_mac_address`, `card_serial_number`) across
stack updates while allowing controlled modifications and deletions
when explicitly specified in the template.
[Where problems could occur]
If the API call to fetch current binding:profile from Neutron fails,
the stack update will fail visibly, which is intentional—failing fast
is better than silently losing SR-IOV fields. The fix modifies how
`merge_value_specs()` is called to exclude binding:profile when
already handled; potential regressions in merging other value_specs
fields should be caught by testing non-SR-IOV ports. Null value
handling for field removal should work as specified in the test
procedures. Concurrent updates by other clients represent a general
OpenStack limitation.
[Other Info]
All the work is on storyboard -
https://storyboard.openstack.org/#!/story/2011481
Original Bug Description Below
===========
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"}
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'}}
The problem seems to be that this function is incorrectly handling
properties containing nested dicts such as 'binding:profile'.
To manage notifications about this bug go to:
https://bugs.launchpad.net/cloud-archive/+bug/2114984/+subscriptions
More information about the Ubuntu-openstack-bugs
mailing list