From cc8823b3b4a89762a7c710e92eefdda93daede1e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Felix=20K=C3=A4stner?= Date: Mon, 10 Aug 2026 12:19:01 +0200 Subject: [PATCH] Add getting-started and device-onboarding documentation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add overview/getting-started.md covering Helm installation, provider selection, and provisioning server configuration. Add tutorials/device-onboarding.md covering device lifecycle phases, ZTP provisioning endpoints, day-2 configuration, and maintenance operations. Update VitePress sidebar to include new pages and deduplicate the Concepts section. Signed-off-by: Felix Kästner --- docs/.vitepress/config.mts | 11 +- docs/overview/getting-started.md | 78 +++++++ docs/tutorials/device-onboarding.md | 325 ++++++++++++++++++++++++++++ 3 files changed, 406 insertions(+), 8 deletions(-) create mode 100644 docs/overview/getting-started.md create mode 100644 docs/tutorials/device-onboarding.md diff --git a/docs/.vitepress/config.mts b/docs/.vitepress/config.mts index ce5aa24b4..39baf8bb5 100644 --- a/docs/.vitepress/config.mts +++ b/docs/.vitepress/config.mts @@ -71,6 +71,7 @@ export default withMermaid({ text: 'Overview', items: [ { text: 'Index', link: '/overview/' }, + { text: 'Getting Started', link: '/overview/getting-started' }, { text: 'Architecture', link: '/architecture-overview' }, ], }, @@ -80,13 +81,6 @@ export default withMermaid({ { text: 'Index', link: '/concepts/' }, { text: 'Config Backups', link: '/concepts/config-backup' }, { text: 'Pausing Reconciliation', link: '/concepts/pausing' }, - ], - }, - { - text: 'Concepts', - items: [ - { text: 'Index', link: '/concepts/' }, - { text: 'Pausing Reconciliation', link: '/concepts/pausing' }, { text: 'Numbered Resources', link: '/concepts/numbered-resources' }, ], }, @@ -94,7 +88,8 @@ export default withMermaid({ text: 'Tutorials', items: [ { text: 'Index', link: '/tutorials/' }, - { text: 'EVPN/VXLAN Fabric', link: '/tutorials/evpn-vxlan-fabric' } + { text: 'Device Onboarding', link: '/tutorials/device-onboarding' }, + { text: 'EVPN/VXLAN Fabric', link: '/tutorials/evpn-vxlan-fabric' }, ], }, { diff --git a/docs/overview/getting-started.md b/docs/overview/getting-started.md new file mode 100644 index 000000000..d7d25e9d5 --- /dev/null +++ b/docs/overview/getting-started.md @@ -0,0 +1,78 @@ +# Getting Started + +This page covers installing the Network Operator into a Kubernetes cluster using the published Helm chart. + +## Prerequisites + +- A Kubernetes cluster (v1.28+) +- [Helm](https://helm.sh/) v3.12+ +- [cert-manager](https://cert-manager.io/) installed in the cluster (required for webhook and metrics TLS certificates). If not yet installed, follow the [cert-manager installation guide](https://cert-manager.io/docs/installation/). +- Network connectivity from the cluster to your managed devices' management interfaces + +## Installation + +### 1. Install with Helm OCI + +The chart is published as an OCI artifact at `ghcr.io/ironcore-dev/charts/network-operator`. + +```bash +helm install network-operator \ + oci://ghcr.io/ironcore-dev/charts/network-operator \ + --namespace network-operator \ + --create-namespace +``` + +Available versions can be found on the [GitHub packages page](https://github.com/ironcore-dev/network-operator/pkgs/container/charts%2Fnetwork-operator). + +### 2. Verify the installation + +```bash +kubectl get pods -n network-operator +kubectl get crds | grep networking.metal.ironcore.dev +``` + +You should see the controller-manager pod running and CRDs like `devices.networking.metal.ironcore.dev`, `interfaces.networking.metal.ironcore.dev`, etc. + +## Configuration + +The Helm chart is configured through `values.yaml` overrides. See the [`charts/network-operator/values.yaml`](https://github.com/ironcore-dev/network-operator/blob/main/charts/network-operator/values.yaml) file for all available options and their defaults. + +### Provider Selection + +Pass the `--provider` flag via `manager.args`. Available providers: + +| Provider | Flag value | +| -------------------- | ------------------ | +| OpenConfig (default) | `openconfig` | +| Cisco NX-OS (gNMI) | `cisco-nxos-gnmi` | +| Cisco IOS-XR (gNMI) | `cisco-iosxr-gnmi` | + +```yaml +manager: + args: + - --leader-elect + - --provider=cisco-nxos-gnmi +``` + +### Provisioning Server + +The operator embeds an HTTP server for ZTP provisioning (port 8080) and a TFTP server (port 1069). These are exposed as container ports automatically. + +To expose the provisioning endpoints outside the cluster, create a `Service` of type `LoadBalancer` or `NodePort` targeting ports 8080 (TCP) and 1069 (UDP). + +## Upgrading + +Upgrade to a newer version using `helm upgrade` with the desired `--version` flag. CRDs are updated automatically with the chart. + +## Uninstalling + +CRDs persist after a `helm uninstall` by default (`crd.keep: true`). To remove them manually: + +```bash +kubectl get crds -o name | grep networking.metal.ironcore.dev | xargs kubectl delete +``` + +## Next Steps + +- [Device Onboarding](../tutorials/device-onboarding.md) — create your first Device and walk through ZTP provisioning and day-2 configuration. +- [EVPN-VXLAN Fabric Tutorial](../tutorials/evpn-vxlan-fabric.md) — build a full EVPN-VXLAN fabric from scratch. diff --git a/docs/tutorials/device-onboarding.md b/docs/tutorials/device-onboarding.md new file mode 100644 index 000000000..4c425b912 --- /dev/null +++ b/docs/tutorials/device-onboarding.md @@ -0,0 +1,325 @@ +# Device Onboarding Guide + +This page covers the end-to-end process of onboarding a network device into +the Network Operator — from prerequisites through Zero Touch Provisioning +(ZTP) to day-2 configuration. + +## Prerequisites + +Before creating a `Device` resource, ensure: + +1. **Network Operator is deployed** in your Kubernetes cluster with a + compatible provider (e.g. Cisco NX-OS, Openconfig). +2. **Management connectivity** exists between the operator pods and the + device's management IP. If ZTP is used, the device must also be able + to reach the operator's HTTP provisioning server. +3. **Credentials secret** is created — a `kubernetes.io/basic-auth` secret + with `username` and `password` keys: + + ```yaml + apiVersion: v1 + kind: Secret + metadata: + name: device-credentials + type: kubernetes.io/basic-auth + stringData: + username: admin + password: changeme + ``` + +4. **(Optional) TLS certificates** — if your device uses gNMI over TLS, create + secrets for the CA and (for mTLS) the client certificate. +5. **(Optional) Provisioning image** — if you want ZTP, have the NOS image + accessible via HTTP/HTTPS with a known checksum. + +## Creating a Device + +A minimal `Device` resource without ZTP: + +```yaml +apiVersion: networking.metal.ironcore.dev/v1alpha1 +kind: Device +metadata: + name: leaf1 + labels: + topology.kubernetes.io/zone: dc-1a + networking.metal.ironcore.dev/role: evpn-leaf +spec: + endpoint: + address: 192.168.1.10:50051 + secretRef: + name: device-credentials +``` + +A `Device` with ZTP provisioning enabled: + +```yaml +apiVersion: networking.metal.ironcore.dev/v1alpha1 +kind: Device +metadata: + name: leaf2 +spec: + endpoint: + address: 192.168.1.11:50051 + secretRef: + name: device-credentials + tls: + ca: + secretRef: + name: gnmi-ca + key: ca.crt + provisioning: + image: + url: http://images.example.com/nxos-10.4.3.bin + checksum: "abc123..." + checksumType: MD5 + bootScript: + configMapRef: + name: ztp-boot-script + key: script.sh +``` + +## Device Lifecycle Phases + +Once created, a Device progresses through the following phases: + +```mermaid +stateDiagram-v2 + [*] --> Pending + Pending --> Provisioning : spec.provisioning set + Pending --> Running : no provisioning configured + Provisioning --> Provisioned : ZTP reports success + Provisioning --> Failed : timeout or error + Provisioned --> Running : post-provisioning checks pass + Provisioned --> Failed : checks timeout + Running --> [*] + Failed --> Pending : maintenance reset-phase +``` + +| Phase | Description | +| ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | +| **Pending** | Initial state. If `spec.provisioning` is set and the provider supports it, transitions to `Provisioning`. Otherwise skips directly to `Running`. | +| **Provisioning** | The device is expected to call the operator's provisioning HTTP endpoints. Times out after 1 hour if no provisioning request is received. | +| **Provisioned** | The ZTP script reported success. The operator waits for any reboot to complete, then runs post-provisioning verification (e.g. confirming the correct firmware version). | +| **Running** | The device is fully operational. The operator periodically connects, fetches hardware info, port inventory, and reports reachability. | +| **Failed** | Provisioning timed out or reported an error. Requires manual intervention (see [Maintenance Operations](#maintenance-operations)). | + +## Zero Touch Provisioning (ZTP) + +When `spec.provisioning` is configured, the operator exposes HTTP endpoints +that the ZTP script must call during bootstrap. + +### Provisioning HTTP Endpoints + +The operator runs an embedded HTTP server (default port configurable via the +operator deployment). The following endpoints are available: + +#### `GET /provisioning/config?serial=` + +The boot script must call this endpoint to retrieve provisioning configuration. + +**Source IP validation:** If enabled, the operator verifies that the request +originates from the device's configured `spec.endpoint.address`. + +**Response:** + +```json +{ + "provisioningToken": "", + "image": { + "url": "http://images.example.com/nxos-10.4.3.bin", + "checksum": "abc123...", + "checksumType": "MD5" + }, + "userAccounts": [ + { + "username": "admin", + "hashedPassword": "$6$...", + "hashAlgorithm": "SHA512" + } + ], + "hostname": "leaf2" +} +``` + +The device uses this to download the correct image, set up initial user +accounts, and configure its hostname. + +#### `PUT /provisioning/status-report?serial=` + +The ZTP script must call this endpoint to report progress. Requires the +`Authorization: Bearer ` header obtained from the config +endpoint. + +**Request body:** + +```json +{ + "status": "", + "detail": "optional description" +} +``` + +Valid status values: + +| Status | Effect | +| -------------------------------- | ---------------------------------------------------------------- | +| `DataRetrieved` | Informational — logged as event | +| `ScriptExecutionStarted` | Informational | +| `DownloadingImage` | Informational | +| `InstallingCertificates` | Informational | +| `UpgradeStarting` | Informational | +| `ExecutionFinishedWithoutReboot` | **Success** — device moves to `Provisioned` | +| `RebootingDevice` | **Success** — device moves to `Provisioned`, reboot timer starts | +| `ScriptExecutionFailed` | **Failure** — device moves to `Failed` | +| `ImageDownloadFailed` | **Failure** — device moves to `Failed` | +| `UpgradeFailed` | **Failure** — device moves to `Failed` | + +#### `GET /provisioning/device-certificate?serial=` + +Returns the TLS certificate, private key, and CA certificate for the device +(from the associated `Certificate` resource). Used to install mTLS +credentials during ZTP. + +Requires `Authorization: Bearer `. + +#### `GET /provisioning/mtls-client-ca?serial=` + +Returns the CA certificate that the device should trust for mTLS client +authentication (i.e. the CA the operator uses to connect to the device). + +Requires `Authorization: Bearer `. + +### ZTP Flow Summary + +```mermaid +sequenceDiagram + participant Device + participant DHCP/TFTP + participant Operator + + Device->>DHCP/TFTP: Boot, obtain boot script + Device->>Operator: GET /provisioning/config?serial=XYZ + Operator-->>Device: Config + provisioning token + Device->>Device: Download image, apply config, install certs + Device->>Operator: PUT /provisioning/status-report (progress) + alt Success + Device->>Operator: PUT /provisioning/status-report (finished/rebooting) + Operator->>Device: Post-provisioning verification + Note over Device,Operator: Phase → Running + else Failure + Device->>Operator: PUT /provisioning/status-report (failed) + Note over Device,Operator: Phase → Failed + end +``` + +## Running Phase — Steady State + +Once in `Running`, the operator periodically: + +1. **Connects** to the device using the configured endpoint and credentials. +2. **Fetches hardware info** (hostname, manufacturer, model, serial, firmware + version) — refreshed after each reboot. +3. **Discovers ports** — populates `status.ports` with physical port + inventory and transceiver info. +4. **Maps Interface resources** — links any `Interface` CR that references + this device back to the corresponding physical port. +5. **Reports conditions** — sets `Ready=True` and `Reachable=True` when the + device responds successfully. + +If the device becomes unreachable, conditions reflect this but the phase +remains `Running`. + +## Configuring the Device (Day-2) + +After a Device reaches `Running`, you configure it by creating additional +resources that reference it via `spec.deviceRef`. Each resource type has its +own controller that connects to the device and applies configuration. + +Example — creating a loopback interface: + +```yaml +apiVersion: networking.metal.ironcore.dev/v1alpha1 +kind: Interface +metadata: + name: leaf2-lo0 +spec: + deviceRef: + name: leaf2 + name: lo0 + type: Loopback + adminState: Up + ipv4: + addresses: + - 10.0.0.11/32 +``` + +Example — enabling BGP: + +```yaml +apiVersion: networking.metal.ironcore.dev/v1alpha1 +kind: BGP +metadata: + name: leaf2-bgp +spec: + deviceRef: + name: leaf2 + asNumber: 65001 + routerId: 10.0.0.11 + addressFamilies: + ipv4Unicast: + enabled: true +``` + +All configuration resources follow the same pattern: + +1. Create the resource with `spec.deviceRef.name` pointing to the Device. +2. The resource's controller connects to the device and applies the config. +3. The resource reports its own status conditions independently. + +For a complete list of available configuration resources, see the +[API Reference](../api-reference/index.md). + +## Maintenance Operations + +The operator supports maintenance actions via an annotation on the Device: + +```bash +# Reboot the device +kubectl annotate device leaf2 \ + networking.metal.ironcore.dev/maintenance=reboot + +# Factory reset (erases config, resets phase to Pending) +kubectl annotate device leaf2 \ + networking.metal.ironcore.dev/maintenance=factory-reset + +# Re-trigger ZTP provisioning +kubectl annotate device leaf2 \ + networking.metal.ironcore.dev/maintenance=reprovision + +# Reset phase to Pending (recover from Failed without device-side action) +kubectl annotate device leaf2 \ + networking.metal.ironcore.dev/maintenance=reset-phase +``` + +The annotation is removed automatically after the operation succeeds. Failed +operations are retried on subsequent reconciliations. + +## Pausing a Device + +Set `spec.paused: true` to stop all reconciliation for a device and its +associated resources. Useful during manual maintenance windows: + +```bash +kubectl patch device leaf2 --type merge -p '{"spec":{"paused":true}}' +``` + +## Troubleshooting + +| Symptom | Check | +| ---------------------------------- | --------------------------------------------------------------------------------------------------- | +| Device stuck in `Pending` | Verify `spec.provisioning` is set and the provider supports provisioning. | +| Device stuck in `Provisioning` | Ensure the device can reach the operator's provisioning HTTP port. Check the device serial matches. | +| `Failed` phase | Inspect `status.provisioning[].error`. Use `reset-phase` annotation after fixing the root cause. | +| `Ready=Unknown`, `Reachable=False` | Verify network connectivity, credentials, and TLS configuration. | +| Ports not populated | Ports refresh after reboot detection. Check `status.lastRebootTime`. |