Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions cmd/openshift-install/internal_integration_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -133,17 +133,18 @@ func runIntegrationTest(t *testing.T, testFolder string) {
},

Cmds: map[string]func(*testscript.TestScript, bool, []string){
"isocmp": tshelpers.IsoCmp,
"ignitionImgContains": tshelpers.IgnitionImgContains,
"configImgContains": tshelpers.ConfigImgContains,
"initrdImgContains": tshelpers.InitrdImgContains,
"unconfiguredIgnContains": tshelpers.UnconfiguredIgnContains,
"unconfiguredIgnCmp": tshelpers.UnconfiguredIgnCmp,
"expandFile": tshelpers.ExpandFile,
"isoContains": tshelpers.IsoContains,
"isoIgnitionContains": tshelpers.IsoIgnitionContains,
"isoSizeMin": tshelpers.IsoSizeMin,
"isoSizeMax": tshelpers.IsoSizeMax,
"isocmp": tshelpers.IsoCmp,
"ignitionImgContains": tshelpers.IgnitionImgContains,
"configImgContains": tshelpers.ConfigImgContains,
"initrdImgContains": tshelpers.InitrdImgContains,
"unconfiguredIgnContains": tshelpers.UnconfiguredIgnContains,
"unconfiguredIgnCmp": tshelpers.UnconfiguredIgnCmp,
"expandFile": tshelpers.ExpandFile,
"isoContains": tshelpers.IsoContains,
"isoIgnitionContains": tshelpers.IsoIgnitionContains,
"isoIgnitionSystemdContains": tshelpers.IsoIgnitionSystemdContains,
"isoSizeMin": tshelpers.IsoSizeMin,
"isoSizeMax": tshelpers.IsoSizeMax,
},
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,34 @@ initrdImgContains agent.x86_64.iso /agent-files/agent-tui
initrdImgContains agent.x86_64.iso /agent-files/libnmstate.so.*
initrdImgContains agent.x86_64.iso /usr/lib/dracut/hooks/pre-pivot/99-agent-copy-files.sh

# Verify all systemd units from getDefaultEnabledServices() are present in the ignition config
isoIgnitionSystemdContains agent.x86_64.iso agent-extract-tui.service
isoIgnitionSystemdContains agent.x86_64.iso agent-import-cluster.service
isoIgnitionSystemdContains agent.x86_64.iso agent-interactive-console-serial@.service
isoIgnitionSystemdContains agent.x86_64.iso agent-interactive-console.service
isoIgnitionSystemdContains agent.x86_64.iso agent-register-cluster.service
isoIgnitionSystemdContains agent.x86_64.iso agent-register-infraenv.service
isoIgnitionSystemdContains agent.x86_64.iso agent-set-host-copy-network-arg.service
isoIgnitionSystemdContains agent.x86_64.iso agent-ui.service
isoIgnitionSystemdContains agent.x86_64.iso agent.service
isoIgnitionSystemdContains agent.x86_64.iso assisted-service-db.service
isoIgnitionSystemdContains agent.x86_64.iso assisted-service-pod.service
isoIgnitionSystemdContains agent.x86_64.iso assisted-service.service
isoIgnitionSystemdContains agent.x86_64.iso install-status.service
isoIgnitionSystemdContains agent.x86_64.iso iscsiadm.service
isoIgnitionSystemdContains agent.x86_64.iso iscsistart.service
# multipathd.service is listed in getDefaultEnabledServices() but has no unit file
# in data/data/agent/systemd/units/, so AddSystemdUnits never adds it to the ignition
# config. It relies on RHCOS presets to be enabled and cannot be verified this way.
# isoIgnitionSystemdContains agent.x86_64.iso multipathd.service
isoIgnitionSystemdContains agent.x86_64.iso node-zero.service
isoIgnitionSystemdContains agent.x86_64.iso oci-eval-user-data.service
isoIgnitionSystemdContains agent.x86_64.iso selinux.service
isoIgnitionSystemdContains agent.x86_64.iso set-hostname.service

# Verify that the script for persisting manual network config is included
isoIgnitionContains agent.x86_64.iso /usr/local/bin/agent-set-host-copy-network-arg.sh

-- install-config.yaml --
apiVersion: v1
baseDomain: test.metalkube.org
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
#!/bin/bash
set -euo pipefail

# shellcheck disable=SC1091
source "common.sh"

NM_CONNECTIONS_DIR="/etc/NetworkManager/system-connections"

