DRA Network Device Discovery Design

Policy-driven network device discovery with DRA compatibility groups, local consumable capacity, and optional shared budgets

51 min read

DRA Network Device Discovery and ResourceSlice Design

Status: Revised selected design; driver implementation has not started.
Revision: 2026-09-16 — replace counter-based mode exclusion with KEP 5963.
Scope: This document defines the proposed DeviceExposurePolicy API and its translation into Kubernetes DRA objects. Custom policy fields below are our design, not upstream Kubernetes fields.

Upstream baseline and feature boundaries

The implementation observations in this revision are pinned to Kubernetes commit a255808f6c0deb6e75ce031b5804e722306e46a8, inspected on 2026-09-16. A source-level feature version is not a promise that a particular OpenShift release enables or supports that feature. Validate the actual target distribution before deployment. S1 S2

ConcernUpstream mechanismBaseline / dependency
Device inventory and claimsresource.k8s.io/v1Use the current v1 object shape.
Counter-set associationsKEP 4815 / DRAPartitionableDevicesInspected source: beta from 1.36, enabled by default. Do not label this dependency GA in 1.35.
Repeated allocations and local limitsKEP 5075 / DRAConsumableCapacityInspected source: beta from 1.36, enabled by default.
Incompatible network modesKEP 5963 / DRADeviceCompatibilityGroupsInspected source: alpha from 1.37, disabled by default; requires partitionable devices.
supportedCNIs list attributeKEP 5491 / DRAListTypeAttributesInspected source: alpha from 1.36, disabled by default. List values use strings.
Per-allocation shared budgetsKEP 5941 / PR #140084Optional follow-on work. Not required for the baseline exclusion design or reference manifests.

Important implementation caveat: KEP 5963 describes allocation-time group snapshots, but the inspected allocator reconstructs compatibility from live ResourceSlices and allocated device identities. This revision targets that actual behavior. Do not assume claim status already preserves compatibility independently of the inventory. S3 S4


1. Overview and Design Principles

This document specifies how a DRA network driver discovers host network devices and publishes them as ResourceSlices for scheduler-aware allocation.

The Cardinal Rule: The Driver Is Generic

sysfs / netlink observations
          |
          v
raw device inventory + PF/VF relationships
          |
          v
DeviceExposurePolicy selection and validation
          |
          v
persona entries + scoped compatibility domains + local capacities
          |
          v
separate counter-bearing and device-bearing ResourceSlices
Diagram 1 — Device Discovery Flow: Hardware → Driver → Policy Evaluation → ResourceSlice

The discovery engine walks the host interfaces and records raw facts: interface name, MAC, MTU, PCI address, NUMA information, link speed when available, parent PF, VF index, and bridge membership. It does not infer CNI compatibility from plugin names.

The administrator’s policy declares which devices are exposed, which CNI plugins can use an entry, how much local capacity each allocation consumes, and which usage modes may coexist. The driver validates and compiles these declarations. The node-side execution path must enforce the compiled compatibility contract and verify the actual device identity before changing networking; being generic does not mean skipping safety checks.

A PF/VF relationship is a discovered topology fact. A declaration that PF passthrough must exclude all child allocations is a policy rule, not a universally assumed consequence of every operation on every PF. In particular, this design does not assert that every PF namespace move destroys its VFs.

Other Design Decisions

  1. Real interfaces, not synthetic allocation slots. A physical or software interface may have multiple persona entries when it supports different allocation semantics. A persona is an alternative representation, not additional hardware.
  2. One winning policy produces one persona. allowMultipleAllocations is the single source of truth for sharing that entry. Multiple CNI names in one policy are alternatives with identical allocation semantics.
  3. Local capacity limits repeated allocations. A shared persona uses allowMultipleAllocations: true plus capacity. The examples reserve one unit per claim allocation, with validValues: ["1"].
  4. Compatibility groups express mode exclusion. Distinct incompatible personas consume from a common counter set and declare disjoint compatibilityGroups. No local-capacity mirroring and no cross-draining are generated.
  5. Independent constraints use independent domains. Interface mode and PF-family access are separate counter-set associations when both are needed. PF macvlan and child VFs can share a family group while PF macvlan and PF ipvlan remain incompatible in the interface domain.
  6. Bandwidth is a separate, optional budget. Link speed remains an attribute in the baseline. Do not publish a bandwidth guarantee or an invented fair-share policy just because a speed was discovered.
  7. Deny-by-default, without forgetting active allocations. Unmatched or excluded devices are not newly exposed. Existing active publication is handled through the drain protocol, not deleted immediately.
  8. CNI selection is explicit. DeviceClasses select the driver and test supportedCNIs using exact list membership. Advertising a CNI name is not proof that its invocation and controller integration work.
  9. Stable identities and declarations. Active device names, pool membership, counter-set associations, and groups must survive rescan, policy changes, interface moves, and driver restarts until safely drained.

The upstream API separates compatibility predicates from local and shared capacity accounting. Our policy compiler preserves that separation. S1 S3 S4


2. DeviceExposurePolicy CRD Schema

2.1 Proposed Custom Resource Shape

This is an example custom resource, not an apiextensions.k8s.io/v1 CRD definition. The structural OpenAPI schema, defaults, CEL validation, status schema, and admission integration will be implemented after this design is accepted.

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-macvlan
spec:
  # Optional; defaults to all nodes.
  nodeSelector:
    matchLabels:
      network.example.com/role: data

  # 0-1000, default 100. Used only among expose policies for the same suffix.
  priority: 200
  selector:
    cel: >-
      device.attributes["dra.networking"].type == "pf" &&
      device.attributes["dra.networking"].ifName == "enp3s0f0"
  action: expose

  exposure:
    # One policy winner per suffix; no automatic splitting by CNI name.
    deviceNameSuffix: "-macvlan"
    allowMultipleAllocations: true

    # Local capacity only. This does not create shared counter capacity.
    capacity:
      macvlans:
        value: "64"
        requestPolicy:
          default: "1"
          validValues: ["1"]

    supportedCNIPlugins:
      - name: macvlan
        # Compiler validation / topology contract, not a Kubernetes field.
        consumePerAllocation:
          macvlans: "1"

    # Proposed policy API. Each binding compiles to a consumesCounters entry.
    compatibility:
      - scope: interface
        domain: interface-mode
        groups: [macvlan]
      - scope: sriovFamily
        domain: pf-access
        groups: [host-retained]

    # Selection metadata only; do not override discovered or reserved attributes.
    additionalAttributes:
      dra.networking/physicalNetworkName: data-network

Removed fields: exposure.exclusionGroup and supportedCNIPlugins[].exclusive are not part of the revised API. There is no implicit conversion of an old exclusion group into a compatibility group: equal compatibility group names mean compatible, whereas the old exclusion group linked conflicting personas. See the migration guide accompanying this document.

2.2 Field Semantics

FieldRequired / defaultMeaning
nodeSelectorOptional; all nodesNodes on which the policy participates in resolution.
priorityOptional; 100Highest value wins within the same suffix; range 0-1000.
selector.celRequiredBoolean expression against the driver’s normalized discovered attributes.
actionOptional; exposeexpose creates desired publication; exclude prevents new exposure of the physical interface.
exposure.deviceNameSuffixOptional; empty stringDistinguishes persona policies for one interface. Published identities are pinned after creation.
exposure.allowMultipleAllocationsOptional; falseSharing permission for this DRA device entry, not for every alias of the underlying interface.
exposure.capacityRequired for shared entries in this baselineLocal DeviceCapacity definitions. The compiler qualifies unqualified keys with dra.networking/.
exposure.supportedCNIPluginsRequired for expose policiesNon-empty list of supported plugin names. All plugins on one entry must have identical allocation semantics.
consumePerAllocationOptional; use local defaultsA driver/topology consistency declaration. Baseline amounts must equal requestPolicy.default; the scheduler does not read this custom field.
exposure.compatibilityOptional unless sharing an exclusion domainUp to two domain bindings after compilation. Each binding has scope, domain, and non-empty groups.
compatibility[].scopeRequiredinterface: this interface’s stable identity. sriovFamily: the PF root’s stable identity, shared with its VFs.
compatibility[].domainRequiredAdministrator-chosen DNS-label name for one independent compatibility constraint.
compatibility[].groupsRequiredOne or two unique DNS-label tokens copied into the upstream compatibilityGroups field.
exposure.additionalAttributesOptionalAdditional scalar selection attributes; string, boolean, or integer. Reserved/discovered keys cannot be overwritten.

scope and domain resolve a comparison boundary; groups expresses compatibility within that boundary. Group names have no CNI interpretation in the driver.

2.3 Policy Resolution

For each discovered interface:
  1. Select policies whose nodeSelector applies to this node.
  2. Evaluate selector.cel against normalized discovered facts.
  3. If any matching policy says exclude: desired exposure is empty.
  4. If no expose policy matches: desired exposure is empty.
  5. Otherwise group matching expose policies by deviceNameSuffix.
  6. Pick highest priority per group; break ties by policy name.
  7. Produce exactly one candidate persona per winner.
  8. Resolve compatibility bindings against the discovered topology.
  9. Validate the complete interface/family publication, including retained entries.
 10. Reconcile desired publication with active allocations and the drain state.
