aboutsummaryrefslogtreecommitdiff
path: root/README.md
blob: 4e11332d300b230847494636d73a33b550098477 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
# 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...

```shell
#!/usr/bin/env bash

if type -p jupyter_safe_port &>/dev/null; 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 (( port_remote < 0 )); then {
    # case 1: no remote ports available
    # case 2: if using '-c', no service is present on the requested port
    exit 1
}

# Spawn a local screen session to manage a remote jupyter server.
# You'd really want to use screen on the remote instead... This is just an example.
screen -dmS "jupyter_$server_$remote_port" ssh $server "source ~/miniconda3/etc/profile.d/conda.sh \
    && conda activate XYZ \
    && jupyter notebook --no-browser --port=$port_remote"

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