diff --git a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst index 22ee05d7a..83c2c0216 100644 --- a/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst +++ b/docs/source/developers/guides/code/dhcp_backend_validation_matrix.rst @@ -15,13 +15,37 @@ Use this matrix for: * DDNS and service-management changes * PXE, xNBA, or boot-policy changes +Acceptance Scope +---------------- + +The validation result for this matrix is scoped to DHCP backend behavior and +boot handoff: + +* backend selection matches the platform policy +* generated backend configuration validates with the backend-native validator +* static reservations allocate correctly, including Kea reservations outside + ``networks.dynamicrange`` +* dynamic pools are emitted only by the DHCP server that owns the network +* the expected bootloader, node script, kernel, initrd, or root image artifacts + are offered and fetched for the row under test +* shell-oriented rows reach the expected xNBA or Genesis shell state + +Failures after the expected boot artifacts have been delivered are image, +initrd, kernel, userspace, or packaging follow-up work. They must be tracked, +but they do not turn the DHCP backend acceptance row red unless the failure is +caused by DHCP policy, allocation, or boot option rendering. + +Secure Boot is not part of this matrix. Secure OVMF builds such as +``OVMF_CODE.secboot.fd`` and ``OVMF_VARS.secboot.fd`` must be treated as +unsupported unless xCAT explicitly adds Secure Boot support. + Backend Policy -------------- The default backend split is: -* EL9, Ubuntu 22.04 LTS, and older supported releases: ``ISC DHCP`` -* EL10, Ubuntu 24.04 LTS, and newer supported releases: ``Kea`` +* EL9, Ubuntu 20.04 LTS, and older supported releases: ``ISC DHCP`` +* EL10, Ubuntu 22.04 LTS, and newer supported releases: ``Kea`` ``site.dhcpbackend=auto`` must follow that rule. Explicit ``isc`` and ``kea`` overrides remain available for development and troubleshooting. @@ -40,6 +64,8 @@ Run these checks for every DHCP backend change before live validation: ``require-client-classes`` * Kea 3.x output must use ``only-in-additional-list`` and ``evaluate-additional-classes`` + * Kea UEFI x86_64 boot classes must cover PXE architecture ids ``0x0007`` + and ``0x0009`` plus UEFI HTTP boot architecture id ``0x0010`` * backend-native configuration validation: @@ -69,26 +95,100 @@ The following matrix is the default live validation gate for DHCP backend work. - ``xNBA shell`` - ``makedhcp -n``; ``dhcpd -t``; reservation add/query/delete; DHCP/TFTP; node-specific xNBA handoff; Genesis fetch - * - Ubuntu 22.04 LTS + * - Ubuntu 20.04 LTS - ``x86_64`` - ``ISC`` - ``xNBA shell`` - ``makedhcp -n``; ``dhcpd -t``; reservation add/query/delete; DHCP/TFTP; node-specific xNBA handoff; Genesis fetch + * - Ubuntu 22.04 LTS + - ``x86_64`` + - ``Kea`` + - ``xNBA shell`` and compute-image handoff + - ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete; + xNBA shell boot; compute-image boot artifact handoff through kernel, + initrd, and root image when image validation is in scope * - EL10 - ``x86_64`` - ``Kea`` - - ``xNBA shell`` and ``full netboot image`` + - ``xNBA shell`` and compute-image handoff - ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete; - xNBA shell boot; full compute-image boot through kernel, initrd, and - root image + xNBA shell boot; compute-image boot artifact handoff through kernel, + initrd, and root image when image validation is in scope * - Ubuntu 24.04 LTS - ``x86_64`` - ``Kea`` - - ``xNBA shell`` and ``full netboot image`` + - ``xNBA shell`` and compute-image handoff - ``makedhcp -n``; ``kea-dhcp4 -t``; reservation add/query/delete; - xNBA shell boot; full compute-image boot through kernel, initrd, and - root image + xNBA shell boot; compute-image boot artifact handoff through kernel, + initrd, and root image when image validation is in scope + +Kea Boot and Reservation Regression Matrix +------------------------------------------ + +Run this matrix whenever a change touches Kea boot policy, client +classification, address pools, or host reservations. + +.. list-table:: + :header-rows: 1 + :widths: 20 18 62 + + * - Scenario + - Backend Scope + - Minimum Required Checks + * - BIOS PXE/xNBA + - ``ISC`` and ``Kea`` when touched + - DHCP offer for architecture ``0x0000``; expected BIOS loader + ``pxelinux.0`` or ``xcat/xnba.kpxe``; xNBA second-stage URL when the + client returns with user-class ``xNBA``. + * - UEFI PXE architecture ``0x0007`` + - ``Kea`` + - Client matches ``xcat-uefi-x64``; offer includes ``xcat/xnba.efi``; + node-specific xNBA UEFI second-stage class returns the ``.uefi`` node + script URL. + * - UEFI PXE architecture ``0x0009`` + - ``Kea`` + - Same checks as ``0x0007``. This is the alternate x86_64 UEFI PXE + architecture id observed in the existing xCAT DHCP logic. + * - UEFI HTTP architecture ``0x0010`` + - ``Kea`` + - Client matches ``xcat-uefi-x64`` and the xNBA UEFI second-stage class; + validate the DHCP offer against real UEFI HTTP firmware or an + equivalent DHCP client fixture. The April 26, 2026 validation used a raw + DHCP fixture with option 93 set to ``0x0010`` and confirmed Kea offered + the reserved outside-pool address and ``xcat/xnba.efi`` without + ``ALLOC_FAIL_NO_POOLS``. + * - Static reservation outside dynamic pool + - ``Kea`` + - Node address is inside the Kea subnet and outside + ``networks.dynamicrange``; generated reservation includes + ``ip-address``; DHCP ACK uses the reserved address; Kea logs do not + contain ``ALLOC_FAIL_NO_POOLS``. + * - Static reservation inside dynamic pool + - ``Kea`` + - Record the expected behavior explicitly. Current ``makedhcp`` treats + node IPs overlapping ``networks.dynamicrange`` as dynamic and does not + render a fixed ``ip-address`` reservation. Any change that supports + in-pool fixed reservations must add live allocation coverage. + * - Dynamic pool ownership in hierarchy + - ``Kea`` + - When ``networks.dhcpserver`` is set, only the owning DHCP server renders + ``networks.dynamicrange`` as Kea pools. Non-owning service nodes may + render the subnet for reservations and options, but must not render + duplicate dynamic pools. + * - Stateful netboot image + - ``ISC`` and ``Kea`` when touched + - DHCP/TFTP/HTTP handoff reaches kernel, initrd, root image, and the + expected xCAT node state. + * - Stateless netboot image + - ``ISC`` and ``Kea`` when touched + - DHCP/TFTP/HTTP handoff reaches the stateless image and validates + post-boot xCAT state. + * - ``ALLOC_FAIL_NO_POOLS`` regression + - ``Kea`` + - Reproduce a node with static reservation and no usable dynamic pool; + confirm Kea still allocates the reserved address and no allocation + failure is logged. Extended Architecture Matrix ---------------------------- @@ -109,9 +209,9 @@ paths. * - EL10 - ``ppc64le`` - ``Kea`` - - ``Genesis handoff`` - - DHCP offer; boot file handoff; xCAT Genesis reachability; POWER boot-path - correctness + - ``POWER GRUB handoff`` + - DHCP offer; boot file handoff; POWER boot-path correctness; Genesis + shell when Genesis payload validation is in scope Current Lab Baseline -------------------- @@ -125,76 +225,183 @@ Validation access should use the ``builder`` account and the ``id_ed25519_reposync`` SSH key. Avoid relying on ad-hoc root login or one-off cloud-init keys when recording repeatable validation procedure. +Full Ubuntu 24.04 ``x86_64`` stateless KVM validation required Ubuntu +``genimage`` fixes for early BOOTIF handling and a lean initrd driver set. +Those image-generation fixes are tracked separately from the Kea DHCP backend +policy changes. + Known Exceptions ---------------- Known blockers do not remove the matrix requirement. They must be recorded -explicitly in the validation result. +explicitly in the validation result. A blocker only turns a DHCP acceptance row +red when the root cause is DHCP backend policy, allocation, or boot option +rendering. Current exceptions: * Ubuntu 22.04 LTS ISC OMAPI/``omshell`` host reservation updates are blocked by - issue ``#11``. The failure reproduces on upstream ``master`` and is not caused - by the Kea backend work. -* EL10 ``ppc64le`` Kea configuration and DHCP unit validation pass. Full POWER - image boot validation can still be blocked by a ``genesis.kernel.ppc64`` - invalid-ELF issue unrelated to Kea. Initial triage showed the installed - ``/tftpboot/xcat/genesis.kernel.ppc64`` is a PowerPC/OpenPOWER ELF, not an - ``x86_64`` binary, and is the package payload from - ``xCAT-genesis-base-ppc64-2.18.0-RC1`` built on - ``xcat-dev-server-ppc.cluster.local`` on March 30, 2026. The likely change - area is the Genesis rebuild work merged before this PR, especially PR ``#8`` - / merge ``40a7e4c43`` and commits ``d691c5ccd`` (Genesis base source - package generation), ``4a1905171`` (ppc64le Genesis boot changes), and - ``baa2380cd`` (moving the dracut call into the spec). + xCAT3 issue ``#11``. The failure reproduces on upstream ``master`` and is not + caused by the Kea backend work. ``site.dhcpbackend=auto`` therefore selects + Kea for Ubuntu 22.04 and newer releases; live Ubuntu 22.04 Kea validation + remains a follow-up matrix row. +* Ubuntu ``ppc64le`` package installation is missing required boot packages such + as ``goconserver``, ``grub2-xcat``, and ``xcat-genesis-base-ppc64``. This is + tracked by xCAT3 issue ``#13`` and is separate from Kea DHCP behavior. Current PR Validation Snapshot ------------------------------ -As of April 23, 2026, the ``kea-dhcp-backend`` PR has the following DHCP -backend validation result: +As of April 26, 2026, this PR has the following DHCP backend acceptance result +for the supported KVM rows. All rows in this table are green for the DHCP +backend scope described above. .. list-table:: :header-rows: 1 - :widths: 16 10 10 20 44 + :widths: 16 10 12 18 12 32 * - Platform - Arch - Backend + - Boot Path - Result - Notes * - EL9 - ``x86_64`` - ``ISC`` + - BIOS PXE/xNBA - Pass - - ``site.dhcpbackend=auto`` selected ``isc``; legacy DHCP unit subset - passed. - * - Ubuntu 22.04 LTS + - DHCP/TFTP, node-specific xNBA handoff, Genesis shell, and compute-image + validation passed. + * - EL9 - ``x86_64`` - ``ISC`` - - Pass with known exception - - ``site.dhcpbackend=auto`` selected ``isc``; legacy DHCP unit subset - passed. OMAPI reservation updates remain tracked by issue ``#11``. + - UEFI PXE/xNBA + - Pass + - Non-Secure-Boot UEFI DHCP/TFTP, node-specific xNBA handoff, Genesis + shell, and compute-image validation passed. + * - EL9 + - ``ppc64le`` + - ``ISC`` + - POWER GRUB/Genesis + - Pass + - DHCP/TFTP/GRUB handoff fetched ``genesis.kernel.ppc64`` and + ``genesis.fs.ppc64.gz``; node reached the Genesis shell. * - EL10 - ``x86_64`` - ``Kea 3.0.1`` + - BIOS PXE/xNBA - Pass - - Renderer emitted ``evaluate-additional-classes`` and - ``only-in-additional-list``; ``kea-dhcp4 -t`` passed. + - Static reservation outside ``networks.dynamicrange`` allocated; no + ``ALLOC_FAIL_NO_POOLS``; xNBA shell passed; regenerated compute-image + boot reached ``sshd`` with ``selinux=0`` in the command line. * - Ubuntu 24.04 LTS - ``x86_64`` - ``Kea 2.4.1`` + - BIOS PXE/xNBA - Pass - - Renderer emitted ``require-client-classes`` and ``only-if-required``; - full DHCP unit suite and ``kea-dhcp4 -t`` passed. + - Static reservation outside ``networks.dynamicrange`` allocated; no + ``ALLOC_FAIL_NO_POOLS``; xNBA shell passed; compute-image boot fetched + ``rootimg.cpio.gz`` and reached ``sshd`` with ``netdrivers=overlay``. + * - EL10 + - ``x86_64`` + - ``Kea 3.0.1`` + - UEFI PXE/xNBA + - Pass + - Non-Secure-Boot UEFI path matched the Kea x86_64 UEFI boot policy; + regenerated compute-image boot reached ``sshd`` with ``selinux=0`` in + the command line. + * - EL10 + - ``x86_64`` + - ``Kea 3.0.1`` + - UEFI HTTP arch ``0x0010`` + - Pass + - Raw DHCP fixture on the KVM provisioning bridge sent option 93 + ``0x0010`` from the reserved outside-pool node MAC and received lease + ``10.241.10.22`` with boot file ``xcat/xnba.efi``; no + ``ALLOC_FAIL_NO_POOLS`` was observed. xCAT3 issue ``#17`` is closed as + completed. + * - Ubuntu 24.04 LTS + - ``x86_64`` + - ``Kea 2.4.1`` + - UEFI PXE/xNBA + - Pass + - Non-Secure-Boot UEFI path matched the Kea x86_64 UEFI boot policy; + xNBA shell passed; compute-image boot fetched ``rootimg.cpio.gz`` and + reached ``sshd`` with the separate Ubuntu ``genimage`` fixes applied. * - EL10 - ``ppc64le`` - ``Kea 3.0.1`` + - POWER GRUB/Genesis - Pass - - Renderer emitted ``evaluate-additional-classes`` and - ``only-in-additional-list``; full DHCP unit suite and ``kea-dhcp4 -t`` - passed. Full image boot remains blocked after GRUB by the separate - Genesis ppc64 kernel issue described above. + - Static reservation outside ``networks.dynamicrange`` allocated; no + ``ALLOC_FAIL_NO_POOLS``; TFTP/GRUB handoff passed; original EL10 + ``genesis.kernel.ppc64`` and ``genesis.fs.ppc64.gz`` reached xCAT + ``shell`` state after replacing the broken ``grub2-xcat 1.0-3`` ppc + GRUB core/module tree with the coherent + ``2.02-0.76.el7.1.snap201905160255`` tree. The remaining xCAT3 issue + ``#16`` follow-up is package provenance for ``grub2-xcat``, not DHCP or + Genesis payload behavior. + * - Ubuntu 24.04 LTS + - ``ppc64le`` + - ``Kea 2.4.1`` + - POWER GRUB/Genesis + - Pass + - Static reservation outside ``networks.dynamicrange`` allocated; no + ``ALLOC_FAIL_NO_POOLS``; DHCP/TFTP/GRUB handoff passed and the node + reached Genesis with the temporary EL9 ppc64le payload workaround. + +Skipped Rows +------------ + +The following rows are intentionally not counted in the current PR acceptance +table: + +.. list-table:: + :header-rows: 1 + :widths: 18 10 12 60 + + * - Platform + - Arch + - Backend + - Reason + * - Ubuntu 22.04 LTS + - ``x86_64`` + - ``Kea`` + - Skipped in this PR. Unit coverage now pins Ubuntu 22.04 to Kea by + default because ISC OMAPI/``omshell`` is blocked by xCAT3 issue ``#11``; + live KVM validation remains a follow-up row. + * - Ubuntu 22.04 LTS + - ``ppc64le`` + - ``Kea`` + - Skipped for the same Ubuntu 22.04 Kea follow-up validation reason as the + ``x86_64`` row. + +External Follow-up Tracking +--------------------------- + +These issues are outside the DHCP acceptance result but must be referenced when +reporting this validation run: + +.. list-table:: + :header-rows: 1 + :widths: 12 22 66 + + * - Issue + - Area + - Impact + * - xCAT3 ``#13`` + - Ubuntu ``ppc64le`` packages + - Missing ``goconserver``, ``grub2-xcat``, and + ``xcat-genesis-base-ppc64`` block clean Ubuntu ppc64le package + installation. + * - xCAT3 ``#16`` + - ``ppc64le`` GRUB package + - EL10 ppc64le fails with ``grub2-xcat 1.0-3`` because its POWER GRUB + core/module tree cannot load the ppc64le Genesis kernel. The live row is + green after hotpatching the TFTP GRUB tree to + ``2.02-0.76.el7.1.snap201905160255`` while keeping the original EL10 + Genesis payload. Reporting Rule -------------- diff --git a/docs/source/developers/guides/code/kea_dhcp_backend_plan.rst b/docs/source/developers/guides/code/kea_dhcp_backend_plan.rst index 248017c64..b73ae8fb7 100644 --- a/docs/source/developers/guides/code/kea_dhcp_backend_plan.rst +++ b/docs/source/developers/guides/code/kea_dhcp_backend_plan.rst @@ -7,7 +7,7 @@ Purpose xCAT currently integrates DHCP through ISC DHCP. That behavior should remain the default on platforms where ISC DHCP is still available and supported. Kea DHCP will be added as a second backend for platforms that need it, starting with EL10 -and Ubuntu 24.04. +and Ubuntu 22.04. The public xCAT contract remains ``makedhcp``. The implementation underneath ``makedhcp`` will select an ISC or Kea backend based on site configuration and @@ -20,7 +20,7 @@ The ``kea-dhcp-backend`` work implements the Kea backend foundation: * backend selection through ``site.dhcpbackend`` * ISC as the preserved default on platforms that still support it -* Kea as the automatic default for EL10 and Ubuntu 24.04+ +* Kea as the automatic default for EL10 and Ubuntu 22.04+ * Kea DHCPv4 and DHCPv6 JSON rendering with Perl's ``JSON`` module * Kea DHCPv4, DHCPv6, Control Agent, and DHCP-DDNS configuration validation before install @@ -46,7 +46,7 @@ Remaining work is validation and hardening: * full PXE boot validation on real hardware or nested guests for every supported architecture * complete service-node and disjoint-DHCP scenario validation -* CI integration for EL10 and Ubuntu 24.04 containers +* CI integration for EL10 and Ubuntu 22.04+ containers Backend Selection ----------------- @@ -58,8 +58,8 @@ Add a site attribute: Selection rules: * ``auto`` keeps ISC DHCP on existing supported platforms such as EL8, EL9, - older Ubuntu/Debian releases, and SLES. -* ``auto`` selects Kea DHCP on EL10 and Ubuntu 24.04. + Ubuntu 20.04 and older Ubuntu/Debian releases, and SLES. +* ``auto`` selects Kea DHCP on EL10 and Ubuntu 22.04+. * ``isc`` forces the ISC backend. * ``kea`` forces the Kea backend. * A forced backend that is unavailable must fail with a clear error. @@ -159,10 +159,10 @@ ISC services: Kea services: -* ``kea-dhcp4`` -* optional ``kea-dhcp6`` +* ``kea-dhcp4`` or Debian-style ``kea-dhcp4-server`` +* optional ``kea-dhcp6`` or Debian-style ``kea-dhcp6-server`` * optional ``kea-ctrl-agent`` -* optional ``kea-dhcp-ddns`` +* optional ``kea-dhcp-ddns`` or Debian-style ``kea-dhcp-ddns-server`` Control Agent must be running before REST operations are attempted. D2 should only be managed when Kea DDNS support is configured. @@ -187,7 +187,8 @@ Backends render the same intent as: Boot coverage must include: * x86 BIOS -* x86_64 UEFI +* x86_64 UEFI PXE architecture ids ``0x0007`` and ``0x0009`` +* x86_64 UEFI HTTP boot architecture id ``0x0010`` * ARM64 * OpenPOWER/OPAL * ONIE @@ -205,10 +206,28 @@ Baseline Kea behavior should be deterministic and not depend on optional hooks: * validate generated configuration * reload Kea +Kea reservation policy must map xCAT's existing ``makedhcp`` semantics +explicitly: + +* ``networks.dynamicrange`` renders as Kea dynamic address pools. +* Node addresses outside ``networks.dynamicrange`` render as static + ``ip-address`` host reservations. +* Node addresses inside ``networks.dynamicrange`` are currently treated as + dynamic by ``makedhcp`` and do not render fixed ``ip-address`` reservations; + enabling in-pool fixed reservations is a separate behavior change that needs + explicit live validation. +* DHCPv4 output should keep Kea subnet host reservations enabled with + ``reservations-in-subnet`` set to ``true`` and should not switch globally to + out-of-pool-only mode unless all fixed reservations are known to be outside + dynamic pools. +* In hierarchical deployments, ``networks.dhcpserver`` ownership must be + honored before rendering ``networks.dynamicrange`` as Kea pools, matching + the legacy ISC behavior that prevents duplicate dynamic leases. + Optimized behavior can use Kea Control Agent plus host-commands when available. This requires verifying that the target distribution packages include the host commands hook library, such as ``libdhcp_host_cmds.so``. Do not assume this -library is present in EL10 or Ubuntu 24.04 without testing the actual packages. +library is present in EL10 or Ubuntu 22.04+ without testing the actual packages. If host-commands are unavailable, the JSON render and reload path must still work. @@ -240,7 +259,7 @@ dependencies only for platforms using Kea. Known areas: * ``xCAT.spec`` and ``xCATsn.spec`` currently depend on ``/usr/sbin/dhcpd``. -* EL10 and Ubuntu 24.04 packaging should depend on the correct Kea server +* EL10 and Ubuntu 22.04+ packaging should depend on the correct Kea server packages. * ``dhclient`` and ``dhcp-client`` are separate client-side genesis/netboot issues and should not be conflated with the server backend. @@ -270,6 +289,8 @@ Unit tests: * Kea JSON renderer coverage * host reservation formatting * subnet and pool mapping +* Kea reservation policy flags for subnet reservations and out-of-pool-only + overrides * backend selection and override behavior Configuration validation tests: @@ -287,8 +308,9 @@ Configuration validation tests: Backend selection tests: * ``auto`` uses ISC on EL9 +* ``auto`` uses ISC on Ubuntu 20.04 * ``auto`` uses Kea on EL10 -* ``auto`` uses Kea on Ubuntu 24.04 +* ``auto`` uses Kea on Ubuntu 22.04 and Ubuntu 24.04 * forced ``kea`` works on EL9 when Kea packages are installed * forced unavailable backend fails clearly @@ -297,6 +319,7 @@ Integration matrix: * EL9 plus ISC * EL9 plus forced Kea * EL10 plus Kea +* Ubuntu 22.04 plus Kea * Ubuntu 24.04 plus Kea Semantic parity tests: @@ -315,6 +338,8 @@ Functional smoke tests: * ``XCAT_KEA_LIVE_SMOKE=1`` validates live Control Agent host-commands when Kea and the host-commands hook are installed * DHCP offers contain expected boot options +* Kea static reservations outside dynamic pools allocate without + ``ALLOC_FAIL_NO_POOLS`` * real PXE boot behavior is validated for each supported architecture Test Infrastructure @@ -328,7 +353,7 @@ ordinary containers. Open test infrastructure details to confirm: * SSH access method and user for ``rome01.local.versatushpc.com.br`` -* available base images for EL9, EL10, and Ubuntu 24.04 +* available base images for EL9, EL10, Ubuntu 22.04, and Ubuntu 24.04 * libvirt network names and whether isolated DHCP test networks are already available * whether nested or privileged test guests can run DHCP client and PXE tests @@ -339,21 +364,26 @@ Open test infrastructure details to confirm: Manual Validation Snapshot -------------------------- -As of April 23, 2026, the branch has been exercised on KVM guests across ISC +As of April 26, 2026, the branch has been exercised on KVM guests across ISC and Kea backends: * EL10 plus Kea on x86_64: passed end-to-end xNBA netboot with a Rocky 10.1 compute image. The node fetched the xNBA script, kernel, initrd, and rootimg, then reached xCAT ``netbooting`` state. -* Ubuntu 24.04 plus Kea on x86_64: passed xNBA shell boot and full netboot - image fetch. The node downloaded the node script, Genesis artifacts, and the - generated root image, and Kea reservation queries succeeded. +* Ubuntu 24.04 plus Kea on x86_64: passed BIOS and non-Secure-Boot UEFI xNBA + shell boot and full stateless compute-image boot. The nodes downloaded the + node script, kernel, initrd, and generated root image, then reached ``sshd``. + Kea allocated static reservations outside ``networks.dynamicrange`` without + ``ALLOC_FAIL_NO_POOLS``. The KVM osimage used ``netdrivers=overlay`` because + ``virtio_net`` is built into the Ubuntu 24.04 generic kernel. * EL9 plus ISC on x86_64: passed legacy ISC plus xNBA shell boot, including DHCP, TFTP, node-script handoff, and Genesis fetch. -* Ubuntu 22.04 plus ISC on x86_64: passed legacy ISC DHCP, TFTP, generated +* Ubuntu 22.04 plus forced ISC on x86_64: passed legacy ISC DHCP, TFTP, generated xNBA network script, and Genesis fetch. Per-node OMAPI reservation updates on Jammy still fail with ``omshell`` descriptor errors and appear to be a - preexisting Ubuntu-specific ISC issue outside the Kea scope. + preexisting Ubuntu-specific ISC issue outside the Kea scope. Because of that + issue, ``site.dhcpbackend=auto`` now selects Kea on Ubuntu 22.04 and newer + Ubuntu releases. * EL10 plus Kea on ppc64le: passed Kea 3.x renderer validation with ``evaluate-additional-classes`` and ``only-in-additional-list``; passed ``kea-dhcp4 -t``; passed the full DHCP unit suite on ppc64le after installing @@ -392,5 +422,6 @@ Guiding Rule ------------ ``makedhcp`` remains the stable xCAT interface. ISC remains the default backend -where it works. Kea is added as a backend for platforms that need it, with shared -DHCP intent and backend-specific rendering and control. +where it works and remains supported. Kea is added as a backend for platforms +that need it, with shared DHCP intent and backend-specific rendering and +control. diff --git a/perl-xCAT/xCAT/DHCP/Backend.pm b/perl-xCAT/xCAT/DHCP/Backend.pm index ecd4c1dc4..1a753b981 100644 --- a/perl-xCAT/xCAT/DHCP/Backend.pm +++ b/perl-xCAT/xCAT/DHCP/Backend.pm @@ -59,7 +59,7 @@ sub default_backend { my $os_name = exists $args{os_name} ? $args{os_name} : $class->_osver('os'); my $version = exists $args{version} ? $args{version} : $class->_osver('version'); - if ( defined($os_name) && $os_name =~ /^ubuntu$/i && _version_at_least( $version, '24.04' ) ) { + if ( defined($os_name) && $os_name =~ /^ubuntu$/i && _version_at_least( $version, '22.04' ) ) { return 'kea'; } @@ -156,7 +156,7 @@ sub _command_exists { sub _version_at_least { my ( $version, $minimum ) = @_; - return 0 unless defined($version) && $version =~ /^\d+(?:\.\d+)*/; + return 0 unless defined($version) && $version =~ /^\d+\.\d+(?:\.\d+)*$/; my @version_parts = split /\./, $version; my @minimum_parts = split /\./, $minimum; diff --git a/perl-xCAT/xCAT/DHCP/Backend/Kea.pm b/perl-xCAT/xCAT/DHCP/Backend/Kea.pm index ba2b4de5e..f9a5aa8fe 100644 --- a/perl-xCAT/xCAT/DHCP/Backend/Kea.pm +++ b/perl-xCAT/xCAT/DHCP/Backend/Kea.pm @@ -10,6 +10,13 @@ use Math::BigInt; use xCAT::DHCP::Range; use xCAT::NetworkUtils; +my %KEA_SERVICE_CANDIDATES = ( + 'kea-dhcp4' => [ 'kea-dhcp4', 'kea-dhcp4-server' ], + 'kea-dhcp6' => [ 'kea-dhcp6', 'kea-dhcp6-server' ], + 'kea-dhcp-ddns' => [ 'kea-dhcp-ddns', 'kea-dhcp-ddns-server' ], + 'kea-ctrl-agent' => [ 'kea-ctrl-agent' ], +); + sub new { my ( $class, %args ) = @_; return bless \%args, $class; @@ -56,8 +63,10 @@ sub render_dhcp4_config { type => 'memfile', name => '/var/lib/kea/kea-leases4.csv', }, - 'valid-lifetime' => _integer( _first_defined( $intent->{'valid-lifetime'}, $intent->{valid_lifetime}, 43200 ) ), - subnet4 => [ map { $self->_render_subnet4($_) } @{ _first_defined( $intent->{subnet4}, $intent->{subnets}, [] ) } ], + 'valid-lifetime' => _integer( _first_defined( $intent->{'valid-lifetime'}, $intent->{valid_lifetime}, 43200 ) ), + 'reservations-in-subnet' => _json_bool( _first_defined( $intent->{'reservations-in-subnet'}, $intent->{reservations_in_subnet}, 1 ) ), + 'reservations-out-of-pool' => _json_bool( _first_defined( $intent->{'reservations-out-of-pool'}, $intent->{reservations_out_of_pool}, 1 ) ), + subnet4 => [ map { $self->_render_subnet4($_) } @{ _first_defined( $intent->{subnet4}, $intent->{subnets}, [] ) } ], ); $dhcp4{'control-socket'} = $intent->{'control-socket'} if $intent->{'control-socket'}; @@ -434,16 +443,19 @@ sub restart_services { push @services, 'kea-dhcp6' if $opts{ipv6}; push @services, 'kea-ctrl-agent' if $opts{ctrl_agent}; + my @units; foreach my $service (@services) { + my $unit = $self->_kea_service($service); + push @units, $unit; if ( $opts{enable} ) { - my $enable_ret = xCAT::Utils->enableservice($service); - return { error => "Failed to enable $service." } if $enable_ret != 0; + my $enable_ret = xCAT::Utils->enableservice($unit); + return { error => "Failed to enable $unit." } if $enable_ret != 0; } - my $ret = xCAT::Utils->restartservice($service); - return { error => "Failed to restart $service." } if $ret != 0; + my $ret = xCAT::Utils->restartservice($unit); + return { error => "Failed to restart $unit." } if $ret != 0; } - return { services => \@services }; + return { services => \@units }; } sub check_services { @@ -454,12 +466,15 @@ sub check_services { my @services = ('kea-dhcp4'); push @services, 'kea-dhcp6' if $opts{ipv6}; + my @units; foreach my $service (@services) { - my $ret = xCAT::Utils->checkservicestatus($service); - return { error => "$service is not running. Please start the Kea DHCP service." } if $ret != 0; + my $unit = $self->_kea_service($service); + push @units, $unit; + my $ret = xCAT::Utils->checkservicestatus($unit); + return { error => "$unit is not running. Please start the Kea DHCP service." } if $ret != 0; } - return { services => \@services }; + return { services => \@units }; } sub upsert_reservations { @@ -867,6 +882,13 @@ sub _integer { return 0 + $value; } +sub _json_bool { + my ($value) = @_; + + return $value if ref($value) eq 'JSON::PP::Boolean' || ref($value) eq 'JSON::Boolean'; + return $value ? JSON::true : JSON::false; +} + sub _set_config_permissions { my ($path) = @_; @@ -895,6 +917,41 @@ sub _kea_group { return; } +sub _kea_service { + my ( $self, $service ) = @_; + + # Kea service names are package-specific, not strictly distribution-specific. + # Prefer the unit that is actually installed so derivatives, backports, and + # locally rebuilt packages do not need a distro/version decision tree here. + foreach my $candidate ( @{ $KEA_SERVICE_CANDIDATES{$service} || [$service] } ) { + return $candidate if $self->_service_available($candidate); + } + + return $service; +} + +sub _service_available { + my ( $self, $service ) = @_; + + my $unit = "$service.service"; + foreach my $dir ( @{ $self->{service_unit_dirs} || _systemd_unit_dirs() } ) { + return 1 if -e "$dir/$unit"; + } + + return 1 if -x "/etc/init.d/$service"; + + return 0; +} + +sub _systemd_unit_dirs { + return [ + '/etc/systemd/system', + '/run/systemd/system', + '/usr/lib/systemd/system', + '/lib/systemd/system', + ]; +} + sub _command_path { my ($command) = @_; diff --git a/perl-xCAT/xCAT/DHCP/BootPolicy.pm b/perl-xCAT/xCAT/DHCP/BootPolicy.pm index 1d918e5b3..3161693ea 100644 --- a/perl-xCAT/xCAT/DHCP/BootPolicy.pm +++ b/perl-xCAT/xCAT/DHCP/BootPolicy.pm @@ -7,6 +7,7 @@ sub kea_client_classes { my ( $class, %opts ) = @_; my $xnba_user_class = xnba_user_class_test(); + my $uefi_x64_arch_match = uefi_x64_client_architecture_match_expr(); my $bios_boot = $opts{xnba_kpxe} ? 'xcat/xnba.kpxe' : 'pxelinux.0'; my $uefi_boot = $opts{xnba_efi} ? 'xcat/xnba.efi' : ''; my @classes; @@ -24,7 +25,7 @@ sub kea_client_classes { if ($uefi_boot ne '') { push @classes, { name => 'xcat-uefi-x64', - test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009) and not ($xnba_user_class)", + test => "($uefi_x64_arch_match) and not ($xnba_user_class)", 'boot-file-name' => $uefi_boot, }; } @@ -55,6 +56,7 @@ sub kea_xnba_node_classes { my $nodes = $opts{nodes} || []; my $xnba_user_class = xnba_user_class_test(); + my $uefi_x64_arch_match = uefi_x64_client_architecture_match_expr(); my @classes; foreach my $node (@$nodes) { @@ -73,7 +75,7 @@ sub kea_xnba_node_classes { if ( $opts{xnba_efi} ) { push @classes, { name => "$class_base-uefi", - test => "$xnba_user_class and option[93].hex == 0x0009 and $mac_test", + test => "$xnba_user_class and ($uefi_x64_arch_match) and $mac_test", 'boot-file-name' => "$base_url.uefi", 'user-context' => _xnba_user_context($node), }; @@ -87,6 +89,10 @@ sub xnba_user_class_test { return "(option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA'))"; } +sub uefi_x64_client_architecture_match_expr { + return "option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010"; +} + sub _xnba_class_base { my ( $node, $mac ) = @_; diff --git a/xCAT-server/lib/xcat/plugins/anaconda.pm b/xCAT-server/lib/xcat/plugins/anaconda.pm index a9dcfae05..8f4f66a65 100644 --- a/xCAT-server/lib/xcat/plugins/anaconda.pm +++ b/xCAT-server/lib/xcat/plugins/anaconda.pm @@ -762,7 +762,7 @@ sub mknetboot } # turn off the selinux - if ($osver =~ m/(fedora12|fedora13|rhels7|rhels8|ol7|ol8|alma8|rocky8)/) { + if ($osver =~ m/(fedora12|fedora13|(?:rhels|ol|alma|rocky)(?:[7-9]|10))/) { $kcmdline .= " selinux=0 "; } diff --git a/xCAT-server/lib/xcat/plugins/dhcp.pm b/xCAT-server/lib/xcat/plugins/dhcp.pm index a17e83462..2ec8df1c3 100644 --- a/xCAT-server/lib/xcat/plugins/dhcp.pm +++ b/xCAT-server/lib/xcat/plugins/dhcp.pm @@ -2359,9 +2359,6 @@ sub kea_build_dhcp4_intent %netcfgs = (); @alldomains = (); - my @interfaces = grep { $_ ne '!remote!' && $_ !~ /!remote!/ } sort keys %$activenics; - @interfaces = ('*') unless @interfaces; - my $httpport = "80"; my @hports = xCAT::TableUtils->get_site_attribute("httpport"); if ($hports[0]) { @@ -2387,6 +2384,23 @@ sub kea_build_dhcp4_intent } my @routes = kea_ipv4_routes(@vnets); + my %dhcp_interfaces = %$activenics; + if (!keys %dhcp_interfaces) { + my %configured_local_interfaces = map { $_->{mgtifname} => 1 } grep { $_->{mgtifname} && $_->{mgtifname} !~ /!remote!/ } @vnets; + foreach my $route (@routes) { + my ( $net, $netif, undef, $flags ) = @$route; + next if kea_skip_ipv4_network($net); + next if defined($flags) && $flags =~ /G/; + next if $netif =~ /!remote!/; + $dhcp_interfaces{$netif} = 1 if $configured_local_interfaces{$netif}; + } + } + + my @interfaces = grep { $_ ne '!remote!' && $_ !~ /!remote!/ } sort keys %dhcp_interfaces; + if (!@interfaces && !$dhcp_interfaces{'!remote!'}) { + return { error => "Unable to infer local Kea DHCP interfaces. Set site.dhcpinterfaces to the intended provisioning interface." }; + } + my @subnets; my @opal_classes; my $id = 1; @@ -2400,9 +2414,9 @@ sub kea_build_dhcp4_intent if ($interface =~ /!remote!\S*/) { $remote = 1; $interface =~ s/!remote!\s*(.*)$/$1/; - next unless $activenics->{'!remote!'}; + next unless $dhcp_interfaces{'!remote!'}; } else { - next unless $activenics->{$interface}; + next unless $dhcp_interfaces{$interface}; } my $subnet = kea_subnet4_intent($nettab, $net, $mask, $interface, $remote, $id, $httpport); @@ -2767,11 +2781,16 @@ sub kea_subnet4_intent push @option_data, { name => 'domain-search', data => $domainstring } if $domainstring; my $prefix = kea_mask_to_prefix($mask); + my $dynamicrange = $ent ? $ent->{dynamicrange} : undef; + if ( $dynamicrange && $ent->{dhcpserver} && xCAT::NetworkUtils->thishostisnot( $ent->{dhcpserver} ) ) { + $dynamicrange = undef; + } + my $opal_class = kea_opal_client_class($net, $prefix, $tftp, $httpport); my %subnet = ( id => $id, subnet => "$net/$prefix", - dynamicrange => $ent ? $ent->{dynamicrange} : undef, + dynamicrange => $dynamicrange, option_data => \@option_data, next_server => $tftp, ); diff --git a/xCAT-test/unit/dhcp_backend_selection.t b/xCAT-test/unit/dhcp_backend_selection.t index 9675c62d1..75b25e57e 100644 --- a/xCAT-test/unit/dhcp_backend_selection.t +++ b/xCAT-test/unit/dhcp_backend_selection.t @@ -45,8 +45,14 @@ is( is( xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu22.04', os_name => 'ubuntu', version => '22.04' ), + 'kea', + 'Ubuntu 22.04 defaults to Kea' +); + +is( + xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu20.04', os_name => 'ubuntu', version => '20.04' ), 'isc', - 'Ubuntu 22.04 defaults to ISC' + 'Ubuntu 20.04 defaults to ISC' ); is( @@ -55,6 +61,18 @@ is( 'Ubuntu 24.04 defaults to Kea' ); +is( + xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu24.04', os_name => 'ubuntu', version => '24.04.4' ), + 'kea', + 'Ubuntu 24.04 point releases default to Kea' +); + +is( + xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu24.04', os_name => 'ubuntu', version => '24' ), + 'isc', + 'Ubuntu major-only version is not treated as a date-based release' +); + is( xCAT::DHCP::Backend->default_backend( platform => '', os => 'ubuntu24.10', os_name => 'ubuntu', version => '24.10' ), 'kea', @@ -97,6 +115,12 @@ is( 'auto selects Kea on Ubuntu 24.04' ); +is( + xCAT::DHCP::Backend->choose( requested => 'auto', os => 'ubuntu22.04', os_name => 'ubuntu', version => '22.04' )->{name}, + 'kea', + 'auto selects Kea on Ubuntu 22.04' +); + like( xCAT::DHCP::Backend->choose( requested => 'invalid' )->{error}, qr/Invalid site\.dhcpbackend/, diff --git a/xCAT-test/unit/dhcp_boot_policy.t b/xCAT-test/unit/dhcp_boot_policy.t index 6446c4bc2..fab0c2afd 100644 --- a/xCAT-test/unit/dhcp_boot_policy.t +++ b/xCAT-test/unit/dhcp_boot_policy.t @@ -22,6 +22,7 @@ is( $by_name{'xcat-bios'}{'boot-file-name'}, 'xcat/xnba.kpxe', 'BIOS clients rec like( $by_name{'xcat-bios'}{test}, qr/not \(\(option\[77\]\.exists/, 'generic BIOS class excludes xNBA second-stage clients' ); like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0007/, 'UEFI x64 class matches architecture 7' ); like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0009/, 'UEFI x64 class matches architecture 9' ); +like( $by_name{'xcat-uefi-x64'}{test}, qr/0x0010/, 'UEFI x64 class matches HTTP boot architecture 16' ); like( $by_name{'xcat-uefi-x64'}{test}, qr/not \(\(option\[77\]\.exists/, 'generic UEFI class excludes xNBA second-stage clients' ); is( $by_name{'xcat-aarch64'}{'boot-file-name'}, 'boot/grub2/grub2.aarch64', 'AArch64 clients receive grub2 boot file' ); is( $by_name{'xcat-ppc64'}{'boot-file-name'}, '/boot/grub2/grub2.ppc', 'POWER clients receive grub2 Open Firmware boot file' ); @@ -48,6 +49,9 @@ like( $xnba_bios->{test}, qr/pkt4\.mac == 0x52544b100011/, 'xNBA second-stage cl is( $xnba_bios->{'boot-file-name'}, 'http://10.241.10.1:80/tftpboot/xcat/xnba/nodes/cn01', 'xNBA BIOS class returns the node script URL' ); is( $xnba_bios->{'user-context'}{'xcat-purpose'}, 'xnba-second-stage', 'xNBA class carries removable user-context' ); is( $xnba_by_name{'xcat-xnba-cn01-52544b100011-uefi'}{'boot-file-name'}, 'http://10.241.10.1:80/tftpboot/xcat/xnba/nodes/cn01.uefi', 'xNBA UEFI class returns the UEFI node script URL' ); +like( $xnba_by_name{'xcat-xnba-cn01-52544b100011-uefi'}{test}, qr/0x0007/, 'xNBA UEFI class matches standard UEFI PXE architecture 7' ); +like( $xnba_by_name{'xcat-xnba-cn01-52544b100011-uefi'}{test}, qr/0x0009/, 'xNBA UEFI class matches alternate UEFI PXE architecture 9' ); +like( $xnba_by_name{'xcat-xnba-cn01-52544b100011-uefi'}{test}, qr/0x0010/, 'xNBA UEFI class matches UEFI HTTP boot architecture 16' ); my $combined_classes = xCAT::DHCP::BootPolicy->kea_client_classes( xnba_kpxe => 1, diff --git a/xCAT-test/unit/dhcp_kea_config_validation.t b/xCAT-test/unit/dhcp_kea_config_validation.t index 95f1fe483..7c1408394 100644 --- a/xCAT-test/unit/dhcp_kea_config_validation.t +++ b/xCAT-test/unit/dhcp_kea_config_validation.t @@ -42,7 +42,7 @@ my $json = $backend->render_dhcp4_config( }, { name => 'xcat-uefi-x64', - test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", + test => "(option[93].hex == 0x0007 or option[93].hex == 0x0009 or option[93].hex == 0x0010) and not ((option[77].exists and (option[77].text == 'xNBA' or option[77].hex == 0x784e4241 or substring(option[77].hex,1,4) == 'xNBA')))", 'boot-file-name' => 'xcat/xnba.efi', }, ], @@ -82,6 +82,18 @@ my $json = $backend->render_dhcp4_config( }, ], }, + { + id => 2, + subnet => '192.168.123.0/24', + pools => [], + reservations => [ + { + 'hw-address' => '52:54:00:65:43:21', + 'ip-address' => '192.168.123.50', + hostname => 'node02', + }, + ], + }, ], } ); diff --git a/xCAT-test/unit/dhcp_kea_plugin_intent.t b/xCAT-test/unit/dhcp_kea_plugin_intent.t new file mode 100644 index 000000000..cc4890386 --- /dev/null +++ b/xCAT-test/unit/dhcp_kea_plugin_intent.t @@ -0,0 +1,132 @@ +use strict; +use warnings; +no warnings 'once'; + +use FindBin; +use lib "$FindBin::Bin/../../perl-xCAT"; + +use Test::More; + +BEGIN { + package xCAT::Table; + our $networks; + sub new { + my ( $class, $name ) = @_; + return $name eq 'networks' ? $networks : undef; + } + $INC{'xCAT/Table.pm'} = __FILE__; + + package xCAT::TableUtils; + sub getTftpDir { return '/tftpboot'; } + sub get_site_attribute { return; } + $INC{'xCAT/TableUtils.pm'} = __FILE__; + + package xCAT::Utils; + sub osver { return 'rhels9'; } + sub runcmd { return; } + $INC{'xCAT/Utils.pm'} = __FILE__; + + package xCAT::NetworkUtils; + sub import { + my $caller = caller; + no strict 'refs'; + *{"${caller}::getipaddr"} = \&getipaddr; + } + sub getipaddr { return '10.0.0.1'; } + sub my_ip_facing { return ( 0, '10.0.0.1' ); } + sub thishostisnot { return 0; } + sub ip_forwarding_enabled { return 0; } + sub nodeonmynet { return 1; } + $INC{'xCAT/NetworkUtils.pm'} = __FILE__; + + package xCAT::ServiceNodeUtils; + sub getSNList { return; } + $INC{'xCAT/ServiceNodeUtils.pm'} = __FILE__; + + package xCAT::NodeRange; + $INC{'xCAT/NodeRange.pm'} = __FILE__; +} + +require "$FindBin::Bin/../../xCAT-server/lib/xcat/plugins/dhcp.pm"; + +{ + package DHCPKeaIntentNetTable; + sub new { + my ( $class, $entry ) = @_; + return bless { entry => $entry }, $class; + } + sub getAllAttribs { + my ( $self, @attrs ) = @_; + return { domain => $self->{entry}{domain} } if @attrs == 1 && $attrs[0] eq 'domain'; + return { %{ $self->{entry} } }; + } + sub getAttribs { + my ($self) = @_; + return { %{ $self->{entry} } }; + } + sub close { return; } +} + +my %network_entry = ( + net => '10.0.0.0', + mask => '255.255.255.0', + mgtifname => 'eth0', + dynamicrange => '10.0.0.100-10.0.0.150', + domain => 'cluster.test', + tftpserver => '', +); + +{ + no warnings 'redefine'; + local *xCAT_plugin::dhcp::kea_ipv4_routes = sub { + return ( + [ '10.0.0.0', 'eth0', '255.255.255.0', '' ], + [ '192.168.1.0', 'enp3s0', '255.255.255.0', '' ], + ); + }; + local *xCAT_plugin::dhcp::kea_boot_client_classes = sub { return []; }; + local *xCAT_plugin::dhcp::kea_option_defs = sub { return []; }; + local *xCAT_plugin::dhcp::kea_global_option_data = sub { return []; }; + local *xCAT_plugin::dhcp::kea_dhcp_lease_time = sub { return 43200; }; + local *xCAT_plugin::dhcp::kea_control_agent_enabled = sub { return 0; }; + + local $xCAT::Table::networks = DHCPKeaIntentNetTable->new( \%network_entry ); + + my $intent = xCAT_plugin::dhcp::kea_build_dhcp4_intent( bless({}, 'DHCPKeaIntentBackend'), {} ); + + is_deeply( $intent->{interfaces}, ['eth0'], 'empty dhcpinterfaces infers the local provisioning interface' ); + is( scalar @{ $intent->{subnets} }, 1, 'empty dhcpinterfaces still renders local routed subnet' ); + is( $intent->{subnets}[0]{subnet}, '10.0.0.0/24', 'rendered subnet comes from local route' ); +} + +{ + no warnings 'redefine'; + local *xCAT::NetworkUtils::thishostisnot = sub { return 1; }; + + my $nettab = DHCPKeaIntentNetTable->new( + { + %network_entry, + dhcpserver => 'service-node-a', + } + ); + + my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 ); + ok( !defined( $subnet->{dynamicrange} ), 'non-owning Kea server does not render dynamic pool' ); +} + +{ + no warnings 'redefine'; + local *xCAT::NetworkUtils::thishostisnot = sub { return 0; }; + + my $nettab = DHCPKeaIntentNetTable->new( + { + %network_entry, + dhcpserver => 'service-node-a', + } + ); + + my $subnet = xCAT_plugin::dhcp::kea_subnet4_intent( $nettab, '10.0.0.0', '255.255.255.0', 'eth0', 0, 1, 80 ); + is( $subnet->{dynamicrange}, $network_entry{dynamicrange}, 'owning Kea server renders dynamic pool' ); +} + +done_testing(); diff --git a/xCAT-test/unit/dhcp_kea_renderer.t b/xCAT-test/unit/dhcp_kea_renderer.t index 4446a20de..365f0e0a4 100644 --- a/xCAT-test/unit/dhcp_kea_renderer.t +++ b/xCAT-test/unit/dhcp_kea_renderer.t @@ -11,6 +11,21 @@ use Test::More; use xCAT::DHCP::Backend::Kea; my $backend = xCAT::DHCP::Backend::Kea->new( kea_version => '2.4.1' ); +my $unit_dir = tempdir( CLEANUP => 1 ); +foreach my $unit (qw/kea-dhcp4-server.service kea-dhcp6-server.service kea-dhcp-ddns-server.service kea-ctrl-agent.service/) { + open( my $unit_fh, '>', "$unit_dir/$unit" ) or die "Unable to write $unit_dir/$unit: $!"; + close($unit_fh); +} +my $service_backend = xCAT::DHCP::Backend::Kea->new( service_unit_dirs => [$unit_dir] ); +is( $service_backend->_kea_service('kea-dhcp4'), 'kea-dhcp4-server', 'Debian-style DHCPv4 service name is detected' ); +is( $service_backend->_kea_service('kea-dhcp6'), 'kea-dhcp6-server', 'Debian-style DHCPv6 service name is detected' ); +is( $service_backend->_kea_service('kea-dhcp-ddns'), 'kea-dhcp-ddns-server', 'Debian-style DHCP-DDNS service name is detected' ); +is( $service_backend->_kea_service('kea-ctrl-agent'), 'kea-ctrl-agent', 'control agent service keeps canonical name' ); + +open( my $canonical_unit_fh, '>', "$unit_dir/kea-dhcp4.service" ) or die "Unable to write canonical Kea unit: $!"; +close($canonical_unit_fh); +is( $service_backend->_kea_service('kea-dhcp4'), 'kea-dhcp4', 'canonical Kea service name is preferred when present' ); + my $json = $backend->render_dhcp4_config( { interfaces => ['eth0'], @@ -60,6 +75,8 @@ ok( $config->{Dhcp4}, 'renderer creates a Dhcp4 document' ); is_deeply( $config->{Dhcp4}{'interfaces-config'}{interfaces}, ['eth0'], 'interfaces are rendered' ); is( $config->{Dhcp4}{'valid-lifetime'}, 600, 'valid lifetime is rendered' ); is( $config->{Dhcp4}{'lease-database'}{type}, 'memfile', 'memfile lease backend is the default' ); +is( $config->{Dhcp4}{'reservations-in-subnet'}, JSON::true, 'subnet host reservations are enabled by default' ); +is( $config->{Dhcp4}{'reservations-out-of-pool'}, JSON::true, 'out-of-pool host reservations are enabled by default' ); my $subnet = $config->{Dhcp4}{subnet4}[0]; is( $subnet->{id}, 1, 'subnet id is rendered' ); @@ -172,6 +189,23 @@ my $empty_boot_subnet = decode_json($empty_boot_json)->{Dhcp4}{subnet4}[0]; is( $empty_boot_subnet->{'next-server'}, '0.0.0.0', 'false-looking next-server value is preserved' ); is( $empty_boot_subnet->{'boot-file-name'}, '', 'empty boot-file-name is preserved' ); +my $reservation_policy_json = $backend->render_dhcp4_config( + { + 'reservations-in-subnet' => 1, + 'reservations-out-of-pool' => 1, + subnets => [ + { + id => 9, + subnet => '10.0.9.0/24', + pools => [], + }, + ], + } +); +my $reservation_policy_config = decode_json($reservation_policy_json); +is( $reservation_policy_config->{Dhcp4}{'reservations-in-subnet'}, JSON::true, 'reservation in-subnet policy can be overridden' ); +is( $reservation_policy_config->{Dhcp4}{'reservations-out-of-pool'}, JSON::true, 'reservation out-of-pool policy can be overridden' ); + my $comment_dir = tempdir(CLEANUP => 1); my $commented_config = "$comment_dir/kea-dhcp4.conf"; my $commented_content = <<'COMMENTED_JSON'; @@ -222,6 +256,7 @@ $backend->upsert_reservations( ] ); is( scalar @{ $reservation_config->{Dhcp4}{subnet4}[0]{reservations} }, 1, 'reservation is added to matching subnet' ); +is_deeply( $reservation_config->{Dhcp4}{subnet4}[0]{pools}, [], 'out-of-pool static reservation does not require a dynamic pool' ); $backend->upsert_reservations( $reservation_config,