[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