remote_jupyter_manager.sh 8.48 KB
Newer Older
1 2 3 4 5
#!/bin/bash
# install / startup jupyterlab environment(s)
# Sebastian Wahl 08/2018
#
# tested on ursus/taurus, science0[1,2].geomar.de, bdata2.hlrn.de, nesh-fe1.rz.uni-kiel.de
Willi Rath's avatar
Willi Rath committed
6
#
7 8 9 10 11 12 13 14 15 16
# TODO:
# - add option to provide an input file with the tasks to do e.g
#   $0 hosts.txt
#   with hosts.txt in the form of
#   swahl@ursus.geomar.de install
#   swahl@science01.geomar.de install
#   swahl@science01.geomar.de update
# - add option for matlab kernel?
# - add more documentation to the code
# - make basepath (currently $HOME) flexible
Sebastian Wahl's avatar
Sebastian Wahl committed
17
#
18 19 20 21
function connect() {

	hname=$1

Sebastian Wahl's avatar
Sebastian Wahl committed
22 23 24 25
	socks_5_port=$(python -c 'import socket; s=socket.socket(); s.bind(("", 0)); print(s.getsockname()[1]); s.close()')
	[[ $socks_5_port =~ ^[0-9]+$ ]] || socks_5_port=54321

	ssh -f -D localhost:${socks_5_port} $hname sleep 15
26 27
	page=$(ssh $hname "\${HOME}/miniconda3/bin/python \${HOME}/miniconda3/bin/jupyter notebook list" | grep http | awk '{print$1}')

28 29 30 31 32 33 34
   # Proxy bypass list (this will make sure certain domains are _not_ contacted
	# through the proxy), courtesy of Willi Rath:
	add_proxy_bypass=$(
	    echo {'*.',}{google.,googleapis.}{com,fr,de} '*.gvt1.com' '*.gstatic.com' \
	        | tr '[:blank:]' '\n' | paste -s -d';')
	echo " Won't use proxy for any of:" ${add_proxy_bypass}

Willi Rath's avatar
Willi Rath committed
35
	if [[ "$(uname)" == "Darwin" ]]; then
36
		chrome=/Applications/Google\ Chrome.app/Contents/MacOS/Google\ Chrome
Willi Rath's avatar
Willi Rath committed
37

38 39
		if [[ -f "$chrome" ]]; then
			# TODO: disable warning output
40
			"$chrome" --new-window --user-data-dir="/tmp/chromium_user_data_$(date +%s%N)" \
41
			    --proxy-bypass-list="<-loopback>;${add_proxy_bypass}" --proxy-server='socks5://localhost:'${socks_5_port} $page &
42 43
			echo " Cleaning tmp data on /tmp (if any)"
			find /tmp/ -maxdepth 1 -mtime +30 -name "chromium_user_data*" -exec rm -rf {} \;
44 45 46 47 48 49
		else
			echo
			echo " ERROR: chrome browser needs to be installed. Go to https://www.google.com/chrome/, install Chrome and try again"
			echo
		fi
	else
50
		if which chromium-browser >/dev/null; then
51
			chromium-browser --new-window --user-data-dir="/tmp/chromium_user_data_$(date +%s%N)" \
52
				--proxy-bypass-list="<-loopback>;${add_proxy_bypass}" --proxy-server="socks5://localhost:${socks_5_port}" \
53
				$page &
Willi Rath's avatar
Willi Rath committed
54
			# delete user data older than 30 days
55 56
			echo " Cleaning tmp data on /tmp (if any)"
			find /tmp/ -maxdepth 1 -mtime +30 -name "chromium_user_data*" -exec rm -rf {} \;
57 58
		else
			echo
Willi Rath's avatar
Willi Rath committed
59
			echo " ERROR: chrome browser needs to be installed. On Ubuntu try sudo apt-get install chromium-browser and try again."
60 61
			echo
		fi
Willi Rath's avatar
Willi Rath committed
62
	fi
63 64 65

}