# Check for manually-created keyfiles, excluding those auto-generated by nm-initrd-generator.
# nm-initrd-generator creates keyfiles for DHCP during boot; these should not trigger
# --copy-network since they were not manually configured by the user.
# We additionally filter by the presence of a [connection] section to ensure we only
# act on valid NM keyfiles. NetworkManager may leave stray files (e.g. empty temp files)
# in the directory that lack the nm-initrd-generator marker but are not valid connections.
manual_keyfiles=$(find "$NM_CONNECTIONS_DIR" -maxdepth 1 -type f -print0 2>/dev/null | \
xargs -r -0 grep -lZ "^\\[connection\\]" 2>/dev/null | \
xargs -r -0 grep -LZ "org.freedesktop.NetworkManager.origin=nm-initrd-generator" 2>/dev/null | \
tr '\0' '\n' || true)

if [ -z "$manual_keyfiles" ]; then
echo "No manually-created network keyfiles found in $NM_CONNECTIONS_DIR, skipping"
exit 0
fi

echo "Found manually-created network keyfiles in $NM_CONNECTIONS_DIR, will set --copy-network for this host"
echo "$manual_keyfiles"

# Query INFRA_ENV_ID dynamically from the API on all nodes - this avoids relying on
# rendezvous-host.env which only has INFRA_ENV_ID on the rendezvous node.
# Retry indefinitely until the API is available and the infra-env is registered.
INFRA_ENV_ID=""
until [ -n "$INFRA_ENV_ID" ] && [ "$INFRA_ENV_ID" != "null" ]; do
INFRA_ENV_ID=$(curl_assisted_service "/infra-envs" GET 2>/dev/null | jq -r '.[0].id' 2>/dev/null) || true
if [ -z "$INFRA_ENV_ID" ] || [ "$INFRA_ENV_ID" = "null" ]; then
echo "Waiting for assisted-service and infra-env to be available..."
sleep 5
fi
done
Comment on lines +31 to +38
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Do not select infra-env by .[0].id.

Line 33 assumes the first infra-env belongs to this host. In environments with multiple infra-envs (notably add-nodes/unconfigured flows), this can patch the wrong host or wait forever in later host lookup.

