| name | validate-platform-config |
| description | Validate whether a provisioned platform is correctly configured over SSH, including k3s pod health, binary paths, cloud-init state, network and proxy setup, and device readiness. |
Trigger Phrases
- validate platform config
- validate node over ssh
- check provisioned node health
- validate k3s platform readiness
- post provision ssh validation
- ssh health check
Required Inputs
- enib_home: absolute path to this repository root (default: current workspace root)
- ssh_host: target node IP or hostname
- ssh_user: remote login user (default:
user)
- ssh_port: SSH port (default:
22)
- kubeconfig_path: expected kubeconfig path (default:
/etc/rancher/k3s/k3s.yaml)
Note: Private key authentication is auto-detected from ~/.ssh/ (searches id_rsa, id_ed25519, and id_ecdsa in order). No prompt for key path.
Preconditions
Run silently without user prompts:
Prompt only for missing required inputs:
Steps
- Build SSH command using the authentication method established in preconditions.
- If
SSH_AUTH=default (direct login worked): ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -p <ssh_port> <ssh_user>@<ssh_host>
- Otherwise, use the discovered
$ssh_key from the fallback step: ssh -o BatchMode=yes -o ConnectTimeout=10 -o StrictHostKeyChecking=accept-new -i $ssh_key -p <ssh_port> <ssh_user>@<ssh_host>
- Detect the host_type from the remote configuration.
- command:
grep -E '^host_type' /etc/cloud/config-file 2>/dev/null || echo 'host_type=unknown'
- parse the output to extract the value (e.g.
kubernetes, container, or unknown)
- store as
HOST_TYPE for conditional branching in Steps 3 and 4
- If HOST_TYPE=kubernetes: Validate k3s pods and device plugins.
- command (try in order until one succeeds):
kubectl get pods -A --no-headers
KUBECONFIG=~/.kube/config kubectl get pods -A --no-headers
sudo -n kubectl get pods -A --no-headers
sudo -n k3s kubectl get pods -A --no-headers
- required pod name prefixes and expected status:
intel-gpu-plugin in default namespace, Running, READY 1/1
intel-npu-plugin in default namespace, Running, READY 1/1
coredns in kube-system, Running, READY 1/1
local-path-provisioner in kube-system, Running, READY 1/1
metrics-server in kube-system, Running, READY 1/1
nfd-gc in node-feature-discovery, Running, READY 1/1
nfd-master in node-feature-discovery, Running, READY 1/1
nfd-worker in node-feature-discovery, Running, READY 1/1
- also validate binaries:
command -v kubectl
command -v k3s
ls -l /usr/local/bin/kubectl /usr/local/bin/k3s 2>/dev/null || true
systemctl is-enabled k3s 2>/dev/null || true
systemctl is-active k3s 2>/dev/null || true
- expected:
k3s found in PATH
kubectl found in PATH (binary or symlink)
- one of expected locations exists:
/usr/local/bin/kubectl, /usr/bin/kubectl
- one of expected locations exists:
/usr/local/bin/k3s,
- If HOST_TYPE!=kubernetes (container or unknown): Validate Docker, Docker Compose, and Container Device Interface (CDI).
- commands:
command -v docker
docker --version 2>/dev/null || true
docker compose version 2>/dev/null || true
ls -l /usr/local/bin/docker /usr/bin/docker 2>/dev/null || true
systemctl is-enabled docker 2>/dev/null || true
systemctl is-active docker 2>/dev/null || true
docker info 2>/dev/null | grep -E 'Server Version|Storage Driver|Cgroup|data-root' || true
ls /etc/cdi/ 2>/dev/null || true
ls /var/run/cdi/ 2>/dev/null || true
cat /etc/cdi/*.json 2>/dev/null | python3 -c "import sys,json; [print(d.get('kind','?')) for d in json.load(sys.stdin).get('cdiDevices',json.load(open('/dev/stdin'))) if isinstance(d,dict)]" 2>/dev/null || ls /etc/cdi/ 2>/dev/null || true
docker info 2>/dev/null | grep -i cdi || true
- expected:
docker found in PATH
- Docker version reported
- Docker Compose plugin available (
docker compose version succeeds)
- Docker service is enabled and active
- CDI spec files present in
/etc/cdi/ or /var/run/cdi/ (e.g. intel-gpu.json, intel-npu.json)
- CDI devices exposed to Docker runtime
- Validate cloud-init completion.
- commands:
cloud-init status --long || true
test -f /var/lib/cloud/instance/boot-finished && echo CLOUD_INIT_BOOT_FINISHED=1 || echo CLOUD_INIT_BOOT_FINISHED=0
grep -Ei 'error|failed|traceback' /var/log/cloud-init.log | tail -n 20 || true
- expected:
- cloud-init reports
status: done
/var/lib/cloud/instance/boot-finished exists
- no blocking cloud-init errors relevant to first boot provisioning
- Validate network connectivity and assigned IP.
- commands:
ip -o -4 addr show scope global
ip route show default
ping -c 2 -W 2 8.8.8.8 >/dev/null 2>&1 && echo NET_INTERNET=ok || echo NET_INTERNET=fail
getent hosts github.com >/dev/null 2>&1 && echo DNS=ok || echo DNS=fail
curl -I --max-time 8 https://github.com >/dev/null 2>&1 && echo HTTPS_EGRESS=ok || echo HTTPS_EGRESS=fail
- expected:
- at least one global IPv4 address assigned
- default route present
- connectivity result is classified explicitly:
- direct internet OK:
NET_INTERNET=ok
- restricted/proxy-likely:
NET_INTERNET=fail and DNS=ok
- DNS/config issue:
DNS=fail
- if
NET_INTERNET=fail and DNS=ok, do not hard-fail validation; report as "likely proxy required" with proxy evidence from Step 6
- Collect proxy values (brief).
- command:
grep -hsE '^(https?_proxy|no_proxy)=' /etc/environment 2>/dev/null | head -5 || echo 'no proxy configured'
- expected:
- report proxy variables from
/etc/environment if set; otherwise report "no proxy configured"
- do NOT dump k3s.service.env or docker proxy.conf unless network checks in Step 6 indicate proxy issues
- Inventory CPU/GPU/NPU devices.
- commands:
nproc
lscpu
lscpu | grep -E 'Model name|Vendor ID|CPU family|Model:|Stepping'
grep -E '^(model name|flags|cpu cores|siblings)' /proc/cpuinfo | head -20
cat /sys/devices/system/cpu/cpu0/cpufreq/scaling_driver 2>/dev/null || true
cat /proc/cpuinfo | grep -E 'cpu family|model|stepping|flags' | head -10
if ls /sys/devices/system/cpu/cpu*/topology/core_type >/dev/null 2>&1; then for f in /sys/devices/system/cpu/cpu*/topology/core_type; do cat "$f"; done | awk '{c[$1]++} END {for (t in c) printf "CORE_TYPE_RAW_%s_COUNT=%d\n", t, c[t]}' | sort; else echo CORE_TYPE_EXPOSED=no; fi
if ls /sys/devices/system/cpu/cpu*/topology/core_type >/dev/null 2>&1; then for f in /sys/devices/system/cpu/cpu*/topology/core_type; do cat "$f"; done | awk '{c[$1]++} END {printf "P_CORE_COUNT=%d\n", c[2]+0; printf "E_CORE_COUNT=%d\n", c[1]+0; printf "LPE_CORE_COUNT=%d\n", c[3]+0}'; else echo "P_CORE_COUNT=unavailable"; echo "E_CORE_COUNT=unavailable"; echo "LPE_CORE_COUNT=unavailable"; fi
for cpu in /sys/devices/system/cpu/cpu*/topology/thread_siblings_list; do [ -f "$cpu" ] && echo "$cpu=$(cat $cpu)"; done | head -10
lspci -nn | grep -Ei 'vga|3d|display|npu|neural|vpu|accel|intel' || true
ls -l /dev/dri 2>/dev/null || true
- expected:
- Total CPU core count and logical processors reported
- P-core, E-core, and LPE-core counts are reported when
core_type is exposed
- raw
core_type counts are always reported alongside decoded counts for traceability
- if
core_type is not exposed by the kernel or platform, report P/E/LPE counts as unavailable
- CPU model, family, stepping, and feature flags documented
- CPU codename is reported only when verified from trusted identifiers (family/model/stepping mapping); never infer codename from model name text alone
- if codename cannot be verified confidently, report
CPU_CODENAME=unverified instead of guessing
- CPU frequency scaling driver reported
- Core type information from
/sys/devices/system/cpu/cpu*/topology/core_type is decoded using common Linux hybrid mapping (2=P, 1=E, 3=LPE) and may vary by kernel/platform
- If GPU is present, report GPU Virtual Function (VF) counts.
- commands:
for f in /sys/class/drm/card*/device/sriov_numvfs; do [ -f "$f" ] && echo "$f=$(cat $f)"; done
for f in /sys/class/drm/card*/device/sriov_totalvfs; do [ -f "$f" ] && echo "$f=$(cat $f)"; done
- expected:
- report per-GPU
sriov_numvfs and sriov_totalvfs
- if GPU exists but no SR-IOV files are present, report as unsupported/not enabled.
- Check SR-IOV service if
enable_sriov is set to true.
- first check:
grep -E '^enable_sriov' /etc/cloud/config-file 2>/dev/null || echo 'enable_sriov=unset'
- if value is
true, validate:
systemctl is-enabled intel-sriov-vf.service 2>/dev/null || echo 'not-found'
- expected:
- if enabled: report
intel-sriov-vf.service is enabled — VFs will be preserved across reboots
- if disabled/not-found: report service not enabled — VFs will NOT persist across reboots
- if
enable_sriov is not true, SKIP this check
Validation
Validation section is criteria-only. Do not render the pass/fail results table here.
- SSH connectivity check passes.
host_type is detected and reported; conditional checks branch accordingly.
- If kubernetes: All required k3s pods listed in Step 3 are found in the correct namespaces and are healthy (
Running, 1/1); kubectl and k3s binaries are present.
- If container/unknown: Docker and Docker Compose are available, Docker service is active, and CDI specification files are present.
- cloud-init completion indicators are successful.
- Network check reports assigned IP and route; connectivity is classified as direct or proxy/restricted with explicit reason.
- Proxy values are collected briefly from
/etc/environment; expanded only if network issues are detected.
- CPU/GPU/NPU inventory is collected with clear present/absent status.
- CPU codename labeling is verification-based and avoids false platform naming.
- GPU VF data is reported when the GPU exists.
- SR-IOV service (
intel-sriov-vf.service) is validated when enable_sriov=true in config-file.
Rollback
This is a read-only validation skill. No rollback required.
Safety Rules
- Never print private key contents or paths in user-visible output.
- Prefer read-only commands; do not alter target host configuration in this skill.
- Do not use destructive or privileged write operations.
- If a check fails, continue collecting remaining checks and return a complete report.
Expected Result Summary
Render the report as the following tables.
Run Metadata
| Field | Value |
|---|
| Preconditions | PASS/FAIL |
| SSH endpoint | <ssh_user>@<ssh_host>:<ssh_port> |
| Auth method | default (agent/default keys) or key:<auto-discovered key name> (mask path) |
Validation Results
| Check Area | Status | Evidence | Notes |
|---|
| host_type detection | PASS/FAIL/WARN | value from /etc/cloud/config-file | kubernetes, container, or unknown |
| k3s pods (kubernetes only) | PASS/FAIL/WARN/SKIP | key pod states from kubectl get pods -A | skipped if host_type!=kubernetes |
| k3s binaries (kubernetes only) | PASS/FAIL/WARN/SKIP | command -v k3s, kubectl | skipped if host_type!=kubernetes |
| docker + compose (container only) | PASS/FAIL/WARN/SKIP | docker --version, docker compose version | skipped if host_type=kubernetes |
| CDI specs (container only) | PASS/FAIL/WARN/SKIP | /etc/cdi/, /var/run/cdi/ contents | skipped if host_type=kubernetes |
| cloud-init | PASS/FAIL/WARN | cloud-init status, boot-finished marker | include relevant error lines |
| network and IP | PASS/FAIL/WARN | IP/route, NET_INTERNET, DNS, HTTPS_EGRESS | classify restricted/proxy-likely cases |
| proxy values | PASS/FAIL/WARN | /etc/environment summary | only expanded if network issues detected |
| SR-IOV service | PASS/FAIL/WARN/SKIP | intel-sriov-vf.service state | skipped if enable_sriov!=true |
| CPU/GPU/NPU inventory | PASS/FAIL/WARN | CPU topology, lspci, /dev/dri | codename must be verified or unverified |
| GPU VF counts | PASS/FAIL/WARN | sriov_numvfs, sriov_totalvfs | unsupported/not-enabled if files missing |
Observed Proxy Values
| Variable | Value |
|---|
http_proxy | <value or unset> |
https_proxy | <value or unset> |
no_proxy | <value or unset> |
GPU VF Counts
| Device | sriov_numvfs | sriov_totalvfs |
|---|
/sys/class/drm/card<N> | <n> | <total> |
Failures and Troubleshooting
| Failed Check | Raw Evidence | Troubleshooting Note |
|---|
<check area> | <snippet> | <action> |
Troubleshooting Notes
- If
kubectl fails due to kubeconfig permissions, retry with k3s kubectl.
- If Docker is installed but inactive, include
systemctl status docker --no-pager and recent journalctl -u docker -n 50 --no-pager output.
- If reading k3s.service.env or docker proxy.conf returns "Permission denied", try:
sudo cat /etc/systemd/system/k3s.service.env or sudo cat /etc/systemd/system/docker.service.d/proxy.conf respectively.
- If pods are
Pending or CrashLoopBackOff, include kubectl describe and recent logs for those pods.
- If cloud-init is not complete, inspect
/var/log/cloud-init-output.log and relevant systemd units.
- If
NET_INTERNET=fail but DNS=ok, classify as "likely proxy required/restricted ICMP" and include proxy values, route output, and HTTPS_EGRESS result in findings.
- If detected CPU codename conflicts with known platform information (for example Panther Lake vs Lunar Lake), treat codename as
unverified unless family/model/stepping mapping confirms it.
- If GPU exists but VF count is 0, report whether SR-IOV is disabled or unsupported on that platform.