mirror of
https://github.com/xcat2/xcat-core.git
synced 2026-10-12 08:12:33 +00:00
feat(genesis): add provisioning runtime
This commit is contained in:
+10
@@ -0,0 +1,10 @@
|
||||
[main]
|
||||
dhcp=internal
|
||||
dns=default
|
||||
plugins=keyfile
|
||||
rc-manager=symlink
|
||||
|
||||
[connection]
|
||||
ipv4.dhcp-timeout=20
|
||||
ipv6.dhcp-duid=ll
|
||||
ipv6.dhcp-timeout=20
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
SUMMARY = "xCAT Genesis network configuration"
|
||||
LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
SRC_URI = "file://10-xcat-genesis.conf"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
RDEPENDS:${PN} = "networkmanager-daemon networkmanager-nmcli"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${sysconfdir}/NetworkManager/conf.d
|
||||
install -m 0644 ${UNPACKDIR}/10-xcat-genesis.conf \
|
||||
${D}${sysconfdir}/NetworkManager/conf.d/10-xcat-genesis.conf
|
||||
}
|
||||
|
||||
FILES:${PN} = "${sysconfdir}/NetworkManager/conf.d/10-xcat-genesis.conf"
|
||||
+1
@@ -0,0 +1 @@
|
||||
disable getty@.service
|
||||
Executable
+273
@@ -0,0 +1,273 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
# shellcheck source=genesis-functions
|
||||
source "$functions_file"
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
destiny_file=${XCAT_DESTINY_FILE:-$state_dir/destiny}
|
||||
metadata_file=${XCAT_METADATA_FILE:-$state_dir/xcat-response.env}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
discover_command=${XCAT_DISCOVER_COMMAND:-/usr/libexec/xcat/genesis-discover}
|
||||
certificate_command=${XCAT_CERTIFICATE_COMMAND:-/usr/libexec/xcat/genesis-getcert}
|
||||
certificate_file=${XCAT_CERTIFICATE_FILE:-/etc/xcat/cert.pem}
|
||||
getdestiny_command=${XCAT_GETDESTINY_COMMAND:-getdestiny}
|
||||
nextdestiny_command=${XCAT_NEXTDESTINY_COMMAND:-nextdestiny}
|
||||
approved_command_dir=${XCAT_ACTION_COMMAND_DIR:-/usr/libexec/xcat/genesis/actions}
|
||||
reboot_command=${XCAT_REBOOT_COMMAND:-systemctl}
|
||||
poweroff_command=${XCAT_POWEROFF_COMMAND:-systemctl}
|
||||
poll_seconds=${XCAT_ACTION_POLL_SECONDS:-15}
|
||||
request_timeout=${XCAT_ACTION_REQUEST_TIMEOUT:-20}
|
||||
operation_timeout=${XCAT_ACTION_OPERATION_TIMEOUT:-300}
|
||||
maximum_steps=${XCAT_ACTION_MAX_STEPS:-0}
|
||||
failure_code=ACTION_FAILED
|
||||
failure_detail='Genesis action failed'
|
||||
failure_recovery='Review Genesis logs and the assigned xCAT action'
|
||||
current_action=
|
||||
current_target=
|
||||
last_verified_seconds=
|
||||
|
||||
[[ "$poll_seconds" =~ ^[0-9]+$ ]] || poll_seconds=15
|
||||
[[ "$request_timeout" =~ ^[1-9][0-9]*$ ]] || request_timeout=20
|
||||
[[ "$operation_timeout" =~ ^[1-9][0-9]*$ ]] || operation_timeout=300
|
||||
[[ "$maximum_steps" =~ ^[0-9]+$ ]] || maximum_steps=0
|
||||
|
||||
publish_status() {
|
||||
local state=$1 detail=$2
|
||||
local -a fields
|
||||
shift 2
|
||||
|
||||
fields=("ACTION=$current_action" "TARGET=$current_target")
|
||||
[[ -z $last_verified_seconds ]] \
|
||||
|| fields+=("VERIFIED_SECONDS=$last_verified_seconds")
|
||||
genesis_publish_status "$status_command" action xcat-genesis-action \
|
||||
"$state" "$detail" "${fields[@]}" "$@"
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
finish_status() {
|
||||
local result=$?
|
||||
|
||||
trap - EXIT
|
||||
if ((result != 0)); then
|
||||
publish_status FAILED "$failure_detail" \
|
||||
"CODE=$failure_code" "RECOVERY=$failure_recovery"
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
fail_action() {
|
||||
failure_code=$1
|
||||
failure_detail=$2
|
||||
failure_recovery=$3
|
||||
logger -t xcat-genesis-action -- "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
|
||||
parse_destiny() {
|
||||
local raw=$1
|
||||
|
||||
if ! genesis_parse_destiny "$raw"; then
|
||||
fail_action INVALID_XCAT_ACTION \
|
||||
'xCAT returned an invalid action' \
|
||||
'Correct the assigned node destiny'
|
||||
fi
|
||||
current_action=$XCAT_GENESIS_ACTION
|
||||
current_target=$XCAT_GENESIS_TARGET
|
||||
}
|
||||
|
||||
read_destiny() {
|
||||
local raw
|
||||
|
||||
[[ -r $destiny_file ]] || fail_action XCAT_ACTION_MISSING \
|
||||
'No xCAT action is available' \
|
||||
'Retry registration or assign a node destiny'
|
||||
IFS= read -r raw <"$destiny_file" || true
|
||||
[[ -n $raw && ${#raw} -le 4096 ]] || fail_action XCAT_ACTION_INVALID \
|
||||
'The assigned xCAT action is empty or too large' \
|
||||
'Correct the assigned node destiny'
|
||||
parse_destiny "$raw"
|
||||
}
|
||||
|
||||
store_destiny() {
|
||||
local raw=$1 temporary_file
|
||||
|
||||
[[ -n $raw && ${#raw} -le 4096 ]] || return 1
|
||||
umask 077
|
||||
temporary_file=${destiny_file}.new
|
||||
printf '%s\n' "$raw" >"$temporary_file"
|
||||
mv "$temporary_file" "$destiny_file"
|
||||
parse_destiny "$raw"
|
||||
}
|
||||
|
||||
request_current_action() {
|
||||
local action
|
||||
|
||||
if ! action=$(timeout "$request_timeout" "$getdestiny_command" \
|
||||
"$XCATDEST" --metadata "$metadata_file" --once); then
|
||||
return 1
|
||||
fi
|
||||
store_destiny "$action" || return 1
|
||||
last_verified_seconds=$(genesis_uptime_seconds)
|
||||
}
|
||||
|
||||
request_next_action() {
|
||||
local action
|
||||
|
||||
if ! action=$(timeout "$request_timeout" "$nextdestiny_command" \
|
||||
"$XCATDEST" --metadata "$metadata_file" --once); then
|
||||
return 1
|
||||
fi
|
||||
store_destiny "$action" || return 1
|
||||
last_verified_seconds=$(genesis_uptime_seconds)
|
||||
}
|
||||
|
||||
run_approved_command() {
|
||||
local command_name argument_text executor
|
||||
local -a arguments=()
|
||||
|
||||
command_name=${current_target%%[[:space:]]*}
|
||||
[[ $command_name =~ ^[a-z][a-z0-9._-]{0,63}$ ]] \
|
||||
|| fail_action ACTION_COMMAND_INVALID \
|
||||
'xCAT assigned an invalid command name' \
|
||||
'Use an action packaged in the Genesis image'
|
||||
executor=$approved_command_dir/$command_name
|
||||
[[ -f $executor && ! -L $executor && -x $executor ]] \
|
||||
|| fail_action ACTION_COMMAND_NOT_APPROVED \
|
||||
"The command is not approved by this Genesis image: $command_name" \
|
||||
'Use a packaged Genesis action or signed system extension'
|
||||
argument_text=${current_target#"$command_name"}
|
||||
argument_text=${argument_text#"${argument_text%%[![:space:]]*}"}
|
||||
if [[ -n $argument_text ]]; then
|
||||
read -r -a arguments <<<"$argument_text"
|
||||
fi
|
||||
publish_status RUNNING "Running approved action $command_name"
|
||||
timeout "$operation_timeout" "$executor" "${arguments[@]}" \
|
||||
|| fail_action ACTION_COMMAND_FAILED \
|
||||
"The approved action failed: $command_name" \
|
||||
'Review Genesis logs and the action parameters'
|
||||
}
|
||||
|
||||
prepare_network_boot() {
|
||||
if ipmitool mc info >/dev/null 2>&1; then
|
||||
ipmitool chassis bootdev pxe >/dev/null 2>&1 \
|
||||
|| logger -t xcat-genesis-action -- \
|
||||
'unable to set the in-band PXE boot override'
|
||||
fi
|
||||
}
|
||||
|
||||
trap finish_status EXIT HUP INT TERM
|
||||
|
||||
[[ -r $network_file ]] || fail_action ACTION_NETWORK_STATE_MISSING \
|
||||
'Management network state is unavailable' \
|
||||
'Wait for Genesis networking or review its failure'
|
||||
# shellcheck disable=SC1090
|
||||
source "$network_file"
|
||||
: "${XCATDEST:?xCAT endpoint is missing from network state}"
|
||||
read_destiny
|
||||
if [[ $current_action != discover && ! -s $certificate_file ]]; then
|
||||
publish_status RUNNING 'Requesting the node certificate'
|
||||
timeout "$operation_timeout" "$certificate_command" \
|
||||
|| fail_action CERTIFICATE_ACTION_FAILED \
|
||||
'The node could not obtain an xCAT certificate' \
|
||||
'Check callback port 300 and xCAT credentials policy'
|
||||
fi
|
||||
|
||||
for ((step = 1; ; step++)); do
|
||||
if ((maximum_steps > 0 && step > maximum_steps)); then
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$current_action" in
|
||||
discover)
|
||||
publish_status RUNNING 'Discovering node hardware'
|
||||
timeout "$operation_timeout" "$discover_command" \
|
||||
|| fail_action DISCOVERY_ACTION_FAILED \
|
||||
'xCAT did not accept the node discovery' \
|
||||
'Review active discovery methods and node matching'
|
||||
publish_status RUNNING 'Loading the discovered node assignment'
|
||||
request_current_action \
|
||||
|| fail_action DISCOVERY_ASSIGNMENT_FAILED \
|
||||
'The discovered node has no xCAT assignment' \
|
||||
'Review the discovered node definition and chain'
|
||||
timeout "$operation_timeout" "$certificate_command" \
|
||||
|| fail_action DISCOVERY_CERTIFICATE_FAILED \
|
||||
'The discovered node could not obtain a certificate' \
|
||||
'Check callback port 300 and xCAT credentials policy'
|
||||
;;
|
||||
standby|shell|offline)
|
||||
publish_status IDLE 'Waiting for an action from xCAT' \
|
||||
"NEXT_RETRY_SECONDS=$poll_seconds"
|
||||
sleep "$poll_seconds"
|
||||
if ! request_current_action; then
|
||||
publish_status DEGRADED 'xCAT did not answer the action poll' \
|
||||
'CODE=XCAT_ACTION_POLL_FAILED' \
|
||||
'RECOVERY=Check xcatd and the management network' \
|
||||
"NEXT_RETRY_SECONDS=$poll_seconds"
|
||||
fi
|
||||
;;
|
||||
osimage|ondiscover)
|
||||
publish_status RUNNING 'Advancing the xCAT action chain'
|
||||
request_next_action \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance the node action chain' \
|
||||
'Review the node chain and xcatd logs'
|
||||
;;
|
||||
runcmd)
|
||||
run_approved_command
|
||||
request_next_action \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance after the approved action' \
|
||||
'Review the node chain and xcatd logs'
|
||||
;;
|
||||
runimage)
|
||||
fail_action UNSAFE_LEGACY_ACTION \
|
||||
'Unsigned runimage actions are not supported' \
|
||||
'Package the operation as a signed Genesis system extension'
|
||||
;;
|
||||
configraid)
|
||||
fail_action LEGACY_STORAGE_ACTION \
|
||||
'Legacy configraid actions are not supported' \
|
||||
'Assign a validated Genesis hardware-provider task'
|
||||
;;
|
||||
sysclone)
|
||||
fail_action LEGACY_SYSCLONE_ACTION \
|
||||
'Legacy sysclone actions are not supported' \
|
||||
'Use an xCAT image workflow supported by this Genesis release'
|
||||
;;
|
||||
boot|reboot)
|
||||
publish_status RUNNING 'Advancing the chain before reboot'
|
||||
request_next_action \
|
||||
|| fail_action XCAT_CHAIN_ADVANCE_FAILED \
|
||||
'xCAT did not advance the node action chain' \
|
||||
'Review the node chain and xcatd logs'
|
||||
prepare_network_boot
|
||||
publish_status RUNNING 'Rebooting into the assigned action'
|
||||
"$reboot_command" reboot
|
||||
exit 0
|
||||
;;
|
||||
install|netboot|statelite)
|
||||
prepare_network_boot
|
||||
publish_status RUNNING 'Rebooting into the assigned image'
|
||||
"$reboot_command" reboot
|
||||
exit 0
|
||||
;;
|
||||
shutdown)
|
||||
publish_status RUNNING 'Powering off at xCAT request'
|
||||
"$poweroff_command" poweroff
|
||||
exit 0
|
||||
;;
|
||||
error)
|
||||
fail_action XCAT_ACTION_ERROR \
|
||||
"xCAT rejected the action: ${current_target:-no detail}" \
|
||||
'Correct the node destiny or action chain'
|
||||
;;
|
||||
*)
|
||||
fail_action XCAT_ACTION_UNSUPPORTED \
|
||||
"Unsupported xCAT action: $current_action" \
|
||||
'Assign an action supported by this Genesis release'
|
||||
;;
|
||||
esac
|
||||
done
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
# Shared Bash helpers for the Genesis runtime.
|
||||
|
||||
genesis_publish_status() {
|
||||
local status_command=$1 component=$2 log_tag=$3
|
||||
shift 3
|
||||
|
||||
"$status_command" "$component" "$@" \
|
||||
|| logger -t "$log_tag" -- "unable to publish $component status"
|
||||
}
|
||||
|
||||
genesis_uptime_seconds() {
|
||||
local uptime_file=${1:-${XCAT_UPTIME_FILE:-/proc/uptime}}
|
||||
local seconds=0
|
||||
|
||||
if [[ -r $uptime_file ]]; then
|
||||
read -r seconds _ <"$uptime_file" || seconds=0
|
||||
seconds=${seconds%%.*}
|
||||
[[ $seconds =~ ^[0-9]+$ ]] || seconds=0
|
||||
fi
|
||||
printf '%s\n' "$seconds"
|
||||
}
|
||||
|
||||
genesis_parse_destiny() {
|
||||
local raw=$1 prefix
|
||||
|
||||
XCAT_GENESIS_ACTION=
|
||||
XCAT_GENESIS_TARGET=
|
||||
[[ -n $raw && ${#raw} -le 4096 ]] || return 1
|
||||
|
||||
prefix=${raw%%=*}
|
||||
XCAT_GENESIS_ACTION=${prefix%%[[:space:]]*}
|
||||
if [[ $raw = *=* ]]; then
|
||||
XCAT_GENESIS_TARGET=${raw#*=}
|
||||
elif [[ $raw = *[[:space:]]* ]]; then
|
||||
XCAT_GENESIS_TARGET=${raw#"$XCAT_GENESIS_ACTION"}
|
||||
XCAT_GENESIS_TARGET=${XCAT_GENESIS_TARGET#"${XCAT_GENESIS_TARGET%%[![:space:]]*}"}
|
||||
fi
|
||||
|
||||
[[ $XCAT_GENESIS_ACTION =~ ^[a-z][a-z0-9_-]*$ ]]
|
||||
}
|
||||
|
||||
genesis_component_state() {
|
||||
local status_dir=$1 component=$2 key value
|
||||
|
||||
[[ -r $status_dir/$component.env ]] || return 1
|
||||
while IFS='=' read -r key value; do
|
||||
if [[ $key == STATE ]]; then
|
||||
printf '%s\n' "$value"
|
||||
return 0
|
||||
fi
|
||||
done <"$status_dir/$component.env"
|
||||
return 1
|
||||
}
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
#!/bin/bash
|
||||
set -u
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
# shellcheck source=genesis-functions
|
||||
source "$functions_file"
|
||||
|
||||
status_dir=${XCAT_STATUS_DIR:-/run/xcat/status}
|
||||
|
||||
status=STARTING
|
||||
for component in network extensions registration; do
|
||||
component_state=$(genesis_component_state "$status_dir" "$component") \
|
||||
|| continue
|
||||
if [[ ${component_state} == FAILED ]]; then
|
||||
status=${component_state}
|
||||
break
|
||||
fi
|
||||
done
|
||||
if [[ ${status} != FAILED ]]; then
|
||||
for component in action registration network; do
|
||||
component_state=$(genesis_component_state "$status_dir" "$component") \
|
||||
|| continue
|
||||
status=${component_state}
|
||||
break
|
||||
done
|
||||
fi
|
||||
|
||||
node=unknown
|
||||
[[ -r /proc/sys/kernel/hostname ]] && read -r node </proc/sys/kernel/hostname
|
||||
|
||||
printf '\n'
|
||||
printf 'xCAT Genesis maintenance shell\n'
|
||||
printf 'Node: %s\n' "${node}"
|
||||
printf 'Overall state: %s\n' "${status}"
|
||||
printf 'Exit returns to the status console.\n\n'
|
||||
|
||||
export HISTFILE=/dev/null
|
||||
export PS1='genesis# '
|
||||
exec /bin/bash --noprofile --norc -i
|
||||
Executable
+87
@@ -0,0 +1,87 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
registration_file=${XCAT_REGISTRATION_STATUS_FILE:-/run/xcat/status/registration.env}
|
||||
meminfo_file=${XCAT_MEMINFO_FILE:-/proc/meminfo}
|
||||
uptime_file=${XCAT_UPTIME_FILE:-/proc/uptime}
|
||||
|
||||
fail() {
|
||||
printf 'genesis-metrics: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
case "$#" in
|
||||
0) output_file= ;;
|
||||
2)
|
||||
[ "$1" = '--output' ] || fail 'usage: genesis-metrics [--output FILE]'
|
||||
output_file=$2
|
||||
;;
|
||||
*) fail 'usage: genesis-metrics [--output FILE]' ;;
|
||||
esac
|
||||
|
||||
schema=
|
||||
registration_state=
|
||||
boot_ready_seconds=
|
||||
while IFS='=' read -r name value; do
|
||||
case "$name" in
|
||||
SCHEMA) schema=$value ;;
|
||||
STATE) registration_state=$value ;;
|
||||
UPDATED_SECONDS) boot_ready_seconds=$value ;;
|
||||
esac
|
||||
done <"$registration_file"
|
||||
|
||||
[ "$schema" = 1 ] && [ "$registration_state" = ACTION_RECEIVED ] \
|
||||
|| fail 'Genesis has not received an xCAT action'
|
||||
case "$boot_ready_seconds" in
|
||||
''|*[!0-9]*) fail 'registration time is invalid' ;;
|
||||
esac
|
||||
|
||||
read -r capture_uptime_seconds _ <"$uptime_file"
|
||||
capture_uptime_seconds=${capture_uptime_seconds%%.*}
|
||||
case "$capture_uptime_seconds" in
|
||||
''|*[!0-9]*) fail 'uptime is invalid' ;;
|
||||
esac
|
||||
|
||||
memory_total_kib=
|
||||
memory_available_kib=
|
||||
while read -r name value _; do
|
||||
case "$name" in
|
||||
MemTotal:) memory_total_kib=$value ;;
|
||||
MemAvailable:) memory_available_kib=$value ;;
|
||||
esac
|
||||
done <"$meminfo_file"
|
||||
case "$memory_total_kib" in
|
||||
''|*[!0-9]*) fail 'total memory is invalid' ;;
|
||||
esac
|
||||
case "$memory_available_kib" in
|
||||
''|*[!0-9]*) fail 'available memory is invalid' ;;
|
||||
esac
|
||||
[ "$memory_available_kib" -le "$memory_total_kib" ] \
|
||||
|| fail 'available memory exceeds total memory'
|
||||
memory_used_kib=$((memory_total_kib - memory_available_kib))
|
||||
|
||||
write_metrics() {
|
||||
printf 'SCHEMA=1\n'
|
||||
printf 'BOOT_READY_SECONDS=%s\n' "$boot_ready_seconds"
|
||||
printf 'CAPTURE_UPTIME_SECONDS=%s\n' "$capture_uptime_seconds"
|
||||
printf 'MEMORY_TOTAL_KIB=%s\n' "$memory_total_kib"
|
||||
printf 'MEMORY_AVAILABLE_KIB=%s\n' "$memory_available_kib"
|
||||
printf 'MEMORY_USED_KIB=%s\n' "$memory_used_kib"
|
||||
}
|
||||
|
||||
if [ -z "$output_file" ]; then
|
||||
write_metrics
|
||||
exit 0
|
||||
fi
|
||||
|
||||
case "$output_file" in
|
||||
*/*) output_dir=${output_file%/*}; [ -n "$output_dir" ] || output_dir=/ ;;
|
||||
*) output_dir=. ;;
|
||||
esac
|
||||
install -d -m 0755 "$output_dir"
|
||||
temporary_file=$(mktemp "$output_dir/.metrics.XXXXXX")
|
||||
trap 'rm -f -- "$temporary_file"' EXIT HUP INT TERM
|
||||
write_metrics >"$temporary_file"
|
||||
chmod 0644 "$temporary_file"
|
||||
mv -f -- "$temporary_file" "$output_file"
|
||||
trap - EXIT HUP INT TERM
|
||||
+55
@@ -0,0 +1,55 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
network_state_command=${XCAT_NETWORK_STATE_COMMAND:-/usr/libexec/xcat/genesis-network-state}
|
||||
sys_class_net=${XCAT_SYS_CLASS_NET:-/sys/class/net}
|
||||
callback=${1:-restart}
|
||||
|
||||
[[ -r $network_file ]] || exit 1
|
||||
# shellcheck disable=SC1090
|
||||
source "$network_file"
|
||||
|
||||
[[ ${XCAT_NETWORK_METHOD:-auto} = auto ]] || exit 0
|
||||
: "${XCAT_INTERFACE:?management interface is missing from network state}"
|
||||
|
||||
declare -a requested_interfaces=()
|
||||
if [[ $callback = 'restart ('*')' ]]; then
|
||||
requested=${callback#restart (}
|
||||
requested=${requested%)}
|
||||
IFS='|' read -r -a requested_interfaces <<<"$requested"
|
||||
fi
|
||||
if ((${#requested_interfaces[@]} == 0)); then
|
||||
requested_interfaces=("$XCAT_INTERFACE")
|
||||
fi
|
||||
|
||||
for interface in "${requested_interfaces[@]}"; do
|
||||
[[ $interface =~ ^[A-Za-z0-9_.-]+$ && -e $sys_class_net/$interface ]] \
|
||||
|| exit 1
|
||||
done
|
||||
|
||||
if [[ $callback = 'restart ('*')' ]]; then
|
||||
while IFS=: read -r interface device_type; do
|
||||
[[ $device_type = ethernet ]] || continue
|
||||
keep=0
|
||||
for requested_interface in "${requested_interfaces[@]}"; do
|
||||
if [[ $interface = "$requested_interface" ]]; then
|
||||
keep=1
|
||||
break
|
||||
fi
|
||||
done
|
||||
((keep == 1)) \
|
||||
|| nmcli --wait 10 device disconnect "$interface" >/dev/null 2>&1 \
|
||||
|| true
|
||||
done < <(nmcli -t -f DEVICE,TYPE device status 2>/dev/null || true)
|
||||
fi
|
||||
|
||||
for interface in "${requested_interfaces[@]}"; do
|
||||
nmcli --wait 10 device disconnect "$interface" >/dev/null 2>&1 || true
|
||||
done
|
||||
for interface in "${requested_interfaces[@]}"; do
|
||||
nmcli --wait 30 device connect "$interface" >/dev/null
|
||||
done
|
||||
|
||||
"$network_state_command"
|
||||
+327
@@ -0,0 +1,327 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
# shellcheck source=genesis-functions
|
||||
source "$functions_file"
|
||||
|
||||
log() {
|
||||
logger -t xcat-genesis-network-state -- "$*"
|
||||
}
|
||||
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
failure_code=NETWORK_CONFIGURATION_FAILED
|
||||
failure_detail='Network configuration failed'
|
||||
failure_recovery='Check the management network and boot parameters'
|
||||
|
||||
publish_status() {
|
||||
genesis_publish_status "$status_command" network \
|
||||
xcat-genesis-network-state "$@"
|
||||
}
|
||||
|
||||
finish_status() {
|
||||
local result=$?
|
||||
|
||||
trap - EXIT
|
||||
if [ "$result" -ne 0 ]; then
|
||||
publish_status FAILED "$failure_detail" \
|
||||
"CODE=$failure_code" "RECOVERY=$failure_recovery"
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
fail_network() {
|
||||
failure_code=$1
|
||||
failure_detail=$2
|
||||
failure_recovery=$3
|
||||
log "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap finish_status EXIT
|
||||
publish_status CONFIGURING_NETWORK 'Selecting the management network'
|
||||
|
||||
tcp_probe() {
|
||||
local probe_pid probe_wait
|
||||
|
||||
( exec 3<>"/dev/tcp/$master/$port" ) 2>/dev/null &
|
||||
probe_pid=$!
|
||||
for ((probe_wait = 0; probe_wait < 10; probe_wait++)); do
|
||||
if ! kill -0 "$probe_pid" 2>/dev/null; then
|
||||
wait "$probe_pid"
|
||||
return $?
|
||||
fi
|
||||
sleep 0.1
|
||||
done
|
||||
kill "$probe_pid" 2>/dev/null || true
|
||||
wait "$probe_pid" 2>/dev/null || true
|
||||
return 1
|
||||
}
|
||||
|
||||
netmask_to_prefix() {
|
||||
local mask=$1 octet prefix=0 partial=0
|
||||
local -a octets
|
||||
|
||||
IFS=. read -r -a octets <<<"$mask"
|
||||
[ "${#octets[@]}" -eq 4 ] || return 1
|
||||
|
||||
for octet in "${octets[@]}"; do
|
||||
[ "$partial" -eq 0 ] || [ "$octet" = 0 ] || return 1
|
||||
case "$octet" in
|
||||
255) prefix=$((prefix + 8)) ;;
|
||||
254) prefix=$((prefix + 7)); partial=1 ;;
|
||||
252) prefix=$((prefix + 6)); partial=1 ;;
|
||||
248) prefix=$((prefix + 5)); partial=1 ;;
|
||||
240) prefix=$((prefix + 4)); partial=1 ;;
|
||||
224) prefix=$((prefix + 3)); partial=1 ;;
|
||||
192) prefix=$((prefix + 2)); partial=1 ;;
|
||||
128) prefix=$((prefix + 1)); partial=1 ;;
|
||||
0) partial=1 ;;
|
||||
*) return 1 ;;
|
||||
esac
|
||||
done
|
||||
|
||||
printf '%s\n' "$prefix"
|
||||
}
|
||||
|
||||
destination=
|
||||
boot_mac=
|
||||
host_ip=
|
||||
netmask=
|
||||
gateway=
|
||||
cmdline_file=${XCAT_CMDLINE_FILE:-/proc/cmdline}
|
||||
sys_class_net=${XCAT_SYS_CLASS_NET:-/sys/class/net}
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
uptime_file=${XCAT_UPTIME_FILE:-/proc/uptime}
|
||||
declare -a cmdline_arguments
|
||||
|
||||
read -r -a cmdline_arguments <"$cmdline_file"
|
||||
for argument in "${cmdline_arguments[@]}"; do
|
||||
case "$argument" in
|
||||
xcatd=*) destination=${argument#xcatd=} ;;
|
||||
BOOTIF=*) boot_mac=${argument#BOOTIF=} ;;
|
||||
hostip=*|ipaddr=*) host_ip=${argument#*=} ;;
|
||||
netmask=*) netmask=${argument#netmask=} ;;
|
||||
gateway=*) gateway=${argument#gateway=} ;;
|
||||
esac
|
||||
done
|
||||
|
||||
[ -n "$destination" ] || fail_network INVALID_BOOT_PARAMETERS \
|
||||
'xcatd is missing from the kernel command line' \
|
||||
'Add xcatd=<address>:<port> to the kernel command line'
|
||||
|
||||
if [[ "$destination" =~ ^\[([^]]+)\]:([0-9]+)$ ]]; then
|
||||
master=${BASH_REMATCH[1]}
|
||||
port=${BASH_REMATCH[2]}
|
||||
elif [[ "$destination" =~ ^([^:]+):([0-9]+)$ ]]; then
|
||||
master=${BASH_REMATCH[1]}
|
||||
port=${BASH_REMATCH[2]}
|
||||
elif [[ "$destination" = *:* ]]; then
|
||||
master=$destination
|
||||
port=3001
|
||||
destination="[$master]:$port"
|
||||
else
|
||||
master=$destination
|
||||
port=3001
|
||||
destination="$master:$port"
|
||||
fi
|
||||
|
||||
[[ "$port" =~ ^[0-9]+$ ]] && [ "$port" -ge 1 ] && [ "$port" -le 65535 ] || {
|
||||
fail_network INVALID_XCAT_ENDPOINT "Invalid xcatd port: $port" \
|
||||
'Correct the xcatd endpoint in the boot parameters'
|
||||
}
|
||||
|
||||
preferred_interface=
|
||||
if [ -n "$boot_mac" ]; then
|
||||
boot_mac=${boot_mac#01-}
|
||||
boot_mac=$(printf '%s' "$boot_mac" | tr 'A-Z-' 'a-z:')
|
||||
for address_file in "$sys_class_net"/*/address; do
|
||||
[ -r "$address_file" ] || continue
|
||||
if [ "$(<"$address_file")" = "$boot_mac" ]; then
|
||||
preferred_interface=${address_file%/address}
|
||||
preferred_interface=${preferred_interface##*/}
|
||||
break
|
||||
fi
|
||||
done
|
||||
[ -n "$preferred_interface" ] || fail_network BOOT_INTERFACE_NOT_FOUND \
|
||||
"BOOTIF device not found: $boot_mac" \
|
||||
'Check the PXE interface and BOOTIF value'
|
||||
fi
|
||||
|
||||
network_method=auto
|
||||
if [ -n "$host_ip" ] || [ -n "$netmask" ] || [ -n "$gateway" ]; then
|
||||
network_method=static
|
||||
[ -n "$host_ip" ] && [ -n "$netmask" ] && [ -n "$gateway" ] \
|
||||
|| fail_network INVALID_STATIC_NETWORK \
|
||||
'Static networking requires hostip, netmask, and gateway' \
|
||||
'Supply all static network boot parameters or remove them'
|
||||
[ -n "$preferred_interface" ] || fail_network INVALID_STATIC_NETWORK \
|
||||
'Static networking requires BOOTIF' \
|
||||
'Add BOOTIF for the management interface'
|
||||
|
||||
if [[ "$host_ip" = */* ]]; then
|
||||
address=$host_ip
|
||||
elif [[ "$netmask" =~ ^[0-9]+$ ]]; then
|
||||
address="$host_ip/$netmask"
|
||||
elif [[ "$host_ip" = *:* ]]; then
|
||||
fail_network INVALID_STATIC_NETWORK \
|
||||
"Invalid IPv6 prefix length: $netmask" \
|
||||
'Use an IPv6 prefix length from 0 to 128'
|
||||
else
|
||||
prefix=$(netmask_to_prefix "$netmask") \
|
||||
|| fail_network INVALID_STATIC_NETWORK \
|
||||
"Invalid IPv4 netmask: $netmask" \
|
||||
'Use a valid dotted netmask or prefix length'
|
||||
address="$host_ip/$prefix"
|
||||
fi
|
||||
|
||||
nmcli connection delete xcat-genesis-static >/dev/null 2>&1 || true
|
||||
if [[ "$host_ip" = *:* ]]; then
|
||||
network_arguments=(
|
||||
ipv4.method disabled
|
||||
ipv6.method manual
|
||||
ipv6.addresses "$address"
|
||||
ipv6.gateway "$gateway"
|
||||
)
|
||||
else
|
||||
network_arguments=(
|
||||
ipv4.method manual
|
||||
ipv4.addresses "$address"
|
||||
ipv4.gateway "$gateway"
|
||||
ipv6.method auto
|
||||
)
|
||||
fi
|
||||
if ! nmcli connection add type ethernet ifname "$preferred_interface" \
|
||||
con-name xcat-genesis-static "${network_arguments[@]}" \
|
||||
connection.autoconnect yes >/dev/null; then
|
||||
fail_network STATIC_NETWORK_REJECTED \
|
||||
"NetworkManager rejected the static configuration on $preferred_interface" \
|
||||
'Check the address, gateway, and interface'
|
||||
fi
|
||||
if ! nmcli --wait 20 connection up xcat-genesis-static >/dev/null; then
|
||||
fail_network STATIC_NETWORK_FAILED \
|
||||
"Static network activation failed on $preferred_interface" \
|
||||
'Check link state and the static network boot parameters'
|
||||
fi
|
||||
fi
|
||||
|
||||
selected_interface=
|
||||
source_address=
|
||||
publish_status WAITING_FOR_LINK "Waiting for a route to $destination" \
|
||||
'ATTEMPT=1' 'ATTEMPT_LIMIT=60'
|
||||
for ((attempt = 0; attempt < 60; attempt++)); do
|
||||
if [[ "$master" = *:* ]]; then
|
||||
route=$(ip -6 -o route get "$master" 2>/dev/null || true)
|
||||
elif [[ "$master" =~ ^[0-9]+(\.[0-9]+){3}$ ]]; then
|
||||
route=$(ip -4 -o route get "$master" 2>/dev/null || true)
|
||||
else
|
||||
route=$(ip -4 -o route get "$master" 2>/dev/null \
|
||||
|| ip -6 -o route get "$master" 2>/dev/null || true)
|
||||
fi
|
||||
route_interface=$(awk '{for (i=1; i<=NF; i++) if ($i == "dev") print $(i+1)}' <<<"$route")
|
||||
route_source=$(awk '{for (i=1; i<=NF; i++) if ($i == "src") print $(i+1)}' <<<"$route")
|
||||
|
||||
if [ -n "$route_interface" ]; then
|
||||
state=$(nmcli -g GENERAL.STATE device show "$route_interface" 2>/dev/null || true)
|
||||
if [[ "$state" = 100* ]]; then
|
||||
if [ -z "$preferred_interface" ] || \
|
||||
[ "$route_interface" = "$preferred_interface" ] || \
|
||||
[ "$attempt" -ge 20 ]; then
|
||||
selected_interface=$route_interface
|
||||
source_address=$route_source
|
||||
break
|
||||
fi
|
||||
fi
|
||||
fi
|
||||
publish_status WAITING_FOR_LINK "Waiting for a route to $destination" \
|
||||
"ATTEMPT=$((attempt + 1))" 'ATTEMPT_LIMIT=60' \
|
||||
'NEXT_RETRY_SECONDS=1'
|
||||
sleep 1
|
||||
done
|
||||
|
||||
[ -n "$selected_interface" ] || fail_network MANAGEMENT_ROUTE_UNAVAILABLE \
|
||||
"No interface can route to $master" \
|
||||
'Check link, DHCP or static settings, VLANs, and routing'
|
||||
|
||||
master_reachable=0
|
||||
publish_status CONTACTING_XCAT "Contacting xCAT at $destination" \
|
||||
'ATTEMPT=1' 'ATTEMPT_LIMIT=10'
|
||||
for ((attempt = 0; attempt < 10; attempt++)); do
|
||||
if tcp_probe; then
|
||||
master_reachable=1
|
||||
break
|
||||
fi
|
||||
publish_status CONTACTING_XCAT "Contacting xCAT at $destination" \
|
||||
"ATTEMPT=$((attempt + 1))" 'ATTEMPT_LIMIT=10' \
|
||||
'NEXT_RETRY_SECONDS=1'
|
||||
sleep 1
|
||||
done
|
||||
|
||||
[ "$master_reachable" -eq 1 ] || fail_network XCAT_ENDPOINT_UNREACHABLE \
|
||||
"Cannot connect to $destination" \
|
||||
'Check xcatd, firewalls, routing, and the configured port'
|
||||
|
||||
route_gateway=$(awk '{for (i=1; i<=NF; i++) if ($i == "via") print $(i+1)}' <<<"$route")
|
||||
source_prefixed_address=
|
||||
if [[ "$source_address" = *:* ]]; then
|
||||
address_family=-6
|
||||
else
|
||||
address_family=-4
|
||||
fi
|
||||
while read -r candidate; do
|
||||
if [ "${candidate%%/*}" = "$source_address" ]; then
|
||||
source_prefixed_address=$candidate
|
||||
break
|
||||
fi
|
||||
done < <(ip "$address_family" -o address show dev "$selected_interface" \
|
||||
scope global 2>/dev/null | awk '{print $4}')
|
||||
[ -n "$source_prefixed_address" ] || source_prefixed_address=$source_address
|
||||
|
||||
actual_gateway=${route_gateway:-$gateway}
|
||||
dns_servers=
|
||||
while IFS= read -r dns_line; do
|
||||
IFS='|' read -r -a dns_candidates <<<"$dns_line"
|
||||
for dns_server in "${dns_candidates[@]}"; do
|
||||
dns_server=${dns_server//[[:space:]]/}
|
||||
[ -n "$dns_server" ] || continue
|
||||
if [ -n "$dns_servers" ]; then
|
||||
dns_servers="$dns_servers,$dns_server"
|
||||
else
|
||||
dns_servers=$dns_server
|
||||
fi
|
||||
done
|
||||
done < <(nmcli --terse --escape no -g IP4.DNS,IP6.DNS \
|
||||
device show "$selected_interface" \
|
||||
2>/dev/null || true)
|
||||
link_state=unknown
|
||||
[ ! -r "$sys_class_net/$selected_interface/operstate" ] \
|
||||
|| read -r link_state <"$sys_class_net/$selected_interface/operstate"
|
||||
mac_address=
|
||||
[ ! -r "$sys_class_net/$selected_interface/address" ] \
|
||||
|| read -r mac_address <"$sys_class_net/$selected_interface/address"
|
||||
verified_seconds=$(genesis_uptime_seconds "$uptime_file")
|
||||
|
||||
mkdir -p "$state_dir"
|
||||
chmod 0755 "$state_dir"
|
||||
umask 077
|
||||
state_file=$state_dir/genesis.env
|
||||
temporary_file=${state_file}.new
|
||||
{
|
||||
printf 'XCATDEST=%s\n' "$destination"
|
||||
printf 'XCATMASTER=%s\n' "$master"
|
||||
printf 'XCATPORT=%s\n' "$port"
|
||||
printf 'XCAT_INTERFACE=%s\n' "$selected_interface"
|
||||
printf 'XCAT_SOURCE_ADDRESS=%s\n' "$source_address"
|
||||
printf 'XCAT_SOURCE_PREFIXED_ADDRESS=%s\n' "$source_prefixed_address"
|
||||
printf 'XCAT_GATEWAY=%s\n' "$actual_gateway"
|
||||
printf 'XCAT_DNS_SERVERS=%s\n' "$dns_servers"
|
||||
printf 'XCAT_NETWORK_METHOD=%s\n' "$network_method"
|
||||
printf 'XCAT_LINK_STATE=%s\n' "$link_state"
|
||||
printf 'XCAT_MAC_ADDRESS=%s\n' "$mac_address"
|
||||
printf 'XCAT_VERIFIED_SECONDS=%s\n' "$verified_seconds"
|
||||
} >"$temporary_file"
|
||||
mv "$temporary_file" "$state_file"
|
||||
|
||||
publish_status READY "Management network ready on $selected_interface" \
|
||||
"VERIFIED_SECONDS=$verified_seconds"
|
||||
log "using $selected_interface to reach $destination"
|
||||
+120
@@ -0,0 +1,120 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
functions_file=${XCAT_GENESIS_FUNCTIONS:-/usr/libexec/xcat/genesis-functions}
|
||||
# shellcheck source=genesis-functions
|
||||
source "$functions_file"
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
cmdline_file=${XCAT_CMDLINE_FILE:-/proc/cmdline}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
metadata_file=$state_dir/xcat-response.env
|
||||
kernel_destiny=
|
||||
failure_code=XCAT_REGISTRATION_FAILED
|
||||
failure_detail='xCAT registration failed'
|
||||
failure_recovery='Check xcatd, certificates, and the management network'
|
||||
maximum_attempts=${XCAT_REGISTRATION_ATTEMPTS:-6}
|
||||
retry_seconds=${XCAT_REGISTRATION_RETRY_SECONDS:-5}
|
||||
request_timeout=${XCAT_REGISTRATION_REQUEST_TIMEOUT:-10}
|
||||
declare -a cmdline_arguments
|
||||
|
||||
[[ "$maximum_attempts" =~ ^[1-9][0-9]*$ ]] \
|
||||
|| maximum_attempts=6
|
||||
[[ "$retry_seconds" =~ ^[0-9]+$ ]] \
|
||||
|| retry_seconds=5
|
||||
[[ "$request_timeout" =~ ^[1-9][0-9]*$ ]] \
|
||||
|| request_timeout=10
|
||||
|
||||
publish_status() {
|
||||
genesis_publish_status "$status_command" registration \
|
||||
xcat-genesis-register "$@"
|
||||
}
|
||||
|
||||
finish_status() {
|
||||
local result=$?
|
||||
|
||||
trap - EXIT
|
||||
if [ "$result" -ne 0 ]; then
|
||||
publish_status FAILED "$failure_detail" \
|
||||
"CODE=$failure_code" "RECOVERY=$failure_recovery"
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
trap finish_status EXIT
|
||||
|
||||
read -r -a cmdline_arguments <"$cmdline_file"
|
||||
for argument in "${cmdline_arguments[@]}"; do
|
||||
case "$argument" in
|
||||
destiny=*) kernel_destiny=${argument#destiny=} ;;
|
||||
esac
|
||||
done
|
||||
|
||||
selected_destiny=
|
||||
for ((attempt = 1; attempt <= maximum_attempts; attempt++)); do
|
||||
publish_status CONTACTING_XCAT 'Requesting an action from xCAT' \
|
||||
"ATTEMPT=$attempt" "ATTEMPT_LIMIT=$maximum_attempts"
|
||||
if [ -n "$kernel_destiny" ]; then
|
||||
if timeout "$request_timeout" getdestiny "$XCATDEST" \
|
||||
--metadata "$metadata_file" --once >/dev/null; then
|
||||
selected_destiny=$kernel_destiny
|
||||
break
|
||||
fi
|
||||
elif selected_destiny=$(timeout "$request_timeout" getdestiny "$XCATDEST" \
|
||||
--metadata "$metadata_file" --once); then
|
||||
break
|
||||
fi
|
||||
selected_destiny=
|
||||
if [ "$attempt" -lt "$maximum_attempts" ]; then
|
||||
publish_status CONTACTING_XCAT 'xCAT has not answered yet' \
|
||||
"ATTEMPT=$attempt" "ATTEMPT_LIMIT=$maximum_attempts" \
|
||||
"NEXT_RETRY_SECONDS=$retry_seconds"
|
||||
sleep "$retry_seconds"
|
||||
fi
|
||||
done
|
||||
|
||||
[ -n "$selected_destiny" ] || {
|
||||
failure_code=XCAT_RESPONSE_UNAVAILABLE
|
||||
failure_detail="No valid response from xCAT after $maximum_attempts attempts"
|
||||
failure_recovery='Check xcatd, certificates, and the assigned node action'
|
||||
logger -t xcat-genesis-register -- "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
|
||||
genesis_parse_destiny "$selected_destiny" || {
|
||||
failure_code=INVALID_XCAT_ACTION
|
||||
failure_detail='xCAT returned an invalid action'
|
||||
failure_recovery='Correct the node destiny on the xCAT management node'
|
||||
logger -t xcat-genesis-register -- "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
selected_action=$XCAT_GENESIS_ACTION
|
||||
selected_target=$XCAT_GENESIS_TARGET
|
||||
|
||||
confirmed_node=
|
||||
if [ -r "$metadata_file" ]; then
|
||||
while IFS='=' read -r name value; do
|
||||
case "$name" in
|
||||
XCAT_NODE_NAME) confirmed_node=$value ;;
|
||||
esac
|
||||
done <"$metadata_file"
|
||||
fi
|
||||
|
||||
verified_seconds=$(genesis_uptime_seconds)
|
||||
|
||||
umask 077
|
||||
temporary_file=$state_dir/destiny.new
|
||||
printf '%s\n' "$selected_destiny" >"$temporary_file"
|
||||
mv "$temporary_file" "$state_dir/destiny"
|
||||
|
||||
declare -a status_fields
|
||||
status_fields=(
|
||||
"VERIFIED_SECONDS=$verified_seconds"
|
||||
"ACTION=$selected_action"
|
||||
"TARGET=$selected_target"
|
||||
)
|
||||
[ -z "$confirmed_node" ] || status_fields+=("NODE_NAME=$confirmed_node")
|
||||
publish_status ACTION_RECEIVED "Action $selected_action received" \
|
||||
"${status_fields[@]}"
|
||||
logger -t xcat-genesis-register -- \
|
||||
"received action from $XCATDEST: $selected_action"
|
||||
Executable
+110
@@ -0,0 +1,110 @@
|
||||
#!/bin/sh
|
||||
set -eu
|
||||
|
||||
LC_ALL=C
|
||||
export LC_ALL
|
||||
|
||||
status_dir=${XCAT_STATUS_DIR:-/run/xcat/status}
|
||||
uptime_file=${XCAT_UPTIME_FILE:-/proc/uptime}
|
||||
|
||||
fail() {
|
||||
printf 'genesis-status: %s\n' "$*" >&2
|
||||
exit 1
|
||||
}
|
||||
|
||||
[ "$#" -ge 2 ] \
|
||||
|| fail 'usage: genesis-status COMPONENT STATE [DETAIL] [NAME=VALUE ...]'
|
||||
|
||||
component=$1
|
||||
state=$2
|
||||
shift 2
|
||||
detail=${1:-}
|
||||
[ "$#" -eq 0 ] || shift
|
||||
|
||||
case "$component" in
|
||||
''|*[!a-z0-9-]*) fail 'invalid component' ;;
|
||||
esac
|
||||
[ "${#component}" -le 32 ] || fail 'invalid component'
|
||||
|
||||
case "$state" in
|
||||
STARTING|IDLE|WAITING_FOR_LINK|CONFIGURING_NETWORK|CONTACTING_XCAT|ACTION_RECEIVED|RUNNING|READY|DEGRADED|FAILED) ;;
|
||||
*) fail 'invalid state' ;;
|
||||
esac
|
||||
|
||||
detail=$(printf '%s' "$detail" \
|
||||
| tr '\r\n\t' ' ' \
|
||||
| tr -cd '\040-\176' \
|
||||
| cut -c 1-160)
|
||||
|
||||
uptime=0
|
||||
if [ -r "$uptime_file" ]; then
|
||||
read -r uptime _ <"$uptime_file" || uptime=0
|
||||
uptime=${uptime%%.*}
|
||||
case "$uptime" in
|
||||
''|*[!0-9]*) uptime=0 ;;
|
||||
esac
|
||||
fi
|
||||
|
||||
started_seconds=$uptime
|
||||
status_file=$status_dir/$component.env
|
||||
if [ -r "$status_file" ]; then
|
||||
previous_schema=
|
||||
previous_state=
|
||||
previous_started=
|
||||
while IFS='=' read -r name value; do
|
||||
case "$name" in
|
||||
SCHEMA) previous_schema=$value ;;
|
||||
STATE) previous_state=$value ;;
|
||||
STARTED_SECONDS) previous_started=$value ;;
|
||||
esac
|
||||
done <"$status_file"
|
||||
case "$previous_started" in
|
||||
''|*[!0-9]*) ;;
|
||||
*)
|
||||
if [ "$previous_schema" = 1 ] && [ "$previous_state" = "$state" ]; then
|
||||
started_seconds=$previous_started
|
||||
fi
|
||||
;;
|
||||
esac
|
||||
fi
|
||||
|
||||
install -d -m 0755 "$status_dir"
|
||||
temporary_file=$(mktemp "$status_dir/.${component}.XXXXXX")
|
||||
trap 'rm -f -- "$temporary_file"' EXIT HUP INT TERM
|
||||
{
|
||||
printf 'SCHEMA=1\n'
|
||||
printf 'STATE=%s\n' "$state"
|
||||
printf 'DETAIL=%s\n' "$detail"
|
||||
printf 'STARTED_SECONDS=%s\n' "$started_seconds"
|
||||
printf 'UPDATED_SECONDS=%s\n' "$uptime"
|
||||
seen_fields=' '
|
||||
for field in "$@"; do
|
||||
case "$field" in
|
||||
*=*) name=${field%%=*}; value=${field#*=} ;;
|
||||
*) fail 'invalid field' ;;
|
||||
esac
|
||||
case "$name" in
|
||||
CODE|RECOVERY|ATTEMPT|ATTEMPT_LIMIT|NEXT_RETRY_SECONDS|VERIFIED_SECONDS|NODE_NAME|ACTION|TARGET|PROGRESS_PERCENT) ;;
|
||||
*) fail 'invalid field name' ;;
|
||||
esac
|
||||
case "$seen_fields" in
|
||||
*" $name "*) fail 'duplicate field name' ;;
|
||||
esac
|
||||
seen_fields="$seen_fields$name "
|
||||
value=$(printf '%s' "$value" \
|
||||
| tr '\r\n\t' ' ' \
|
||||
| tr -cd '\040-\176' \
|
||||
| cut -c 1-240)
|
||||
case "$name" in
|
||||
ATTEMPT|ATTEMPT_LIMIT|NEXT_RETRY_SECONDS|VERIFIED_SECONDS|PROGRESS_PERCENT)
|
||||
case "$value" in
|
||||
''|*[!0-9]*) fail 'invalid numeric field' ;;
|
||||
esac
|
||||
;;
|
||||
esac
|
||||
printf '%s=%s\n' "$name" "$value"
|
||||
done
|
||||
} >"$temporary_file"
|
||||
chmod 0644 "$temporary_file"
|
||||
mv -f -- "$temporary_file" "$status_dir/$component.env"
|
||||
trap - EXIT HUP INT TERM
|
||||
+16
@@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Execute the assigned xCAT Genesis action
|
||||
Requires=xcat-genesis-register.service
|
||||
After=xcat-genesis-register.service
|
||||
StartLimitIntervalSec=0
|
||||
|
||||
[Service]
|
||||
Type=simple
|
||||
EnvironmentFile=/run/xcat/genesis.env
|
||||
ExecStart=/usr/libexec/xcat/genesis-action
|
||||
Restart=on-failure
|
||||
RestartSec=10
|
||||
TimeoutStartSec=infinity
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+13
@@ -0,0 +1,13 @@
|
||||
[Unit]
|
||||
Description=Record xCAT Genesis boot metrics
|
||||
ConditionKernelCommandLine=xcatd
|
||||
Requires=xcat-genesis-register.service
|
||||
After=xcat-genesis-register.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/xcat/genesis-metrics --output /run/xcat/metrics.env
|
||||
RemainAfterExit=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+8
@@ -0,0 +1,8 @@
|
||||
[Unit]
|
||||
Description=xCAT Genesis management network is ready
|
||||
ConditionKernelCommandLine=xcatd
|
||||
Requires=xcat-genesis-network-state.service
|
||||
After=xcat-genesis-network-state.service
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
[Unit]
|
||||
Description=Select the xCAT Genesis management network
|
||||
ConditionKernelCommandLine=xcatd
|
||||
Wants=NetworkManager.service
|
||||
After=NetworkManager.service
|
||||
Before=xcat-genesis-network-ready.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/xcat/genesis-network-state
|
||||
RemainAfterExit=yes
|
||||
TimeoutStartSec=120
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
[Unit]
|
||||
Description=Register Genesis with xCAT
|
||||
ConditionKernelCommandLine=xcatd
|
||||
Requires=xcat-genesis-network-ready.target xcat-genesis-extensions.service
|
||||
After=xcat-genesis-network-ready.target xcat-genesis-extensions.service
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
EnvironmentFile=/run/xcat/genesis.env
|
||||
ExecStart=/usr/libexec/xcat/genesis-register
|
||||
RemainAfterExit=yes
|
||||
TimeoutStartSec=90
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
+83
@@ -0,0 +1,83 @@
|
||||
SUMMARY = "xCAT Genesis systemd units"
|
||||
LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
SRC_URI = "file://00-xcat-genesis.preset \
|
||||
file://genesis-functions \
|
||||
file://genesis-maintenance-shell \
|
||||
file://genesis-action \
|
||||
file://genesis-metrics \
|
||||
file://genesis-network-refresh \
|
||||
file://genesis-network-state \
|
||||
file://genesis-register \
|
||||
file://genesis-status \
|
||||
file://xcat-genesis-action.service \
|
||||
file://xcat-genesis-metrics.service \
|
||||
file://xcat-genesis-network-ready.target \
|
||||
file://xcat-genesis-network-state.service \
|
||||
file://xcat-genesis-register.service \
|
||||
"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
inherit systemd
|
||||
|
||||
RDEPENDS:${PN} = "bash coreutils ipmitool iproute2 networkmanager-nmcli xcat-genesis-discovery xcat-genesis-protocol"
|
||||
|
||||
SYSTEMD_SERVICE:${PN} = "xcat-genesis-action.service \
|
||||
xcat-genesis-metrics.service \
|
||||
xcat-genesis-network-ready.target \
|
||||
xcat-genesis-register.service \
|
||||
"
|
||||
SYSTEMD_AUTO_ENABLE = "enable"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${libexecdir}/xcat
|
||||
install -m 0644 ${UNPACKDIR}/genesis-functions \
|
||||
${D}${libexecdir}/xcat/genesis-functions
|
||||
install -m 0755 ${UNPACKDIR}/genesis-maintenance-shell \
|
||||
${D}${libexecdir}/xcat/genesis-maintenance-shell
|
||||
install -m 0755 ${UNPACKDIR}/genesis-action \
|
||||
${D}${libexecdir}/xcat/genesis-action
|
||||
install -m 0755 ${UNPACKDIR}/genesis-metrics \
|
||||
${D}${libexecdir}/xcat/genesis-metrics
|
||||
install -m 0755 ${UNPACKDIR}/genesis-network-refresh \
|
||||
${D}${libexecdir}/xcat/genesis-network-refresh
|
||||
install -m 0755 ${UNPACKDIR}/genesis-network-state \
|
||||
${D}${libexecdir}/xcat/genesis-network-state
|
||||
install -m 0755 ${UNPACKDIR}/genesis-register \
|
||||
${D}${libexecdir}/xcat/genesis-register
|
||||
install -m 0755 ${UNPACKDIR}/genesis-status \
|
||||
${D}${libexecdir}/xcat/genesis-status
|
||||
|
||||
install -d ${D}${systemd_system_unitdir}
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-action.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-action.service
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-metrics.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-metrics.service
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-network-ready.target \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-network-ready.target
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-network-state.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-network-state.service
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-register.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-register.service
|
||||
|
||||
install -d ${D}${systemd_unitdir}/system-preset
|
||||
install -m 0644 ${UNPACKDIR}/00-xcat-genesis.preset \
|
||||
${D}${systemd_unitdir}/system-preset/00-xcat-genesis.preset
|
||||
}
|
||||
|
||||
FILES:${PN} = "${libexecdir}/xcat/genesis-maintenance-shell \
|
||||
${libexecdir}/xcat/genesis-functions \
|
||||
${libexecdir}/xcat/genesis-action \
|
||||
${libexecdir}/xcat/genesis-metrics \
|
||||
${libexecdir}/xcat/genesis-network-refresh \
|
||||
${libexecdir}/xcat/genesis-network-state \
|
||||
${libexecdir}/xcat/genesis-register \
|
||||
${libexecdir}/xcat/genesis-status \
|
||||
${systemd_system_unitdir}/xcat-genesis-action.service \
|
||||
${systemd_system_unitdir}/xcat-genesis-metrics.service \
|
||||
${systemd_system_unitdir}/xcat-genesis-network-ready.target \
|
||||
${systemd_system_unitdir}/xcat-genesis-network-state.service \
|
||||
${systemd_system_unitdir}/xcat-genesis-register.service \
|
||||
${systemd_unitdir}/system-preset/00-xcat-genesis.preset \
|
||||
"
|
||||
+12
@@ -0,0 +1,12 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
request=
|
||||
IFS= read -r request || true
|
||||
if [[ $request != *CREDOKBYYOU?* ]]; then
|
||||
logger -t xcat-genesis-credential-callback -- \
|
||||
'ignored an invalid credential check'
|
||||
exit 1
|
||||
fi
|
||||
|
||||
printf '%s\n' CREDOKBYME
|
||||
xCAT-genesis-builder/oe/meta-xcat-genesis/recipes-xcat/xcat-genesis-discovery/files/genesis-discover
Executable
+317
@@ -0,0 +1,317 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
response_file=${XCAT_DISCOVERY_RESPONSE_FILE:-$state_dir/discovery-response}
|
||||
network_refresh_command=${XCAT_NETWORK_REFRESH_COMMAND:-/usr/libexec/xcat/genesis-network-refresh}
|
||||
sys_root=${XCAT_SYS_ROOT:-}
|
||||
proc_root=${XCAT_PROC_ROOT:-/proc}
|
||||
key_dir=${XCAT_KEY_DIR:-/etc/xcat}
|
||||
maximum_attempts=${XCAT_DISCOVERY_ATTEMPTS:-36}
|
||||
retry_seconds=${XCAT_DISCOVERY_RETRY_SECONDS:-5}
|
||||
ipmitool_command=${XCAT_IPMITOOL_COMMAND:-ipmitool}
|
||||
lldp_command=${XCAT_LLDP_COMMAND:-lldpcli}
|
||||
failure_code=DISCOVERY_FAILED
|
||||
failure_detail='Node discovery failed'
|
||||
failure_recovery='Check xCAT discovery policy and the management network'
|
||||
temporary_dir=
|
||||
|
||||
[[ "$maximum_attempts" =~ ^[1-9][0-9]*$ ]] || maximum_attempts=36
|
||||
[[ "$retry_seconds" =~ ^[0-9]+$ ]] || retry_seconds=5
|
||||
|
||||
publish_status() {
|
||||
"$status_command" discovery "$@" \
|
||||
|| logger -t xcat-genesis-discover -- 'unable to publish discovery status'
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
finish_status() {
|
||||
local result=$?
|
||||
|
||||
trap - EXIT
|
||||
[[ -z $temporary_dir ]] || rm -rf -- "$temporary_dir"
|
||||
if ((result != 0)); then
|
||||
publish_status FAILED "$failure_detail" \
|
||||
"CODE=$failure_code" "RECOVERY=$failure_recovery"
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
fail_discovery() {
|
||||
failure_code=$1
|
||||
failure_detail=$2
|
||||
failure_recovery=$3
|
||||
logger -t xcat-genesis-discover -- "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
|
||||
xml_escape() {
|
||||
local value=$1
|
||||
|
||||
value=${value//&/\&}
|
||||
value=${value//</\<}
|
||||
value=${value//>/\>}
|
||||
value=${value//\"/\"}
|
||||
value=${value//\'/\'}
|
||||
printf '%s' "$value"
|
||||
}
|
||||
|
||||
append_element() {
|
||||
local name=$1 value=${2:-}
|
||||
|
||||
[[ -n $value ]] || return 0
|
||||
printf '<%s>%s</%s>\n' "$name" "$(xml_escape "$value")" "$name" \
|
||||
>>"$unsigned_packet"
|
||||
}
|
||||
|
||||
read_value() {
|
||||
local value_file=$1
|
||||
|
||||
[[ -r $value_file ]] || return 1
|
||||
tr -d '\000\r\n' <"$value_file" | head -c 512
|
||||
}
|
||||
|
||||
send_packet() {
|
||||
if [[ -n ${XCAT_DISCOVERY_SEND_COMMAND:-} ]]; then
|
||||
"$XCAT_DISCOVERY_SEND_COMMAND" "$compressed_packet" "$XCATMASTER" "$XCATPORT"
|
||||
return
|
||||
fi
|
||||
|
||||
/usr/libexec/xcat/genesis-udp-send \
|
||||
"$compressed_packet" "$XCATMASTER" "$XCATPORT"
|
||||
}
|
||||
|
||||
trap finish_status EXIT HUP INT TERM
|
||||
|
||||
[[ -r $network_file ]] || fail_discovery DISCOVERY_NETWORK_STATE_MISSING \
|
||||
'Management network state is unavailable' \
|
||||
'Wait for Genesis networking or review its failure'
|
||||
# shellcheck disable=SC1090
|
||||
source "$network_file"
|
||||
: "${XCATMASTER:?xCAT master is missing from network state}"
|
||||
: "${XCATPORT:?xCAT port is missing from network state}"
|
||||
: "${XCAT_INTERFACE:?management interface is missing from network state}"
|
||||
|
||||
install -d -m 0700 "$key_dir"
|
||||
if [[ ! -s $key_dir/privkey.pem ]]; then
|
||||
umask 077
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
|
||||
-out "$key_dir/privkey.pem" >/dev/null 2>&1 \
|
||||
|| fail_discovery DISCOVERY_KEY_FAILED \
|
||||
'Unable to create the discovery identity' \
|
||||
'Review entropy and OpenSSL diagnostics'
|
||||
fi
|
||||
openssl pkey -in "$key_dir/privkey.pem" -noout >/dev/null 2>&1 \
|
||||
|| fail_discovery DISCOVERY_KEY_INVALID \
|
||||
'The discovery identity is invalid' \
|
||||
'Replace the damaged Genesis runtime state and retry'
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/genesis-discovery.XXXXXX")
|
||||
unsigned_packet=$temporary_dir/request.xml
|
||||
signed_packet=$temporary_dir/request-signed.xml
|
||||
signature_file=$temporary_dir/signature
|
||||
compressed_packet=$temporary_dir/request.xml.gz
|
||||
|
||||
architecture=$(uname -m)
|
||||
case "$architecture" in
|
||||
i?86) architecture=x86 ;;
|
||||
armv7l) architecture=armv7hf ;;
|
||||
esac
|
||||
|
||||
product_name=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_name" || true)
|
||||
system_vendor=$(read_value "$sys_root/sys/devices/virtual/dmi/id/sys_vendor" || true)
|
||||
serial=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_serial" || true)
|
||||
uuid=$(read_value "$sys_root/sys/devices/virtual/dmi/id/product_uuid" || true)
|
||||
machine_type=
|
||||
[[ -z $system_vendor && -z $product_name ]] \
|
||||
|| machine_type=${system_vendor:+$system_vendor:}$product_name
|
||||
platform=$(awk -F ':' \
|
||||
'/^platform[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
|
||||
"$proc_root/cpuinfo" 2>/dev/null || true)
|
||||
if [[ -z $product_name ]]; then
|
||||
product_name=$(read_value "$proc_root/device-tree/model" || true)
|
||||
[[ -n $product_name ]] \
|
||||
|| product_name=$(read_value \
|
||||
"$sys_root/sys/firmware/devicetree/base/model" || true)
|
||||
machine_type=${product_name##*,}
|
||||
serial=$(read_value "$proc_root/device-tree/system-id" || true)
|
||||
[[ -n $serial ]] \
|
||||
|| serial=$(read_value \
|
||||
"$sys_root/sys/firmware/devicetree/base/serial-number" || true)
|
||||
serial=${serial##*,}
|
||||
fi
|
||||
machine_type=${machine_type//./}
|
||||
serial=${serial//./}
|
||||
node_type=
|
||||
case "$system_vendor $product_name" in
|
||||
*KVM*|*QEMU*|*VMware*|*VirtualBox*|*Bochs*|*Hyper-V*) node_type=virtual ;;
|
||||
esac
|
||||
|
||||
cpu_count=$(awk '/^(processor|cpu)[[:space:]]*:/ {count++} END {print count + 0}' \
|
||||
"$proc_root/cpuinfo" 2>/dev/null || printf '0')
|
||||
cpu_type=$(awk -F ':' \
|
||||
'/^(model name|cpu)[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
|
||||
"$proc_root/cpuinfo" 2>/dev/null || true)
|
||||
memory=$(awk '/^MemTotal:/ {printf "%.0fMB", $2 / 1024; exit}' \
|
||||
"$proc_root/meminfo" 2>/dev/null || true)
|
||||
disk_size=$(lsblk -b -dn -o NAME,SIZE,TYPE 2>/dev/null \
|
||||
| awk '$3 == "disk" {printf "%s%s:%.0fGB", separator, $1, $2 / 1073741824; separator=","}')
|
||||
|
||||
printf '%s\n' '<xcatrequest>' '<command>findme</command>' \
|
||||
'<sequential>1</sequential>' >"$unsigned_packet"
|
||||
append_element arch "$architecture"
|
||||
append_element nodetype "$node_type"
|
||||
append_element cpucount "$cpu_count"
|
||||
append_element cputype "$cpu_type"
|
||||
append_element memory "$memory"
|
||||
append_element disksize "$disk_size"
|
||||
append_element mtm "$machine_type"
|
||||
append_element serial "$serial"
|
||||
append_element platform "$platform"
|
||||
|
||||
if [[ -e $sys_root/dev/ipmi0 || -e $sys_root/dev/ipmi/0 || -e $sys_root/dev/ipmidev/0 ]]; then
|
||||
append_element bmcinband 1
|
||||
fi
|
||||
if command -v "$ipmitool_command" >/dev/null 2>&1 \
|
||||
&& "$ipmitool_command" mc info >/dev/null 2>&1; then
|
||||
lan_channel=$("$ipmitool_command" sol info 2>/dev/null \
|
||||
| awk -F ':' '/Payload Channel/ {gsub(/[[:space:]]/, "", $2); print $2; exit}')
|
||||
bmc_data=$("$ipmitool_command" lan print "$lan_channel" 2>/dev/null \
|
||||
|| "$ipmitool_command" lan print 2>/dev/null || true)
|
||||
bmc_source=$(awk -F ':' \
|
||||
'/IP Address Source/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
|
||||
<<<"$bmc_data")
|
||||
bmc_address=$(awk -F ':' \
|
||||
'/^IP Address[[:space:]]*:/ {sub(/^[[:space:]]+/, "", $2); print $2; exit}' \
|
||||
<<<"$bmc_data")
|
||||
bmc_mac=$(awk -F ':' \
|
||||
'/MAC Address/ {sub(/^[[:space:]]+/, "", $2); print $2 ":" $3 ":" $4 ":" $5 ":" $6 ":" $7; exit}' \
|
||||
<<<"$bmc_data")
|
||||
[[ $bmc_source != *Static* ]] || append_element bmc "$bmc_address"
|
||||
append_element bmcmac "$bmc_mac"
|
||||
fi
|
||||
|
||||
shopt -s nullglob
|
||||
first_mac=
|
||||
for interface_dir in "$sys_root/sys/class/net"/*; do
|
||||
interface=${interface_dir##*/}
|
||||
[[ $interface != lo && -r $interface_dir/address ]] || continue
|
||||
mac_address=$(read_value "$interface_dir/address" || true)
|
||||
[[ -n $mac_address ]] || continue
|
||||
driver=unknown
|
||||
if [[ -L $interface_dir/device/driver ]]; then
|
||||
driver=$(basename "$(readlink -f "$interface_dir/device/driver")")
|
||||
fi
|
||||
normalized_mac=$(printf '%s' "$mac_address" | tr 'a-f' 'A-F')
|
||||
[[ -n $first_mac ]] || first_mac=${normalized_mac//:/}
|
||||
ipv4_address=$(ip -4 -o address show dev "$interface" scope global 2>/dev/null \
|
||||
| awk 'NR == 1 {print $4}')
|
||||
ipv6_address=$(ip -6 -o address show dev "$interface" scope global 2>/dev/null \
|
||||
| awk 'NR == 1 {print $4}')
|
||||
address=${ipv4_address:-$ipv6_address}
|
||||
pci_slot=$(awk -F= '$1 == "PCI_SLOT_NAME" {print $2; exit}' \
|
||||
"$interface_dir/device/uevent" 2>/dev/null || true)
|
||||
mac_record="$driver|$interface|$normalized_mac|$address"
|
||||
append_element mac "$mac_record"
|
||||
printf '%s\n' '<nic>' >>"$unsigned_packet"
|
||||
append_element devname "$interface"
|
||||
append_element driver "$driver"
|
||||
append_element ip4address "$ipv4_address"
|
||||
append_element ip6address "$ipv6_address"
|
||||
append_element hwaddr "$normalized_mac"
|
||||
append_element pcidev "$pci_slot"
|
||||
location=$(read_value "$interface_dir/device/physical_slot" || true)
|
||||
[[ -n $location ]] \
|
||||
|| location=$(read_value "$interface_dir/device/label" || true)
|
||||
append_element location "$location"
|
||||
if command -v "$lldp_command" >/dev/null 2>&1; then
|
||||
lldp_data=$("$lldp_command" show neighbors ports "$interface" \
|
||||
details -f keyvalue 2>/dev/null || true)
|
||||
switch_name=$(awk -F= \
|
||||
'/\.chassis\.name=/ {print substr($0, index($0, "=") + 1); exit}' \
|
||||
<<<"$lldp_data")
|
||||
switch_address=$(awk -F= \
|
||||
'/\.chassis\.mgmt-ip=/ {print substr($0, index($0, "=") + 1); exit}' \
|
||||
<<<"$lldp_data")
|
||||
switch_description=$(awk -F= \
|
||||
'/\.chassis\.descr=/ {print substr($0, index($0, "=") + 1); exit}' \
|
||||
<<<"$lldp_data")
|
||||
switch_port=$(awk -F= \
|
||||
'/\.port\.descr=/ {print substr($0, index($0, "=") + 1); exit}' \
|
||||
<<<"$lldp_data")
|
||||
append_element switchname "$switch_name"
|
||||
append_element switchaddr "$switch_address"
|
||||
append_element switchdesc "$switch_description"
|
||||
append_element switchport "$switch_port"
|
||||
fi
|
||||
printf '%s\n' '</nic>' >>"$unsigned_packet"
|
||||
done
|
||||
|
||||
if [[ -z $uuid ]]; then
|
||||
uuid=${machine_type:-$architecture}-${serial:-unknown}-${first_mac:-unknown}
|
||||
uuid=$(printf '%s' "$uuid" | tr '[:upper:]' '[:lower:]')
|
||||
fi
|
||||
append_element uuid "$uuid"
|
||||
|
||||
public_key=$(openssl pkey -in "$key_dir/privkey.pem" -pubout 2>/dev/null \
|
||||
| awk '!/PUBLIC KEY/ {printf "%s", $0}')
|
||||
append_element xcatpubkey "$public_key"
|
||||
printf '%s\n' '<sha512sig>' '</sha512sig>' '</xcatrequest>' \
|
||||
>>"$unsigned_packet"
|
||||
|
||||
openssl dgst -sha512 -sign "$key_dir/privkey.pem" \
|
||||
-out "$signature_file" "$unsigned_packet" \
|
||||
|| fail_discovery DISCOVERY_SIGNATURE_FAILED \
|
||||
'Unable to sign the discovery inventory' \
|
||||
'Review the Genesis discovery identity'
|
||||
signature=$(openssl base64 -A -in "$signature_file")
|
||||
awk -v signature="$signature" \
|
||||
'/<\/sha512sig>/ {print signature} {print}' \
|
||||
"$unsigned_packet" >"$signed_packet"
|
||||
gzip -9 -c "$signed_packet" >"$compressed_packet"
|
||||
packet_size=$(wc -c <"$compressed_packet")
|
||||
((packet_size <= 60000)) || fail_discovery DISCOVERY_PACKET_TOO_LARGE \
|
||||
"Discovery inventory is too large: $packet_size bytes" \
|
||||
'Review the attached device inventory'
|
||||
|
||||
rm -f -- "$response_file"
|
||||
for ((attempt = 1; attempt <= maximum_attempts; attempt++)); do
|
||||
publish_status RUNNING 'Sending hardware inventory to xCAT' \
|
||||
"ATTEMPT=$attempt" "ATTEMPT_LIMIT=$maximum_attempts"
|
||||
if ! send_packet; then
|
||||
logger -t xcat-genesis-discover -- 'unable to send discovery inventory'
|
||||
fi
|
||||
for ((waited = 0; waited < retry_seconds; waited++)); do
|
||||
if [[ -s $response_file ]]; then
|
||||
response=$(head -n 1 "$response_file")
|
||||
case "$response" in
|
||||
restart|restart\ \(*\))
|
||||
publish_status CONFIGURING_NETWORK \
|
||||
'Applying the discovered network identity'
|
||||
"$network_refresh_command" "$response" \
|
||||
|| fail_discovery DISCOVERY_NETWORK_REFRESH_FAILED \
|
||||
'The discovered network identity could not be applied' \
|
||||
'Check the new DHCP reservation and management interface'
|
||||
publish_status READY 'xCAT accepted the discovered node' \
|
||||
'PROGRESS_PERCENT=100'
|
||||
logger -t xcat-genesis-discover -- \
|
||||
'xCAT accepted the discovery inventory'
|
||||
exit 0
|
||||
;;
|
||||
processed)
|
||||
fail_discovery DISCOVERY_NOT_MATCHED \
|
||||
'xCAT received the node but did not match it' \
|
||||
'Review active discovery methods and node definitions'
|
||||
;;
|
||||
processing) rm -f -- "$response_file" ;;
|
||||
esac
|
||||
fi
|
||||
sleep 1
|
||||
done
|
||||
done
|
||||
|
||||
fail_discovery DISCOVERY_TIMEOUT \
|
||||
"xCAT did not accept the node after $maximum_attempts attempts" \
|
||||
'Check discovery status, node matching, and callback connectivity'
|
||||
+24
@@ -0,0 +1,24 @@
|
||||
#!/bin/bash
|
||||
set -eu
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
response_file=${XCAT_DISCOVERY_RESPONSE_FILE:-$state_dir/discovery-response}
|
||||
|
||||
response=
|
||||
IFS= read -r response || true
|
||||
case "$response" in
|
||||
restart|restart\ \(*\)|processing|processed) ;;
|
||||
*)
|
||||
logger -t xcat-genesis-discovery-callback -- \
|
||||
'ignored an invalid discovery callback'
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
|
||||
mkdir -p "$state_dir"
|
||||
chmod 0755 "$state_dir"
|
||||
umask 077
|
||||
temporary_file=${response_file}.new
|
||||
printf '%s\n' "$response" >"$temporary_file"
|
||||
mv "$temporary_file" "$response_file"
|
||||
printf '%s\n' 'callback received'
|
||||
Executable
+145
@@ -0,0 +1,145 @@
|
||||
#!/bin/bash
|
||||
set -euo pipefail
|
||||
|
||||
state_dir=${XCAT_STATE_DIR:-/run/xcat}
|
||||
status_command=${XCAT_STATUS_COMMAND:-/usr/libexec/xcat/genesis-status}
|
||||
network_file=${XCAT_NETWORK_FILE:-$state_dir/genesis.env}
|
||||
metadata_file=${XCAT_METADATA_FILE:-$state_dir/xcat-response.env}
|
||||
key_dir=${XCAT_KEY_DIR:-/etc/xcat}
|
||||
request_timeout=${XCAT_CREDENTIAL_REQUEST_TIMEOUT:-20}
|
||||
failure_code=CERTIFICATE_REQUEST_FAILED
|
||||
failure_detail='Unable to obtain an xCAT client certificate'
|
||||
failure_recovery='Check node identity, callback port 300, and xCAT credentials policy'
|
||||
temporary_dir=
|
||||
|
||||
[[ "$request_timeout" =~ ^[1-9][0-9]*$ ]] || request_timeout=20
|
||||
|
||||
publish_status() {
|
||||
"$status_command" credentials "$@" \
|
||||
|| logger -t xcat-genesis-getcert -- 'unable to publish credential status'
|
||||
}
|
||||
|
||||
# shellcheck disable=SC2329
|
||||
finish_status() {
|
||||
local result=$?
|
||||
|
||||
trap - EXIT
|
||||
[[ -z $temporary_dir ]] || rm -rf -- "$temporary_dir"
|
||||
if ((result != 0)); then
|
||||
publish_status FAILED "$failure_detail" \
|
||||
"CODE=$failure_code" "RECOVERY=$failure_recovery"
|
||||
fi
|
||||
exit "$result"
|
||||
}
|
||||
|
||||
fail_certificate() {
|
||||
failure_code=$1
|
||||
failure_detail=$2
|
||||
failure_recovery=$3
|
||||
logger -t xcat-genesis-getcert -- "$failure_detail"
|
||||
exit 1
|
||||
}
|
||||
|
||||
trap finish_status EXIT HUP INT TERM
|
||||
|
||||
[[ -r $network_file ]] || fail_certificate CERTIFICATE_NETWORK_STATE_MISSING \
|
||||
'Management network state is unavailable' \
|
||||
'Wait for Genesis networking or review its failure'
|
||||
# shellcheck disable=SC1090
|
||||
source "$network_file"
|
||||
: "${XCATDEST:?xCAT endpoint is missing from network state}"
|
||||
|
||||
node_name=
|
||||
if [[ -r $metadata_file ]]; then
|
||||
while IFS='=' read -r name value; do
|
||||
case "$name" in
|
||||
XCAT_NODE_NAME) node_name=$value ;;
|
||||
esac
|
||||
done <"$metadata_file"
|
||||
fi
|
||||
[[ $node_name =~ ^[A-Za-z0-9][A-Za-z0-9._-]{0,254}$ ]] \
|
||||
|| fail_certificate CERTIFICATE_NODE_IDENTITY_MISSING \
|
||||
'xCAT has not confirmed the node name' \
|
||||
'Complete discovery and refresh the node assignment'
|
||||
|
||||
install -d -m 0700 "$key_dir"
|
||||
if [[ ! -s $key_dir/privkey.pem ]]; then
|
||||
umask 077
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:2048 \
|
||||
-out "$key_dir/privkey.pem" >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_IDENTITY_KEY_FAILED \
|
||||
'Unable to create the Genesis identity key' \
|
||||
'Review entropy and OpenSSL diagnostics'
|
||||
fi
|
||||
if [[ ! -s $key_dir/certkey.pem ]]; then
|
||||
umask 077
|
||||
openssl genpkey -algorithm RSA -pkeyopt rsa_keygen_bits:3072 \
|
||||
-out "$key_dir/certkey.pem" >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_KEY_FAILED \
|
||||
'Unable to create the client certificate key' \
|
||||
'Review entropy and OpenSSL diagnostics'
|
||||
fi
|
||||
openssl pkey -in "$key_dir/privkey.pem" -noout >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_IDENTITY_KEY_INVALID \
|
||||
'The Genesis identity key is invalid' \
|
||||
'Replace the damaged Genesis runtime state and retry'
|
||||
openssl pkey -in "$key_dir/certkey.pem" -noout >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_KEY_INVALID \
|
||||
'The client certificate key is invalid' \
|
||||
'Replace the damaged Genesis runtime state and retry'
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/genesis-certificate.XXXXXX")
|
||||
csr_file=$temporary_dir/client.csr
|
||||
unsigned_request=$temporary_dir/request.xml
|
||||
signed_request=$temporary_dir/request-signed.xml
|
||||
signature_file=$temporary_dir/signature
|
||||
response_file=$temporary_dir/response.xml
|
||||
certificate_file=$temporary_dir/client.pem
|
||||
|
||||
openssl req -new -key "$key_dir/certkey.pem" -out "$csr_file" \
|
||||
-subj "/CN=$node_name" >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_CSR_FAILED \
|
||||
'Unable to create the client certificate request' \
|
||||
'Review the client key and confirmed node name'
|
||||
|
||||
{
|
||||
printf '%s\n' '<xcatrequest>' '<command>getcredentials</command>' \
|
||||
'<arg>x509cert</arg>' '<callback_port>300</callback_port>' '<csr>'
|
||||
cat "$csr_file"
|
||||
printf '%s\n' '</csr>' '<sha512sig>' '</sha512sig>' '</xcatrequest>'
|
||||
} >"$unsigned_request"
|
||||
openssl dgst -sha512 -sign "$key_dir/privkey.pem" \
|
||||
-out "$signature_file" "$unsigned_request" \
|
||||
|| fail_certificate CERTIFICATE_SIGNATURE_FAILED \
|
||||
'Unable to sign the client certificate request' \
|
||||
'Review the Genesis identity key'
|
||||
signature=$(openssl base64 -A -in "$signature_file")
|
||||
awk -v signature="$signature" \
|
||||
'/<\/sha512sig>/ {print signature} {print}' \
|
||||
"$unsigned_request" >"$signed_request"
|
||||
|
||||
publish_status RUNNING 'Requesting an xCAT client certificate' \
|
||||
"NODE_NAME=$node_name"
|
||||
if ! timeout "$request_timeout" openssl s_client -connect "$XCATDEST" \
|
||||
-quiet 2>/dev/null <"$signed_request" >"$response_file"; then
|
||||
fail_certificate CERTIFICATE_RESPONSE_UNAVAILABLE \
|
||||
'xCAT did not answer the client certificate request' \
|
||||
'Check xcatd and callback connectivity to port 300'
|
||||
fi
|
||||
awk '/-----BEGIN CERTIFICATE-----/,/-----END CERTIFICATE-----/' \
|
||||
"$response_file" >"$certificate_file"
|
||||
[[ -s $certificate_file ]] \
|
||||
|| fail_certificate CERTIFICATE_RESPONSE_INVALID \
|
||||
'xCAT returned no client certificate' \
|
||||
'Check node identity and xCAT credentials policy'
|
||||
openssl x509 -in "$certificate_file" -noout >/dev/null 2>&1 \
|
||||
|| fail_certificate CERTIFICATE_RESPONSE_INVALID \
|
||||
'xCAT returned an invalid client certificate' \
|
||||
'Review xcatd certificate service diagnostics'
|
||||
|
||||
chmod 0600 "$certificate_file"
|
||||
mv -f -- "$certificate_file" "$key_dir/cert.pem"
|
||||
publish_status READY 'xCAT client certificate installed' \
|
||||
"NODE_NAME=$node_name" 'PROGRESS_PERCENT=100'
|
||||
logger -t xcat-genesis-getcert -- \
|
||||
"installed an xCAT client certificate for $node_name"
|
||||
+115
@@ -0,0 +1,115 @@
|
||||
// SPDX-License-Identifier: EPL-1.0
|
||||
|
||||
#define _POSIX_C_SOURCE 200112L
|
||||
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <netdb.h>
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
enum {
|
||||
SOURCE_PORT = 301,
|
||||
MAX_DATAGRAM = 65507,
|
||||
};
|
||||
|
||||
int main(int argc, char **argv)
|
||||
{
|
||||
struct addrinfo hints = { 0 };
|
||||
struct addrinfo *addresses = NULL;
|
||||
unsigned char packet[MAX_DATAGRAM + 1];
|
||||
char *end = NULL;
|
||||
long port;
|
||||
FILE *input;
|
||||
size_t length;
|
||||
int error;
|
||||
int last_error = 0;
|
||||
int socket_fd;
|
||||
int result = EXIT_FAILURE;
|
||||
|
||||
if (argc != 4) {
|
||||
fprintf(stderr, "usage: %s packet host port\n", argv[0]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
errno = 0;
|
||||
port = strtol(argv[3], &end, 10);
|
||||
if (errno || !end || *end || port < 1 || port > UINT16_MAX) {
|
||||
fprintf(stderr, "invalid destination port: %s\n", argv[3]);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
input = fopen(argv[1], "rb");
|
||||
if (!input) {
|
||||
perror("unable to open discovery packet");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
length = fread(packet, 1, sizeof(packet), input);
|
||||
if (ferror(input) || length == 0 || length > MAX_DATAGRAM) {
|
||||
fprintf(stderr, "invalid discovery packet size\n");
|
||||
fclose(input);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
fclose(input);
|
||||
|
||||
hints.ai_family = AF_UNSPEC;
|
||||
hints.ai_socktype = SOCK_DGRAM;
|
||||
error = getaddrinfo(argv[2], argv[3], &hints, &addresses);
|
||||
if (error != 0) {
|
||||
fprintf(stderr, "unable to resolve discovery endpoint: %s\n",
|
||||
gai_strerror(error));
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
for (struct addrinfo *address = addresses; address; address = address->ai_next) {
|
||||
struct sockaddr_storage source = { 0 };
|
||||
socklen_t source_length;
|
||||
|
||||
if (address->ai_family == AF_INET) {
|
||||
struct sockaddr_in *source4 = (struct sockaddr_in *)&source;
|
||||
source4->sin_family = AF_INET;
|
||||
source4->sin_addr.s_addr = htonl(INADDR_ANY);
|
||||
source4->sin_port = htons(SOURCE_PORT);
|
||||
source_length = sizeof(*source4);
|
||||
} else if (address->ai_family == AF_INET6) {
|
||||
struct sockaddr_in6 *source6 = (struct sockaddr_in6 *)&source;
|
||||
source6->sin6_family = AF_INET6;
|
||||
source6->sin6_addr = in6addr_any;
|
||||
source6->sin6_port = htons(SOURCE_PORT);
|
||||
source_length = sizeof(*source6);
|
||||
} else {
|
||||
continue;
|
||||
}
|
||||
|
||||
socket_fd = socket(address->ai_family, address->ai_socktype,
|
||||
address->ai_protocol);
|
||||
if (socket_fd < 0) {
|
||||
last_error = errno;
|
||||
continue;
|
||||
}
|
||||
if (bind(socket_fd, (struct sockaddr *)&source, source_length) < 0) {
|
||||
last_error = errno;
|
||||
close(socket_fd);
|
||||
continue;
|
||||
}
|
||||
if (sendto(socket_fd, packet, length, 0, address->ai_addr,
|
||||
address->ai_addrlen) == (ssize_t)length) {
|
||||
close(socket_fd);
|
||||
result = EXIT_SUCCESS;
|
||||
break;
|
||||
}
|
||||
last_error = errno;
|
||||
close(socket_fd);
|
||||
}
|
||||
if (result != EXIT_SUCCESS && last_error) {
|
||||
errno = last_error;
|
||||
perror("unable to send discovery packet");
|
||||
}
|
||||
|
||||
freeaddrinfo(addresses);
|
||||
return result;
|
||||
}
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Answer xCAT credential checks
|
||||
|
||||
[Socket]
|
||||
ListenStream=300
|
||||
Accept=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Handle an xCAT credential check
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/xcat/genesis-credential-callback
|
||||
StandardInput=socket
|
||||
StandardOutput=socket
|
||||
StandardError=journal
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Receive xCAT discovery callbacks
|
||||
|
||||
[Socket]
|
||||
ListenStream=3001
|
||||
Accept=yes
|
||||
|
||||
[Install]
|
||||
WantedBy=sockets.target
|
||||
+9
@@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Handle an xCAT discovery callback
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/libexec/xcat/genesis-discovery-callback
|
||||
StandardInput=socket
|
||||
StandardOutput=socket
|
||||
StandardError=journal
|
||||
+65
@@ -0,0 +1,65 @@
|
||||
SUMMARY = "xCAT Genesis node discovery"
|
||||
LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
SRC_URI = "file://genesis-discover \
|
||||
file://genesis-udp-send.c \
|
||||
file://genesis-discovery-callback \
|
||||
file://genesis-getcert \
|
||||
file://genesis-credential-callback \
|
||||
file://xcat-genesis-discovery.socket \
|
||||
file://xcat-genesis-discovery@.service \
|
||||
file://xcat-genesis-credential.socket \
|
||||
file://xcat-genesis-credential@.service \
|
||||
"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
inherit systemd
|
||||
|
||||
do_compile() {
|
||||
${CC} ${CPPFLAGS} ${CFLAGS} -std=c17 -Wall -Wextra -Wpedantic -Werror \
|
||||
${LDFLAGS} ${UNPACKDIR}/genesis-udp-send.c \
|
||||
-o genesis-udp-send
|
||||
}
|
||||
|
||||
RDEPENDS:${PN} = "bash coreutils gzip iproute2 openssl-bin util-linux-lsblk"
|
||||
|
||||
SYSTEMD_SERVICE:${PN} = "xcat-genesis-discovery.socket \
|
||||
xcat-genesis-credential.socket \
|
||||
"
|
||||
SYSTEMD_AUTO_ENABLE = "enable"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${libexecdir}/xcat
|
||||
install -m 0755 ${UNPACKDIR}/genesis-discover \
|
||||
${D}${libexecdir}/xcat/genesis-discover
|
||||
install -m 0755 ${B}/genesis-udp-send \
|
||||
${D}${libexecdir}/xcat/genesis-udp-send
|
||||
install -m 0755 ${UNPACKDIR}/genesis-discovery-callback \
|
||||
${D}${libexecdir}/xcat/genesis-discovery-callback
|
||||
install -m 0755 ${UNPACKDIR}/genesis-getcert \
|
||||
${D}${libexecdir}/xcat/genesis-getcert
|
||||
install -m 0755 ${UNPACKDIR}/genesis-credential-callback \
|
||||
${D}${libexecdir}/xcat/genesis-credential-callback
|
||||
|
||||
install -d ${D}${systemd_system_unitdir}
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-discovery.socket \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-discovery.socket
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-discovery@.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-discovery@.service
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-credential.socket \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-credential.socket
|
||||
install -m 0644 ${UNPACKDIR}/xcat-genesis-credential@.service \
|
||||
${D}${systemd_system_unitdir}/xcat-genesis-credential@.service
|
||||
}
|
||||
|
||||
FILES:${PN} = "${libexecdir}/xcat/genesis-discover \
|
||||
${libexecdir}/xcat/genesis-udp-send \
|
||||
${libexecdir}/xcat/genesis-discovery-callback \
|
||||
${libexecdir}/xcat/genesis-getcert \
|
||||
${libexecdir}/xcat/genesis-credential-callback \
|
||||
${systemd_system_unitdir}/xcat-genesis-discovery.socket \
|
||||
${systemd_system_unitdir}/xcat-genesis-discovery@.service \
|
||||
${systemd_system_unitdir}/xcat-genesis-credential.socket \
|
||||
${systemd_system_unitdir}/xcat-genesis-credential@.service \
|
||||
"
|
||||
+18
@@ -0,0 +1,18 @@
|
||||
SUMMARY = "xCAT Genesis protocol clients"
|
||||
LICENSE = "EPL-1.0"
|
||||
LIC_FILES_CHKSUM = "file://${COMMON_LICENSE_DIR}/EPL-1.0;md5=57f8d5e2b3e98ac6e088986c12bf94e6"
|
||||
|
||||
FILESEXTRAPATHS:prepend := "${THISDIR}/../../../../../xCAT-genesis-scripts/usr/bin:"
|
||||
|
||||
SRC_URI = "file://getdestiny \
|
||||
file://nextdestiny \
|
||||
"
|
||||
S = "${UNPACKDIR}"
|
||||
|
||||
RDEPENDS:${PN} = "bash openssl-bin"
|
||||
|
||||
do_install() {
|
||||
install -d ${D}${bindir}
|
||||
install -m 0755 ${UNPACKDIR}/getdestiny ${D}${bindir}/getdestiny
|
||||
install -m 0755 ${UNPACKDIR}/nextdestiny ${D}${bindir}/nextdestiny
|
||||
}
|
||||
@@ -1,30 +1,111 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$XCATDEST" ]; then
|
||||
XCATDEST=$1
|
||||
fi
|
||||
echo "<xcatrequest>
|
||||
<command>getdestiny</command>
|
||||
<callback_port>300</callback_port>
|
||||
</xcatrequest>" > /tmp/destreq.xml
|
||||
if [ -f /tmp/destiny.xml ]; then rm /tmp/destiny.xml; fi
|
||||
while [ ! -f /tmp/destiny.xml ] || grep error /tmp/destiny.xml; do
|
||||
if [ -f /tmp/destiny.xml ]; then
|
||||
timer=60
|
||||
while [ $timer -gt 0 ]; do
|
||||
echo -en "Retrying in $timer seconds \r" >&2
|
||||
sleep 1
|
||||
timer=$(($timer-1));
|
||||
done
|
||||
fi
|
||||
echo " " >&2
|
||||
if [ -f /etc/xcat/cert.pem -a -f /etc/xcat/certkey.pem ]; then #use client cert if available
|
||||
openssl s_client -key /etc/xcat/certkey.pem -cert /etc/xcat/cert.pem -connect "$XCATDEST" -quiet 2> /dev/null < /tmp/destreq.xml > /tmp/destiny.xml
|
||||
else
|
||||
openssl s_client -connect "$XCATDEST" -quiet 2> /dev/null < /tmp/destreq.xml > /tmp/destiny.xml
|
||||
fi
|
||||
done
|
||||
rm /tmp/destreq.xml
|
||||
DESTINY="$(grep '<destiny>' /tmp/destiny.xml | awk -F'>' '{print $2}'|awk -F'<' '{print $1}')"
|
||||
rm /tmp/destiny.xml
|
||||
echo "$DESTINY"
|
||||
set -u
|
||||
|
||||
metadata_file=
|
||||
metadata_temporary=
|
||||
once=0
|
||||
destination=${XCATDEST:-}
|
||||
|
||||
usage() {
|
||||
printf '%s\n' 'Usage: getdestiny [ENDPOINT] [--metadata FILE] [--once]' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
while (($# > 0)); do
|
||||
case "$1" in
|
||||
--metadata)
|
||||
(($# >= 2)) || usage
|
||||
metadata_file=$2
|
||||
shift 2
|
||||
;;
|
||||
--once)
|
||||
once=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
(($# <= 1)) || usage
|
||||
if (($# == 1)); then
|
||||
destination=$1
|
||||
fi
|
||||
break
|
||||
;;
|
||||
-*) usage ;;
|
||||
*)
|
||||
if [[ -z $destination ]]; then
|
||||
destination=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
|
||||
[[ -n $destination ]] || usage
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/getdestiny.XXXXXX") || exit 1
|
||||
request_file=$temporary_dir/request.xml
|
||||
response_file=$temporary_dir/response.xml
|
||||
trap 'rm -rf -- "$temporary_dir"; [[ -z $metadata_temporary ]] || rm -f -- "$metadata_temporary"' EXIT HUP INT TERM
|
||||
|
||||
printf '%s\n' \
|
||||
'<xcatrequest>' \
|
||||
'<command>getdestiny</command>' \
|
||||
'<callback_port>300</callback_port>' \
|
||||
'</xcatrequest>' >"$request_file"
|
||||
|
||||
request_destiny() {
|
||||
if [[ -f /etc/xcat/cert.pem && -f /etc/xcat/certkey.pem ]]; then
|
||||
openssl s_client -key /etc/xcat/certkey.pem \
|
||||
-cert /etc/xcat/cert.pem -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file"
|
||||
else
|
||||
openssl s_client -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file"
|
||||
fi
|
||||
[[ -s $response_file ]] && ! grep -q '<error>' "$response_file" \
|
||||
&& grep -q '<destiny>' "$response_file"
|
||||
}
|
||||
|
||||
while ! request_destiny; do
|
||||
if ((once)); then
|
||||
exit 1
|
||||
fi
|
||||
for ((timer = 60; timer > 0; timer--)); do
|
||||
printf 'Retrying in %s seconds \r' "$timer" >&2
|
||||
sleep 1
|
||||
done
|
||||
printf '%*s\r' 48 '' >&2
|
||||
done
|
||||
|
||||
extract_element() {
|
||||
local element=$1
|
||||
|
||||
sed -n "s:.*<$element>\([^<]*\)</$element>.*:\1:p" "$response_file" \
|
||||
| head -n 1 | tr -cd '\40-\176'
|
||||
}
|
||||
|
||||
destiny=$(extract_element destiny)
|
||||
[[ -n $destiny ]] || exit 1
|
||||
|
||||
if [[ -n $metadata_file ]]; then
|
||||
metadata_dir=${metadata_file%/*}
|
||||
if [[ $metadata_dir == "$metadata_file" ]]; then
|
||||
metadata_dir=.
|
||||
elif [[ -z $metadata_dir ]]; then
|
||||
metadata_dir=/
|
||||
fi
|
||||
[[ -d $metadata_dir ]] || exit 1
|
||||
metadata_temporary=$(mktemp "$metadata_dir/.getdestiny.XXXXXX") || exit 1
|
||||
{
|
||||
printf 'XCAT_NODE_NAME=%s\n' "$(extract_element name)"
|
||||
printf 'XCAT_DESTINY=%s\n' "$destiny"
|
||||
printf 'XCAT_IMAGE_SERVER=%s\n' "$(extract_element imgserver)"
|
||||
printf 'XCAT_KERNEL=%s\n' "$(extract_element kernel)"
|
||||
printf 'XCAT_INITRD=%s\n' "$(extract_element initrd)"
|
||||
printf 'XCAT_KERNEL_COMMAND_LINE=%s\n' "$(extract_element kcmdline)"
|
||||
} >"$metadata_temporary"
|
||||
chmod 0600 "$metadata_temporary"
|
||||
mv -f -- "$metadata_temporary" "$metadata_file"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$destiny"
|
||||
|
||||
@@ -1,36 +1,115 @@
|
||||
#!/bin/bash
|
||||
if [ -z "$XCATDEST" ]; then
|
||||
XCATDEST=$1
|
||||
fi
|
||||
echo "<xcatrequest>
|
||||
<command>nextdestiny</command>
|
||||
<callback_port>300</callback_port>
|
||||
</xcatrequest>" > /tmp/destreq.xml
|
||||
if [ -f /tmp/destiny.xml ]; then rm /tmp/destiny.xml; fi
|
||||
timer=0
|
||||
while [ ! -f /tmp/destiny.xml ]; do
|
||||
while [ $timer -gt 0 ]; do
|
||||
echo -en "No destiny command received, retrying in $timer seconds \r" >&2
|
||||
sleep 1
|
||||
timer=$(($timer-1));
|
||||
done
|
||||
echo " " >&2;
|
||||
if [ -f /etc/xcat/cert.pem -a -f /etc/xcat/certkey.pem ]; then #use client cert if available
|
||||
openssl s_client -key /etc/xcat/certkey.pem -cert /etc/xcat/cert.pem -connect "$XCATDEST" -quiet 2> /dev/null < /tmp/destreq.xml > /tmp/destiny.xml
|
||||
else
|
||||
openssl s_client -connect "$XCATDEST" -quiet 2> /dev/null < /tmp/destreq.xml > /tmp/destiny.xml
|
||||
fi
|
||||
timer=60
|
||||
set -u
|
||||
|
||||
metadata_file=
|
||||
metadata_temporary=
|
||||
once=0
|
||||
destination=${XCATDEST:-}
|
||||
|
||||
usage() {
|
||||
printf '%s\n' 'Usage: nextdestiny [ENDPOINT] [--metadata FILE] [--once]' >&2
|
||||
exit 2
|
||||
}
|
||||
|
||||
while (($# > 0)); do
|
||||
case "$1" in
|
||||
--metadata)
|
||||
(($# >= 2)) || usage
|
||||
metadata_file=$2
|
||||
shift 2
|
||||
;;
|
||||
--once)
|
||||
once=1
|
||||
shift
|
||||
;;
|
||||
--)
|
||||
shift
|
||||
(($# <= 1)) || usage
|
||||
if (($# == 1)); then
|
||||
destination=$1
|
||||
fi
|
||||
break
|
||||
;;
|
||||
-*) usage ;;
|
||||
*)
|
||||
if [[ -z $destination ]]; then
|
||||
destination=$1
|
||||
fi
|
||||
shift
|
||||
;;
|
||||
esac
|
||||
done
|
||||
rm /tmp/destreq.xml
|
||||
DESTINY="$(grep '<destiny>' /tmp/destiny.xml | awk -F'>' '{print $2}'|awk -F'<' '{print $1}')"
|
||||
if [ -z "$DESTINY" ]; then
|
||||
ERROR="$(grep '<error>' /tmp/destiny.xml | awk -F'>' '{print $2}'|awk -F'<' '{print $1}')"
|
||||
if [ ! -z "$ERROR" ]; then
|
||||
DESTINY="error=$ERROR"
|
||||
else
|
||||
DESTINY="error=No destiny command received"
|
||||
fi
|
||||
|
||||
[[ -n $destination ]] || usage
|
||||
|
||||
temporary_dir=$(mktemp -d "${TMPDIR:-/tmp}/nextdestiny.XXXXXX") || exit 1
|
||||
request_file=$temporary_dir/request.xml
|
||||
response_file=$temporary_dir/response.xml
|
||||
trap 'rm -rf -- "$temporary_dir"; [[ -z $metadata_temporary ]] || rm -f -- "$metadata_temporary"' EXIT HUP INT TERM
|
||||
|
||||
printf '%s\n' \
|
||||
'<xcatrequest>' \
|
||||
'<command>nextdestiny</command>' \
|
||||
'<callback_port>300</callback_port>' \
|
||||
'</xcatrequest>' >"$request_file"
|
||||
|
||||
request_destiny() {
|
||||
if [[ -f /etc/xcat/cert.pem && -f /etc/xcat/certkey.pem ]]; then
|
||||
openssl s_client -key /etc/xcat/certkey.pem \
|
||||
-cert /etc/xcat/cert.pem -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file"
|
||||
else
|
||||
openssl s_client -connect "$destination" -quiet \
|
||||
2>/dev/null <"$request_file" >"$response_file"
|
||||
fi
|
||||
[[ -s $response_file ]] && \
|
||||
{ grep -q '<destiny>' "$response_file" || grep -q '<error>' "$response_file"; }
|
||||
}
|
||||
|
||||
while ! request_destiny; do
|
||||
if ((once)); then
|
||||
exit 1
|
||||
fi
|
||||
for ((timer = 60; timer > 0; timer--)); do
|
||||
printf 'Retrying in %s seconds \r' "$timer" >&2
|
||||
sleep 1
|
||||
done
|
||||
printf '%*s\r' 48 '' >&2
|
||||
done
|
||||
|
||||
extract_element() {
|
||||
local element=$1
|
||||
|
||||
sed -n "s:.*<$element>\([^<]*\)</$element>.*:\1:p" "$response_file" \
|
||||
| head -n 1 | tr -cd '\40-\176'
|
||||
}
|
||||
|
||||
destiny=$(extract_element destiny)
|
||||
if [[ -z $destiny ]]; then
|
||||
error=$(extract_element error)
|
||||
[[ -n $error ]] || error='No destiny command received'
|
||||
destiny="error=$error"
|
||||
fi
|
||||
rm /tmp/destiny.xml
|
||||
echo "$DESTINY"
|
||||
|
||||
if [[ -n $metadata_file ]]; then
|
||||
metadata_dir=${metadata_file%/*}
|
||||
if [[ $metadata_dir == "$metadata_file" ]]; then
|
||||
metadata_dir=.
|
||||
elif [[ -z $metadata_dir ]]; then
|
||||
metadata_dir=/
|
||||
fi
|
||||
[[ -d $metadata_dir ]] || exit 1
|
||||
metadata_temporary=$(mktemp "$metadata_dir/.nextdestiny.XXXXXX") || exit 1
|
||||
{
|
||||
printf 'XCAT_NODE_NAME=%s\n' "$(extract_element name)"
|
||||
printf 'XCAT_DESTINY=%s\n' "$destiny"
|
||||
printf 'XCAT_IMAGE_SERVER=%s\n' "$(extract_element imgserver)"
|
||||
printf 'XCAT_KERNEL=%s\n' "$(extract_element kernel)"
|
||||
printf 'XCAT_INITRD=%s\n' "$(extract_element initrd)"
|
||||
printf 'XCAT_KERNEL_COMMAND_LINE=%s\n' "$(extract_element kcmdline)"
|
||||
} >"$metadata_temporary"
|
||||
chmod 0600 "$metadata_temporary"
|
||||
mv -f -- "$metadata_temporary" "$metadata_file"
|
||||
fi
|
||||
|
||||
printf '%s\n' "$destiny"
|
||||
|
||||
Reference in New Issue
Block a user