Diagram 2 — Policy Resolution Flow: selector matching through compatibility binding to final persona output

Exclude priority is irrelevant: an exclusion always takes precedence over exposure. Two policies with different suffixes are not automatically mutually exclusive; their explicit compatibility bindings determine which combinations are allowed.

A selector compilation error rejects the policy. A selector evaluation error makes reconciliation fail closed for the affected inventory evaluation and records a diagnostic; it must not silently skip a potentially applicable exclusion policy. Previously committed active publication remains retained until its lifecycle is resolved.

2.4 Validation and Cross-Policy Checks

These are proposed driver requirements, in addition to upstream API validation:

  • An exclusive entry (allowMultipleAllocations: false) has no local request policy or consumePerAllocation in the baseline. A shared entry has positive local capacity and an explicit default for each dimension.
  • Plugin-specific amounts are positive, reference existing capacity keys after qualification, and equal those dimensions’ defaults in the initial implementation. Different plugin costs require different persona policies and suitable compatibility guards; they are not inferred at scheduling time.
  • When more than one persona aliases an interface, all aliases must participate in a common interface-scoped domain. Any exclusive persona must be incompatible there with every other alias. The compiler rejects ambiguous aliasing rather than assuming different names imply separate hardware.
  • When an exposed PF persona excludes child use, every exposed child and every competing PF persona must participate in the same family-scoped domain. This is configured in policy; topology resolution alone does not invent group membership.
  • A sriovFamily binding on an interface with no resolvable PF root is invalid. Bridges with indirect PF dependencies are not silently assigned a PF family.
  • Resolve bindings before enforcing limits: a device may consume from at most two counter sets; a counter-set consumption supports at most two compatibility groups. Reject a third independent domain instead of silently dropping it. S1
  • Conflicting domain definitions, duplicate references, invalid names, overriding reserved attributes, or exceeding upstream attribute limits invalidate the affected compilation unit.
  • All initial counter-set associations generated for exclusion have non-empty groups. An empty list must never be emitted as a wildcard.

Policy admission can validate shape and local consistency. Node reconciliation must validate topology-dependent and cross-policy rules. A future webhook must not claim it can statically validate every node’s current hardware inventory.


3. How Compatibility Domains Work: The Multi-Persona Pattern

The selected pattern remains separate DRA device entries referring to a common resource, but the enforcement mechanism changes to KEP 5963 compatibility groups.

3.1 Three Uses of the Same Compatibility Predicate

RelationshipComparison domainCompatible group assignment
Macvlan vs ipvlan on one interfaceInterface interface-modeMacvlan: [macvlan]; ipvlan: [ipvlan].
Shared macvlan vs exclusive host-device on one interfaceSame interface domainMacvlan: [macvlan]; passthrough: [passthrough].
PF passthrough vs normal PF/VF usePF family pf-accessPF passthrough: [pf-passthrough]; retained PF and VFs: [host-retained].
PF0 interface-mode:       macvlan [macvlan]  !=  passthrough [passthrough]
PF0 family pf-access:     macvlan [host-retained]
                         VF0..VF7 [host-retained]
                         passthrough [pf-passthrough]
Diagram 3 — Three Compatibility Group Patterns: interface-mode (shared vs shared), interface-mode (shared vs exclusive), family pf-access (PF passthrough vs VFs)

This allows PF macvlan and VFs together while excluding PF passthrough. Adding a PF ipvlan persona uses [ipvlan] in the interface domain and [host-retained] in the family domain. It remains compatible with VFs but not with PF macvlan.

A VF’s CNI can be exclusive for that VF entry while its family group is host-retained: the group describes access to the parent PF family, not whether that VF is shared.

3.2 Exact Upstream Compatibility Semantics

For each counter set, the scheduler maintains an intersection across the active devices’ group sets. A new candidate is compatible only if including its groups leaves a common group. A candidate associated with two counter sets must pass the predicate on both. Devices associated with different counter sets are not compared through this feature. S1 S3

{macvlan} intersection {macvlan} = {macvlan}   -> compatible
{macvlan} intersection {ipvlan}  = {}          -> incompatible

Pairwise overlap is insufficient. {a,b}, {b,c}, and {a,c} intersect pairwise, but the intersection of all three is empty. Empty/unset groups only coexist with other empty/unset groups on the same counter set; they do not mean “compatible with everything”. S3

Do not publish groups: [macvlan, shared] and groups: [ipvlan, shared] when the modes conflict: the common shared token would permit them together.

Diagram 4 — PF/VF Family Domain: compatibility groups showing host-retained vs pf-passthrough intersection

3.3 Local Capacity and the Non-Binding Membership Counter

The upstream field attaches to consumesCounters[]; a compatibility-only relationship still needs a valid counter-set association. This design generates one positive bookkeeping counter named members per compatibility domain. It is not a lock and not a per-Pod quota.

Let N be the number of distinct published persona/device identities referencing a domain, including retained active entries. Publish members = max(1, N) and a fixed consumption of members: "1" for each member. Do not shrink this bookkeeping total during an active drain transition. Normal allocation permits at most one active fixed charge per member identity, so the counter itself does not decide which modes may coexist. Compatibility groups do. S1 S4

For two macvlan/ipvlan personas, members: "2" intentionally leaves enough numerical capacity for both. Their disjoint groups still prevent coexistence.

QuantityRoleConsumption
Local capacity.macvlansNumber of macvlan allocation unitsPer allocation; examples use one.
Domain membersValid counter-set association with non-binding accountingFixed charge once per active device identity in the inspected baseline.
Optional shared bandwidthA real shared resource budgetPer allocation only with the separately supported shared-consumable feature.

A local quota applies to one persona identity. Publishing two compatible aliases with independent local quotas does not create a single aggregate hardware limit. Use one canonical persona for the same allocation contract, explicitly partition the administrator budget, or introduce a genuine shared budget in the follow-on profile. Do not represent the same 64 physical units as two independent 64-unit guarantees.

The old cross-drain model is removed. Local capacity does not automatically decrement a similarly named shared counter. In the old example, allocating macvlan drained the ipvlan mirror but left the macvlan mirror available, so ipvlan could consume that other mirror. Merely charging the fixed amount once did not create mutual exclusion.

3.4 Allocation and Release Lifecycle

With stable publication, the inspected implementation detects that a shared device remains allocated after one of its claims is released. It reconstructs the compatibility baseline using that active device and its live group declarations. The compatibility restriction is not attached to the first claim’s private counter snapshot. S4 S5

EventMacvlan allocationsIPvlan result
Initial0May allocate.
Allocate A1Blocked.
Allocate B on the same macvlan device2Blocked.
Release A; keep B1Still blocked.
Reconstruct scheduler state or restart the driver1Must remain blocked.
Release B and complete cleanup0May allocate.
Diagram 9 — Allocation Lifecycle State Progression: macvlan vs ipvlan group intersection through allocation and release cycles

“Allocation released” means the scheduler no longer considers the ResourceClaim allocation active. Pod termination or a driver-local reference count reaching zero is not, by itself, sufficient evidence. One claim may serve multiple Pods; a capacity of 64 means 64 one-unit allocation results, not necessarily 64 Pods. Prefer per-Pod ResourceClaimTemplates where a per-Pod budget is intended. S7

This is not a claim that PR #140084’s static-counter lifetime issue has been fixed. The baseline does not depend on that PR. A combined build must get its own regression tests.

3.5 Feature-Disabled and Unsupported-Cluster Behavior

The baseline requires compatibility support whenever incompatible personas are exposed. Do not remove the group field and publish the same alternatives as a fallback. The API server can strip disabled feature fields; the scheduler can skip slices containing unsupported groups, and a skipped slice can make a pool incomplete. Neither behavior is a substitute for a driver capability contract. S3 S6

Installation must verify the selected API server and scheduler configuration. An explicit deployment capability setting plus API round-trip validation and a controlled scheduler conformance test is preferable to guessing from a version string or OpenAPI schema alone. A schema can describe fields whose feature gates are disabled.

For an unsupported cluster, a separately selected reduced profile may expose only one preselected mode per actual exclusion domain. It must not expose PF passthrough together with active child uses. Node-side checks remain required. This is a deliberate reduced feature profile, not an automatic downgrade of the same policy set.


4. How the Driver Uses Policies

The driver compiles discovered facts and winning policies into a complete desired pool model, validates it, then reconciles it against active allocations.

4.1 Attribute Translation

Discovered facts use the dra.networking domain, except explicitly standardized attributes such as device.k8s.io/pcieRoot. Values below are observations, not inferred CNI capabilities.

