Files
Tobias Waldekranz 5d6180a395 yanger: Improve option names and validation after review
Break up the --test parameter into either:

--capture: Write system command output to a local directory for later
           consumption by...
--replay:  Which can replay a previous capture

Rename --wrap-commands => --cmd-prefix
2025-01-22 09:44:52 +01:00

334 lines
5.3 KiB
Bash

set -e
usage()
{
local me="$(basename $0)"
cat <<EOF
usage: $me [<command>]
Verify that yanger produces the expected operational data for
$yang_models, and that cli_pretty produces the expected output for
$cli_commands.
Commands:
If no command is specified it defaults to "check"
gen
(Re)generate system state, operational.json and expected
cli_pretty output based on $gen_test
check
Check yanger and CLI output
yanger gen
Run $gen_test and capture the system status from the Infix
device behind $gen_iface
yanger check
Verify that yanger produces the operational data previously
recorded in operational.json
yanger live
Produce the operational data for $yang_models from the Infix
device behind $gen_iface, and write it to stdout
cli gen
Update the expected output of $cli_commands from the current
operational.json
cli check
Verify that cli_pretty produces the expected output for
$cli_commands, based on the current operational.json
EOF
}
casedir=$(readlink -f $(dirname $0))
ixdir=$(readlink -f $casedir/../../../../)
YANGER=${YANGER:-$ixdir/src/statd/python/yanger/yanger}
CLI=${CLI:-$ixdir/src/statd/python/cli_pretty/cli_pretty.py}
wrapper="$ixdir/utils/ixll -A ssh $gen_iface sudo"
# Make sure topology matchings are stable since we capture system
# states by referencing a _physical_ interface, which must match the
# expected _logical_ node.
export PYTHONHASHSEED=0
exitcode=0
n_steps=0
step()
{
local status="$1"
shift
if [ "$status" != "ok" ]; then
exitcode=1
fi
n_steps=$((n_steps+1))
echo "$status $n_steps - $*"
}
plan()
{
echo "$n_steps..$n_steps"
exit $exitcode
}
prepare_infamy_test()
{
$ixdir/test/case/meta/wait.py && \
$ixdir/test/case/$1
}
yanger_exec()
{
local opts=
while getopts "c:r:x:" opt; do
case ${opt} in
c|r|x)
opts="$opts -$opt \"$OPTARG\""
;;
esac
done
shift $((OPTIND - 1))
eval "$YANGER" $opts "$1"
}
yanger_gen()
{
local operfiles=
if type gen_exec &>/dev/null; then
gen_exec
return
fi
if ! [ "$gen_test" ] && [ "$gen_iface" ]; then
echo "!!! Not implemented" >&2
false
fi
echo ">>> RUNNING $gen_test" >&2
prepare_infamy_test "$gen_test"
set $yang_models
while [ $# -gt 0 ]; do
echo ">>> CAPTURING $1 from $gen_iface" >&2
yanger_exec \
-c "$casedir/system" \
-x "$wrapper" \
$1 \
>"$casedir/$1.json"
operfiles="$operfiles $casedir/$1.json"
echo ">>> OK" >&2
shift
done
echo ">>> GENERATING operational.json" >&2
cat $operfiles | jq --slurp --sort-keys \
'reduce .[] as $item ({}; . * $item)' \
>"$casedir/operational.json"
echo ">>> OK" >&2
}
yanger_check()
{
local status=
local diff=$(mktemp)
set $yang_models
while [ $# -gt 0 ]; do
status="ok"
if ! diff -up \
"$casedir/$1.json" \
<(yanger_exec -r "$casedir/system" $1) \
>"$diff"; then
cat $diff | sed 's/^/# /'
status="not ok"
fi
rm $diff
step "$status" \
"yanger output of \"$1\" matches $1.json"
shift
done
}
yanger_cat()
{
if [ $# -eq 0 ]; then
set $yang_models
fi
while [ $# -gt 0 ]; do
yanger_exec -r "$casedir/system" $1
shift
done
}
yanger_live()
{
if [ $# -eq 0 ]; then
set $yang_models
fi
while [ $# -gt 0 ]; do
yanger_exec -x "$wrapper" $1
shift
done
}
yanger()
{
local cmd=check
if [ $# -gt 0 ]; then
cmd="$1"
shift
fi
case "$cmd" in
cat|gen|live)
"yanger_$cmd" "$@"
return
;;
check)
yanger_check "$@"
plan
esac
false
}
cli_exec()
{
$CLI -t $(echo "$1" | tr '_' ' ')
}
cli_gen()
{
mkdir -p "$casedir/cli"
set $cli_commands
while [ $# -gt 0 ]; do
cli_exec "$1" <"$casedir/operational.json" >"$casedir/cli/$1"
shift
done
}
cli_check()
{
local diff=
local status=
set $cli_commands
while [ $# -gt 0 ]; do
diff="$(mktemp)"
status="ok"
if ! diff -up \
"$casedir/cli/$1" \
<(cli_exec "$1" <"$casedir/operational.json") \
>"$diff"; then
cat $diff | sed 's/^/# /'
status="not ok"
fi
rm $diff
step "$status" \
"cli output of \"$1\" matches cli/$1"
shift
done
}
cli_cat()
{
if [ $# -eq 0 ]; then
set $cli_commands
fi
while [ $# -gt 0 ]; do
echo ">>> Running $1" >&2
cli_exec "$1" <"$casedir/operational.json"
shift
done
}
cli_live()
{
local json=$(mktemp)
echo ">>> Capturing live state" >&2
yanger_live >$json
echo ">>> OK" >&2
if [ $# -eq 0 ]; then
set $cli_commands
fi
while [ $# -gt 0 ]; do
echo ">>> Running $1" >&2
cli_exec "$1" <"$casedir/cli/$1"
shift
done
rm $json
}
cli()
{
local cmd=check
if [ $# -gt 0 ]; then
cmd="$1"
shift
fi
case "$cmd" in
cat|gen|live)
"cli_$cmd" "$@"
return
;;
check)
cli_check "$@"
plan
;;
esac
false
}
main()
{
local cmd=check
if [ $# -gt 0 ]; then
cmd="$1"
shift
fi
case $cmd in
gen)
yanger gen && cli gen
;;
check)
yanger_check && cli_check
plan
;;
yanger|cli)
"$cmd" "$@"
;;
-h|--help|help)
usage
;;
*)
echo "!!! Unknown command \"$cmd\"" >&2
usage >&2
exit 1
;;
esac
}