66 67 68 69 70
# setup remote installation
function install_conda_environment() {

	 cd $HOME
	 test -d miniconda3 && mv -v miniconda3 miniconda_$(date +%Y%m%d_%H%M%S)
71
	 if [[ "`uname`" == "Darwin" ]] ; then
72
		 curl https://repo.anaconda.com/miniconda/Miniconda3-latest-MacOSX-x86_64.sh -o Miniconda3.sh
73
	 else
74
	    curl https://repo.anaconda.com/miniconda/Miniconda3-latest-Linux-x86_64.sh -o Miniconda3.sh
75
	 fi
76 77 78 79
	 bash Miniconda3.sh -b -p \${HOME}/miniconda3
	 rm -f Miniconda3.sh
    source ${HOME}/miniconda3/bin/activate base;
	 conda install jupyterlab nb_conda_kernels --yes
80

81 82 83
	 # OSX client or host machines do not like /dev/stdin as it is used below
    # conda env create -f /dev/stdin;
    conda env create -f py3_std.yml
84 85 86 87

	 # on ursus7 I had to execute the following lines to make py3_std available in jupyterlab
	 source ${HOME}/miniconda3/bin/activate py3_std;
	 python -m ipykernel install --user --name py3_std --display-name "py3_std"
88 89
}