Discovered factPublished attributeDiscovery input
Interface namedra.networking/ifNameHost interface inventory.
MAC addressdra.networking/macNetlink.
MTUdra.networking/mtuNetlink / sysfs.
Link speed, when knowndra.networking/linkSpeedDriver/ethtool observation, documented in Mbps.
Operational statedra.networking/operStateNetlink.
PCI BDFdra.networking/pciAddressSysfs device relationship.
NUMA nodedra.networking/numaNodePCI sysfs topology, when meaningful.
PCIe rootdevice.k8s.io/pcieRootPCI topology walk.
Vendor and product IDsdra.networking/vendor, dra.networking/productPCI identifiers.
Bound kernel driverdra.networking/driverSysfs driver association.
Interface classificationdra.networking/typeDiscovery classification: pf/vf/bridge/vlan/bond/nic.
RDMA associationdra.networking/rdmaValidated netdevice/RDMA relationship.
SR-IOV capability and configured VF countdra.networking/sriovCapable, dra.networking/numVFsPF sysfs facts.
Parent PF and VF indexdra.networking/pfName, dra.networking/vfIndexphysfn / virtfn relationships.
Bridge factsdra.networking/bridgeName, bridgeType, vlanFilteringSupported bridge discovery backend.
Master bridgedra.networking/masterBridgeMaster relationship, resolved to a bridge where appropriate.

Unknown is not zero. The discovery contract must distinguish unavailable observations from legitimate values. For the bridge-selection examples, masterBridge is normalized to an empty string when the interface is known to have no bridge master. Selectors must guard attributes that may be absent.

Discovery must not forget a device solely because the driver moved it out of the host network namespace or changed its kernel binding. Preserve an allocation-aware inventory record and reconcile against node execution state.

4.2 Policy-Driven Fields

OutputSource / transformation
Device nameStable published identity for physical interface plus winning suffix.
allowMultipleAllocationsCopy from policy.
Local capacityPolicy quantities and request policies; qualify names consistently.
dra.networking/supportedCNIsPlugin names, serialized as {strings: [...]}.
Additional attributesValidated non-reserved policy metadata, converted to typed attribute values.
Counter-set referenceResolved (pool, scope root, domain) binding.
compatibilityGroupsCopy the binding’s groups after validation.
Domain members totalNumber of referencing published identities, including retained entries.
Fixed members consumptionOne per referencing identity.

attributes and capacity are direct fields under a v1 device entry. Do not wrap them in basic. Counter definitions and device entries belong in different ResourceSlice objects. S1

4.3 Topology Resolution and Pool Layout

One PF and its VFs form a pool. Standalone interfaces use standalone pools unless a separately supported topology relationship requires a common pool. Domain comparisons never cross pool boundaries.

For scope: interface, resolve the interface’s stable identity. For scope: sriovFamily, use the parent PF root for both the PF and all of its VFs. The driver discovers these relationships but does not invent mode groups.

A domain key is:

(driver, pool identity, resolved scope-root identity, administrator domain name)

The counter-set name is a stable DNS-label encoding of this key within its pool. The implementation must define deterministic normalization, truncation and collision handling, for example a readable prefix with a stable hash suffix. Group names remain opaque; do not append each device’s unique identity to a shared group token.

The examples use short readable names such as pf0-interface-mode and pf0-family-pf-access. Production identity must not depend only on the current interface name or an unstable enumeration order. Persist the mapping used for active allocations. PCI BDF reuse after hardware replacement must not be mistaken for continuity of the old allocated hardware.

4.4 Translation Algorithm

INPUT:
  discovered inventory, all relevant policies, deployment capabilities,
  last committed publication, allocated claims, node execution journal
OUTPUT:
  validated desired pools, status/diagnostics, staged publication operations

Phase 1 — Resolve policies and personas
  Evaluate node and device selectors.
  Apply exclude / deny-by-default rules.
  Pick one winner per (physical interface, suffix).
  Build candidate persona records without publishing them.

Phase 2 — Resolve compatibility domains
  Resolve each interface or sriovFamily scope root.
  Place family members in the same pool.
  Resolve each (root, domain) into one counter-set identity.
  Copy groups exactly; do not infer them from CNI names.
  Require complete guards for aliases and any declared family-exclusive mode.

Phase 3 — Merge lifecycle state
  Retain active entries missing from desired discovery/policy results.
  Pin their identity, associations, groups and relevant allocation contract.
  Mark incompatible policy changes or hardware changes as pending drain.
  Do not publish replacement identities that can alias still-active hardware.

Phase 4 — Build resource accounting
  Copy local capacities; never mirror them for exclusion.
  For each domain, publish members >= its distinct retained/current members.
  Add fixed members: 1 consumption to every referencing device entry.
  Do not add automatic bandwidth or fair-share defaults.

Phase 5 — Validate the entire pool
  Check names, field limits, no duplicate device or counter-set identities.
  Check one consumption entry per counter set and at most two per device.
  Check local quantities, group membership, aliases and feature capabilities.
  Fail closed on invalid compilation; report the policy and domain involved.

Phase 6 — Pack and publish
  Pack counter sets into counter-bearing slices.
  Pack devices into device-bearing slices.
  Set the same pool name, generation and total resourceSliceCount on all.
  Reconcile through the upstream ResourceSlice controller where practical.
  Retain old active declarations through transitions; reconcile partial writes.

Phase 7 — Observe and retire
  Reconcile allocated claims and node execution state continuously.
  Remove retired declarations only after the drain/cleanup conditions pass.
Diagram 5 — Translation Algorithm: 7-phase flow from policy resolution through compatibility domains to publication

A pool.generation change allows readers to distinguish pool generations; it is not a multi-object transaction or an instruction to revoke existing claims. Publication must recover from incomplete generations and asynchronous watches. S1 S7

4.5 Optional Follow-On: Shared Bandwidth Budgets

The baseline publishes linkSpeed as a fact and makes no aggregate bandwidth reservation guarantee. The original automatic bandwidth and fair-share logic is removed from the baseline rather than presented as implemented functionality.

If bandwidth reservations are added later, use explicit administrator-supplied budgets and KEP 5941’s request-driven shared consumption. Every traffic consumer intended to participate in that budget must be included: accounting only for VFs while PF macvlan traffic is unaccounted for is not a whole-link reservation model.

The following is an experimental future wire-format fragment, not part of the initial DeviceExposurePolicy schema or baseline manifests. It follows PR #140084 at 51d78febb379ae41f7487a50b9718a4ede0e3f3c; that PR uses valueFrom.capacityName, not capacityKey. S8

# Counter-bearing ResourceSlice.spec fragment, future feature profile.
sharedCounters:
  - name: pf0-family-pf-access
    counters:
      members:
        value: "10"
      bandwidth:
        value: "100000"       # Contract: Mbps, explicitly configured by admin.
        requestPolicy:
          default: "1000"
          validRange:
            min: "100"
            max: "100000"
            step: "100"
# One member's device fragment, future feature profile.
consumesCounters:
  - counterSet: pf0-family-pf-access
    compatibilityGroups: [host-retained]
    counters:
      members:
        value: "1"
      bandwidth:
        valueFrom:
          capacityName: dra.networking/bandwidth
# ResourceClaim request fragment for that future feature profile.
exactly:
  deviceClassName: network-vf-pf0
  capacity:
    requests:
      dra.networking/bandwidth: "2500"

Reuse a compatible existing domain counter set where appropriate; do not add a third counter-set association to a device that already uses two. Define one authoritative budget specification and conflict resolution before implementing policy-to-budget translation. Capacity accounting alone also does not configure a hardware rate limit: scheduling reservation and enforcement are separate contracts.

The PR’s fixed-versus-per-share accounting, release behavior, feature transitions and combination with KEP 5963 must be retested on the actual combined code. Do not make baseline mode exclusion depend on a presumed fix in that PR.

4.6 ResourceSlice Packing Rules

In the inspected v1 API: S1

  • spec.devices and spec.sharedCounters are mutually exclusive in one object.
  • resourceSliceCount is the total number of ResourceSlices in the pool generation, including both kinds of slices.
  • A device has at most two consumesCounters entries, one per referenced counter set; each has at most two compatibility group names.
  • One counter-bearing slice has at most eight counter sets, each with at most 32 counters.
  • Use at most 64 devices per slice when counters, list attributes or other applicable advanced features are used. Validate the target API’s combined attribute/capacity and list-value limits too.

Do not duplicate a counter set in each device slice. Split the inventory into additional slices and update the total count when limits require it.

4.7 Capacity Requests and the Topology Contract

The scheduler does not execute our policies or choose an amount based on a CNI binary name. It reads local requestPolicy and any ResourceClaim capacity requests. Consequently, the baseline fixes each example’s default and allowed amount to one unit and requires all listed CNI alternatives to agree with that amount. S1 S7

A request for four units reserves four units; it does not automatically cause the execution engine to create four interfaces. Supporting variable quantities requires an explicit topology/execution contract and generated or user-authored capacity.requests. This is why the examples no longer advertise a one-to-four range while describing exactly one interface per allocation.

DeviceClass selectors select suitable devices but do not create plugin-specific capacity requests. The no-extra-capacity-fields application experience relies on consistent per-device defaults, not hidden scheduler interpretation of consumePerAllocation.


5. Examples

The nine scenarios from the original document are retained. Policy fields are the proposed API in Section 2; ResourceSlice examples use the inspected upstream v1 shape. Examples labelled fragment are not complete Kubernetes objects. Complete objects and every reference VF appear in Section 6.

