Skip to content

NE: vif.binding=lswitch hook for OVS-backed extensions#4

Merged
weizhouapache merged 3 commits intoweizhouapache:4.23-network-extensionfrom
msinhore:ne-vif-binding-lswitch
May 6, 2026
Merged

NE: vif.binding=lswitch hook for OVS-backed extensions#4
weizhouapache merged 3 commits intoweizhouapache:4.23-network-extensionfrom
msinhore:ne-vif-binding-lswitch

Conversation

@msinhore
Copy link
Copy Markdown

@msinhore msinhore commented May 6, 2026

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 declare vif.binding=lswitch
in its extension_details and have the framework wire the binding for
free.

When the hint is set, NetworkExtensionElement.prepare() flips the
NicProfile to BroadcastDomainType.Lswitch and persists
broadcast_uri / isolation_uri as ovn://cs-net-<networkId> on the
nics row, and implement() does the same on the networks row.
That is enough for the existing OvsVifDriver on 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-controller binds the LSP whose
name matches.

The framework also propagates nic.getUuid() to per-NIC script
commands as --nic-uuid, so the extension can use the canonical UUID
as 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

  1. NE: VIF binding hooks for OVS-backed extensions — adds the two
    public constants on ExtensionHelper (VIF_BINDING_DETAIL_KEY,
    VIF_BINDING_LSWITCH), the --nic-uuid propagation, and the
    broadcast-type override in prepare(). Also extends the framework
    README with the binding contract:
    nic.uuid == libvirt iface-id == OVS external_ids:iface-id == OVN LSP name == OVN SB Port_Binding.logical_port.
  2. NE: persist OVN broadcast/isolation URI on NIC — sets
    nic.broadcastUri / nic.isolationUri to ovn://cs-net-<id> and
    persists via nicDao.update() so listNics and the UI advertise
    the OVN URI rather than the VLAN URI the GuestNetworkGuru allocates
    at design-time.
  3. NE: persist OVN broadcast type and URI on Network — does the
    same on the Network row in implement() so listNetworks
    matches.

Why these belong in the framework

The broadcast type / URI are read by OvsVifDriver before the
extension 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:

  • VM NIC bound on libvirt → OVS iface-id matches nic.uuid
    → OVN SB Port_Binding.logical_port matches → ovn-controller
    installs flows.
  • listNetworks / listNics / network-detail UI show
    ovn://cs-net-<id> instead of vlan://<id>.
  • DHCP, DNS, SourceNat, StaticNat, PortForward, Firewall (ingress and
    egress), Public LB and Internal LB all functional.

Test plan

  • Boot a VM on an extension-backed network whose extension declares
    vif.binding=lswitch; confirm ovs-vsctl list interface shows
    iface-id=<nic.uuid> and that ovn-sbctl find port_binding logical_port=<nic.uuid> returns one row with the chassis bound.
  • Boot a VM on an extension that does NOT declare the detail;
    confirm broadcast type stays Vlan and --nic-uuid is not
    propagated to the script.
  • Run listNetworks and listNics and verify the URI fields read
    ovn://cs-net-<id> after the network is implemented.

msinhore added 3 commits May 5, 2026 10:22
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.
@weizhouapache weizhouapache merged commit 1b7c958 into weizhouapache:4.23-network-extension May 6, 2026
@weizhouapache
Copy link
Copy Markdown
Owner

Merged, thanks @msinhore !!!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants