NE: vif.binding=lswitch hook for OVS-backed extensions#4
Merged
weizhouapache merged 3 commits intoweizhouapache:4.23-network-extensionfrom May 6, 2026
Merged
Conversation
CloudStack's existing OvsVifDriver already binds NICs correctly when the
NicProfile's BroadcastDomainType is Lswitch: it emits libvirt
<virtualport type='openvswitch' interfaceid='<nic.getUuid()>'/> and
libvirt sets external_ids:iface-id atomically with tap creation. No
agent patch is required for OVS-backed extensions to consume this path
-- they just need (a) a way to opt in, and (b) nic.getUuid() carried in
per-NIC script commands so the SDN-side port identifier can match.
Add the framework hooks to enable this without any KVM agent change:
* ExtensionHelper.VIF_BINDING_DETAIL_KEY ("vif.binding") -- new
top-level extension detail. Currently supported value: "lswitch".
* NetworkExtensionElement.prepare(...) -- when the extension owning the
NIC's network declares vif.binding=lswitch in its extension_details,
override nic.setBroadcastType(Networks.BroadcastDomainType.Lswitch).
OvsVifDriver on the KVM agent then picks the existing Lswitch path
unchanged. Without the opt-in, the previous default (typically Vlan)
is preserved -- existing reference extensions like network-namespace
are unaffected.
* NetworkExtensionElement.getNicUuidArgs(network, nic) -- helper that
returns ["--nic-uuid", "<uuid>"] only when vif.binding=lswitch is
declared. Wired into add-dhcp-entry, remove-dhcp-entry,
add-dns-entry, save-vm-data, save-password, save-userdata,
save-sshkey, and save-hypervisor-hostname. Extensions that do not
declare the hint never see --nic-uuid, so backwards-compatible.
* README -- new section "VIF Binding for OVS-backed Extensions"
documenting the contract end-to-end: cmk createExtension snippet,
what prepare() does, how --nic-uuid flows, why the extension never
writes iface-id remotely on the boot path. Also notes the new
argument in the add-dhcp-entry table.
Result: an OVN extension (or any future OVS-backed extension) gets
correct VIF binding by adding a single detail key at extension creation
time. No host-side agent patch, no libvirt patch, no OVS schema
change.
Delta 1 already overrides nic.setBroadcastType(Lswitch) on the NicProfile during prepare() so the KVM agent picks the OVS Lswitch path. But the underlying nics row still carried the cosmetic ``vlan://<id>`` URI allocated by GuestNetworkGuru at design-time, which is misleading on listNics / DB queries: a NIC sitting on an OVN logical switch should not advertise a VLAN URI. Override broadcast_uri and isolation_uri on the NicProfile to ``ovn://cs-net-<networkId>`` (the convention used by the legacy ovn-plugin) and persist the same on the nics row via nicDao.update. The VLAN that the guru allocated stays as a ghost in op_dc_vnet_alloc -- it is never used on the wire because the VIF attaches to br-int and traffic flows through OVN's logical pipeline over geneve. Releasing the VLAN back to the pool would require intercepting the design phase, which is out of scope for this hook. Verified end-to-end: i-2-24-VM on network 214 now lists broadcast_uri = ovn://cs-net-214 isolation_uri = ovn://cs-net-214 and the OVN NB LSP / OVS iface-id / OVN SB Port_Binding remain correctly bound to the chassis, as before.
…itch Companion to the NIC-level URI override -- when an extension declares vif.binding=lswitch, the Network row itself should advertise ``broadcast_domain_type=Lswitch`` and ``broadcast_uri=ovn://cs-net-<id>`` so listNetworks / details views are consistent with what the OVN control plane represents. Without this hook the GuestNetworkGuru still allocated a VLAN at design time, which leaks back into the UI: VLAN/VNI: 138 Broadcast URI: vlan://138 Apply the override on a successful ``implement-network`` script return in NetworkExtensionElement.implement(). The VLAN that the guru allocated stays as a ghost in op_dc_vnet_alloc -- it is never used on the wire because the VIF attaches to br-int and traffic flows through OVN's logical pipeline over geneve. Releasing the VLAN back to the pool would require intercepting the design phase and is out of scope for this hook. Verified end-to-end: i-2-27-VM on network 216 now lists networks.broadcast_uri = ovn://cs-net-216 networks.broadcast_domain_type = Lswitch nics.broadcast_uri = ovn://cs-net-216 nics.isolation_uri = ovn://cs-net-216 The OVN NB LSP / OVS iface-id / OVN SB Port_Binding remain bound, as before.
14 tasks
weizhouapache
approved these changes
May 6, 2026
Owner
|
Merged, thanks @msinhore !!! |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
Adds an opt-in hook so a NetworkOrchestrator extension backed by an OVS+OVN
datapath (or any other host-side stack that consumes the libvirt
<virtualport interfaceid=…>element) can declarevif.binding=lswitchin its
extension_detailsand have the framework wire the binding forfree.
When the hint is set,
NetworkExtensionElement.prepare()flips theNicProfiletoBroadcastDomainType.Lswitchand persistsbroadcast_uri/isolation_uriasovn://cs-net-<networkId>on thenicsrow, andimplement()does the same on thenetworksrow.That is enough for the existing
OvsVifDriveron the KVM agent to emit<virtualport type='openvswitch'><parameters interfaceid='<nic.uuid>'/>without any agent patch — libvirt sets
external_ids:iface-id=<nic.uuid>atomically with tap creation, and
ovn-controllerbinds the LSP whosename matches.
The framework also propagates
nic.getUuid()to per-NIC scriptcommands as
--nic-uuid, so the extension can use the canonical UUIDas the LSP name and stay in sync with what libvirt writes on the host.
Without the hint, behaviour is byte-identical to the current PR.
Commits
public constants on
ExtensionHelper(VIF_BINDING_DETAIL_KEY,VIF_BINDING_LSWITCH), the--nic-uuidpropagation, and thebroadcast-type override in
prepare(). Also extends the frameworkREADME with the binding contract:
nic.uuid == libvirt iface-id == OVS external_ids:iface-id == OVN LSP name == OVN SB Port_Binding.logical_port.nic.broadcastUri/nic.isolationUritoovn://cs-net-<id>andpersists via
nicDao.update()solistNicsand the UI advertisethe OVN URI rather than the VLAN URI the GuestNetworkGuru allocates
at design-time.
same on the
Networkrow inimplement()solistNetworksmatches.
Why these belong in the framework
The broadcast type / URI are read by
OvsVifDriverbefore theextension script runs, and they live in DAOs the script cannot touch.
The hook has to be in the Java
prepare()/implement()flow.Risk surface
Three files, +219/−2 lines, all under
framework/extensions/.../network/plus 24 lines of constants in
api/.../extension/ExtensionHelper.java.No SQL migration, no new Cmd, no new response object. Existing
extensions (HyperV, MaaS, Proxmox) are untouched because they don't
declare
vif.binding=lswitch.Lab validation
End-to-end on an isolated OVN network and a VPC tier:
iface-idmatchesnic.uuid→ OVN SB
Port_Binding.logical_portmatches → ovn-controllerinstalls flows.
ovn://cs-net-<id>instead ofvlan://<id>.egress), Public LB and Internal LB all functional.
Test plan
vif.binding=lswitch; confirmovs-vsctl list interfaceshowsiface-id=<nic.uuid>and thatovn-sbctl find port_binding logical_port=<nic.uuid>returns one row with the chassis bound.confirm broadcast type stays
Vlanand--nic-uuidis notpropagated to the script.
listNetworksandlistNicsand verify the URI fields readovn://cs-net-<id>after the network is implemented.