Interface names, PCI addresses, capacities and speeds are illustrative inventory values from the reference scenario, not discovery defaults or vendor guarantees. The actual driver must use observed identities and administrator-selected limits.

5.1 Example 1: Simple SR-IOV VFs

Scenario: VFs under enp3s0f0 support sriov. Each VF is a normal, exclusively allocated DRA device. Normal use of the family is explicitly labelled host-retained.

DeviceExposurePolicy

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: sriov-vfs-pf0
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f0"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained

Resulting ResourceSlice Fragments

This scenario exposes eight VFs and no PF persona, so the family domain has eight member identities. A future PF persona must use a compatible family declaration or be drained into a new configuration before publication.

Counter-bearing ResourceSlice.spec fragment:

sharedCounters:
  - name: pf0-family-pf-access
    counters:
      members:
        value: '8'

Device-bearing ResourceSlice.spec fragment (same pool):

devices:
  - name: enp3s0f0v0
    attributes:
      dra.networking/type:
        string: vf
      dra.networking/ifName:
        string: enp3s0f0v0
      dra.networking/pfName:
        string: enp3s0f0
      dra.networking/vfIndex:
        int: 0
      dra.networking/pciAddress:
        string: '0000:03:00.2'
      dra.networking/numaNode:
        int: 0
      dra.networking/rdma:
        bool: true
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101e
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - sriov
    consumesCounters:
      - counterSet: pf0-family-pf-access
        compatibilityGroups:
          - host-retained
        counters:
          members:
            value: '1'

The remaining seven VF entries use the same family group and each consume one member. VFs are independently allocatable because they are distinct devices with the same group. A VF is exclusive because allowMultipleAllocations is false/omitted, not because its CNI has a custom exclusive flag. There is no automatic numVFs + 1 lock counter.

5.2 Example 2: SR-IOV VFs with Multiple CNI Options

Scenario: A VF may be selected for sriov or host-device. The two names are execution alternatives for one exclusive entry, not permission to allocate the VF twice. The scenario assumes the execution path validates each plugin’s configuration requirements.

DeviceExposurePolicy

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-vfs
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f0"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained

Resulting ResourceSlice Fragments

Counter-bearing ResourceSlice.spec fragment:

sharedCounters:
  - name: pf0-family-pf-access
    counters:
      members:
        value: '8'

Device-bearing ResourceSlice.spec fragment (same pool):

devices:
  - name: enp3s0f0v0
    attributes:
      dra.networking/type:
        string: vf
      dra.networking/ifName:
        string: enp3s0f0v0
      dra.networking/pfName:
        string: enp3s0f0
      dra.networking/vfIndex:
        int: 0
      dra.networking/pciAddress:
        string: '0000:03:00.2'
      dra.networking/numaNode:
        int: 0
      dra.networking/rdma:
        bool: true
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101e
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - sriov
          - host-device
    consumesCounters:
      - counterSet: pf0-family-pf-access
        compatibilityGroups:
          - host-retained
        counters:
          members:
            value: '1'

The supported-CNI list is serialized with strings. A generated DeviceClass can use device.attributes["dra.networking"].supportedCNIs.includes("sriov") or the corresponding host-device selection. .includes() is the DRA list-aware CEL helper; it does not reserve resources or invoke the plugin. S7

5.3 Example 3: PF as Macvlan Parent

Scenario: enp3s0f1 has no exposed VFs or competing persona in this example. It provides 64 one-unit macvlan allocations. This isolated, single-persona case does not need a compatibility domain.

DeviceExposurePolicy

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf1-macvlan-parent
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f1"
  action: expose
  exposure:
    allowMultipleAllocations: true
    capacity:
      macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: macvlan
        consumePerAllocation:
          macvlans: '1'

Resulting Device Entry

name: enp3s0f1
allowMultipleAllocations: true
attributes:
  dra.networking/type:
    string: pf
  dra.networking/ifName:
    string: enp3s0f1
  dra.networking/pciAddress:
    string: '0000:03:00.1'
  dra.networking/mac:
    string: 04:3f:72:b0:d4:61
  dra.networking/mtu:
    int: 1500
  dra.networking/linkSpeed:
    int: 25000
  dra.networking/numVFs:
    int: 0
  dra.networking/rdma:
    bool: true
  dra.networking/numaNode:
    int: 0
  dra.networking/vendor:
    string: 15b3
  dra.networking/product:
    string: 101d
  dra.networking/driver:
    string: mlx5_core
  device.k8s.io/pcieRoot:
    string: pci0000:00
  dra.networking/supportedCNIs:
    strings:
      - macvlan
capacity:
  dra.networking/macvlans:
    value: '64'
    requestPolicy:
      default: '1'
      validValues:
        - '1'

Each claim allocation consumes one local unit. A 65th one-unit allocation cannot fit. The capacity is an administrator-defined quota, not an automatically discovered macvlan maximum. Adding another persona later requires a common interface domain and a safe migration of any already-active entry; do not silently attach new groups to existing allocations.

5.4 Example 4: PF with Macvlan AND Ipvlan

Scenario: The administrator declares macvlan and ipvlan incompatible on enp3s0f1, as motivated by the original document’s receive-handler conflict. The compiler does not contain that CNI-specific rule. Both modes have independent local limits of 64 allocations.

DeviceExposurePolicy — Macvlan Persona

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf1-macvlan
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f1"
  action: expose
  exposure:
    deviceNameSuffix: -macvlan
    allowMultipleAllocations: true
    capacity:
      macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: macvlan
        consumePerAllocation:
          macvlans: '1'
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - macvlan

DeviceExposurePolicy — Ipvlan Persona

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf1-ipvlan
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f1"
  action: expose
  exposure:
    deviceNameSuffix: -ipvlan
    allowMultipleAllocations: true
    capacity:
      ipvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: ipvlan
        consumePerAllocation:
          ipvlans: '1'
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - ipvlan

Resulting ResourceSlices — Two Objects in One Pool

apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-pf1-mode-example-counters
spec:
  driver: dra.networking
  pool:
    name: worker-1-pf1-mode-example
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  sharedCounters:
    - name: pf1-interface-mode
      counters:
        members:
          value: '2'
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-pf1-mode-example-devices
spec:
  driver: dra.networking
  pool:
    name: worker-1-pf1-mode-example
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  devices:
    - name: enp3s0f1-macvlan
      allowMultipleAllocations: true
      attributes:
        dra.networking/type:
          string: pf
        dra.networking/ifName:
          string: enp3s0f1
        dra.networking/pciAddress:
          string: '0000:03:00.1'
        dra.networking/mac:
          string: 04:3f:72:b0:d4:61
        dra.networking/mtu:
          int: 1500
        dra.networking/linkSpeed:
          int: 25000
        dra.networking/numVFs:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/numaNode:
          int: 0
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101d
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - macvlan
      capacity:
        dra.networking/macvlans:
          value: '64'
          requestPolicy:
            default: '1'
            validValues:
              - '1'
      consumesCounters:
        - counterSet: pf1-interface-mode
          compatibilityGroups:
            - macvlan
          counters:
            members:
              value: '1'
    - name: enp3s0f1-ipvlan
      allowMultipleAllocations: true
      attributes:
        dra.networking/type:
          string: pf
        dra.networking/ifName:
          string: enp3s0f1
        dra.networking/pciAddress:
          string: '0000:03:00.1'
        dra.networking/mac:
          string: 04:3f:72:b0:d4:61
        dra.networking/mtu:
          int: 1500
        dra.networking/linkSpeed:
          int: 25000
        dra.networking/numVFs:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/numaNode:
          int: 0
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101d
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - ipvlan
      capacity:
        dra.networking/ipvlans:
          value: '64'
          requestPolicy:
            default: '1'
            validValues:
              - '1'
      consumesCounters:
        - counterSet: pf1-interface-mode
          compatibilityGroups:
            - ipvlan
          counters:
            members:
              value: '1'

Walkthrough

EventRemaining numerical membersLocal macvlan units remainingGroup state / outcome
Initial264No active mode.
First macvlan claim163Active group is macvlan.
Second macvlan claim162Same device; no new fixed member charge.
Try ipvlan162Numerical member capacity is sufficient, but groups do not intersect: rejected.
Release the first macvlan claim163Second claim keeps macvlan active; ipvlan remains rejected.
Release the last macvlan claim264No active mode; ipvlan can allocate.

These are expected outcomes from the model, not a report of a cluster test performed for this document. The acceptance suite must verify them across successive allocation cycles and state reconstruction.

5.5 Example 5: PF with Macvlan AND Host-Device Passthrough

Scenario: The same interface supports shared macvlan and a normal exclusive passthrough persona. Keep separate entries to make allocation semantics explicit. It is not necessary to claim that capacity can never model a fixed full-capacity request; the selected design simply does not encode mode compatibility through plugin-specific capacity tricks.

DeviceExposurePolicies

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-macvlan
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -macvlan
    allowMultipleAllocations: true
    capacity:
      macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: macvlan
        consumePerAllocation:
          macvlans: '1'
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - macvlan
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-passthrough
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -passthrough
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: host-device
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - passthrough

Resulting ResourceSlice Fragments

