[Bug 2114984] Re: Incorrect handling of value_specs upon port update via stack update

Alejandro Santoyo Gonzalez 2114984 at bugs.launchpad.net
Thu Jun 26 13:55:54 UTC 2025


** Description changed:

  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'.
- 
- I will submit a patch shortly.

-- 
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"}

  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/ubuntu/+source/heat/+bug/2114984/+subscriptions




More information about the Ubuntu-openstack-bugs mailing list