Files
chrony/test/simulation/test.common
Miroslav Lichvar 75bbccf518 configure: make NTP and ASYNCDNS support nonoptional
Don't allow the NTP support and asynchronous name resolving to be
disabled. pthreads are now a hard requirement.

NTP is the primary task of chrony. This functionality doesn't seem to be
commonly disabled (allowing only refclocks and manual input).

This removes rarely (if ever) used code and simplifies testing.
2025-03-20 16:34:35 +01:00

558 lines
14 KiB
Plaintext

# Copyright (C) 2013-2014 Miroslav Lichvar <mlichvar@redhat.com>
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
export LC_ALL=C
export PATH=../../:$PATH
export CLKNETSIM_PATH=${CLKNETSIM_PATH:-clknetsim}
if [ ! -x $CLKNETSIM_PATH/clknetsim ]; then
echo "SKIP (clknetsim not found)"
exit 9
fi
. $CLKNETSIM_PATH/clknetsim.bash
# Default test testings
default_limit=10000
default_primary_time_offset=0.0
default_time_offset=1e-1
default_freq_offset=1e-4
default_base_delay=1e-4
default_delay_correction=""
default_jitter=1e-4
default_jitter_asymmetry=0.0
default_wander=1e-9
default_refclock_jitter=""
default_refclock_offset=0.0
default_update_interval=0
default_shift_pll=2
default_server_strata=1
default_servers=1
default_clients=1
default_peers=0
default_falsetickers=0
default_server_start=0.0
default_client_start=0.0
default_chronyc_start=1000.0
default_server_step=""
default_client_step=""
default_client_server_conf=""
default_server_server_options=""
default_client_server_options=""
default_server_peer_options=""
default_server_lpeer_options=""
default_server_rpeer_options=""
default_client_peer_options=""
default_client_lpeer_options=""
default_client_rpeer_options=""
default_server_conf=""
default_client_conf=""
default_chronyc_conf=""
default_server_chronyd_options=""
default_client_chronyd_options=""
default_chronyc_options=""
default_time_max_limit=1e-3
default_freq_max_limit=5e-4
default_time_rms_limit=3e-4
default_freq_rms_limit=1e-5
default_min_sync_time=120
default_max_sync_time=210
default_client_min_mean_out_interval=0.0
default_client_max_min_out_interval=inf
default_cmdmon_unix=1
default_pcap_dumps=0
default_dns=0
# Initialize test settings from their defaults
for defoptname in ${!default_*}; do
optname=${defoptname#default_}
[ -z "${!optname}" ] && declare "$optname"="${!defoptname}"
done
test_start() {
rm -rf tmp/*
echo "Testing $@:"
}
test_pass() {
echo "PASS"
exit 0
}
test_fail() {
echo "FAIL"
exit 1
}
test_skip() {
echo "SKIP"
exit 9
}
test_ok() {
pad_line
echo -e "\tOK"
return 0
}
test_bad() {
pad_line
echo -e "\tBAD"
return 1
}
test_error() {
pad_line
echo -e "\tERROR"
return 1
}
msg_length=0
pad_line() {
local line_length=56
[ $msg_length -lt $line_length ] && \
printf "%$[$line_length - $msg_length]s" ""
msg_length=0
}
# Print aligned message
test_message() {
local level=$1 eol=$2
shift 2
local msg="$*"
while [ $level -gt 0 ]; do
echo -n " "
level=$[$level - 1]
msg_length=$[$msg_length + 2]
done
echo -n "$msg"
msg_length=$[$msg_length + ${#msg}]
if [ $eol -ne 0 ]; then
echo
msg_length=0
fi
}
get_wander_expr() {
local scaled_wander
scaled_wander=$(awk "BEGIN {print $wander / \
sqrt($update_interval < 0 ? 2^-($update_interval) : 1)}")
echo "(+ $freq_offset (sum (* $scaled_wander (normal))))"
}
get_delay_expr() {
local direction=$1 asym
if [ $jitter_asymmetry == "0.0" ]; then
asym=""
elif [ $direction = "up" ]; then
asym=$(awk "BEGIN {print 1 - 2 * $jitter_asymmetry}")
elif [ $direction = "down" ]; then
asym=$(awk "BEGIN {print 1 + 2 * $jitter_asymmetry}")
fi
echo "(+ $base_delay (* $asym $jitter (exponential)))"
}
get_refclock_expr() {
echo "(+ $refclock_offset (* $refclock_jitter (normal)))"
}
get_chronyd_nodes() {
echo $[$servers * $server_strata + $clients]
}
get_node_name() {
local index=$1
if [ $dns -ne 0 ]; then
echo "node$index.net1.clk"
else
echo "192.168.123.$index"
fi
}
get_chronyd_conf() {
local i stratum=$1 peer=$2
if [ $stratum -eq 1 ]; then
echo "local stratum 1"
echo "$server_conf"
elif [ $stratum -le $server_strata ]; then
for i in $(seq 1 $servers); do
echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $server_server_options"
done
for i in $(seq 1 $peers); do
[ $i -eq $peer -o $i -gt $servers ] && continue
echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $server_peer_options "
[ $i -lt $peer ] && echo "$server_lpeer_options" || echo "$server_rpeer_options"
done
echo "$server_conf"
else
echo "deny"
if [ -n "$client_server_conf" ]; then
echo "$client_server_conf"
else
for i in $(seq 1 $servers); do
echo "server $(get_node_name $[$servers * ($stratum - 2) + $i]) $client_server_options"
done
fi
for i in $(seq 1 $peers); do
[ $i -eq $peer -o $i -gt $clients ] && continue
echo -n "peer $(get_node_name $[$servers * ($stratum - 1) + $i]) $client_peer_options "
[ $i -lt $peer ] && echo "$client_lpeer_options" || echo "$client_rpeer_options"
done
echo "$client_conf"
fi
}
# Check if chrony was built with specified option in config.h
check_config_h() {
local pattern=$1
grep -q "^#define $pattern" ../../config.h
}
# Check if the clock was well synchronized
check_sync() {
local i msg sync_time max_time_error max_freq_error r ret=0
local rms_time_error rms_freq_error
test_message 2 1 "checking clock sync time, max/rms time/freq error:"
for i in $(seq 1 $(get_chronyd_nodes)); do
[ $i -gt $[$servers * $server_strata] ] || continue
sync_time=$(find_sync tmp/log.offset tmp/log.freq $i \
$time_max_limit $freq_max_limit 1.0)
max_time_error=$(get_stat 'Maximum absolute offset' $i)
max_freq_error=$(get_stat 'Maximum absolute frequency' $i)
rms_time_error=$(get_stat 'RMS offset' $i)
rms_freq_error=$(get_stat 'RMS frequency' $i)
r=0
msg="$sync_time"
if ! check_stat $sync_time $min_sync_time $max_sync_time; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $max_time_error)"
if ! check_stat $max_time_error 0.0 $time_max_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $max_freq_error)"
if ! check_stat $max_freq_error 0.0 $freq_max_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $rms_time_error)"
if ! check_stat $rms_time_error 0.0 $time_rms_limit; then
msg+="!"
r=1
fi
msg+="$(printf ' %.2e' $rms_freq_error)"
if ! check_stat $rms_freq_error 0.0 $freq_rms_limit; then
msg+="!"
r=1
fi
test_message 3 0 "node $i: $msg"
[ $r -eq 0 ] || ret=1
[ $r -eq 0 ] && test_ok || test_bad
done
return $ret
}
# Check if chronyd exited properly
check_chronyd_exit() {
local i ret=0
test_message 2 1 "checking chronyd exit:"
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
grep -q 'chronyd exiting' tmp/log.$i && \
! grep -q 'Adjustment.*exceeds.*exiting' tmp/log.$i && \
! grep -q 'Assertion.*failed' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check for problems in source selection
check_source_selection() {
local i ret=0
test_message 2 1 "checking source selection:"
for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
! grep -q 'no majority\|no selectable sources' tmp/log.$i && \
grep -q 'Selected source' tmp/log.$i && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if incoming and outgoing packet intervals are sane
check_packet_interval() {
local i ret=0 mean_in_interval mean_out_interval min_in_interval min_out_interval
test_message 2 1 "checking mean/min incoming/outgoing packet interval:"
for i in $(seq 1 $(get_chronyd_nodes)); do
mean_in_interval=$(get_stat 'Mean incoming packet interval' $i)
mean_out_interval=$(get_stat 'Mean outgoing packet interval' $i)
min_in_interval=$(get_stat 'Minimum incoming packet interval' $i)
min_out_interval=$(get_stat 'Minimum outgoing packet interval' $i)
test_message 3 0 "node $i: $(printf '%.2e %.2e %.2e %.2e' \
$mean_in_interval $mean_out_interval $min_in_interval $min_out_interval)"
# Check that the mean intervals are non-zero and shorter than
# limit, incoming is not longer than outgoing for stratum 1
# servers, outgoing is not longer than incoming for clients,
# and the minimum outgoing interval is not shorter than the NTP
# sampling separation or iburst interval for clients
nodes=$[$servers * $server_strata + $clients]
check_stat $mean_in_interval 0.1 inf && \
check_stat $mean_out_interval 0.1 inf && \
([ $i -gt $servers ] || \
check_stat $mean_in_interval 0.0 $mean_out_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $mean_out_interval $client_min_mean_out_interval \
$mean_in_interval 10*$jitter) && \
([ $i -le $[$servers * $server_strata] ] || \
check_stat $min_out_interval \
$([ $servers -gt 1 ] && echo 0.18 || echo 1.8) \
$client_max_min_out_interval) && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Compare chronyc output with specified pattern
check_chronyc_output() {
local i ret=0 pattern=$1
test_message 2 1 "checking chronyc output:"
for i in $(seq $[$(get_chronyd_nodes) + 1] $[$(get_chronyd_nodes) + $clients]); do
test_message 3 0 "node $i:"
[[ "$(cat tmp/log.$i)" =~ $pattern ]] && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check the number of messages matching a pattern in the client logs
check_log_messages() {
local i count ret=0 pattern=$1 min=$2 max=$3
test_message 2 1 "checking number of messages \"$pattern\":"
for i in $(seq $[$servers * $server_strata + 1] $(get_chronyd_nodes)); do
count=$(grep "$pattern" tmp/log.$i | wc -l)
test_message 3 0 "node $i: $count"
[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check the number of messages matching a pattern in a specified file
check_file_messages() {
local i count ret=0 pattern=$1 min=$2 max=$3
shift 3
test_message 2 1 "checking number of messages \"$pattern\":"
for i; do
count=$(grep "$pattern" tmp/$i | wc -l)
test_message 3 0 "$i: $count"
[ "$min" -le "$count" ] && [ "$count" -le "$max" ] && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Check if only NTP port (123) was used
check_packet_port() {
local i ret=0 port=123
test_message 2 1 "checking port numbers in packet log:"
for i in $(seq 1 $(get_chronyd_nodes)); do
test_message 3 0 "node $i:"
grep -E -q "^([0-9e.+-]+ ){5}$port " tmp/log.packets && \
! grep -E "^[0-9e.+-]+ $i " tmp/log.packets | \
grep -E -q -v "^([0-9e.+-]+ ){5}$port " && \
test_ok || test_bad
[ $? -eq 0 ] || ret=1
done
return $ret
}
# Print test settings which differ from default value
print_nondefaults() {
local defoptname optname
test_message 2 1 "non-default settings:"
for defoptname in ${!default_*}; do
optname=${defoptname#default_}
[ "${!defoptname}" = "${!optname}" ] || \
test_message 3 1 $optname=${!optname}
done
}
run_simulation() {
local nodes=$1
test_message 2 0 "running simulation:"
start_server $nodes \
-n 2 \
-o tmp/log.offset -f tmp/log.freq -p tmp/log.packets \
-R $(awk "BEGIN {print $update_interval < 0 ? 2^-($update_interval) : 1}") \
-r $(awk "BEGIN {print $max_sync_time * 2^$update_interval}") \
-l $(awk "BEGIN {print $limit * 2^$update_interval}") && test_ok || test_error
}
run_test() {
local i j n stratum node nodes step start freq offset conf options
test_message 1 1 "network with $servers*$server_strata servers and $clients clients:"
print_nondefaults
nodes=$(get_chronyd_nodes)
[ -n "$chronyc_conf" ] && nodes=$[$nodes + $clients]
export CLKNETSIM_UNIX_SUBNET=$[$cmdmon_unix != 0 ? 2 : 0]
for i in $(seq 1 $nodes); do
echo "node${i}_shift_pll = $shift_pll"
for j in $(seq 1 $nodes); do
echo "node${i}_delay${j} = $(get_delay_expr up)"
echo "node${j}_delay${i} = $(get_delay_expr down)"
if [ -n "$delay_correction" ]; then
echo "node${i}_delay_correction${j} = $delay_correction"
echo "node${j}_delay_correction${i} = $delay_correction"
fi
done
done > tmp/conf
node=1
for stratum in $(seq 1 $[$server_strata + 1]); do
[ $stratum -le $server_strata ] && n=$servers || n=$clients
for i in $(seq 1 $n); do
test_message 2 0 "starting node $node:"
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
if [ $stratum -eq 1 ]; then
step=$server_step
start=$server_start
freq=""
[ $i -le $falsetickers ] &&
offset=$i.0 || offset=$primary_time_offset
options=$server_chronyd_options
elif [ $stratum -le $server_strata ]; then
step=$server_step
start=$server_start
freq=$(get_wander_expr)
offset=0.0
options=$server_chronyd_options
else
step=$client_step
start=$client_start
freq=$(get_wander_expr)
offset=$time_offset
options=$client_chronyd_options
fi
conf=$(get_chronyd_conf $stratum $i $n)
[ -z "$freq" ] || echo "node${node}_freq = $freq" >> tmp/conf
[ -z "$step" ] || echo "node${node}_step = $step" >> tmp/conf
[ -z "$refclock_jitter" ] || \
echo "node${node}_refclock = $(get_refclock_expr)" >> tmp/conf
echo "node${node}_offset = $offset" >> tmp/conf
echo "node${node}_start = $start" >> tmp/conf
start_client $node chronyd "$conf" "" "$options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1
node=$[$node + 1]
done
done
for i in $(seq 1 $[$nodes - $node + 1]); do
test_message 2 0 "starting node $node:"
[ $pcap_dumps -ne 0 ] && export CLKNETSIM_PCAP_DUMP=tmp/pcap.$node
options=$([ $dns -eq 0 ] && printf "%s" "-n")
if [ $cmdmon_unix -ne 0 ]; then
options+=" -h /clknetsim/unix/$[$node - $clients]:1"
else
options+=" -h $(get_node_name $[$node - $clients])"
fi
echo "node${node}_start = $chronyc_start" >> tmp/conf
start_client $node chronyc "$chronyc_conf" "" "$options $chronyc_options" && \
test_ok || test_error
[ $? -ne 0 ] && return 1
node=$[$node + 1]
done
run_simulation $nodes
}