Last active
September 3, 2025 20:38
-
-
Save RobertKrawitz/88b43e755d43002ccf88a96976f9c865 to your computer and use it in GitHub Desktop.
Find one unused disk on each OCP node supporting storage for use in e. g. creating a LocalVolumeSet
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/bin/bash | |
declare args=() | |
declare nodes= | |
declare -i verbose=0 | |
declare label=cluster.ocs.openshift.io/openshift-storage | |
read -r -d '' script <<'EOF' | |
#!/bin/bash | |
declare -a sata_drives | |
declare -a nvme_drives | |
declare -i minsize=1500 | |
declare -i maxsize=0 | |
declare -i doit=0 | |
declare -i verbose=0 | |
declare devdir= | |
declare -i all_drives=0 | |
declare -i status=0 | |
declare -i found=0 | |
help() { | |
if [[ -z "$*" ]] ; then echo "Usage: $0 args"; fi | |
cat <<EOD | |
-a Check for all matching drives | |
-t Print the by-path name of the drive | |
-i Print a by-id name of the drive | |
-s Check for matching SATA drives | |
-S Don't check for SATA drives | |
-p Check for matching NVMe drives | |
-P Don't check for NVMe drives | |
-z Zap the found drive | |
-Z Print the command, but don't zap the drive | |
-n Only print the name of the found drive | |
-v Print verbose debugging messages | |
-m minsize Only look at drives at least as big as minsize GB | |
-M maxsize Only look at drives no bigger than maxsize GB | |
EOD | |
exit 1 | |
} | |
if [[ -n "${___HELP___}" ]] ; then | |
help 1 | |
fi | |
while getopts 'sSpPvzZnm:M:tiah' opt ; do | |
case "$opt" in | |
s) sata_drives=(/dev/sd?) ;; | |
S) sata_drives=() ;; | |
p) nvme_drives=(/dev/nvme?n1) ;; | |
P) nvme_drives=() ;; | |
m) minsize=$OPTARG ;; | |
M) maxsize=$OPTARG ;; | |
z) doit=2 ;; | |
Z) doit=1 ;; | |
n) doit=0 ;; | |
t) devdir=by-path ;; | |
i) devdir=by-id ;; | |
a) all_drives=1 ;; | |
v) verbose=1 ;; | |
h) help ;; | |
*) | |
echo "Unknown option $opt" | |
help | |
;; | |
esac | |
done | |
shift $((OPTIND-1)) | |
docmd() { | |
echo "$*" 1>&2 | |
if ((doit > 1)) ; then | |
#"$@" | |
return $! | |
fi | |
} | |
print_drive() { | |
local dev=$1 | |
if [[ -n "${devdir:-}" ]] ; then | |
local path | |
for path in /dev/disk/"$devdir"/* ; do | |
if [[ $(readlink -f "$path") = "$dev" ]] ; then | |
echo "$path" | |
break | |
fi | |
done | |
else | |
echo "$dev" | |
fi | |
} | |
doit() { | |
local dev="${1:-}" | |
if [[ ! -b "$dev" ]] ; then | |
echo "$dev: not block device" | |
exit 1 | |
fi | |
case "$((doit))" in | |
0) print_drive "$f" ;; | |
*) docmd podman run -authfile /var/lib/kubelet --rm --privileged --device "$f" --entrypoint ceph-bluestore-tool quay.io/ceph/ceph:v19 zap-device --dev "$f" --yes-i-really-really-mean-it | |
esac | |
} | |
vecho() { | |
if ((verbose)) ; then echo "$*" 1>&2 ; fi | |
} | |
vecho "> Starting search on $(hostname)" | |
for f in "${sata_drives[@]}" "${nvme_drives[@]}" ; do | |
vecho ">> Looking at $f" | |
vecho ">>> Checking for mounted drive" | |
mountpoints="$(lsblk -J "$f" | jq -r '.blockdevices[0]?.mountpoints[]? | select(. != null)')" | |
if [[ -n "$mountpoints" ]] ; then | |
# shellcheck disable=SC2086 | |
vecho ">>>> SKIPPING: $f already mounted: " $mountpoints | |
continue | |
fi | |
vecho ">>> Checking for mounted partitions" | |
cmountpoints="$(lsblk -J "$f" | jq -r '[.blockdevices[0]?.children[]?.mountpoints[]?] | flatten[] | select(. != null)')" | |
if [[ -n "$cmountpoints" ]] ; then | |
# shellcheck disable=SC2086 | |
vecho ">>>> SKIPPING: $f has mounted partitions: " $cmountpoints | |
continue | |
fi | |
vecho ">>> Checking for unmounted partitions" | |
children="$(lsblk -J "$f" | jq -r '.blockdevices[0]?.children[]?.name | select (. != null)')" | |
if [[ -n "$children" ]] ; then | |
# shellcheck disable=SC2086 | |
vecho ">>>> SKIPPING: $f has unmounted partitions: " $children | |
continue | |
fi | |
# If we immediately continued after finding a used disk | |
# the find would get a broken pipe, which is ugly | |
# albeit harmless | |
failed=0 | |
if [[ -d /mnt/local-storage ]] ; then | |
while read -r l ; do | |
vecho ">>> Looking at local storage entry $l ($(readlink -f "$l"))" | |
if [[ $(readlink -f "$l") = "$f" ]] ; then | |
vecho ">>>> SKIPPING: $f is target of $l ($(readlink -f "$l"))" | |
failed=1 | |
fi | |
done <<< "$(sudo find /mnt/local-storage -type l -print)" | |
if ((failed)) ; then continue; fi | |
fi | |
size="$(lsblk -J "$f" | jq -r '.blockdevices[0].size')" | |
if [[ $size =~ ([0-9]+(\.[0-9]+)?)([KMGTP])? ]] ; then | |
dsize=${BASH_REMATCH[1]} | |
suffix=${BASH_REMATCH[3]} | |
case "$suffix" in | |
K) scale=1000 ;; | |
M) scale=$((1000 * 1000)) ;; | |
G) scale=$((1000 * 1000 * 1000)) ;; | |
T) scale=$((1000 * 1000 * 1000 * 1000)) ;; | |
P) scale=$((1000 * 1000 * 1000 * 1000 * 1000)) ;; | |
*) continue ;; | |
esac | |
bytes=$(python3 <<< "print('%d' % ($dsize * $scale))") | |
vecho ">>> Checking size" | |
if ((bytes < minsize * 1000 * 1000 * 1000 || (maxsize > 0 && bytes > maxsize * 1000 * 1000 * 1000))) ; then | |
vecho ">>>> SKIPPING: $dsize$suffix does not match $minsize < $bytes <= $maxsize" | |
continue | |
fi | |
vecho ">> $f passes all checks" | |
if ((all_drives)) ; then | |
doit "$f" | |
case "$!" in | |
0) found=$((found+1)) ;; | |
*) status=1 ;; | |
esac | |
else | |
doit "$f" | |
exit $! | |
fi | |
fi | |
done | |
exit "$status" | |
EOF | |
help() { | |
cat <<EOD | |
Usage: $0 <args> <nodes> | |
Nodes default to all nodes labeled 'cluster.ocs.openshift.io/openshift-storage' | |
$(___HELP___=1; eval "$script") | |
-l label Use only nodes with the specified label. | |
EOD | |
exit | |
} | |
while getopts 'm:M:l:abcdefghijknopqrstuvwxyzABCDEFGHIJKLNOPQRSTUVWXYZ' opt ; do | |
case "$opt" in | |
m|M) args+=("-$opt" "$OPTARG") ;; | |
l) label=$OPTARG ;; | |
v) verbose=1; args+=("-$opt");; | |
h|H) help ;; | |
*) args+=("-$opt") ;; | |
esac | |
done | |
doit() { | |
if ((verbose)) ; then echo "$*" 1>&2; fi | |
"$@" | |
} | |
shift $((OPTIND-1)) | |
if [[ -n "$*" ]] ; then | |
nodes=("$@") | |
else | |
readarray -t nodes <<< "$(doit oc get node --no-headers -l "$label" | awk '{print $1}')" | |
fi | |
for node in "${nodes[@]}" ; do | |
# shellcheck disable=SC2086 | |
# shellcheck disable=SC2029 | |
if ((verbose)) ; then echo 1>&2; fi | |
disk=$(doit ssh -o StrictHostKeyChecking=no "core@$node" "cat > /tmp/diskfinder && chmod +x /tmp/diskfinder && /tmp/diskfinder ${args[*]}" <<< "$script") | |
if [[ -n "$disk" ]] ; then | |
printf "%s %s\n" "$node" "$disk" | |
else | |
echo "Can't find usable disk on $node" 1>&2 | |
fi | |
done |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment