aboutsummaryrefslogtreecommitdiff

jupyter_safe_port

usage: jupyter_safe_port [-hcdf] {host} [port]

Discovers the next TCP port available for your notebook server and returns
execution instructions. If the argument '-c' is present and the requested
port is already bound on the remote host, return the SSH connection string

Positional arguments:
    host    host name or IP of remote system
    port    notebook server port to poll (default: 1024)

Arguments:
    -h    show this usage statement
    -c    only generate the SSH connection string
    -d    dump ports (useful in scripts)
              format: local remote
    -f    apply SSH argument to daemonize session (i.e. ssh -f)

Install

./install.sh --prefix=/usr/local

Examples

Oh no! I need to run two notebook servers on a remote system but which ports should I use?

$ jupyter_safe_port example.lan
Execute on example.lan:
jupyter notebook --no-browser --port=1024

Connect via:
ssh -N -L1024:localhost:1024 user@example.lan

You start the first notebook server. Now run jupyter_safe_port again...

$ jupyter_safe_port example.lan
Execute on example.lan:
jupyter notebook --no-browser --port=1025

Connect via:
ssh -N -L1025:localhost:1025 user@example.lan

The local port 1024 is already bound to the first server so it gives you 1025. On the remote system, example.lan, port 1024 is bound too so it returns 1025 as well. What if you want to use a higher port number on example.lan? Let's see...

$ jupyter_safe_port example.lan 8080
Execute on example.lan:
jupyter notebook --no-browser --port=8081

Connect via:
ssh -N -L1026:localhost:8081 user@example.lan

Oops, you forgot about that web server test. 8080 is already bound so you're given 8081 instead. Locally 1024 and 1025 are already bound so jupyter_safe_port returns 1026.

Let's say you have closed your laptop and lost all of your connections. If you can remember the remote port you used then -c will get you up and running in no time...

$ jupyter_safe_port example.lan -c 8081
Connect via:
ssh -N -L1024:localhost:8081 user@example.lan

Scripting

You can use jupyter_safe_port in your scripts. -d returns the local port followed by the remote port.

$ jupyter_safe_port example.lan -d 8081
1024 8081

Here are a few ways to use it...

#!/usr/bin/env bash
interrupt() {
    echo "Interrupted" >&2
    exit 0
}
trap interrupt SIGINT SIGTERM

# System to spawn the notebook server on
server=example.lan

# Where is conda located? (Use single-quotes to avoid expanding ~ or variables)
conda_root='~/miniconda3'

# Conda environment to activate
environ=XYZ

# Configure port range (optional)
range_low=
range_high=

if ! [[ -x $(command -v "jupyter_safe_port") ]]; then
    echo "jupyter_safe_port is not installed" >&2
    exit 1
fi

result="$(jupyter_safe_port -d $server)"
if (( $? )) || [ -z "$result" ]; then
    # jupyter_safe_port failed
    # case 1: not enough arguments
    # case 2: invalid argument
    # case 3: invalid port range
    # case 4: ssh failed to connect to $server
    exit 1
fi

# Extract ports from result
read port_local port_remote <<< "$result"

if [[ -z "$port_local" ]] || [[ -z "$port_remote" ]]; then
    # case 1: unhandled exception
    exit 1
fi

if (( port_local < 0 )); then
    # case 1: no local ports are available in range
    exit 1
fi

if (( port_remote < 0 )); then
    # case 1: no remote ports are available in range
    # case 2: if using '-c', no service is present on the requested port
    exit 1
fi

echo "Starting remote jupyter session on $server:$port_remote"
session="jupyter_${port_remote}"
ssh $server "bash -c 'tmux new-session -d -s $session \
    \"source ~/.bash_profile \
    && source $conda_root/etc/profile.d/conda.sh \
    && conda activate $environ \
    && jupyter notebook --no-browser --port=$port_remote\"'"
if (( $? )); then
    echo "Failed to connect to $server" >&2
    exit 1
fi

echo "Remote tmux session name: $session"
echo
echo "To kill the tmux session and the jupyter notebook server:"
echo "ssh $server 'tmux kill-session -t $session'"
echo

# Forward the ports to the local system
echo "Forwarding $server:$port_remote to localhost:$port_local"
echo "To interrupt the session press: ctrl-c"

ssh -N -L$port_local:localhost:$port_remote $server
if (( $? )); then
    echo "Failed to forward port." >&2
    exit 1
fi