Counter-bearing ResourceSlice.spec fragment:

sharedCounters:
  - name: pf0-interface-mode
    counters:
      members:
        value: '2'

Device-bearing ResourceSlice.spec fragment (same pool):

devices:
  - name: enp3s0f0-macvlan
    allowMultipleAllocations: true
    attributes:
      dra.networking/type:
        string: pf
      dra.networking/ifName:
        string: enp3s0f0
      dra.networking/pciAddress:
        string: '0000:03:00.0'
      dra.networking/mac:
        string: 04:3f:72:b0:d4:60
      dra.networking/mtu:
        int: 1500
      dra.networking/linkSpeed:
        int: 100000
      dra.networking/numVFs:
        int: 0
      dra.networking/rdma:
        bool: true
      dra.networking/numaNode:
        int: 0
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101d
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - macvlan
    capacity:
      dra.networking/macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    consumesCounters:
      - counterSet: pf0-interface-mode
        compatibilityGroups:
          - macvlan
        counters:
          members:
            value: '1'
  - name: enp3s0f0-passthrough
    attributes:
      dra.networking/type:
        string: pf
      dra.networking/ifName:
        string: enp3s0f0
      dra.networking/pciAddress:
        string: '0000:03:00.0'
      dra.networking/mac:
        string: 04:3f:72:b0:d4:60
      dra.networking/mtu:
        int: 1500
      dra.networking/linkSpeed:
        int: 100000
      dra.networking/numVFs:
        int: 0
      dra.networking/rdma:
        bool: true
      dra.networking/numaNode:
        int: 0
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101d
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - host-device
    consumesCounters:
      - counterSet: pf0-interface-mode
        compatibilityGroups:
          - passthrough
        counters:
          members:
            value: '1'
Diagram 6 — Dual Device Pattern: macvlan vs host-device passthrough with compatibility group exclusion

Macvlan and passthrough use disjoint groups in the same domain. Passthrough does not drain a mirrored macvlan quota. The same exclusion applies in either allocation order. A second allocation of the passthrough entry is rejected by ordinary device exclusivity.

Multiple exclusive aliases are not safe just because each is individually exclusive. They must also be disjoint from each other in the shared interface domain. Prefer one exclusive entry with several valid CNI alternatives when the allocation contract is identical.

5.6 Example 6: PF with VFs + Macvlan + Host-Device

Scenario: PF0 has eight VFs. Shared PF macvlan and child VF use are permitted concurrently. PF passthrough is declared incompatible with either. This is a policy assumption for the reference node, not a blanket hardware claim.

DeviceExposurePolicies

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-macvlan
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -macvlan
    allowMultipleAllocations: true
    capacity:
      macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: macvlan
        consumePerAllocation:
          macvlans: '1'
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - macvlan
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-passthrough
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -passthrough
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: host-device
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - passthrough
      - scope: sriovFamily
        domain: pf-access
        groups:
          - pf-passthrough
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-vfs
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f0"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained

Resulting ResourceSlice Fragments

PF0 has ten published member identities in the family: two PF personas and eight VFs. Only the two PF personas belong to its interface-mode domain. The following fragment shows both personas and VF0; Section 6 includes all eight VFs in complete objects.

Counter-bearing ResourceSlice.spec fragment:

sharedCounters:
  - name: pf0-interface-mode
    counters:
      members:
        value: '2'
  - name: pf0-family-pf-access
    counters:
      members:
        value: '10'

Device-bearing ResourceSlice.spec fragment (same pool):

devices:
  - name: enp3s0f0-macvlan
    allowMultipleAllocations: true
    attributes:
      dra.networking/type:
        string: pf
      dra.networking/ifName:
        string: enp3s0f0
      dra.networking/pciAddress:
        string: '0000:03:00.0'
      dra.networking/mac:
        string: 04:3f:72:b0:d4:60
      dra.networking/mtu:
        int: 1500
      dra.networking/linkSpeed:
        int: 100000
      dra.networking/numVFs:
        int: 8
      dra.networking/rdma:
        bool: true
      dra.networking/numaNode:
        int: 0
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101d
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - macvlan
    capacity:
      dra.networking/macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    consumesCounters:
      - counterSet: pf0-interface-mode
        compatibilityGroups:
          - macvlan
        counters:
          members:
            value: '1'
      - counterSet: pf0-family-pf-access
        compatibilityGroups:
          - host-retained
        counters:
          members:
            value: '1'
  - name: enp3s0f0-passthrough
    attributes:
      dra.networking/type:
        string: pf
      dra.networking/ifName:
        string: enp3s0f0
      dra.networking/pciAddress:
        string: '0000:03:00.0'
      dra.networking/mac:
        string: 04:3f:72:b0:d4:60
      dra.networking/mtu:
        int: 1500
      dra.networking/linkSpeed:
        int: 100000
      dra.networking/numVFs:
        int: 8
      dra.networking/rdma:
        bool: true
      dra.networking/numaNode:
        int: 0
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101d
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - host-device
    consumesCounters:
      - counterSet: pf0-interface-mode
        compatibilityGroups:
          - passthrough
        counters:
          members:
            value: '1'
      - counterSet: pf0-family-pf-access
        compatibilityGroups:
          - pf-passthrough
        counters:
          members:
            value: '1'
  - name: enp3s0f0v0
    attributes:
      dra.networking/type:
        string: vf
      dra.networking/ifName:
        string: enp3s0f0v0
      dra.networking/pfName:
        string: enp3s0f0
      dra.networking/vfIndex:
        int: 0
      dra.networking/pciAddress:
        string: '0000:03:00.2'
      dra.networking/numaNode:
        int: 0
      dra.networking/rdma:
        bool: true
      dra.networking/vendor:
        string: 15b3
      dra.networking/product:
        string: 101e
      dra.networking/driver:
        string: mlx5_core
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - sriov
          - host-device
    consumesCounters:
      - counterSet: pf0-family-pf-access
        compatibilityGroups:
          - host-retained
        counters:
          members:
            value: '1'

Compatibility Matrix

Candidate combinationInterface-mode resultPF-family resultAllowed?
PF macvlan + another share of PF macvlanSame persona/groupHost-retainedYes, within local capacity.
PF macvlan + VF0VF0 is outside interface domainBoth host-retainedYes.
VF0 + VF1Neither uses PF interface domainBoth host-retainedYes.
PF passthrough + PF macvlanDisjointDisjointNo.
PF passthrough + any VFVF outside interface domainDisjointNo.
Another allocation of the same VFNot relevantCompatible groupNo; the VF entry itself is exclusive.

An optional PF ipvlan persona would use [ipvlan] on pf0-interface-mode and [host-retained] on pf0-family-pf-access. VFs need no extra macvlan/ipvlan token. This two-domain construction avoids treating an empty VF group as a wildcard or forcing every VF to enumerate all PF modes.

Diagram 10 — Two-Domain Construction: PF0 with interface-mode and family pf-access domains showing combined compatibility results

All exclusion in this example is group-based. The baseline has no exclusion-slots, no mirrored macvlan capacity and no bandwidth reservation counter. linkSpeed is still published as an attribute.

Scenario: A free VF is directly allocatable. A VF pre-enslaved to a host bridge is not directly allocatable; the bridge is the shared device offered to Pods. Bridge ownership is host preconfiguration, not a DRA claim for the hidden VF.

DeviceExposurePolicies

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: free-vfs-sriov
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f0" && device.attributes["dra.networking"].masterBridge == ""
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-bridge-slave-vfs
spec:
  priority: 900
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].masterBridge
      != ""
  action: exclude
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: bridge-sriov-data
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "bridge" && device.attributes["dra.networking"].ifName
      == "br-sriov-data"
  action: expose
  exposure:
    allowMultipleAllocations: true
    capacity:
      ports:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: bridge
        consumePerAllocation:
          ports: '1'
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-pf0-host-bridge-uplink
spec:
  priority: 900
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: exclude

Resulting Bridge Entry

name: br-sriov-data
allowMultipleAllocations: true
attributes:
  dra.networking/type:
    string: bridge
  dra.networking/ifName:
    string: br-sriov-data
  dra.networking/bridgeName:
    string: br-sriov-data
  dra.networking/bridgeType:
    string: linux
  dra.networking/mtu:
    int: 9000
  dra.networking/supportedCNIs:
    strings:
      - bridge
capacity:
  dra.networking/ports:
    value: '64'
    requestPolicy:
      default: '1'
      validValues:
        - '1'

The final policy deliberately excludes every PF0 persona in this conservative bridge profile. Do not combine it with PF passthrough while the host bridge depends on a child VF. Free sibling VFs can still be exposed under the family policy.

A hidden VF does not contribute a scheduler compatibility group or consume a counter merely because it is busy outside DRA. Removing one from an advertised quota is not a substitute for an active compatibility declaration. This design does not fabricate a DRA allocation for it. Supporting host reservations with simultaneous PF exposure requires a separate, explicit reservation model; it is outside the baseline.

The node execution path must recheck host ownership before a destructive operation. Host-network configuration must be coordinated with DRA: an administrator attaching a bridge to hardware already allocated to a workload is outside the scheduler’s view.

