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
Bugs, which is subscribed to Ubuntu.
https://bugs.launchpad.net/bugs/2114984

Title:
  Incorrect handling of value_specs upon port update via stack update

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/heat/+bug/2114984/+subscriptions


-- 
ubuntu-bugs mailing list
[email protected]
https://lists.ubuntu.com/mailman/listinfo/ubuntu-bugs

Reply via email to