Suggested fix (resolve host across infra-envs, then patch the matched one)
-INFRA_ENV_ID=""
-until [ -n "$INFRA_ENV_ID" ] && [ "$INFRA_ENV_ID" != "null" ]; do
-    INFRA_ENV_ID=$(curl_assisted_service "/infra-envs" GET 2>/dev/null | jq -r '.[0].id' 2>/dev/null) || true
-    if [ -z "$INFRA_ENV_ID" ] || [ "$INFRA_ENV_ID" = "null" ]; then
-        echo "Waiting for assisted-service and infra-env to be available..."
-        sleep 5
-    fi
-done
-echo "Got INFRA_ENV_ID: $INFRA_ENV_ID"
+INFRA_ENV_ID=""
+host_id=""
+until [ -n "$INFRA_ENV_ID" ] && [ -n "$host_id" ]; do
+    infra_env_ids=$(curl_assisted_service "/infra-envs" GET 2>/dev/null | jq -r '.[].id // empty' 2>/dev/null) || true
+    for infra in $infra_env_ids; do
+        for mac in $local_macs; do
+            host_id=$(curl_assisted_service "/infra-envs/${infra}/hosts" GET 2>/dev/null | \
+                jq -r --arg mac "$mac" '
+                    .[] |
+                    select(.inventory != null and .inventory != "") |
+                    select(
+                        .inventory | fromjson | .interfaces // [] |
+                        map(.mac_address | ascii_downcase) |
+                        index($mac) != null
+                    ) |
+                    .id
+                ' 2>/dev/null | head -1) || true
+            if [ -n "$host_id" ]; then
+                INFRA_ENV_ID="$infra"
+                break 2
+            fi
+        done
+    done
+    [ -z "$INFRA_ENV_ID" ] && echo "Host not yet found in any infra-env, retrying..." && sleep 5
+done
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
INFRA_ENV_ID=""
until [ -n "$INFRA_ENV_ID" ] && [ "$INFRA_ENV_ID" != "null" ]; do
INFRA_ENV_ID=$(curl_assisted_service "/infra-envs" GET 2>/dev/null | jq -r '.[0].id' 2>/dev/null) || true
if [ -z "$INFRA_ENV_ID" ] || [ "$INFRA_ENV_ID" = "null" ]; then
echo "Waiting for assisted-service and infra-env to be available..."
sleep 5
fi
done
INFRA_ENV_ID=""
host_id=""
until [ -n "$INFRA_ENV_ID" ] && [ -n "$host_id" ]; do
infra_env_ids=$(curl_assisted_service "/infra-envs" GET 2>/dev/null | jq -r '.[].id // empty' 2>/dev/null) || true
for infra in $infra_env_ids; do
for mac in $local_macs; do
host_id=$(curl_assisted_service "/infra-envs/${infra}/hosts" GET 2>/dev/null | \
jq -r --arg mac "$mac" '
.[] |
select(.inventory != null and .inventory != "") |
select(
.inventory | fromjson | .interfaces // [] |
map(.mac_address | ascii_downcase) |
index($mac) != null
) |
.id
' 2>/dev/null | head -1) || true
if [ -n "$host_id" ]; then
INFRA_ENV_ID="$infra"
break 2
fi
done
done
[ -z "$INFRA_ENV_ID" ] && echo "Host not yet found in any infra-env, retrying..." && sleep 5
done
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.

In `@data/data/agent/files/usr/local/bin/agent-set-host-copy-network-arg.sh`
around lines 31 - 38, The script currently grabs the first infra-env via jq
'.[0].id' which can pick the wrong infra-env; instead, query all infra-envs
(using curl_assisted_service) and search each infra-env for the host that
matches this machine (e.g., by comparing host identifier fields such as MAC,
hostname, or sensor ID returned in the infra-env's hosts/inventory) and only set
INFRA_ENV_ID when you find the infra-env that contains the matching host; update
the logic around INFRA_ENV_ID, the curl_assisted_service call and the jq filter
(replace '.[0].id' usage) to loop over infra-envs and select the id of the
infra-env that contains the matched host before proceeding.

echo "Got INFRA_ENV_ID: $INFRA_ENV_ID"

# Get local MAC addresses (lowercase, skip loopback). These are used to match
# this host against the inventory reported by the agent to assisted-service,
# since assisted-service identifies hosts by their interface MAC addresses.
local_macs=$(ip link show | awk '/link\/ether/{print tolower($2)}')
if [ -z "$local_macs" ]; then
echo "ERROR: No ethernet interfaces found, cannot identify host in assisted-service"
exit 1
fi

# Poll until this host appears in assisted-service (agent.service has registered it,
# but it may take a moment before the host is visible in the API)
host_id=""
until [ -n "$host_id" ]; do
for mac in $local_macs; do
host_id=$(curl_assisted_service "/infra-envs/${INFRA_ENV_ID}/hosts" GET 2>/dev/null | \
jq -r --arg mac "$mac" '
.[] |
select(.inventory != null and .inventory != "") |
select(
.inventory | fromjson | .interfaces // [] |
map(.mac_address | ascii_downcase) |
index($mac) != null
) |
.id
' 2>/dev/null | head -1) || true
[ -n "$host_id" ] && break
done

if [ -z "$host_id" ]; then
echo "Host not yet found in assisted-service, retrying..."
sleep 5
fi
done

echo "Setting --copy-network installer arg for host ${host_id}"
http_code=$(curl_assisted_service "/infra-envs/${INFRA_ENV_ID}/hosts/${host_id}/installer-args" PATCH \
--data '{"args":["--copy-network"]}' \
-o /dev/null -w "%{http_code}")
if [[ "$http_code" != "201" ]]; then
echo "ERROR: Failed to set --copy-network for host ${host_id}, HTTP status: ${http_code}"
exit 1
fi
echo "Successfully set --copy-network for host ${host_id}"
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
[Unit]
Description=Set --copy-network installer arg if manual network config was detected
After=agent.service
Before=start-cluster-installation.service agent-add-node.service

[Service]
Type=oneshot
RemainAfterExit=true
EnvironmentFile=/etc/assisted/rendezvous-host.env
ExecStart=/usr/local/bin/agent-set-host-copy-network-arg.sh

[Install]
WantedBy=multi-user.target
1 change: 1 addition & 0 deletions docs/user/agent/agent-services.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ step is required because the agent-tui and nmstate libraries are too big to fit
* agent-register-infraenv - reads the ZTP manifests located at /etc/assisted/manifests and registers the infraenv
* install-status - determines the current install status and writes it out to /etc/issue.d/
* apply-host-config - applies root device hints and roles specified in agent-config.yaml
* agent-set-host-copy-network-arg - detects manually-created NetworkManager keyfiles in /etc/NetworkManager/system-connections and sets the --copy-network installer arg for the current host via the assisted-service REST-API, ensuring that network configuration created via the agent-tui persists into the installed OS
* start-cluster-installation - calls assisted-service REST-API to start cluster installation when all hosts are in ready state and have passed validations
* iscsistart - calls the iscsistart utility which allows an iSCSI session to be started for booting off an iSCSI target
* iscsiadm - calls the iscsiadm utility that will start iscsid to manage the iSCSI session needed to install the final rhcos image
Expand Down
Loading