Sebastian Wahl's avatar
Sebastian Wahl committed
90
if [[ $# -lt 2 ]]; then
91 92 93
	echo
	echo " Usage: need two arguments:"
	echo
94
	echo " $0 user@host.example.com start [lookup]"
95
	echo "    to start an existing jupyterlab environment on <user@host.example.com> and connect afterwards"
Willi Rath's avatar
Willi Rath committed
96
	echo "    if jupyterlab is already running on <user@host.example.com> you will be connected with the existing instance."
97
	echo "    the optional argument lookup sets the IP of the jupyterlab server differently."
Willi Rath's avatar
Willi Rath committed
98
	echo
99
	echo " $0 user@host.example.com stop "
Willi Rath's avatar
Willi Rath committed
100 101
	echo "    to stop jupyterlab running on <user@host.example.com>"
	echo
102 103 104 105 106 107 108
	echo " $0 user@host.example.com install "
	echo "    to install a new miniconda environment. (WARNING: takes 5-10 minutes, an existing"
	echo "    $HOME/miniconda3 will be saved before starting the install process."
	echo "    It is highly recommended to set up passwordless ssh login before installation."
	echo "    Run ssh-copy-id user@host.example.com to setup passwordless ssh login and test with"
	echo "    ssh user@host.example.com afterwards."
	echo
Sebastian Wahl's avatar
Sebastian Wahl committed
109 110 111
	echo " $0 user@host.example.com update "
	echo "    to update an existing miniconda environment on user@host.example.com"
	echo
112 113
	exit 1
fi
114 115

# set -vx
116 117
hname=$1
task=$2
118 119
page=
port=
120 121 122 123 124
# setting 0.0.0.0 for login on mistral kind of works if you start up the server but connecting to an existing
# server on one of the login nodes does not work
# see issues on gitlab
ip="127.0.0.1"
[[ $# -eq 3 ]] && ip=$3
125
[[ "$ip" == "lookup" ]] || [[ $hname =~ mistral ]] && ip="\$(getent hosts \${HOSTNAME} | awk '{print \$1}')"
126

127 128 129 130
# set the root path of the miniconda installation, defaults to $HOME
rootpath=$HOME
[[ $# -eq 3 ]] && [[ "$3" =~ "/" ]] && rootpath=$3

131
if [[ "$task" == "start" ]] || [[ "$task" == "stop" ]]; then
132
	page=$(ssh $hname "\${HOME}/miniconda3/bin/python \${HOME}/miniconda3/bin/jupyter notebook list" | grep http | awk '{print$1}')
Willi Rath's avatar
Willi Rath committed
133
	port=$(echo $page | awk -F: '{print$3}' | awk -F/ '{print$1}')
134 135 136 137
fi

echo
if [[ "$task" == "start" ]]; then
138 139 140 141
	if [[ -z "$port" ]] ; then
		echo " No Jupyterlab server running on $hname. Will start a server and connect to it "
		# TODO: avoid hitting Ctrl+C --> how to I start this in the backgroud adding & does not work
		echo " Wait until you see the URL of the server and then hit Ctrl+C to continue"
142 143
		echo " If Chromium shows an empty welcome page, copy the Jupyterlab URL manually to the Chroumium URL field."
      ssh $hname "\${HOME}/miniconda3/bin/python \${HOME}/miniconda3/bin/jupyter lab --no-browser --ip $ip"
144 145 146
		echo " Jupyterlab on $hname started"
	else
		echo " Jupyterlab on $hname, port $port already running. Connecting to existing server."
Willi Rath's avatar
Willi Rath committed
147
	fi
148
	echo
Sebastian Wahl's avatar
Sebastian Wahl committed
149
	connect $hname
150 151 152 153 154 155 156 157

elif [[ "$task" == "stop" ]]; then

	if [[ ! -z "$port" ]] ; then
		echo " Jupyterlab on $hname, port $port, will be stopped"
		ssh $hname "\${HOME}/miniconda3/bin/python \${HOME}/miniconda3/bin/jupyter notebook stop $port"
	else
		echo " No Jupyterlab running on $hname"
Willi Rath's avatar
Willi Rath committed
158
	fi
159
	echo
160

161
elif [[ "$task" == "install" ]]; then
162

163 164 165 166
	# cool trick from https://stackoverflow.com/a/22107893 on how to execute local functions on a remote machine
	# However, OSX client machines do not like /dev/stdin as it has been previously used in the function 
	# install_conda_environment hence we copy py3_std.yml first (see also above)
	#cat py3_std.yml | ssh $hname "$(typeset -f install_conda_environment); install_conda_environment;"
167 168 169 170 171 172 173
	if [[ "$hname" =~ "science" ]]; then
		echo " Using special py3_std_science0x.yml file for science VMs."
		echo " For details see https://git.geomar.de/python/jupyter_on_HPC_setup_guide/issues/24" 
		scp $(dirname $0)/py3_std_science0x.yml $hname:py3_std.yml
	else
		scp $(dirname $0)/py3_std.yml $hname:py3_std.yml
	fi		
174 175
	ssh $hname "$(typeset -f install_conda_environment); install_conda_environment;"
   if [[ $? -ne 0 ]]; then	
176
		echo
177 178 179
		echo " Installation seems to have failed."
		echo " If the install command e.g. failed with 'else: endif nicht gefunden.' or similar"
		echo " then the remote host doesn't use bash as default shell. Try "
180
		echo
181 182 183 184 185 186 187
		echo " scp py3_std.yml $hname:"
		echo " ssh $hname"
		echo " curl https://repo.continuum.io/miniconda/Miniconda3-latest-Linux-x86_64.sh -o Miniconda3.sh"
		echo ' bash Miniconda3.sh -b -p ${HOME}/miniconda3'
		echo ' source ${HOME}/miniconda3/bin/activate base'
		echo ' conda install jupyterlab nb_conda_kernels --yes'
		echo " conda env create -f py3_std.yml " 
188 189 190
	   echo ' # on ursus7 I had to execute the following lines to make py3_std available in jupyterlab'
	   echo ' source ${HOME}/miniconda3/bin/activate py3_std;'
	   echo ' python -m ipykernel install --user --name py3_std --display-name "py3_std"'
191 192 193 194
		echo  
		echo " to perform a manual installation." 
		echo
	else
195 196 197
		echo
		echo " Installation of py3_std conda environment on $hname sucessfull. "
		echo " Now start remote jupyter manager with "
198
		echo " $0 $hname start"
199 200
		echo " and enjoy your jupyter working environment."
		echo
201
   fi
202 203 204

elif [[ "$task" == "update" ]]; then
	echo
Sebastian Wahl's avatar
Sebastian Wahl committed
205 206 207
	echo "Updating all packages on $hname using the conda-forge channel."
	ssh $hname bash <<EOF
source \${HOME}/miniconda3/bin/activate py3_std || echo "No conda installation found in \${HOME}/miniconda3/"
208
conda update -c default -c conda-forge -n py3_std --all --yes
Sebastian Wahl's avatar
Sebastian Wahl committed
209
EOF
210
fi