From dd48a579485bbe294818fbc1b67e3de4bafff24b Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Mon, 12 Jan 2026 11:23:28 +0000 Subject: [PATCH 1/6] feat: add script to check which euclids a program is installed on --- tools/check_installed.sh | 56 ++++++++++++++++++++++++++++++++++++++++ 1 file changed, 56 insertions(+) create mode 100755 tools/check_installed.sh diff --git a/tools/check_installed.sh b/tools/check_installed.sh new file mode 100755 index 0000000..f2b0063 --- /dev/null +++ b/tools/check_installed.sh @@ -0,0 +1,56 @@ +#!/bin/bash + +UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" +RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " +SLURM_SERVERS="13 14 15 16 20 31 33" +RETIRED_SERVERS="11 34" + +SERVERS=$UNRESTRICTED_SERVERS + +cmdToCheck=$1 + +while true; do + read -p "Check whether command '$cmdToCheck' exists on the euclid servers? Y/n " yn + case $yn in + [Yy]* ) break;; + [Nn]* ) exit;; + * ) echo "Please answer Y/n.";; + esac +done + +# Run command & save output to temp directory +tmpdir=$(mktemp -d) +cmd="command -v $cmdToCheck" # Command to run on each euclid +for euclid in $SERVERS; do + echo $(if out=$(ssh "euclid-$euclid" -q -o RemoteCommand="$cmd" 2>&1); then + echo "$out" + else + echo "Not installed" + fi + ) > $tmpdir/euclid-$euclid & +done + +# Display outputs: +n_servers=$(echo $SERVERS | wc --words) +while :; do + done_count=0 + for euclid in $SERVERS; do + if [[ -f "$tmpdir/euclid-$euclid" ]]; then + printf "euclid-$euclid: %s\n" "$(tr '\n' ' ' < $tmpdir/euclid-$euclid)" + ((done_count++)) + else + printf "euclid-$euclid:\n" + fi + done + + # Break if all servers have responded + ((done_count == n_servers)) && break + + # Move cursor back to top + printf "\033[${n_servers}A" + + # Wait before re-checking + sleep 0.2 +done + +rm -r "$tmpdir" From b3a571111ac74ee1e5619bedfc378c431f6512eb Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Mon, 12 Jan 2026 11:23:42 +0000 Subject: [PATCH 2/6] feat: add script to report live usage of all euclids --- tools/usage.sh | 52 ++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 52 insertions(+) create mode 100755 tools/usage.sh diff --git a/tools/usage.sh b/tools/usage.sh new file mode 100755 index 0000000..e191bea --- /dev/null +++ b/tools/usage.sh @@ -0,0 +1,52 @@ +#!/bin/bash + +UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" +RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " +SLURM_SERVERS="13 14 15 16 20 31 33" +RETIRED_SERVERS="11 34" + +SERVERS=$UNRESTRICTED_SERVERS + +# Run command & save output to temp directory +tmpdir=$(mktemp -d) +run_cmd(){ + ssh "euclid-$euclid" -q -o RemoteCommand="$1" +} +for euclid in $SERVERS; do + run_cmd "top -bn 5 -d 0.1 -E M" | grep -e '^%Cpu' -e '^MiB Mem' | tail -n 2 > $tmpdir/euclid-$euclid-cpu-mem & + run_cmd "lscpu" > $tmpdir/euclid-$euclid-lscpu & + run_cmd "if [[ \$(command -v nvidia-smi) ]]; then nvidia-smi --query-gpu=utilization.gpu --format=csv,noheader,nounits; else echo '-1'; fi" > $tmpdir/euclid-$euclid-gpu & # Saves each GPU percentage to a newline +done + +# Display outputs: +n_servers=$(echo $SERVERS | wc --words) +echo "Total CPU Usage; Total Mem Usage; Physical(Logical) Cores; Total Mem; GPU Utilisation per GPU" +while :; do + done_count=0 + for euclid in $SERVERS; do + if [[ -s "$tmpdir/euclid-$euclid-cpu-mem" && -s "$tmpdir/euclid-$euclid-lscpu" && -s "$tmpdir/euclid-$euclid-gpu" ]]; then + cpu_usage=$(cat $tmpdir/euclid-$euclid-cpu-mem | grep -e '^%Cpu' | awk -F'[, ]+' '{print $2+$4+$6}') + mem_usage=$(cat $tmpdir/euclid-$euclid-cpu-mem | grep -e '^MiB Mem' | awk -F'[, ]+' '{print $8*100/$4}') + mem_size=$(cat $tmpdir/euclid-$euclid-cpu-mem | grep -e '^MiB Mem' | awk -F'[, ]+' '{print $4/1024}') + cores=$(cat $tmpdir/euclid-$euclid-lscpu | grep -e '^Core(s) per socket' -e 'Socket(s)' | paste -s | awk '{print $4*$6}') + threads=$(cat $tmpdir/euclid-$euclid-lscpu | grep '^CPU(s)' | awk '{print $2}') + gpu_file=$(cat $tmpdir/euclid-$euclid-gpu) + gpu_string=$(if [[ $gpu_file == "-1" || $gpu_file == *failed* ]]; then echo ""; else printf " %s%% GPU" "$(echo $gpu_file | tr ' ' ', ')"; fi) + printf "euclid-$euclid: %5.1f%% CPU %5.1f%% Mem %-3d(%-3d) Cores %4.0f GiB%s\n" $cpu_usage $mem_usage $cores $threads $mem_size "$gpu_string" + ((done_count++)) + else + printf "euclid-$euclid:\n" + fi + done + + # Break if all servers have responded + ((done_count == n_servers)) && break + + # Move cursor back to top + printf "\033[${n_servers}A" + + # Wait before re-checking + sleep 0.5 +done + +rm -r "$tmpdir" From 06fc8b2054d8b4a891ffed2fb9e2d4882329ee3b Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Mon, 12 Jan 2026 12:07:06 +0000 Subject: [PATCH 3/6] fix: prevent running check_installed with no command --- tools/check_installed.sh | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/tools/check_installed.sh b/tools/check_installed.sh index f2b0063..1240297 100755 --- a/tools/check_installed.sh +++ b/tools/check_installed.sh @@ -1,4 +1,5 @@ #!/bin/bash +# Uses "command" to check whether a command exists on each of the Euclids. UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " @@ -9,6 +10,12 @@ SERVERS=$UNRESTRICTED_SERVERS cmdToCheck=$1 +if [[ -z $cmdToCheck ]]; then + echo "No command to check installation status specified, exiting." + echo "Usage: ./check_installed.sh " + exit +fi + while true; do read -p "Check whether command '$cmdToCheck' exists on the euclid servers? Y/n " yn case $yn in From 9696d50abac6ee044493650e90a8ee8373f9f3ec Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Tue, 13 Jan 2026 15:13:52 +0000 Subject: [PATCH 4/6] docs: add more detailed descriptions of helper scripts --- README.md | 2 +- tools/README.md | 15 ++++++++++++++- tools/check_installed.sh | 3 ++- tools/usage.sh | 2 ++ 4 files changed, 19 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index 211bfb0..5e73b2a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The overall structure is as follow: 2. RServer&CommandlineScreen - A guide to using RServer and the Commandline 3. PureCommandLine - A guide to using pure commandline 4. Slurm - A guide to using our job scheduler. -5. Tools - Contains script for adding SSH aliases for Euclid servers. +5. Tools - Student-written tools for: adding SSH aliases for Euclid servers, checking live usage, checking a program is installed etc. 6. BasicCommands - A collection of simple commands for connecting to the servers. ## Command line use. diff --git a/tools/README.md b/tools/README.md index 5ba79d2..edbbb32 100644 --- a/tools/README.md +++ b/tools/README.md @@ -1,5 +1,7 @@ # Euclid Tools -This folder contains command line tools useful for working with the Euclids. +This folder contains student-written command line tools which you may find useful for working with the Euclids. + +Use at your own risk, and always be careful not to cause any disruption to other users or access machines which you do not have permission to use. The lists of restricted-access and general-use Euclid machines on which these scripts rely were correct as of January 2026. ## Create SSH Aliases To prevent you having to type out `@euclid-.maths.gla.ac.uk` every time, aliases for servers can be added to your SSH config file, usually located at `~/.ssh/config`. @@ -14,3 +16,14 @@ Host euclid-01 # Alias of your choice For Unix/WSL/MacOS, these aliases can be added to your SSH config file using the script located at `tools/euclid-ssh-aliases/create-euclid-aliases.sh`. Use `create-euclid-aliases.sh -h` for help menu. By default aliases will only be created for Euclids with unrestricted access. For Windows, copy the config file you wish to use, use a find-and-replace tool to replace `` with your username, and copy into `~/.ssh/config` (where `~/` specifies your home folder on your machine). + +## Check Installed & Usage +These helper scripts check all public Euclids for the existence of a particular command (check_installed.sh) or the live CPU/Memory/GPU utilisation (usage.sh). + +These can be modified to include other Euclids to which you have access by modifying the "SERVERS" variable in each script. + +To add aliases for these scripts, you can add the following to your `~/.bashrc` file: +```bash +alias euclid-usage='./path/to/ServerInfo/tools/usage.sh' +alias euclid-installed='./path/to/ServerInfo/tools/check_installed.sh' +``` diff --git a/tools/check_installed.sh b/tools/check_installed.sh index 1240297..54076f5 100755 --- a/tools/check_installed.sh +++ b/tools/check_installed.sh @@ -1,5 +1,6 @@ #!/bin/bash -# Uses "command" to check whether a command exists on each of the Euclids. +# Student-written helper script which uses "command" to check whether a command exists on each of the Euclids. +# Use at own risk, and always be careful not to cause any disruption to other users or access machines which you do not have permission to use. UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " diff --git a/tools/usage.sh b/tools/usage.sh index e191bea..020b203 100755 --- a/tools/usage.sh +++ b/tools/usage.sh @@ -1,4 +1,6 @@ #!/bin/bash +# Student-written helper script to check the live CPU/Memory/GPU utilisation of the Euclid servers. +# Use at own risk, and always be careful not to cause any disruption to other users or access machines which you do not have permission to use. UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " From b129ac16b97bb55669c8155957f5b7469d1ba7de Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Fri, 6 Feb 2026 11:47:48 +0000 Subject: [PATCH 5/6] Allow specifying specific Euclids to check --- tools/usage.sh | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/tools/usage.sh b/tools/usage.sh index 020b203..e321e07 100755 --- a/tools/usage.sh +++ b/tools/usage.sh @@ -1,4 +1,5 @@ #!/bin/bash +# Usage: usage.sh [euclid ids to check usage of] # Student-written helper script to check the live CPU/Memory/GPU utilisation of the Euclid servers. # Use at own risk, and always be careful not to cause any disruption to other users or access machines which you do not have permission to use. @@ -9,6 +10,10 @@ RETIRED_SERVERS="11 34" SERVERS=$UNRESTRICTED_SERVERS +if [[ $# > 0 ]]; then + SERVERS="$@" +fi + # Run command & save output to temp directory tmpdir=$(mktemp -d) run_cmd(){ From 4cf2605f4656e049217faa7f627180607c4706fd Mon Sep 17 00:00:00 2001 From: Aidan Wansbrough Date: Fri, 6 Feb 2026 11:48:04 +0000 Subject: [PATCH 6/6] Allow checking for Python package versions --- tools/check_installed.sh | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/tools/check_installed.sh b/tools/check_installed.sh index 54076f5..a65b428 100755 --- a/tools/check_installed.sh +++ b/tools/check_installed.sh @@ -1,6 +1,7 @@ #!/bin/bash # Student-written helper script which uses "command" to check whether a command exists on each of the Euclids. # Use at own risk, and always be careful not to cause any disruption to other users or access machines which you do not have permission to use. +# Specify --pip to check Python libraries instead of installed applications. UNRESTRICTED_SERVERS="01 02 04 05 10 18 19 21 23 25 26 27 28 29 30" RESTRICTED_SERVERS="03 06 07 08 09 12 17 22 24 32 35 36 37 " @@ -11,6 +12,11 @@ SERVERS=$UNRESTRICTED_SERVERS cmdToCheck=$1 +function cleanup { + echo "" +} +trap cleanup EXIT + if [[ -z $cmdToCheck ]]; then echo "No command to check installation status specified, exiting." echo "Usage: ./check_installed.sh " @@ -28,7 +34,11 @@ done # Run command & save output to temp directory tmpdir=$(mktemp -d) -cmd="command -v $cmdToCheck" # Command to run on each euclid +if [[ $* == *--pip* ]]; then + cmd="pip list | grep '$cmdToCheck '" +else + cmd="command -v $cmdToCheck" # Command to run on each euclid +fi for euclid in $SERVERS; do echo $(if out=$(ssh "euclid-$euclid" -q -o RemoteCommand="$cmd" 2>&1); then echo "$out"