Skip to content

Commit a9d2961

Browse files
author
joshuaguy22
committed
init bc_jupyter template
1 parent 2008127 commit a9d2961

16 files changed

+367
-2
lines changed

README.md

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,2 +1,8 @@
1-
# bc_jupyter
2-
Jupyter Interative App for BioHive Portal (Open OnDemand)
1+
# Batch Connect - Jupyter
2+
3+
![GitHub Release](https://img.shields.io/github/release/osc/bc_osc_jupyter.svg)
4+
[![GitHub License](https://img.shields.io/badge/license-MIT-green.svg)](https://opensource.org/licenses/MIT)
5+
6+
An interactive app template designed for the BioHive Portal that launches a Jupyter
7+
server.
8+

form.yml.erb

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
<%-
2+
groups = OodSupport::User.new.groups.sort_by(&:id).tap { |groups|
3+
groups.unshift(groups.delete(OodSupport::Process.group))
4+
}.map(&:name).grep(/^P./)
5+
-%>
6+
---
7+
cluster:
8+
- "slurm"
9+
form:
10+
- bc_queue
11+
- mode
12+
- cuda_version
13+
- version
14+
- bc_num_hours
15+
- num_cores
16+
- memory
17+
- modules
18+
- extra_jupyter_args
19+
- bc_account
20+
- bc_num_slots
21+
- classroom
22+
attributes:
23+
cluster: "slurm"
24+
modules: "python"
25+
cuda_version: "cuda11.8/toolkit/11.8.0"
26+
classroom: 0
27+
version: "3.1"
28+
bc_account: null
29+
bc_num_slots: null
30+
bc_queue:
31+
label: Slurm Partition
32+
widget: select
33+
options:
34+
- ["batch", "batch"]
35+
- ["interactive", "interactive"]
36+
- ["mem", "mem"]
37+
extra_jupyter_args: ""
38+
bc_num_hours:
39+
widget: "number_field"
40+
label: "Number of Hours"
41+
value: 1
42+
min: 1
43+
max: 24
44+
required: true
45+
help: "Specify the number of hours. Min: 1hr, Max: 24hrs"
46+
num_cores:
47+
widget: "number_field"
48+
label: "Number of Cores"
49+
value: 1
50+
min: 1
51+
max: 16
52+
required: true
53+
help: "Specify the number of cores for the job. Max: 16"
54+
memory:
55+
widget: "number_field"
56+
label: "Memory (GB)"
57+
value: 2
58+
min: 1
59+
max: 32
60+
required: true
61+
help: "Specify the amount of memory in MB. Min: 1GB - Max: 32GB"
62+
mode:
63+
widget: "radio"
64+
value: "1"
65+
options:
66+
- ["Jupyter Lab", "1"]
67+
- ["Jupyter Notebook", "0"]
68+

icon.png

4.89 KB
Loading

info.md.erb

Whitespace-only changes.

manifest.yml

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
---
2+
name: Jupyter
3+
category: Interactive Apps
4+
subcategory: Servers
5+
role: batch_connect
6+
description: |
7+
This app will launch a [Jupyter] server using [Python] on the [Ascend], [Owens] or
8+
[Pitzer] clusters.
9+
10+
[Jupyter]: https://jupyter.org/
11+
[Python]: https://www.python.org/
12+
[Owens]: https://www.osc.edu/resources/technical_support/supercomputers/owens
13+
[Pitzer]: https://www.osc.edu/resources/technical_support/supercomputers/pitzer
14+
[Ascend]: https://www.osc.edu/vocabulary/systems/ascend
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
# Disable debuginfo as it causes issues with bundled gems that build libraries
2+
%global debug_package %{nil}
3+
%global repo_name bc_osc_jupyter
4+
%global app_name bc_osc_jupyter
5+
%{!?package_version: %define package_version %{major}.%{minor}.%{patch}}
6+
%{!?package_release: %define package_release 1}
7+
%{!?git_tag: %define git_tag v%{package_version}}
8+
%define git_tag_minus_v %(echo %{git_tag} | sed -r 's/^v//')
9+
10+
Name: ondemand-%{app_name}
11+
Version: %{package_version}
12+
Release: %{package_release}%{?dist}
13+
Summary: Batch Connect - OSC Jupyter Notebook
14+
15+
Group: System Environment/Daemons
16+
License: MIT
17+
URL: https://github.com/OSC/%{repo_name}
18+
Source0: https://github.com/OSC/%{repo_name}/archive/%{git_tag}.tar.gz
19+
20+
Requires: ondemand
21+
22+
# Disable automatic dependencies as it causes issues with bundled gems and
23+
# node.js packages used in the apps
24+
AutoReqProv: no
25+
26+
%description
27+
An interactive app designed for OSC OnDemand that launches a Jupyter Notebook server within an Owens batch job.
28+
29+
30+
%prep
31+
%setup -q -n %{repo_name}-%{git_tag_minus_v}
32+
33+
34+
%build
35+
36+
37+
%install
38+
%__mkdir_p %{buildroot}%{_localstatedir}/www/ood/apps/sys/%{app_name}
39+
%__cp -a ./. %{buildroot}%{_localstatedir}/www/ood/apps/sys/%{app_name}/
40+
echo v%{version} > %{buildroot}%{_localstatedir}/www/ood/apps/sys/%{app_name}/VERSION
41+
42+
43+
%files
44+
%defattr(-,root,root)
45+
%{_localstatedir}/www/ood/apps/sys/%{app_name}
46+
%{_localstatedir}/www/ood/apps/sys/%{app_name}/manifest.yml
47+
48+
49+
%changelog
50+
* Thu Sep 20 2018 Morgan Rodgers <mrodgers@osc.edu> 0.9.0-1
51+
- Update Jupyter to v0.9.0 (mrodgers@osc.edu)
52+
53+
* Fri Aug 24 2018 Morgan Rodgers <mrodgers@osc.edu> 0.8.0-1
54+
- Add IJulia kernel for Julia 0.6.4 (mrodgers@osc.edu)
55+
56+
* Tue Aug 14 2018 Morgan Rodgers <mrodgers@osc.edu> 0.7.0-1
57+
- Added support for Julia 1.0 kernel (mrodgers@osc.edu)
58+
59+
* Fri Apr 27 2018 Jeremy Nicklas <jnicklas@osc.edu> 0.6.2-1
60+
- Bump bc_osc_jupyter to 0.6.2 (jnicklas@osc.edu)
61+
62+
* Thu Apr 26 2018 Jeremy Nicklas <jnicklas@osc.edu> 0.6.1-1
63+
- Bump bc_osc_jupyter to 0.6.1 (jnicklas@osc.edu)
64+
65+
* Tue Apr 17 2018 Jeremy Nicklas <jnicklas@osc.edu> 0.6.0-1
66+
- Bump bc_osc_jupyter to 0.6.0 (jnicklas@osc.edu)
67+
68+
* Wed Mar 28 2018 Jeremy Nicklas <jnicklas@osc.edu> 0.5.0-1
69+
- Bump bc_osc_jupyter to 0.5.0 (jnicklas@osc.edu)
70+
71+
* Tue Mar 06 2018 Jeremy Nicklas <jnicklas@osc.edu> 0.4.1-1
72+
- Bump bc_osc_jupyter to 0.4.1 (jnicklas@osc.edu)
73+
74+
* Tue Mar 06 2018 Jeremy Nicklas <jnicklas@osc.edu>
75+
- Bump bc_osc_jupyter to 0.4.1 (jnicklas@osc.edu)
76+
77+
* Tue Feb 13 2018 Trey Dockendorf <tdockendorf@osc.edu> 0.3.0-1
78+
- new package built with tito
79+

submit.yml.erb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
---
2+
script:
3+
native:
4+
- "--mem=<%= memory.blank? ? 2 : memory.to_i %>G"
5+
- "--cpus-per-task=<%= num_cores.blank? ? 1 : num_cores.to_i %>"
6+
7+
batch_connect:
8+
template: "basic"

template/after.sh.erb

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
<% if context.cluster !~ /kubernetes/ -%>
2+
# Wait for the Jupyter server to start
3+
echo "Waiting for Jupyter server to open port ${port}..."
4+
if wait_until_port_used "${host}:${port}" 600; then
5+
echo "Discovered Jupyter server listening on port ${port}!"
6+
else
7+
echo "Timed out waiting for Jupyter server to open port ${port}!"
8+
clean_up 1
9+
fi
10+
sleep 2
11+
<% end -%>

template/assets/julia/logo-32x32.png

1.28 KB
Loading

template/assets/julia/logo-64x64.png

2.78 KB
Loading

template/assets/python/logo-32x32.png

1.06 KB
Loading

template/assets/python/logo-64x64.png

2.13 KB
Loading

template/assets/pythonconda

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
python

template/before.sh.erb

Lines changed: 112 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,112 @@
1+
# Export the module function if it exists
2+
<% if context.cluster.match?(/kubernetes/) -%>
3+
exec &> >(tee -a "pod.log")
4+
5+
source /etc/profile.d/lmod.sh
6+
7+
source /bin/find_host_port
8+
source /bin/save_passwd_as_secret
9+
source /bin/create_salt_and_sha1
10+
<% else -%>
11+
[[ $(type -t module) == "function" ]] && export -f module
12+
13+
# Find available port to run server on
14+
port=$(find_port ${host})
15+
16+
# Generate SHA1 encrypted password (requires OpenSSL installed)
17+
SALT="$(create_passwd 16)"
18+
password="$(create_passwd 16)"
19+
PASSWORD_SHA1="$(echo -n "${password}${SALT}" | openssl dgst -sha1 | awk '{print $NF}')"
20+
<% end -%>
21+
22+
# The `$CONFIG_FILE` environment variable is exported as it is used in the main
23+
# `script.sh.erb` file when launching the Jupyter server.
24+
export CONFIG_FILE="${PWD}/config.py"
25+
26+
# Safari users hit this error https://github.com/jupyterlab/jupyterlab/issues/5486
27+
# so let's make a new workspace dir that's this job's PWD and copy the defult /lab
28+
# workspace over there so folks can update it.
29+
WORKSPACE_DIR="$HOME/.jupyter/lab/workspaces"
30+
FILES=$(ls $WORKSPACE_DIR)
31+
32+
for FILE in ${FILES[@]}
33+
do
34+
ID=$(jq -r '.metadata.id' $WORKSPACE_DIR/$FILE)
35+
36+
if [[ $ID == "/lab" ]]; then
37+
WORKSPACE_FILE="$WORKSPACE_DIR/$FILE"
38+
break
39+
fi
40+
done
41+
42+
if [[ ${WORKSPACE_FILE+x} ]]; then
43+
cp $WORKSPACE_FILE .
44+
fi
45+
46+
JUPYTER_API="<%= context.mode == "1" ? "lab" : "tree" %>"
47+
48+
<%- if context.classroom == "1" -%>
49+
module purge
50+
module load <%= context.version %>
51+
52+
# Prepare the class environment if the module has set the appropriate variables set.
53+
# OSC_CLASS_FILES is the instructor's project directory we need to copy from.
54+
# OSC_CLASS_ID is the name of the class (eg. BIOCHEM5721).
55+
#
56+
# Note we make the directory even if there's nothing to copy
57+
mkdir -p "$HOME/osc_classes/$OSC_CLASS_ID"
58+
touch "$HOME/osc_classes/$OSC_CLASS_ID/0_${OSC_CLASS_ID:-osc_class}.md"
59+
NOTEBOOK_ROOT="$HOME/osc_classes/$OSC_CLASS_ID"
60+
61+
if [[ -n "$OSC_CLASS_FILES" ]]; then
62+
# Copy over classroom materials
63+
set -x
64+
rsync -avz --ignore-existing "$OSC_CLASS_FILES" "$HOME/osc_classes/$OSC_CLASS_ID"
65+
{ set +x; } 2>/dev/null
66+
fi
67+
68+
# Now set FACLs so that instructors & teaching assistants can copy the
69+
# students' directories and grade their notebooks.
70+
# But first remove any old FACLS we may have set previously if applicable.
71+
RM_ACL_FILE="/users/PZS0645/support/classes/$OSC_CLASS_ID/class.acl.remove"
72+
if [[ -r "$RM_ACL_FILE" ]]; then
73+
nfs4_setfacl -X - "$HOME" "$HOME/osc_classes" "$HOME/osc_classes/$OSC_CLASS_ID" < "$RM_ACL_FILE"
74+
fi
75+
76+
ACL_FILE="/users/PZS0645/support/classes/$OSC_CLASS_ID/class.acl"
77+
if [[ -r $ACL_FILE ]]; then
78+
echo 'setting facl'
79+
nfs4_setfacl -A - "$HOME" "$HOME/osc_classes" "$HOME/osc_classes/$OSC_CLASS_ID" < "$ACL_FILE"
80+
fi
81+
82+
module purge
83+
<%- end -%>
84+
85+
export JUPYTERLAB_WORKSPACES_DIR=$PWD
86+
export jupyter_api="$JUPYTER_API"
87+
88+
# Notebook root directory
89+
export NOTEBOOK_ROOT="${NOTEBOOK_ROOT:-${HOME}}"
90+
91+
# Generate Jupyter configuration file with secure file permissions
92+
(
93+
umask 077
94+
cat > "${CONFIG_FILE}" << EOL
95+
c.KernelSpecManager.ensure_native_kernel = False
96+
c.NotebookApp.ip = '*'
97+
<% if context.cluster.match?(/kubernetes/) -%>
98+
c.NotebookApp.port = 8080
99+
c.NotebookApp.base_url = '/node/${HOST_CFG}/${PORT_CFG}/'
100+
c.NotebookApp.terminado_settings = {'shell_command': ['/bin/bash', '--login', '-i']}
101+
<% else -%>
102+
c.NotebookApp.port = ${port}
103+
c.NotebookApp.base_url = '/node/${host}/${port}/'
104+
<% end -%>
105+
c.NotebookApp.port_retries = 0
106+
c.NotebookApp.password = u'sha1:${SALT}:${PASSWORD_SHA1}'
107+
c.NotebookApp.open_browser = False
108+
c.NotebookApp.allow_origin = '*'
109+
c.NotebookApp.notebook_dir = '${NOTEBOOK_ROOT}'
110+
c.NotebookApp.disable_check_xsrf = True
111+
EOL
112+
)

template/script.sh.erb

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
#!/usr/bin/env bash
2+
<%-
3+
require 'pathname'
4+
-%>
5+
6+
echo "Starting main script..."
7+
echo "TTT - $(date)"
8+
9+
#
10+
# Start Jupyter server
11+
#
12+
13+
# Clean the environment
14+
module purge
15+
16+
# Required modules
17+
module load xalt/latest
18+
<%- unless context.cuda_version.empty? || context.cuda_version == 'none' -%>
19+
module load <%= context.cuda_version %>
20+
<%- end -%>
21+
22+
# Set working directory to notebook root directory
23+
cd "${NOTEBOOK_ROOT}"
24+
25+
# Setup Jupyter environment
26+
module load project/ondemand <%= context.version %>
27+
28+
module list
29+
export JUPYTER_PATH="${PWD}/share/jupyter:${OSC_JUPYTER_PATH}"
30+
31+
echo "TTT - $(date)"
32+
33+
# List available kernels for debugging purposes
34+
set -x
35+
jupyter kernelspec list
36+
{ set +x; } 2>/dev/null
37+
echo "TTT - $(date)"
38+
39+
# Launch the Jupyter server
40+
set -x
41+
/cm/local/apps/python3/bin/jupyter <%= context.mode == "1" ? "lab" : "notebook" %> --config="${CONFIG_FILE}"

view.html.erb

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<%-
2+
base_url = "/node/#{host}/#{port}"
3+
login_url = "#{base_url}/login"
4+
next_url = "#{base_url}/#{jupyter_api}"
5+
6+
full_url="#{login_url}?next=#{CGI.escape(next_url)}"
7+
form_id = "juypyter_form#{login_url.gsub('/', '_')}"
8+
%>
9+
10+
<!-- Zoom on iPad doesn't like opening in a new tab. It won't foward the form parameters. -->
11+
<script type="text/javascript">
12+
function changeTarget() {
13+
const agent = navigator.userAgent;
14+
if (/Mozilla\/5\.0 \(Macintosh; Intel Mac OS X \d+_\d+_\d+\) AppleWebKit\/\d+\.\d+\.\d+ \(KHTML, like Gecko\)/.test(agent)) {
15+
document.getElementById("<%= form_id %>").target = "_self";
16+
}
17+
}
18+
</script>
19+
20+
<form id="<%= form_id %>" action="<%= full_url %>" method="post" target="_blank" onsubmit="changeTarget()" >
21+
<input type="hidden" name="password" value="<%= password %>">
22+
<button class="btn btn-primary" type="submit">
23+
<i class="fa fa-cogs"></i> Connect to Jupyter
24+
</button>
25+
</form>

0 commit comments

Comments
 (0)