Description
On the Flamingo E2E environment, the floatingip-update KUTTL test failed because namespace teardown timed out (4 min) waiting for the Subnet to delete. The Subnet's DELETE request repeatedly got 409 SubnetInUse because a port still held an IP allocation from it — a port belonging to an orphaned FloatingIP that ORC never recorded in status.
Evidence
From neutron-api.log, four POST /networking/v2.0/floatingips requests were made against the test's Network (988f35a2-...) within ~2 seconds:
| time |
req-id |
result |
| 13:55:20 |
req-3f560adf |
400 (malformed) |
| 13:55:20 |
req-482b9a27 |
400 (malformed) |
| 13:55:21 |
req-b1cef117 |
201 → FloatingIP 477f8a6e-65d5-4f2d-b235-287a342e39d4, port 0f5a67e7-437f-460d-bccb-deb50c361675 (192.168.155.171) |
| 13:55:22 |
req-f6905d58 |
201 → FloatingIP b656ccd3-7d81-4ea3-85b0-37b2868c9eba, port 3e7a2d04-... (192.168.155.114) |
b656ccd3 is the one the test/ORC actually tracked (it gets tagged, untagged, and cleanly DELETEd at 13:55:25). 477f8a6e is never deleted anywhere in the log — it's a leaked floating IP, and its port (0f5a67e7) is the one neutron-api.log reports as blocking every subsequent subnet-delete retry:
INFO neutron.db.db_base_plugin_v2 [...] Found port (0f5a67e7-437f-460d-bccb-deb50c361675,
192.168.155.171) having IP allocation on subnet ef7f078a-..., cannot delete
This 409 repeats on every ORC retry from 13:55:25 through 13:58:19 (the last attempt visible before KUTTL's 4-minute timeout at ~13:59:25):
orc-pod.log (13:58:19Z):
ERROR Reconciler error {"controller": "subnet", ... "Subnet": {"name":"floatingip-update","namespace":"kuttl-frank-octopus-lxtf"}, ...
"error": "Expected HTTP response code [202 204] when accessing [DELETE http://10.1.0.32/networking/v2.0/subnets/ef7f078a-b15b-461b-8894-928425555fa0],
but got 409 instead: {\"NeutronError\": {\"type\": \"SubnetInUse\", \"message\": \"Unable to complete operation on subnet
ef7f078a-b15b-461b-8894-928425555fa0: One or more ports have an IP allocation from this subnet.\", \"detail\": \"\"}}"}
Root cause
internal/controllers/floatingip/actuator.go, ListOSResourcesForAdoption:
// we only support adoption of floatingips by IP as they don't have name
if resource.FloatingIP == nil {
return nil, false
}
When no explicit floatingIP address is requested (as in this test — the common/default case), adoption is a no-op. So if a CreateResource call succeeds in Neutron but the reconcile that made the call doesn't end up persisting status.id (e.g. an error/retry happens between the successful POST and the Status().Patch() that records the ID), the next reconcile has status.id == nil, cannot discover the already-created floating IP, and creates a second one — permanently orphaning the first.
I wasn't able to pin down exactly why the first create's status write didn't stick: orc-pod.log in the collected CI artifact only covers 13:58:00 onward, missing the 13:55:19–13:55:25 window where the duplicate creation happened (hack/collectlogs appears to only retain the tail of the controller log).
Impact
- Silent, permanent leak of an OpenStack FloatingIP + its port.
- Can permanently deadlock deletion of any Subnet/Network sharing that port's allocation (as seen here — KUTTL/ORC retried for 4+ minutes with no progress, failing the test).
- Only reproducible when a FloatingIP is created without a specific
floatingIP address, since that's what disables adoption.
Suggested fix directions
- Make FloatingIP creation idempotent/discoverable even without a fixed IP, e.g. by tagging created floating IPs with the ORC object's UID and using that tag as an adoption key.
- And/or ensure "create in OpenStack" + "persist ID to status" happens as a unit that can't silently retry into a duplicate create (e.g. list-by-tag before create).
Environment
Description
On the Flamingo E2E environment, the
floatingip-updateKUTTL test failed because namespace teardown timed out (4 min) waiting for the Subnet to delete. The Subnet'sDELETErequest repeatedly got409 SubnetInUsebecause a port still held an IP allocation from it — a port belonging to an orphaned FloatingIP that ORC never recorded in status.Evidence
From
neutron-api.log, fourPOST /networking/v2.0/floatingipsrequests were made against the test's Network (988f35a2-...) within ~2 seconds:477f8a6e-65d5-4f2d-b235-287a342e39d4, port0f5a67e7-437f-460d-bccb-deb50c361675(192.168.155.171)b656ccd3-7d81-4ea3-85b0-37b2868c9eba, port3e7a2d04-...(192.168.155.114)b656ccd3is the one the test/ORC actually tracked (it gets tagged, untagged, and cleanlyDELETEd at 13:55:25).477f8a6eis never deleted anywhere in the log — it's a leaked floating IP, and its port (0f5a67e7) is the oneneutron-api.logreports as blocking every subsequent subnet-delete retry:This 409 repeats on every ORC retry from 13:55:25 through 13:58:19 (the last attempt visible before KUTTL's 4-minute timeout at ~13:59:25):
Root cause
internal/controllers/floatingip/actuator.go,ListOSResourcesForAdoption:When no explicit
floatingIPaddress is requested (as in this test — the common/default case), adoption is a no-op. So if aCreateResourcecall succeeds in Neutron but the reconcile that made the call doesn't end up persistingstatus.id(e.g. an error/retry happens between the successfulPOSTand theStatus().Patch()that records the ID), the next reconcile hasstatus.id == nil, cannot discover the already-created floating IP, and creates a second one — permanently orphaning the first.I wasn't able to pin down exactly why the first create's status write didn't stick:
orc-pod.login the collected CI artifact only covers 13:58:00 onward, missing the 13:55:19–13:55:25 window where the duplicate creation happened (hack/collectlogsappears to only retain the tail of the controller log).Impact
floatingIPaddress, since that's what disables adoption.Suggested fix directions
Environment
internal/controllers/floatingip/tests/floatingip-update