5.8 Example 8: Excluding Cluster CNI and Management Interfaces

Scenario: Cluster networking and management interfaces are protected from new workload allocations.

DeviceExposurePolicies

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-cluster-cni
spec:
  priority: 1000
  selector:
    cel: device.attributes["dra.networking"].ifName in ["br-int", "ovn-k8s-mp0", "breth0", "ovn-k8s-gw0"]
  action: exclude
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-management
spec:
  priority: 1000
  selector:
    cel: device.attributes["dra.networking"].ifName == "eno1"
  action: exclude

Unallocated matching interfaces are absent from desired publication. Exclusions win regardless of priority. If a newly added exclusion matches an already-allocated device, transition it to drain handling and preserve the active declaration; do not interpret the policy as permission to destroy the running workload’s inventory or network state.

A hard emergency shutdown is an explicit administrative recovery action, not the ordinary reconciliation behavior.

5.9 Example 9: OVN-Kubernetes Networks via the Generic Driver

Scenario retained as a proposed integration, not an established capability. The policy and publication model can advertise ovn-k8s-cni-overlay as a supported CNI for an exclusive VF or a shared localnet uplink. That does not by itself establish that calling the binary is sufficient to provision an OVN network or accelerated VF.

DeviceExposurePolicy — Candidate Accelerated VF Integration

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: ovnk-accelerated-vfs
spec:
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp4s0f0"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: ovn-k8s-cni-overlay
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: ovnk-localnet-uplink
spec:
  priority: 150
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp6s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -localnet
    allowMultipleAllocations: true
    capacity:
      connections:
        value: '128'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: ovn-k8s-cni-overlay
        consumePerAllocation:
          connections: '1'
    additionalAttributes:
      dra.networking/physicalNetworkName: localnet1

Resulting Device Fragments

devices:
  - name: enp4s0f0v0
    attributes:
      dra.networking/type:
        string: vf
      dra.networking/ifName:
        string: enp4s0f0v0
      dra.networking/pfName:
        string: enp4s0f0
      dra.networking/pciAddress:
        string: '0000:04:00.2'
      dra.networking/numaNode:
        int: 0
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - ovn-k8s-cni-overlay
    consumesCounters:
      - counterSet: pf4-family-pf-access
        compatibilityGroups:
          - host-retained
        counters:
          members:
            value: '1'
  - name: enp6s0f0-localnet
    allowMultipleAllocations: true
    attributes:
      dra.networking/type:
        string: pf
      dra.networking/ifName:
        string: enp6s0f0
      dra.networking/numaNode:
        int: 1
      dra.networking/physicalNetworkName:
        string: localnet1
      device.k8s.io/pcieRoot:
        string: pci0000:00
      dra.networking/supportedCNIs:
        strings:
          - ovn-k8s-cni-overlay
    capacity:
      dra.networking/connections:
        value: '128'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
Diagram 7 — OVN-Kubernetes Integration via Generic Driver: accelerated VF and localnet uplink paths

These entries belong in their respective pools. The VF’s pool must also publish the referenced family counter set with a non-binding membership total for its complete inventory; the standalone localnet example has no competing persona and needs no domain.

Network selection metadata such as physicalNetworkName belongs in published attributes. Runtime CNI JSON belongs to the companion NetworkTopology/execution contract. The integration work must separately verify network-controller provisioning, required annotations and identities, representor ownership, VF handoff, namespace timing, CNI arguments, chaining support, cleanup and restart behavior.

Until those checks pass, remove claims such as “no separate OVN-K DRA driver is needed” or “our driver invokes it just like any other plugin” from the implementation guarantees. The discovery policy remains generic; external control-plane integration is not automatically generic.


6. Complete Reference Node

6.1 Node Inventory

Node worker-1 uses the same illustrative inventory as the original design:

DeviceInterfaceTypePCI / topologySpeedNUMARole
PF0enp3s0f0PF0000:03:00.0100 GbE0Two personas, eight VFs.
PF1enp3s0f1PF0000:03:00.125 GbE0Passthrough persona, four VFs.
PF0 VFsenp3s0f0v0 through enp3s0f0v7VFEnumerated addresses belowShared link0Exclusive sriov/host-device alternatives.
PF1 VFsenp3s0f1v0 through enp3s0f1v3VFEnumerated addresses belowShared link0Exclusive sriov/host-device alternatives.
Data bridgebr-dataLinux bridgeNo PCI identity in this exampleNot specifiedNot specifiedShared bridge ports.
Managementeno1NICNot exposedNot relevantNot specifiedExcluded.
Cluster networkingbr-int, ovn-k8s-mp0InfrastructureNot exposedNot relevantNot specifiedExcluded.
Diagram 8 — Complete Reference Node Topology: worker-1 with PF0, PF1, bridge, excluded interfaces, 5 ResourceSlices, 16 device entries

Reference state assumes no unmodeled host-owned dependency on the PFs that are exposed for passthrough. The host-bridge/VF profile in Example 7 has different exclusions and must not be blindly merged into this reference profile.

6.2 Complete DeviceExposurePolicy Set

These eight proposed policy resources are scoped to the reference node. The policy API itself must be installed by the future implementation; these are not replacements for a CRD manifest.

apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-cluster-cni
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 1000
  selector:
    cel: device.attributes["dra.networking"].ifName in ["br-int", "ovn-k8s-mp0", "breth0", "ovn-k8s-gw0"]
  action: exclude
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: exclude-management
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 1000
  selector:
    cel: device.attributes["dra.networking"].ifName == "eno1"
  action: exclude
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-macvlan
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -macvlan
    allowMultipleAllocations: true
    capacity:
      macvlans:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: macvlan
        consumePerAllocation:
          macvlans: '1'
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - macvlan
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-passthrough
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f0"
  action: expose
  exposure:
    deviceNameSuffix: -passthrough
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: host-device
    compatibility:
      - scope: interface
        domain: interface-mode
        groups:
          - passthrough
      - scope: sriovFamily
        domain: pf-access
        groups:
          - pf-passthrough
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf0-vfs
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f0"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf1-passthrough
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "pf" && device.attributes["dra.networking"].ifName
      == "enp3s0f1"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - pf-passthrough
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: pf1-vfs
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 200
  selector:
    cel: device.attributes["dra.networking"].type == "vf" && device.attributes["dra.networking"].pfName
      == "enp3s0f1"
  action: expose
  exposure:
    allowMultipleAllocations: false
    supportedCNIPlugins:
      - name: sriov
      - name: host-device
    compatibility:
      - scope: sriovFamily
        domain: pf-access
        groups:
          - host-retained
---
apiVersion: networking.dra.io/v1alpha1
kind: DeviceExposurePolicy
metadata:
  name: bridge-br-data
spec:
  nodeSelector:
    matchLabels:
      kubernetes.io/hostname: worker-1
  priority: 150
  selector:
    cel: device.attributes["dra.networking"].type == "bridge" && device.attributes["dra.networking"].ifName
      == "br-data"
  action: expose
  exposure:
    allowMultipleAllocations: true
    capacity:
      ports:
        value: '64'
        requestPolicy:
          default: '1'
          validValues:
            - '1'
    supportedCNIPlugins:
      - name: bridge
        consumePerAllocation:
          ports: '1'

6.3 Resulting ResourceSlices

There are five ResourceSlice objects, not three. Each PF pool has one counter-bearing slice and one device-bearing slice. The standalone bridge needs only a device-bearing slice. Every VF is included; there are no omitted placeholder devices in this reference output.

PF0 Pool — Counter Slice and Device Slice

apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-sriov-pf0-counters
spec:
  driver: dra.networking
  pool:
    name: worker-1-sriov-pf0
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  sharedCounters:
    - name: pf0-interface-mode
      counters:
        members:
          value: '2'
    - name: pf0-family-pf-access
      counters:
        members:
          value: '10'
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-sriov-pf0-devices
spec:
  driver: dra.networking
  pool:
    name: worker-1-sriov-pf0
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  devices:
    - name: enp3s0f0-macvlan
      allowMultipleAllocations: true
      attributes:
        dra.networking/type:
          string: pf
        dra.networking/ifName:
          string: enp3s0f0
        dra.networking/pciAddress:
          string: '0000:03:00.0'
        dra.networking/mac:
          string: 04:3f:72:b0:d4:60
        dra.networking/mtu:
          int: 1500
        dra.networking/linkSpeed:
          int: 100000
        dra.networking/numVFs:
          int: 8
        dra.networking/rdma:
          bool: true
        dra.networking/numaNode:
          int: 0
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101d
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - macvlan
      capacity:
        dra.networking/macvlans:
          value: '64'
          requestPolicy:
            default: '1'
            validValues:
              - '1'
      consumesCounters:
        - counterSet: pf0-interface-mode
          compatibilityGroups:
            - macvlan
          counters:
            members:
              value: '1'
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0-passthrough
      attributes:
        dra.networking/type:
          string: pf
        dra.networking/ifName:
          string: enp3s0f0
        dra.networking/pciAddress:
          string: '0000:03:00.0'
        dra.networking/mac:
          string: 04:3f:72:b0:d4:60
        dra.networking/mtu:
          int: 1500
        dra.networking/linkSpeed:
          int: 100000
        dra.networking/numVFs:
          int: 8
        dra.networking/rdma:
          bool: true
        dra.networking/numaNode:
          int: 0
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101d
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - host-device
      consumesCounters:
        - counterSet: pf0-interface-mode
          compatibilityGroups:
            - passthrough
          counters:
            members:
              value: '1'
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - pf-passthrough
          counters:
            members:
              value: '1'
    - name: enp3s0f0v0
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v0
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 0
        dra.networking/pciAddress:
          string: '0000:03:00.2'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v1
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v1
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 1
        dra.networking/pciAddress:
          string: '0000:03:00.3'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v2
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v2
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 2
        dra.networking/pciAddress:
          string: '0000:03:00.4'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v3
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v3
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 3
        dra.networking/pciAddress:
          string: '0000:03:00.5'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v4
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v4
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 4
        dra.networking/pciAddress:
          string: '0000:03:00.6'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v5
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v5
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 5
        dra.networking/pciAddress:
          string: '0000:03:00.7'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v6
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v6
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 6
        dra.networking/pciAddress:
          string: '0000:03:01.0'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f0v7
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f0v7
        dra.networking/pfName:
          string: enp3s0f0
        dra.networking/vfIndex:
          int: 7
        dra.networking/pciAddress:
          string: '0000:03:01.1'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf0-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'

