blob: 34cd4c33963b92e2a8b3c6e3a277070aa1bf0a4b [file] [log] [blame]
#!/bin/bash
cd $(dirname $(readlink -f $0))/..
DRYRUN=echo
NUM=
SPARES=suites
HELP=0
while getopts 'hn:xs:' OPT; do
case $OPT in
n) NUM=$OPTARG ;;
x) DRYRUN= ;;
s) SPARES=$OPTARG ;;
\?|h) HELP=1 ;;
esac
done
shift $(( OPTIND - 1 ))
if [ $HELP -ne 0 -o $# -ne 2 ]; then
echo "usage: $(basename $0) [options] <board> <pool>" >&2
echo "Options:" >&2
echo " -n #: adjust pool size to #" >&2
echo " -x: execute changes (default is to print changes only)" >&2
echo " -s <spares>: specify pool for spares (default: suites)" >&2
exit 1
fi
# List atest information for all DUTs in a pool.
#
# Arguments: The name of a pool
# Standard output: `atest host list` output for the given pool, with
# the initial header line removed.
list_pool() {
cli/atest host list -w cautotest -b pool:$1,board:$BOARD | sed 1d
}
# Find all DUTs in a pool
#
# Arguments: The name of a pool
# Standard output: All DUTs in the pool with the user-specified
# board.
list_duts() {
list_pool $1 | awk '{ print $1 }'
}
# Find DUTs in a pool and not shared with any other pool.
#
# Arguments: The name of a pool
# Standard output: All DUTs that are in that pool, and in no other.
list_unshared_duts() {
list_pool $1 | awk '$0 !~ /pool:.*pool:/ {print $1}'
}
# Find all working DUTs in a list
#
# Arguments: A list of DUTs
# Standard output: The subset of given DUTs that are working
filter_working() {
python site_utils/dut_status.py "$@" | awk '/OK/ {print $1}'
}
# Exchange a list of DUTs between two pools
#
# Arguments:
# 1: Pool to be augmented with DUTs
# 2: Pool from which to take the DUTs
# remainder are DUTs to be exchanged
#
# For a dry run, this function prints the exchange commands rather
# than executing them.
exchange() {
local to=$1
local from=$2
shift 2
[ $# -gt 0 ] || return 0
local list=$(echo "$@" | sed 's/ /,/g')
$DRYRUN cli/atest label remove -m $list pool:$from
$DRYRUN cli/atest label add -m $list pool:$to
}
BOARD=$1
POOL=$2
POOL_ALL=( $(list_duts $POOL) )
POOL_WORKING=( $(filter_working "${POOL_ALL[@]}") )
NWORKING=${#POOL_WORKING[@]}
if [ -z "$NUM" ]; then
NUM=${#POOL_ALL[@]}
fi
REMOVE=()
REPLACEMENTS=()
# OK, command line parsing and setup are done. We're operating in
# three steps here.
#
# Step 1. Find all the broken devices. These will be the first we
# remove from $POOL. Accumulate devices to be removed in the array
# `REMOVE`.
#
widx=0
tidx=0
while [ $tidx -lt ${#POOL_ALL[@]} ]; do
if [ "${POOL_ALL[$tidx]}" != "${POOL_WORKING[$widx]}" ]; then
REMOVE=( "${REMOVE[@]}" ${POOL_ALL[$tidx]} )
else
widx=$(( widx + 1 ))
fi
tidx=$(( tidx + 1 ))
done
# Step 2. Determine whether we have enough working devices, and what
# replacements, if any, we'll take.
#
if [ $NWORKING -gt $NUM ]; then
# Step 2a. More working devices than we need. Add excess devices
# into the `REMOVE` array.
COUNT=$(( $NWORKING - $NUM - 1 ))
for i in $(seq 0 $COUNT); do
REMOVE=( "${REMOVE[@]}" ${POOL_WORKING[$i]} )
done
elif [ $NWORKING -lt $NUM ]; then
# Step 2b. Not enough working devices; try to find spares.
# Accumulate available spares into the `REPLACEMENTS` array.
NEED=$(( NUM - NWORKING ))
REPLACEMENTS=( $(filter_working $(list_unshared_duts $SPARES)) )
if [ $NEED -gt ${#REPLACEMENTS[@]} ]; then
# Step 2b-1. Not enough working spares. Print a warning, then
# make sure we only remove as many broken devices as we have
# spares to replace them.
if [ ${#REPLACEMENTS[@]} -ne 0 ]; then
SPARE_MSG="only ${#REPLACEMENTS[@]}"
else
SPARE_MSG="no"
fi
echo "WARN: Board $BOARD, $SPARE_MSG spares for $NEED needed DUTs" >&2
while [ ${#REMOVE[@]} -gt ${#REPLACEMENTS[@]} ]; do
unset REMOVE[$(( ${#REMOVE[@]} - 1 ))]
done
else
# Step 2b-2. We have enough working spares. Remove excess spares.
while [ ${#REPLACEMENTS[@]} -gt $NEED ]; do
unset REPLACEMENTS[$(( ${#REPLACEMENTS[@]} - 1 ))]
done
fi
fi
# Step 3. If it's a dry run, report some information. Then,
# execute the pool exchanges.
#
# Make the dry run messages look like comments so that the output of
# this command is acceptable as input to the shell.
#
if [ -n "$DRYRUN" ]; then
echo "# targeting $NUM working DUTs in $POOL; currently have $NWORKING"
echo "# ${#REMOVE[@]} removals; ${#REPLACEMENTS[@]} spares"
for h in "${REMOVE[@]}"; do
echo "# remove $h"
done
for h in "${REPLACEMENTS[@]}"; do
echo "# spare $h"
done
fi
exchange $SPARES $POOL "${REMOVE[@]}"
exchange $POOL $SPARES "${REPLACEMENTS[@]}"