In the non-DVR case, the Ml2Plugin._bind_port() function creates a models.PortBinding instance with copies of fields from the port's actual PortBinding instance from the DB, and passes a PortContext referencing this copy to MechanismManager.bind_port() to attempt to bind the port. This allows the results from the binding attempt to be discarded if Ml2Plugin._commit_port_binding determines the port has been concurrently bound or otherwise modified during the binding attempt, which occurs outside any transaction.
In the DVR case, Ml2Plugin.update_dvr_port_binding() calls MechanismManager.bind_port() using a PortContext referencing the actual models.DVRPortBinding instance associated with the DB. Changes made to this DVRPortBinding instance during the binding attempt get committed to the DB in the following transaction, and Ml2Plugin._commit_dvr_port_binding() is not actually comparing the pre-binding and post-binding state as intended to determine whether to commit the changes.
This does not currently cause any obvious issues in normal operation (unless there are concurrent binding attempts), but the changes in https://review.openstack.org/#/c/116122/16/neutron/plugins/ml2/plugin.py expose this issue because db.set_binding_levels() does not get called on line 366, resulting in DVR tempest failures.
This issue would likely be resolved by the work planned for https://bugs.launchpad.net/neutron/+bug/1367391, but may need to be fixed sooner to allow the hierarchical port binding implementation to be merged.
Fix proposed to branch: master /review. openstack. org/151913
Review: https:/