PF1 Pool — Counter Slice and Device Slice

apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-sriov-pf1-counters
spec:
  driver: dra.networking
  pool:
    name: worker-1-sriov-pf1
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  sharedCounters:
    - name: pf1-family-pf-access
      counters:
        members:
          value: '5'
---
apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-sriov-pf1-devices
spec:
  driver: dra.networking
  pool:
    name: worker-1-sriov-pf1
    generation: 1
    resourceSliceCount: 2
  nodeName: worker-1
  devices:
    - name: enp3s0f1
      attributes:
        dra.networking/type:
          string: pf
        dra.networking/ifName:
          string: enp3s0f1
        dra.networking/pciAddress:
          string: '0000:03:00.1'
        dra.networking/mac:
          string: 04:3f:72:b0:d4:61
        dra.networking/mtu:
          int: 1500
        dra.networking/linkSpeed:
          int: 25000
        dra.networking/numVFs:
          int: 4
        dra.networking/rdma:
          bool: true
        dra.networking/numaNode:
          int: 0
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101d
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - host-device
      consumesCounters:
        - counterSet: pf1-family-pf-access
          compatibilityGroups:
            - pf-passthrough
          counters:
            members:
              value: '1'
    - name: enp3s0f1v0
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f1v0
        dra.networking/pfName:
          string: enp3s0f1
        dra.networking/vfIndex:
          int: 0
        dra.networking/pciAddress:
          string: '0000:03:01.2'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf1-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f1v1
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f1v1
        dra.networking/pfName:
          string: enp3s0f1
        dra.networking/vfIndex:
          int: 1
        dra.networking/pciAddress:
          string: '0000:03:01.3'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf1-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f1v2
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f1v2
        dra.networking/pfName:
          string: enp3s0f1
        dra.networking/vfIndex:
          int: 2
        dra.networking/pciAddress:
          string: '0000:03:01.4'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf1-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'
    - name: enp3s0f1v3
      attributes:
        dra.networking/type:
          string: vf
        dra.networking/ifName:
          string: enp3s0f1v3
        dra.networking/pfName:
          string: enp3s0f1
        dra.networking/vfIndex:
          int: 3
        dra.networking/pciAddress:
          string: '0000:03:01.5'
        dra.networking/numaNode:
          int: 0
        dra.networking/rdma:
          bool: true
        dra.networking/vendor:
          string: 15b3
        dra.networking/product:
          string: 101e
        dra.networking/driver:
          string: mlx5_core
        device.k8s.io/pcieRoot:
          string: pci0000:00
        dra.networking/supportedCNIs:
          strings:
            - sriov
            - host-device
      consumesCounters:
        - counterSet: pf1-family-pf-access
          compatibilityGroups:
            - host-retained
          counters:
            members:
              value: '1'

Standalone Bridge Pool

apiVersion: resource.k8s.io/v1
kind: ResourceSlice
metadata:
  name: worker-1-bridge-br-data
spec:
  driver: dra.networking
  pool:
    name: worker-1-bridge-br-data
    generation: 1
    resourceSliceCount: 1
  nodeName: worker-1
  devices:
    - name: br-data
      allowMultipleAllocations: true
      attributes:
        dra.networking/type:
          string: bridge
        dra.networking/ifName:
          string: br-data
        dra.networking/bridgeName:
          string: br-data
        dra.networking/bridgeType:
          string: linux
        dra.networking/mtu:
          int: 9000
        dra.networking/vlanFiltering:
          bool: true
        dra.networking/supportedCNIs:
          strings:
            - bridge
      capacity:
        dra.networking/ports:
          value: '64'
          requestPolicy:
            default: '1'
            validValues:
              - '1'

6.4 Summary

PoolResourceSlice objectsDevice entriesCompatibility domains
worker-1-sriov-pf0210: two PF personas and eight VFspf0-interface-mode has members=2; pf0-family-pf-access has members=10.
worker-1-sriov-pf125: one PF persona and four VFspf1-family-pf-access has members=5.
worker-1-bridge-br-data11None.
Total516Three counter sets across the two PF counter slices.

Not published: management and cluster-network interfaces selected by exclusion policies. Speeds remain attributes; there is no bandwidth reservation in these baseline manifests.

The numerical member totals accommodate all published identities, including incompatible alternatives. They are deliberately not the source of mutual exclusion.

6.5 App Developer Experience

The platform administers exposure policy and DeviceClasses. Applications reference a class, normally through a ResourceClaimTemplate. The companion NetworkTopology controller may generate those classes, but the discovery implementation does not rely on unverified hidden capacity injection.

The following DeviceClass shows the actual selection logic for the reference PF0 macvlan entry. The VF class is included for the optional bandwidth fragment and future examples. Driver and attribute domains are illustrative; a production project must select its owned driver domain consistently.

apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
  name: network-macvlan-pf0
spec:
  selectors:
    - cel:
        expression: device.driver == "dra.networking" && device.allowMultipleAllocations == true && device.attributes["dra.networking"].type
          == "pf" && device.attributes["dra.networking"].ifName == "enp3s0f0" && device.attributes["dra.networking"].supportedCNIs.includes("macvlan")
---
apiVersion: resource.k8s.io/v1
kind: DeviceClass
metadata:
  name: network-vf-pf0
spec:
  selectors:
    - cel:
        expression: device.driver == "dra.networking" && device.allowMultipleAllocations == false && device.attributes["dra.networking"].type
          == "vf" && device.attributes["dra.networking"].pfName == "enp3s0f0" && device.attributes["dra.networking"].supportedCNIs.includes("sriov")
---
apiVersion: resource.k8s.io/v1
kind: ResourceClaimTemplate
metadata:
  name: my-app-network
  namespace: default
spec:
  spec:
    devices:
      requests:
        - name: net
          exactly:
            deviceClassName: network-macvlan-pf0
            allocationMode: ExactCount
            count: 1

No explicit capacity request is needed for this baseline: the selected device’s local policy reserves one macvlan unit. networkTopologyRef and CNI execution configuration are driver/topology integration concerns, not new standard fields on a ResourceClaim. The selectors above demonstrate the allocation layer only; they do not provide the complete CNI execution configuration from the companion proposal. S7 S9

Claims that combine two VF requests, as in the original bonded-RDMA application example, follow the same class-reference pattern. Their network topology and distinct-device/topology constraints must be generated and tested by the companion controller rather than assumed from request names.


7. Decisions and Remaining Questions

The original consume-once question is no longer a blocker: mode exclusion uses compatibility groups. Remaining implementation choices are recorded separately from resolved API behavior.

TopicDecision / remaining work
Mode exclusionResolved: KEP 5963 with explicit scoped group bindings. No mirrored-capacity cross-drain.
PF/VF interactionExplicit family policy. The reference permits host-retained PF macvlan plus VFs and excludes PF passthrough. Hardware support and cleanup remain platform test requirements.
Macvlan/bridge capacity discoveryAdministrator supplied. The driver does not invent maximum counts.
Default local consumptionExplicit local request policies provide one-unit defaults. Validate round-trip behavior and actual allocator results in tests.
Shared-allocation identityLog the claim’s metadata UID, request, driver/pool/device and result shareID when present. Do not claim that a claim UID is a field inside every AllocationResult.
Cross-pool associationsNot supported by this design. A common compatibility domain must be placed in one pool.
Dynamic VF count / device disappearanceNeeds allocation-aware drain and identity retention. Generation changes alone do not make hardware reconfiguration safe.
Bridge creationDiscovery-only in the initial implementation. Host topology changes require coordination.
List-attribute fallbackInitial list-based profile requires its feature gate. No substring-based comma-separated fallback. A reduced exact-scalar profile would need a separate schema/selector contract.
Policy validationLocal admission validation plus node-level whole-pool checks. Normal exclusive policies without capacity are valid.
Shared bandwidthFollow-on feature requiring an explicit budget policy schema, target PR/version integration and accounting/enforcement tests.
OVN-K integrationProposed example only; requires end-to-end control-plane and execution validation.
Exact stable identity encodingDefine persisted keys, replacement detection and hashed DNS-label encoding before coding publication.
Deployment targetPin Kubernetes/client libraries and determine supported OpenShift feature configuration. Do not infer support from generic Kubernetes version numbers.

