[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