2
0
mirror of https://github.com/xcat2/xcat-core.git synced 2026-10-12 08:12:33 +00:00

Merge pull request #7898 from VersatusHPC/fix/openeuler-nicextraparams-ifcfg-persist

fix(configeth): a nicextraparams key never reaches an ifcfg NIC profile
This commit is contained in:
Daniel Hilst
2026-10-06 10:21:31 -03:00
committed by GitHub
5 changed files with 325 additions and 46 deletions
@@ -53,15 +53,21 @@ detect_backend() {
fi
}
# Resolve a connection's on-disk keyfile path. NM names it "<id>-<uuid>.nmconnection"
# (not just "<id>.nmconnection") whenever a same-named file already exists, so resolve by
# UUID rather than assuming the plain name.
nm_keyfile() {
local conn=$1 uuid
# Resolve a connection's on-disk file, which NM reports. The keyfile plugin keeps it under
# /etc/NetworkManager/system-connections; the ifcfg-rh plugin, which owns xCAT connections on
# EL8, keeps it under /etc/sysconfig/network-scripts.
#
# Resolve by UUID: NM names a keyfile "<id>-<uuid>.nmconnection", not "<id>.nmconnection",
# whenever a same-named file already exists.
nm_conn_file() {
local conn=$1 uuid file
uuid=$(nmcli -g connection.uuid connection show "$conn" 2>/dev/null)
if [ -n "$uuid" ]; then
grep -l "uuid=$uuid" "$NMDIR"/*.nmconnection 2>/dev/null | head -1
[ -n "$uuid" ] || return 0
file=$(nmcli -t -f UUID,FILENAME connection show 2>/dev/null | sed -n "s|^${uuid}:||p" | head -1)
if [ -z "$file" ]; then
file=$(grep -l "uuid=$uuid" "$NMDIR"/*.nmconnection 2>/dev/null | head -1)
fi
printf '%s\n' "$file"
}
# Resolve the NetworkManager connection name bound to a device.
@@ -101,14 +107,15 @@ nm_show() {
done
IFS="$oldifs"
mtu=$(nmcli -g 802-3-ethernet.mtu connection show "$conn" 2>/dev/null)
kf=$(nm_keyfile "$conn")
kf=$(nm_conn_file "$conn")
# A keyfile spells it "mtu=", an ifcfg file "MTU=".
if { [ -z "$mtu" ] || [ "$mtu" = "auto" ]; } && [ -n "$kf" ] && [ -r "$kf" ]; then
mtu=$(awk -F= '/^[[:space:]]*mtu=/{print $2; exit}' "$kf")
mtu=$(awk -F= '/^[[:space:]]*[Mm][Tt][Uu]=/{print $2; exit}' "$kf")
fi
[ -n "$mtu" ] && [ "$mtu" != "auto" ] && echo "MTU=$mtu"
# Raw keyfile so anything not normalized above (extra params, slaves, vlan id, ...)
# Raw backend file so anything not normalized above (extra params, slaves, vlan id, ...)
# is still visible and greppable by the case's check: lines.
if [ -r "$kf" ]; then echo "# --- $kf ---"; cat "$kf"; fi
if [ -n "$kf" ] && [ -r "$kf" ]; then echo "# --- $kf ---"; cat "$kf"; fi
}
file_show() {
@@ -135,6 +142,11 @@ nm_del() {
return 0
}
# Sourced by xCAT-test/bats/nic_cfg.bats: define the functions only.
if [ "${BASH_SOURCE[0]}" != "$0" ]; then
return 0
fi
case "$1" in
show)
be=$(detect_backend); dev=$2
+51
View File
@@ -0,0 +1,51 @@
#!/usr/bin/env bats
# nic_cfg.sh show must print the profile NM reports, whichever store holds it, so a case can
# match a key NM does not model. nmcli is a shell function here.
load 'helpers/shell_source'
UUID='c9b049e1-abe6-4df3-9d76-d156d8c764c3'
setup()
{
source "$(require_repo_file 'xCAT-test/autotest/testcase/commoncmd/nic_cfg.sh')"
}
# stub_nmcli <file> -- NM binds xcat-ens4 to ens4 and reports <file> for it.
stub_nmcli()
{
NM_FILE="$1"
nmcli()
{
case "$*" in
"-t -f NAME,DEVICE connection show --active") printf '%s\n' 'xcat-ens4:ens4' ;;
"-g ipv4.method connection show xcat-ens4") printf '%s\n' manual ;;
"-g ipv4.addresses connection show xcat-ens4") printf '%s\n' '100.0.0.9/16' ;;
"-g 802-3-ethernet.mtu connection show xcat-ens4") printf '%s\n' auto ;;
"-g connection.uuid connection show xcat-ens4") printf '%s\n' "$UUID" ;;
"-t -f UUID,FILENAME connection show") printf '%s:%s\n' "$UUID" "$NM_FILE" ;;
*) return 10 ;;
esac
}
}
@test "show prints an ifcfg profile that NM reports" {
f="${BATS_TEST_TMPDIR}/ifcfg-xcat-ens4-1"
printf 'TYPE=Ethernet\nCONNECTED_MODE=yes\n' >"$f"
stub_nmcli "$f"
run nm_show ens4
[ "$status" -eq 0 ]
[[ "$output" == *"# --- $f ---"* ]]
[[ "$output" == *$'\nCONNECTED_MODE=yes'* ]]
[[ "$output" == *$'\nIPADDR=100.0.0.9'* ]]
}
@test "show prints a keyfile that NM reports" {
f="${BATS_TEST_TMPDIR}/xcat-ens4-${UUID}.nmconnection"
printf '[connection]\nid=xcat-ens4\n[user]\nxcat.CONNECTED_MODE=yes\n' >"$f"
stub_nmcli "$f"
run nm_show ens4
[ "$status" -eq 0 ]
[[ "$output" == *"# --- $f ---"* ]]
[[ "$output" == *$'\nxcat.CONNECTED_MODE=yes'* ]]
}
@@ -0,0 +1,145 @@
#!/usr/bin/env bats
# A nicextraparams key NM does not model survives only if configeth writes it into the file
# NM reports. nmcli is a shell function here, so nothing on the host is read.
load 'helpers/shell_source'
UUID='c9b049e1-abe6-4df3-9d76-d156d8c764c3'
setup()
{
source "$(require_repo_file 'xCAT/postscripts/xcatlib.sh')"
}
# stub_nmcli <connection> <file> -- NM knows <connection> and reports <file> for it.
stub_nmcli()
{
NM_CONN="$1"
NM_FILE="$2"
nmcli()
{
case "$*" in
"-g connection.uuid connection show $NM_CONN") printf '%s\n' "$UUID" ;;
"-t -f UUID,FILENAME connection show")
printf '%s\n' '0f9a1c55-0000-4000-8000-000000000001:/etc/sysconfig/network-scripts/ifcfg-eth0'
printf '%s:%s\n' "$UUID" "$NM_FILE"
;;
*) return 10 ;;
esac
}
}
@test "the connection file comes from NetworkManager, not from the connection name" {
# NM suffixes the file when one of the plain name already exists.
stub_nmcli xcat-ens4 /etc/sysconfig/network-scripts/ifcfg-xcat-ens4-1
run xcat_nm_conn_file xcat-ens4
[ "$status" -eq 0 ]
[ "$output" = "/etc/sysconfig/network-scripts/ifcfg-xcat-ens4-1" ]
}
@test "a connection NetworkManager does not know has no file" {
stub_nmcli xcat-other /etc/sysconfig/network-scripts/ifcfg-xcat-other
run xcat_nm_conn_file xcat-ens4
[ "$status" -ne 0 ]
[ -z "$output" ]
}
@test "openEuler uses the keyfile store only when NM reports a keyfile" {
networkmanager_active=1
stub_nmcli xcat-ens4 /etc/NetworkManager/system-connections/xcat-ens4.nmconnection
xcat_uses_nm_keyfile openeuler22.03 xcat-ens4
stub_nmcli xcat-ens4 /etc/sysconfig/network-scripts/ifcfg-xcat-ens4
run xcat_uses_nm_keyfile openeuler22.03 xcat-ens4
[ "$status" -ne 0 ]
}
@test "configeth's repair writes the key into an ifcfg profile NM reports" {
f="${BATS_TEST_TMPDIR}/ifcfg-xcat-ens4-1"
printf 'TYPE=Ethernet\nNAME=xcat-ens4\n' >"$f"
stub_nmcli xcat-ens4 "$f"
OSVER=openeuler20.03 networkmanager_active=1
run xcat_nm_persist_nic_extra_params xcat-ens4 'CONNECTED_MODE=yes MTU=1500' default
[ "$status" -eq 0 ]
grep -qx 'CONNECTED_MODE=yes' "$f"
grep -qx 'MTU=1500' "$f"
refute_grep -q '\[user\]' "$f"
refute_grep -q 'xcat\.' "$f"
}
@test "configeth's repair writes the key into a keyfile NM reports" {
f="${BATS_TEST_TMPDIR}/xcat-ens4-${UUID}.nmconnection"
printf '[connection]\nid=xcat-ens4\n' >"$f"
stub_nmcli xcat-ens4 "$f"
OSVER=openeuler24.03 networkmanager_active=1
run xcat_nm_persist_nic_extra_params xcat-ens4 'CONNECTED_MODE=yes'
[ "$status" -eq 0 ]
[ "$(sed -n '/^\[user\]$/,$p' "$f" | grep -cx 'xcat.CONNECTED_MODE=yes')" -eq 1 ]
refute_grep -qx 'CONNECTED_MODE=yes' "$f"
}
@test "configeth's repair names the connection when NM reports no file" {
stub_nmcli xcat-other /etc/sysconfig/network-scripts/ifcfg-xcat-other
run xcat_nm_persist_nic_extra_params xcat-ens4 'CONNECTED_MODE=yes'
[ "$status" -eq 1 ]
[[ "$output" == *"NetworkManager reports no file for 'xcat-ens4'"* ]]
}
@test "configeth's repair does nothing when no IP carries extra params" {
nmcli() { echo "nmcli must not run" >&2; return 10; }
run xcat_nm_persist_nic_extra_params xcat-ens4 default ''
[ "$status" -eq 0 ]
[ -z "$output" ]
}
@test "an ifcfg value is replaced, not duplicated, and a similar key is left alone" {
f="${BATS_TEST_TMPDIR}/ifcfg-xcat-ens4"
printf 'TYPE=Ethernet\nXCONNECTED_MODE=keep\nCONNECTED_MODE=no\nCONNECTED_MODE_X=keep\n' >"$f"
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
[ "$(grep -c '^CONNECTED_MODE=' "$f")" -eq 1 ]
grep -qx 'CONNECTED_MODE=yes' "$f"
grep -qx 'XCONNECTED_MODE=keep' "$f"
grep -qx 'CONNECTED_MODE_X=keep' "$f"
}
@test "a keyfile value is replaced in [user], not duplicated, and a similar key is left alone" {
f="${BATS_TEST_TMPDIR}/xcat-ens4.nmconnection"
printf '[connection]\nid=xcat-ens4\n\n[user]\nxcat.CONNECTED_MODE=no\nxcat.CONNECTED_MODE_X=keep\n\n[ipv4]\nmethod=manual\n' >"$f"
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
[ "$(grep -c '^\[user\]' "$f")" -eq 1 ]
[ "$(grep -c '^xcat\.CONNECTED_MODE=' "$f")" -eq 1 ]
[ "$(sed -n '/^\[user\]$/,/^\[ipv4\]$/p' "$f" | grep -cx 'xcat.CONNECTED_MODE=yes')" -eq 1 ]
grep -qx 'xcat.CONNECTED_MODE_X=keep' "$f"
}
@test "a keyfile with a [user] section elsewhere gets the key inside that section" {
f="${BATS_TEST_TMPDIR}/xcat-ens4.nmconnection"
printf '[connection]\nid=xcat-ens4\n[user]\nxcat.other=1\n[ipv4]\nmethod=manual\n' >"$f"
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
[ "$(sed -n '/^\[user\]$/,/^\[ipv4\]$/p' "$f" | grep -cx 'xcat.CONNECTED_MODE=yes')" -eq 1 ]
[ "$(stat -c %a "$f")" = 600 ]
}
@test "an ifcfg profile without a final newline keeps its last line" {
f="${BATS_TEST_TMPDIR}/ifcfg-xcat-ens4"
printf 'TYPE=Ethernet\nNAME=xcat-ens4' >"$f"
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
grep -qx 'NAME=xcat-ens4' "$f"
grep -qx 'CONNECTED_MODE=yes' "$f"
}
@test "a keyfile ending in [user] without a final newline keeps the section" {
f="${BATS_TEST_TMPDIR}/xcat-ens4.nmconnection"
printf '[connection]\nid=xcat-ens4\n[user]' >"$f"
xcat_persist_nic_extra_param "$f" CONNECTED_MODE yes
[ "$(grep -cx '\[user\]' "$f")" -eq 1 ]
grep -qx 'xcat.CONNECTED_MODE=yes' "$f"
}
@test "a file that does not exist is refused" {
run xcat_persist_nic_extra_param "${BATS_TEST_TMPDIR}/absent" CONNECTED_MODE yes
[ "$status" -eq 1 ]
[[ "$output" == *"no profile file '${BATS_TEST_TMPDIR}/absent'"* ]]
[ ! -e "${BATS_TEST_TMPDIR}/absent" ]
}
+3 -29
View File
@@ -1726,34 +1726,8 @@ if [ $networkmanager_active -eq 1 ] && [ -n "$tmp_con_name" ]; then
nmcli con delete $tmp_con_name
fi
fi
# Persist nicextraparams that NetworkManager has no native setting for (e.g. CONNECTED_MODE)
# into the connection keyfile's [user] section. Done here as the very last step: the
# nmcli con reload/up in the restart phase above re-serializes the keyfile from NM's
# in-memory model and drops anything NM does not model, so an earlier write would be lost.
# On EL10 (keyfile-only) there is no ifcfg file to hold these. We do NOT reload after, so
# the file keeps the section; NM tolerates an unknown [user] section in keyfile mode.
if [ "$str_os_type" = "redhat" ] && [ "$networkmanager_active" = "1" ] && xcat_uses_nm_keyfile "$OSVER" "$con_name"; then
# Resolve the connection's keyfile by UUID -- NM may name it "<id>-<uuid>.nmconnection"
# (not the plain "<id>.nmconnection") when a same-named file already exists.
ep_con="xcat-${str_nic_name}"
ep_uuid=$(nmcli -g connection.uuid connection show "$ep_con" 2>/dev/null)
ep_kf=""
[ -n "$ep_uuid" ] && ep_kf=$(grep -l "uuid=$ep_uuid" /etc/NetworkManager/system-connections/*.nmconnection 2>/dev/null | head -1)
if [ -n "$ep_kf" ] && [ -f "$ep_kf" ]; then
for ep in "${array_nic_params[@]}"; do
[ -z "$ep" ] && continue
[ "$ep" = "$str_default_token" ] && continue
parse_nic_extra_params "$ep"
j=0
while [ $j -lt ${#array_extra_param_names[@]} ]; do
nm="${array_extra_param_names[$j]}"
vl="${array_extra_param_values[$j]}"
grep -q '^\[user\]' "$ep_kf" || printf '\n[user]\n' >> "$ep_kf"
grep -q "^xcat\.${nm}=" "$ep_kf" || echo "xcat.${nm}=${vl}" >> "$ep_kf"
j=$((j+1))
done
done
chmod 600 "$ep_kf"
fi
# The last nmcli con up above dropped the keys NM does not model. Do not reload after this.
if [ "$str_os_type" = "redhat" ] && [ "$networkmanager_active" = "1" ]; then
xcat_nm_persist_nic_extra_params "xcat-${str_nic_name}" "${array_nic_params[@]}"
fi
exit $error_code
+103 -6
View File
@@ -32,16 +32,113 @@ xcat_is_openeuler()
grep -Eq '^ID="?openEuler"?$' /etc/os-release 2>/dev/null
}
# xcat_nm_conn_file <connection> -- the file NetworkManager reports, or non-zero if none.
# NM renames it when a file of the plain name exists, so resolve rather than compose.
xcat_nm_conn_file()
{
local uuid
local file
uuid=$(nmcli -g connection.uuid connection show "$1" 2>/dev/null)
[ -n "$uuid" ] || return 1
file=$(nmcli -t -f UUID,FILENAME connection show 2>/dev/null | sed -n "s|^${uuid}:||p" | head -1)
[ -n "$file" ] || return 1
echo "$file"
}
# xcat_persist_nic_extra_param <file> <name> <value> -- a keyfile takes the key under [user]
# with an "xcat." prefix, an ifcfg file takes it plain. An existing line of that key is replaced.
xcat_persist_nic_extra_param()
{
local file="$1"
local name="$2"
local value="$3"
local key="$name"
local section=""
local tmp
if [ -z "$file" ] || [ ! -f "$file" ]; then
echo "xcat_persist_nic_extra_param: no profile file '$file'" >&2
return 1
fi
if [ -z "$name" ]; then
echo "xcat_persist_nic_extra_param: no key name for '$file'" >&2
return 1
fi
case "$file" in
*.nmconnection)
key="xcat.${name}"
section="user"
;;
esac
tmp=$(mktemp) || return 1
# awk ends every line it prints with a newline, so a profile without a final newline
# does not join the new key to its last line.
XCAT_KEY="$key" XCAT_VALUE="$value" XCAT_SECTION="$section" awk '
BEGIN {
key = ENVIRON["XCAT_KEY"]; line = key "=" ENVIRON["XCAT_VALUE"]
sect = ENVIRON["XCAT_SECTION"]; insect = (sect == ""); seen = 0; done = 0
}
/^\[.*\]$/ {
if (insect && !done) { print line; done = 1 }
insect = ($0 == "[" sect "]"); if (insect) seen = 1
print; next
}
insect && index($0, key "=") == 1 {
if (!done) { print line; done = 1 }
next
}
{ print }
END {
if (!done) {
if (sect != "" && !seen) print "[" sect "]"
print line
}
}
' "$file" >"$tmp" && cat "$tmp" >"$file"
local rc=$?
rm -f "$tmp"
[ "$rc" -eq 0 ] || return "$rc"
[ -z "$section" ] || chmod 600 "$file"
}
# xcat_nm_persist_nic_extra_params <connection> <params>... -- write each nicextraparams
# entry ("KEY=value KEY2=value", or "default") into the file NM reports for <connection>.
# nmcli con up drops keys NM does not model, so call this after the last one, with no reload.
xcat_nm_persist_nic_extra_params()
{
local conn="$1"
local file
local ep
local j
local wanted=""
shift
for ep in "$@"; do
[ -n "$ep" ] && [ "$ep" != "default" ] && wanted=1
done
[ -n "$wanted" ] || return 0
if ! file=$(xcat_nm_conn_file "$conn") || [ ! -f "$file" ]; then
echo "xcat_nm_persist_nic_extra_params: NetworkManager reports no file for '$conn'" >&2
return 1
fi
for ep in "$@"; do
[ -z "$ep" ] && continue
[ "$ep" = "default" ] && continue
parse_nic_extra_params "$ep"
j=0
while [ $j -lt ${#array_extra_param_names[@]} ]; do
xcat_persist_nic_extra_param "$file" \
"${array_extra_param_names[$j]}" "${array_extra_param_values[$j]}" || return 1
j=$((j+1))
done
done
}
xcat_uses_nm_keyfile()
{
xcat_is_el9_or_later "$1" && return 0
[ "$networkmanager_active" = "1" ] && xcat_is_openeuler "$1" || return 1
local uuid
local filename
uuid=$(nmcli -g connection.uuid connection show "$2" 2>/dev/null)
[ -n "$uuid" ] || return 1
filename=$(nmcli -t -f UUID,FILENAME connection show 2>/dev/null | sed -n "s/^$uuid://p")
case "$filename" in
local file
file=$(xcat_nm_conn_file "$2") || return 1
case "$file" in
/etc/NetworkManager/system-connections/*|/run/NetworkManager/system-connections/*|/var/run/NetworkManager/system-connections/*)
return 0 ;;
*) return 1 ;;