8. Lifecycle, Reconciliation and Safety Contract

8.1 Why Active Publication Must Be Preserved

The inspected KEP 5963 implementation reads groups from live device declarations. Removing an allocated device from the inventory removes its contribution to the compatibility baseline. Changing its groups changes the baseline used for future allocations. These are implementation facts, not behavior this driver should rely on to switch a mode in use. S3 S4

The driver must pin the active publication contract: device and pool identities, domain associations, group tokens, sharing semantics, CNI alternatives and relevant execution/capacity configuration. Store enough committed publication and node execution state to recover this contract after restart; a fresh scan of the host namespace is insufficient when devices have been moved.

Policy state is desired state. It is not permission to mutate the meaning of an already allocated device. A new desired contract becomes active only after a safe transition.

8.2 Drain Protocol

A change that removes a persona, changes its groups, switches sharing semantics, changes a scope root, renames an active identity, or replaces physical hardware enters a drain workflow:

  1. Record the desired change and keep the previous active declaration.
  2. Prevent new admissions into the affected transition through the deployment’s maintenance/admission mechanism; optionally mark retained devices NoSchedule without evicting running Pods.
  3. Wait for allocated claims, in-flight preparation and node cleanup to reconcile. Recheck before committing a new generation.
  4. Publish the new complete model and retire the old one only when safe.
Diagram 11 — Drain Protocol Flow: safe transition steps from trigger through record, block, wait, and publish

A NoSchedule taint can be tolerated by a claim. It is an operational aid, not a security boundary or proof that there are no in-flight allocations. Preventing bypass requires appropriate admission/RBAC policy, and scheduler/watch races still require node-side verification. A poll observing zero claims is not an atomic lock against a concurrent allocator.

For a policy exclusion on an active interface, report PendingDrain rather than immediately deleting the device. For unexpected hardware disappearance, retain the allocation record, fail new preparation clearly, and require recovery; do not expose a replacement under an identity still in use.

8.3 Node-Side Enforcement

The execution path must maintain a persistent, idempotent allocation journal and serialize conflicting work by resolved domain. Acquire multiple domain locks in deterministic order. Validate the actual physical identity and the pinned contract before CNI or device operations, including the actual networking hook if it occurs after NodePrepareResources.

Preparation, retries, cleanup failures, kubelet reconnect and process restarts must not release a domain prematurely. A local cleanup completion does not remove scheduler allocation state, and scheduler deallocation does not justify forgetting hardware cleanup that is still pending.

The journal must distinguish claim identity, allocation result/share identity and Pod consumers. Count claim allocations separately from Pod references when a claim can be reused. Track external host ownership explicitly where supported; otherwise exclude unsafe exposure profiles, as in Example 7.

8.4 Capability and Authorization Boundaries

The installation profile requires DRAPartitionableDevices, DRAConsumableCapacity, DRADeviceCompatibilityGroups and DRAListTypeAttributes for the full examples. DRASharedConsumableCapacity is not part of the baseline. S2

Use a deployment capability contract and verification, not a fictional generic runtime API for reading all feature-gate values. API write/read tests must detect stripped fields. Scheduler behavior needs its own controlled test. Loss of a required capability must not trigger a fallback that removes exclusion constraints from still-published alternatives.

Restrict writes to driver ResourceSlices, exposure policies and relevant claim status. Compatibility is an allocation predicate, not a tenant authorization system or protection against arbitrary privileged host reconfiguration. Administrative-access claims and custom schedulers require an explicit support policy; the baseline guarantees target normal scheduler-managed allocations.


9. Required Acceptance Tests

The following are implementation acceptance criteria. They have not been run against Kubernetes as part of preparing this document.

TestRequired result
Policy priority and exclusionsOne winner per suffix; lexical tie break; any exclusion wins; no match publishes nothing new.
Selector failureInvalid CEL is rejected; runtime evaluation errors fail closed with diagnostics.
Policy-to-v1 serializationNo basic, no stringSlice; attributes/capacity at device level; quantities and attribute types preserved.
Slice separation and limitsCounter and device objects separated; consistent total count and generation; third consumption or third group rejected.
Counter-set resolutionPF/VF members resolve to the same family domain; different interfaces/pools stay independent.
Alias coverageMultiple personas without a common guard rejected; exclusive aliases cannot coexist.
Macvlan repeated allocationsA and B on one device both allocate, consuming two local units and one fixed member.
Cross-mode rejectionIPvlan rejected while any macvlan claim remains, in either allocation order.
First-share releaseRelease A while B remains; reconstruct allocator state; incompatible mode stays rejected.
Last-share releaseRelease B, reconcile cleanup and allocation state; the alternate mode can allocate.
Driver/scheduler state reconstructionRestarts preserve compatible outcomes and cannot unlock an active mode.
Local capacity boundary64 one-unit allocations fit; 65th does not. Releasing one restores exactly one unit.
Shared versus exclusiveBoth orderings blocked; second allocation of a normal exclusive entry rejected.
PF family combinationsPF macvlan plus all eligible VFs permitted; PF passthrough rejected until all incompatible family members are released.
Independent interface and family domainsAdding PF ipvlan does not block VFs but does block PF macvlan; all two-domain checks apply.
Whole-set intersectionPairwise-overlapping but globally disjoint group sets cannot all allocate.
Empty group behaviorEmpty only matches empty; missing groups never treated as a wildcard.
Single allocation search and backtrackingIncompatible requests in one claim/Pod rejected; failed candidates restore group and capacity search state.
Policy / identity mutationActive rename, group change, suffix replacement, sharing change and VF-count shrink require drain; no early retirement.
Host namespace move / rebindingRescan does not forget a still-allocated device after a supported operation moves or rebinds it.
Hidden bridge-owned VFExclusion alone creates no reservation; unsafe PF passthrough is unavailable in the selected host-bridge profile.
Feature disabled / stripped / skewedRequired fields missing or unenforced prevents unsafe publication; no silent downgrade to unconstrained alternatives.
Partial ResourceSlice updatesIncomplete generations and retry/restart converge without exposing conflicting new aliases.
Claim reused by PodsPod reference changes do not double-charge local capacity or release the claim’s device early.
Prepare/unprepare retries and failuresIdempotent journal replay; deterministic domain locking; cleanup failure retains safety state.
Optional shared-bandwidth profileOnly on a combined supported build: per-share accounting, release order, defaults, policy changes, feature transitions and compatibility all tested.
OVN-K profileAdvertised support is promoted to an implementation guarantee only after provisioning, handoff and cleanup tests pass.

Use both upstream structured-allocator tests with published fixtures and integration/e2e tests exercising real claim transitions. A mathematical model can check desired combinations, but it does not prove the actual scheduler, API server, feature gates or node driver implement them.


10. Implementation Handoff

The first implementation should deliver the baseline, not all optional integrations at once:

Policy API and compiler. Implement the proposed schema and admission rules, normalized discovery facts, deterministic policy resolution, scoped domain resolution, stable identity mapping and whole-pool validation. Pure compiler tests should use the examples in this document as fixtures.

Publication and allocation-aware state. Integrate the upstream ResourceSlice publishing machinery, correct packing/counts, committed inventory state, allocated-claim observation and drain handling. Add failure/restart tests before allowing automatic destructive transitions.

Node enforcement and topology integration. Implement the journal and domain safety checks, then connect the companion NetworkTopology/CNI execution path. Keep plugin-specific arguments separate from exposure metadata. Confirm the one-allocation/one-interface baseline contract and claim-sharing behavior.

Target-cluster verification. Run API validation, CEL evaluation and allocator/e2e acceptance on the exact Kubernetes or OpenShift target. Alpha source availability is not production support approval.

Follow-ons. Add an explicit shared-budget policy API and KEP 5941 integration only after the baseline works. Treat OVN-K and host-managed reservations as separately tested integrations, not assumed consequences of the generic discovery engine.

Suggested initial interfaces are conceptual boundaries, not preselected Go APIs: Discover, ResolvePolicies, CompilePools, ReconcilePublication, ObserveAllocations, and EnforceDomainContract. Their concrete signatures should be designed against the actual driver repository in the implementation phase.

No implementation or live site update is implied by this document revision.


11. Source Notes

The uploaded design provides the original scope, terminology, nine scenarios and reference inventory. This revision replaces the exclusion model and adds explicitly proposed policy/lifecycle rules. The custom compatibility schema, non-binding members counters and compiler checks are design decisions in this document, not features supplied automatically by Kubernetes.

Upstream facts were checked against the following primary sources. Prefer the pinned implementation over older prose when the two disagree. Companion documents and the OVN integration were not independently revalidated in this revision.

ON THIS PAGE