diff --git a/ansible/portal.yml b/ansible/portal.yml index d34beca57..58ca69fad 100644 --- a/ansible/portal.yml +++ b/ansible/portal.yml @@ -13,10 +13,11 @@ name: openondemand tasks_from: main.yml -- hosts: openondemand_desktop +- hosts: openondemand_desktop:openondemand_matlab tags: - openondemand - openondemand_desktop + - openondemand_matlab become: yes gather_facts: yes tasks: @@ -36,3 +37,27 @@ name: openondemand tasks_from: jupyter_compute.yml when: appliances_mode != 'configure' # is run during build + +- hosts: openondemand_rstudio + tags: + - openondemand + - openondemand_rstudio + become: yes + gather_facts: yes + tasks: + - import_role: + name: openondemand + tasks_from: rstudio_compute.yml + when: appliances_mode != 'configure' # is run during build + +- hosts: openondemand_codeserver + tags: + - openondemand + - openondemand_codeserver + become: yes + gather_facts: yes + tasks: + - import_role: + name: openondemand + tasks_from: codeserver_compute.yml + when: appliances_mode != 'configure' # is run during build \ No newline at end of file diff --git a/ansible/roles/lustre/defaults/main.yml b/ansible/roles/lustre/defaults/main.yml index 0fe5d61a1..14ddc0551 100644 --- a/ansible/roles/lustre/defaults/main.yml +++ b/ansible/roles/lustre/defaults/main.yml @@ -6,7 +6,7 @@ lustre_mount_state: mounted lustre_mount_options: 'defaults,_netdev,noauto,x-systemd.automount,x-systemd.requires=lnet.service,nosuid,nodev' # below variables are for build and should not generally require changes -lustre_repo: "git://git.whamcloud.com/fs/lustre-release.git" +lustre_repo: "https://github.com/lustre/lustre-release.git" lustre_build_packages: - "kernel-devel-{{ ansible_kernel }}" - git diff --git a/ansible/roles/openondemand/defaults/main.yml b/ansible/roles/openondemand/defaults/main.yml index 23359f01c..851804e03 100644 --- a/ansible/roles/openondemand/defaults/main.yml +++ b/ansible/roles/openondemand/defaults/main.yml @@ -24,6 +24,9 @@ openondemand_desktop_screensaver: false openondemand_filesapp_paths: [] openondemand_jupyter_partition: '' openondemand_dashboard_links: [] +openondemand_rstudio_partition: '' +openondemand_matlab_partition: '' +openondemand_codeserver_partition: '' # Monitoring: openondemand_exporter: true @@ -100,3 +103,7 @@ openondemand_osc_ood_defaults: oidc_uri: "{{ openondemand_auth_defaults.oidc.oidc_uri if (openondemand_auth | lower) == 'oidc' else none }}" ood_auth_openidc: "{{ openondemand_auth_defaults.oidc.ood_auth_openidc if (openondemand_auth | lower) == 'oidc' else none }}" httpd_auth: "{{ openondemand_auth_defaults[openondemand_auth | lower].httpd_auth }}" + +openondemand_code_server_version: 4.102.2 +openondemand_rstudio_version: 2025.05.1-513 +openondemand_matlab_version: '' \ No newline at end of file diff --git a/ansible/roles/openondemand/tasks/codeserver_compute.yml b/ansible/roles/openondemand/tasks/codeserver_compute.yml new file mode 100644 index 000000000..7b39bf75d --- /dev/null +++ b/ansible/roles/openondemand/tasks/codeserver_compute.yml @@ -0,0 +1,40 @@ +- name: Download Code Server RPM + ansible.builtin.get_url: + url: "https://github.com/coder/code-server/releases/download/v{{ openondemand_code_server_version }}/code-server-{{ openondemand_code_server_version }}-amd64.rpm" + dest: /tmp/code-server.rpm + mode: '0644' + +- name: Install Code Server + ansible.builtin.dnf: + name: /tmp/code-server.rpm + state: present + disable_gpg_check: yes + +- name: Create module directory for Code Server + ansible.builtin.file: + path: /opt/ohpc/pub/modulefiles/code-server + state: directory + mode: '0755' + recurse: yes + +- name: Create modulefile for Code Server + copy: + dest: "/opt/ohpc/pub/modulefiles/code-server/{{ openondemand_code_server_version }}" + mode: "0644" + content: | + #%Module1.0##################################################################### + + proc ModulesHelp { } { + puts stderr "This module loads code-server {{ openondemand_code_server_version }}." + puts stderr "\nCode Server provides a browser-based VSCode instance.\n" + } + + module-whatis "Name: code-server" + module-whatis "Version: {{ openondemand_code_server_version }}" + module-whatis "Category: IDE" + module-whatis "Description: Run VS Code in your browser with Code Server" + module-whatis "URL: https://github.com/coder/code-server" + + set root /usr/bin/code-server + + prepend-path PATH $root diff --git a/ansible/roles/openondemand/tasks/rstudio_compute.yml b/ansible/roles/openondemand/tasks/rstudio_compute.yml new file mode 100644 index 000000000..99dd83a9b --- /dev/null +++ b/ansible/roles/openondemand/tasks/rstudio_compute.yml @@ -0,0 +1,50 @@ +# Should be run on compute nodes you want to run RStudio on +# See https://osc.github.io/ood-documentation/latest/tutorials/tutorials-interactive-apps/add-rstudio.html +# - Will already have lmod + +- name: Install R from EPEL + ansible.builtin.dnf: + name: R + state: present + +- name: Download RStudio Server RPM + ansible.builtin.get_url: + url: "https://download2.rstudio.org/server/rhel{{ ansible_distribution_major_version }}/x86_64/rstudio-server-rhel-{{ openondemand_rstudio_version }}-x86_64.rpm" + dest: /tmp/rstudio-server.rpm + mode: '0644' + +- name: Install RStudio Server + ansible.builtin.dnf: + name: /tmp/rstudio-server.rpm + state: present + disable_gpg_check: yes + +- name: Create module directory for RStudio Server + ansible.builtin.file: + path: /opt/ohpc/pub/modulefiles/rstudio-server + state: directory + mode: '0755' + recurse: yes + +- name: Write modulefile for RStudio Server + ansible.builtin.copy: + dest: "/opt/ohpc/pub/modulefiles/rstudio-server/{{ openondemand_rstudio_version }}" + mode: '0644' + content: | + #%Module1.0##################################################################### + + proc ModulesHelp { } { + puts stderr " " + puts stderr "This module loads RStudio Server {{ openondemand_rstudio_version }}" + puts stderr "\nRStudio Server provides a browser-based interface to R.\n" + } + + module-whatis "Name: rstudio-server" + module-whatis "Version: {{ openondemand_rstudio_version }}" + module-whatis "Category: IDE" + module-whatis "Description: RStudio Server - IDE for R" + module-whatis "URL: https://www.rstudio.com" + + set root /usr/lib/rstudio-server + + prepend-path PATH $root/bin diff --git a/docs/openondemand.md b/docs/openondemand.md index 4cde213e4..5dd3029fc 100644 --- a/docs/openondemand.md +++ b/docs/openondemand.md @@ -22,10 +22,17 @@ For examples of all of the above see the `smslabs-example` environment in this r # Enabling Open OnDemand To enable the Open OnDemand server, add single host to the `openondemand` inventory group. Generally, this should be a node in the `login` group, as Open OnDemand must be able to access Slurm commands. -To enable compute nodes for virtual desktops or Jupyter notebook servers (accessed through the Open OnDemand portal), add nodes/groups to the `openondemand_desktop` and `openondemand_jupyter` inventory groups respectively. These may be all or a subset of the `compute` group. +To enable compute nodes for virtual desktops, Jupyter notebooks, RStudio, VSCode, or MATLAB (accessed through the Open OnDemand portal), add nodes/groups to the `openondemand_desktop`, `openondemand_jupyter`, `openondemand_rstudio`, `openondemand_codeserver`, and `openondemand_matlab` inventory groups respectively. These may be all or a subset of the `compute` group. The above functionality is configured by running the `ansible/portal.yml` playbook. This is automatically run as part of `ansible/site.yml`. +## MATLAB +*NB* Due to licensing, the MATLAB batch connect app requires a MATLAB intallation to be present on the relevant compute nodes. The MATLAB app is therefore disabled by default, and must be enabled by setting `openondemand_matlab_partition` in e.g. `environments/site/inventory/group_vars/all/openondemand.yml` to the name of the partition where MATLAB is available. + +An Lmod modulefile also needs to be available on compute nodes - this is not provided by the appliance. See e.g.`roles/openondemand/tasks/rstudio_compute.yml` for an example. The modulefile must be named `matlab/$MATLAB_VERSION`, where the version matches thes `openondemand_matlab_version` variable. This variable is set to empty in the role default so must be defined in `environments/site/inventory/group_vars/all/openondemand.yml`. + +As MATLAB requires a remote desktop, the TurboVNC and Xfce Desktop packages and configuration from the "openondemand_desktop" app will be automatically applied to nodes where the MATLAB app is enabled. + # Default configuration See the [ansible/roles/openondemand/README.md](../ansible/roles/openondemand/README.md) for more details on the variables described below. @@ -39,7 +46,7 @@ The following variables have been given default values to allow Open OnDemand to self-signed certificate is generated, which should probably be replaced for production environments. - `openondemand_auth` and any corresponding options. Defaults to `basic_pam`. -- `openondemand_desktop_partition` and `openondemand_jupyter_partition` if the corresponding inventory groups are defined. Defaults to the first compute group defined in the `compute` OpenTofu variable in `environments/$ENV/tofu`. +- `openondemand_desktop_partition`, `openondemand_jupyter_partition`, `openondemand_rstudio_partition`, and `openondemand_codeserver_partition` if the corresponding inventory groups are defined. Defaults to the first compute group defined in the `compute` OpenTofu variable in `environments/$ENV/tofu`. Note `openondemand_matlab_partition` is not set due to the additional requirements discussed above. It is also recommended to set: - `openondemand_dashboard_support_url` diff --git a/environments/.stackhpc/inventory/group_vars/all/openondemand.yml b/environments/.stackhpc/inventory/group_vars/all/openondemand.yml index 735da25df..a8f88e5c4 100644 --- a/environments/.stackhpc/inventory/group_vars/all/openondemand.yml +++ b/environments/.stackhpc/inventory/group_vars/all/openondemand.yml @@ -1,6 +1,8 @@ openondemand_auth: basic_pam openondemand_jupyter_partition: standard openondemand_desktop_partition: standard +openondemand_rstudio_partition: standard +openondemand_codeserver_partition: standard #openondemand_dashboard_support_url: #openondemand_dashboard_docs_url: #openondemand_filesapp_paths: diff --git a/environments/.stackhpc/tofu/cluster_image.auto.tfvars.json b/environments/.stackhpc/tofu/cluster_image.auto.tfvars.json index 23ba55943..af148393a 100644 --- a/environments/.stackhpc/tofu/cluster_image.auto.tfvars.json +++ b/environments/.stackhpc/tofu/cluster_image.auto.tfvars.json @@ -1,6 +1,6 @@ { "cluster_image": { - "RL8": "openhpc-RL8-250805-1410-35724c15", - "RL9": "openhpc-RL9-250805-1409-35724c15" + "RL8": "openhpc-RL8-250808-1727-faa44755", + "RL9": "openhpc-RL9-250808-1727-faa44755" } } diff --git a/environments/common/inventory/group_vars/all/openondemand.yml b/environments/common/inventory/group_vars/all/openondemand.yml index f2998f4ab..bd8ba7693 100644 --- a/environments/common/inventory/group_vars/all/openondemand.yml +++ b/environments/common/inventory/group_vars/all/openondemand.yml @@ -14,6 +14,9 @@ openondemand_auth: basic_pam openondemand_jupyter_partition: "{{ openhpc_partitions[0]['name'] }}" openondemand_desktop_partition: "{{ openhpc_partitions[0]['name'] }}" +openondemand_rstudio_partition: "{{ openhpc_partitions[0]['name'] }}" +openondemand_matlab_partition: '' # Requires target site to already have MATLAB so set to empty +openondemand_codeserver_partition: "{{ openhpc_partitions[0]['name'] }}" # Regex defining hosts which openondemand can proxy; the default regex is compute nodes (for apps) and grafana host, # e.g. if the group `compute` has hosts `compute-{0,1,2,..}` this will be '(compute-\d+)|(control)'. @@ -47,7 +50,6 @@ openondemand_clusters: basic: script_wrapper: | module purge - export PATH=/opt/jupyter-py39/bin/:$PATH %s set_host: host=$(hostname -s) vnc: @@ -83,13 +85,31 @@ openondemand_clusters_grafana: jobid: "jobid" # define installation of openondemand apps (see openondemand_apps_* below for configuration): -openondemand_install_apps_default: +openondemand_install_app_jupyter: jupyter: repo: https://github.com/OSC/bc_example_jupyter.git version: master # defaults (optional) -openondemand_install_apps_extra: {} # mapping, values as for ansible.builtin.git: repo (required)/dest/version(default main)/umask +openondemand_install_app_rstudio: # mapping, values as for ansible.builtin.git: repo (required)/dest/version(default main)/umask + rstudio: + repo: https://github.com/stackhpc/ood-stackhpc-rstudio.git + version: 2025.08.1 +openondemand_install_app_matlab: + matlab: + repo: https://github.com/stackhpc/ood-stackhpc-matlab.git + version: 2025.08.1 +openondemand_install_app_codeserver: + codeserver: + repo: https://github.com/stackhpc/bc_osc_codeserver.git + version: 2025.08.1 # osc:ood role var (NB only active when not in configure): -ood_install_apps: "{{ openondemand_install_apps_default if openondemand_jupyter_partition | default(none) else {} | combine(openondemand_install_apps_extra) }}" +ood_install_apps: >- + {{ + {} + | combine(openondemand_install_app_jupyter if openondemand_jupyter_partition else {}) + | combine(openondemand_install_app_rstudio if openondemand_rstudio_partition else {}) + | combine(openondemand_install_app_matlab if openondemand_matlab_partition else {}) + | combine(openondemand_install_app_codeserver if openondemand_codeserver_partition else {}) + }} # https://github.com/OSC/ondemand/tree/master/apps/bc_desktop # also https://osc.github.io/ood-documentation/latest/enable-desktops/custom-job-submission.html#enable-desktops-custom-job-submission @@ -151,6 +171,10 @@ openondemand_apps_jupyter_default: --- batch_connect: template: "basic" + script_wrapper: | + module purge + export PATH=/opt/jupyter-py39/bin/:$PATH + %s script: job_name: "ood-jupyter" native: @@ -159,6 +183,216 @@ openondemand_apps_jupyter_default: - <%= "--nodelist=#{node}" %> openondemand_apps_jupyter: "{{ {'jupyter':openondemand_apps_jupyter_default} if openondemand_jupyter_partition | default(none) else {} }}" +openondemand_apps_rstudio_default: + title: RStudio + description: Request a RStudio server + cluster: slurm + attributes: + bc_queue: + value: "{{ openondemand_rstudio_partition | default(none) }}" + rstudio_module: + label: RStudio module + required: true + cachable: true + help: Choose your RStudio module + widget: select + options: + - ["RStudio v{{ openondemand_rstudio_version }}", "rstudio-server/{{ openondemand_rstudio_version }}}"] + extra_modules_script: + label: Extra modules script + help: If you'd like to load additional modules alongside RStudio-Server, put the 'module load ...' commands into a text file (one 'module load...' per line) and specify its path here + widget: text_field + required: false + cores: + label: Number of CPU cores + help: How many CPU cores to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition. + widget: number_field + min: 1 + max: 48 + step: 1 + value: 1 + cachable: true + ram: + widget: number_field + label: RAM in GB + help: How much RAM to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 4 + max: 700 + step: 1 + value: 4 + cachable: true + bc_num_hours: + widget: number_field + help: Maximum runtime in hours for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 1 + max: 240 + step: 1 + value: 30 + cachable: true + bc_email_on_started: false + auto_modules_RStudio-Server: + default: false + form: + - bc_queue + - rstudio_module + - auto_queues + - extra_modules_script + - cores + - ram + - bc_num_hours + - bc_email_on_started + submit: | + --- + batch_connect: + template: "basic" + conn_params: + - csrf_token + - rstudio_module + script: + native: + - "--partition" + - "<%= bc_queue %>" + - "--ntasks" + - "1" + - "--mem" + - "<%= ram.blank? ? 4 : ram.to_i %>G" + - "--cpus-per-task" + - "<%= cores.blank? ? 1 : cores.to_i %>"<% if auto_queues.start_with?("gpu") %> + - "--gpus-per-task" + - "1"<% end %> +openondemand_apps_rstudio: "{{ {'rstudio':openondemand_apps_rstudio_default} if openondemand_rstudio_partition | default(none) else {} }}" + +openondemand_apps_matlab_default: + title: MATLAB + description: Request a desktop to run MATLAB. + cluster: slurm + form: + - desktop + - bc_queue + - bc_num_hours + - matlab_module + - cores + - ram + attributes: + desktop: xfce + # bc_account: # i.e. slurm account + # value: root + bc_queue: + value: "{{ openondemand_matlab_partition | default(none) }}" + matlab_module: + label: MATLAB module + required: true + cachable: true + help: Choose your MATLAB module + widget: select + options: + - ["MATLAB v{{ openondemand_matlab_version }}", "matlab/{{ openondemand_matlab_version }}"] + cores: + label: Number of CPU cores + help: How many CPU cores to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition. + widget: number_field + min: 1 + max: 48 + step: 1 + value: 1 + cachable: true + ram: + widget: number_field + label: RAM in GB + help: How much RAM to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 4 + max: 700 + step: 1 + value: 4 + cachable: true + bc_num_hours: + widget: number_field + help: Maximum runtime in hours for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 1 + max: 240 + step: 1 + value: 30 + cachable: true + submit: | + --- + script: + job_name: "ood-matlab" + native: + - "--partition" + - "<%= bc_queue %>" + - "--ntasks" + - "1" + - "--mem" + - "<%= ram.blank? ? 4 : ram.to_i %>G" + - "--cpus-per-task" + - "<%= cores.blank? ? 1 : cores.to_i %>" +openondemand_apps_matlab: "{{ {'matlab':openondemand_apps_matlab_default} if openondemand_matlab_partition | default(none) else {} }}" + +openondemand_apps_codeserver_default: + title: Code Server + description: Request a Code Server + cluster: slurm + form: + - bc_queue + - bc_num_hours + - codeserver_module + - cores + - ram + attributes: + codeserver_module: + label: Code Server module + required: true + cachable: true + help: Choose your Code Server module + widget: select + options: + - ["Code Server v{{ openondemand_code_server_version}}", "code-server/{{ openondemand_code_server_version }}"] + bc_queue: + value: "{{ openondemand_codeserver_partition | default(none) }}" + cores: + label: Number of CPU cores + help: How many CPU cores to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition. + widget: number_field + min: 1 + max: 48 + step: 1 + value: 1 + cachable: true + ram: + widget: number_field + label: RAM in GB + help: How much RAM to reserve for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 4 + max: 700 + step: 1 + value: 4 + cachable: true + bc_num_hours: + widget: number_field + help: Maximum runtime in hours for your session. NB Ensure this is within the maximum allowed by your chosen partition + min: 1 + max: 240 + step: 1 + value: 30 + submit: | + --- + batch_connect: + template: "basic" + conn_params: + - codeserver_module + script: + job_name: "ood-codeserver" + native: + - "--partition" + - "<%= bc_queue %>" + - "--ntasks" + - "1" + - "--mem" + - "<%= ram.blank? ? 4 : ram.to_i %>G" + - "--cpus-per-task" + - "<%= cores.blank? ? 1 : cores.to_i %>" +openondemand_apps_codeserver: "{{ {'codeserver':openondemand_apps_codeserver_default} if openondemand_codeserver_partition | default(none) else {} }}" + # osc.ood:ood_apps - see https://github.com/OSC/ood-ansible#ood_apps openondemand_dashboard_support_url: '' openondemand_dashboard_docs_url: '' @@ -178,7 +412,15 @@ openondemand_apps: ood_brand_bg_color: "#0e6ec8" ood_dashboard_title: "{{ openhpc_cluster_name }}" -ood_apps: "{{ openondemand_apps | combine(openondemand_apps_jupyter) | combine(openondemand_apps_desktop) }}" +ood_apps: >- + {{ + openondemand_apps + | combine(openondemand_apps_jupyter) + | combine(openondemand_apps_desktop) + | combine(openondemand_apps_rstudio) + | combine(openondemand_apps_matlab) + | combine(openondemand_apps_codeserver) + }} # Prometheus config for Open Ondemand exporter # https://osc.github.io/ood-documentation/latest/monitoring/prometheus.html#prometheus-configuration diff --git a/environments/common/inventory/groups b/environments/common/inventory/groups index 2396fa66a..57b644152 100644 --- a/environments/common/inventory/groups +++ b/environments/common/inventory/groups @@ -99,6 +99,15 @@ fail2ban [openondemand_jupyter] # Subset of compute to run a Jupyter Notebook servers on via Open Ondemand +[openondemand_rstudio] +# Subset of compute to run RStudio servers on via Open Ondemand + +[openondemand_matlab] +# Subset of compute to run RStudio servers on via Open Ondemand + +[openondemand_codeserver] +# Subset of compute to run a Codeserver VSCode instance on via Open Ondemand + [etc_hosts] # Hosts to manage /etc/hosts e.g. if no internal DNS. See ansible/roles/etc_hosts/README.md diff --git a/environments/site/inventory/groups b/environments/site/inventory/groups index da0562060..9df61dc13 100644 --- a/environments/site/inventory/groups +++ b/environments/site/inventory/groups @@ -55,6 +55,18 @@ compute # Subset of compute to run a Jupyter Notebook servers on via Open Ondemand compute +[openondemand_rstudio:children] +# Subset of compute to run RStudio servers on via Open Ondemand +compute + +[openondemand_matlab:children] +# Subset of compute to run a MATLAB interactive desktop on via Open Ondemand +compute + +[openondemand_codeserver:children] +# Subset of compute to run a Codeserver VSCode instance on via Open Ondemand +compute + [etc_hosts:children] # Hosts to manage /etc/hosts e.g. if no internal DNS. See ansible/roles/etc_hosts/README.md cluster