Ubuntu 24.04 packages xpra 3.1.5 which is wire-incompatible with the v6.x macOS client from xpra.org. Add xpra.org apt repo and install current stable release so client and server versions match.
372 lines
11 KiB
YAML
372 lines
11 KiB
YAML
---
|
|
- name: Install Java runtime (full — required for GUI mode)
|
|
ansible.builtin.apt:
|
|
name:
|
|
- openjdk-21-jre
|
|
- openjdk-21-jdk
|
|
state: present
|
|
update_cache: true
|
|
|
|
- name: Create JMRI system group
|
|
ansible.builtin.group:
|
|
name: "{{ jmri_group }}"
|
|
state: present
|
|
system: true
|
|
|
|
- name: Create JMRI service user
|
|
ansible.builtin.user:
|
|
name: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
home: "{{ jmri_home }}"
|
|
shell: /bin/bash
|
|
system: true
|
|
create_home: true
|
|
state: present
|
|
|
|
- name: Deploy SSH authorized key for jmri user
|
|
ansible.posix.authorized_key:
|
|
user: "{{ jmri_user }}"
|
|
key: "{{ jmri_ssh_authorized_key }}"
|
|
state: present
|
|
when: jmri_ssh_authorized_key | length > 0
|
|
|
|
- name: Add JMRI user to dialout group (serial device access)
|
|
ansible.builtin.user:
|
|
name: "{{ jmri_user }}"
|
|
groups: dialout
|
|
append: true
|
|
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Phase 2 — LocoNet traffic monitor
|
|
# Installs jmri-monitor: watches /dev/jmri/loconet, starts/stops jmri.service
|
|
# ---------------------------------------------------------------------------
|
|
- name: Install python3-venv (monitor virtualenv support)
|
|
ansible.builtin.apt:
|
|
name: python3-venv
|
|
state: present
|
|
|
|
- name: Create virtualenv for jmri-monitor
|
|
ansible.builtin.command:
|
|
cmd: python3 -m venv /opt/jmri-monitor
|
|
creates: /opt/jmri-monitor/bin/python3
|
|
|
|
- name: Install decora_wifi into jmri-monitor virtualenv
|
|
ansible.builtin.pip:
|
|
name: decora_wifi
|
|
state: present
|
|
virtualenv: /opt/jmri-monitor
|
|
|
|
- name: Deploy jmri-monitor script
|
|
ansible.builtin.template:
|
|
src: jmri-monitor.py.j2
|
|
dest: /usr/local/bin/jmri-monitor
|
|
owner: root
|
|
group: root
|
|
mode: '0755'
|
|
notify: Restart jmri-monitor
|
|
|
|
- name: Deploy jmri-monitor systemd unit
|
|
ansible.builtin.template:
|
|
src: jmri-monitor.service.j2
|
|
dest: /etc/systemd/system/jmri-monitor.service
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
notify:
|
|
- Reload systemd
|
|
- Restart jmri-monitor
|
|
|
|
- name: Enable jmri-monitor service
|
|
ansible.builtin.systemd:
|
|
name: jmri-monitor
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Phase 2b — LCRR config repo (clone/pull .jmri from Gitea)
|
|
# ---------------------------------------------------------------------------
|
|
- name: Ensure jmri .ssh directory exists
|
|
ansible.builtin.file:
|
|
path: /home/jmri/.ssh
|
|
state: directory
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
mode: '0700'
|
|
|
|
- name: Deploy jmri SSH config for Gitea
|
|
ansible.builtin.copy:
|
|
dest: /home/jmri/.ssh/config
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
mode: '0600'
|
|
content: |
|
|
Host gitea.mk-labs.cloud
|
|
HostName gitea.mk-labs.cloud
|
|
User git
|
|
Port 2221
|
|
IdentityFile {{ jmri_gitea_key }}
|
|
StrictHostKeyChecking accept-new
|
|
|
|
- name: Clone LCRR config repo if not present
|
|
ansible.builtin.git:
|
|
repo: "{{ jmri_lcrr_repo }}"
|
|
dest: "{{ jmri_home }}/LCRR"
|
|
version: "{{ jmri_lcrr_branch }}"
|
|
accept_hostkey: true
|
|
key_file: "{{ jmri_gitea_key }}"
|
|
update: false
|
|
become_user: "{{ jmri_user }}"
|
|
when: jmri_lcrr_repo | length > 0
|
|
notify: Restart jmri
|
|
|
|
- name: Remove auto-generated .jmri dir if it exists (will be replaced by symlink)
|
|
ansible.builtin.file:
|
|
path: "{{ jmri_home }}/.jmri"
|
|
state: absent
|
|
when:
|
|
- jmri_lcrr_repo | length > 0
|
|
|
|
- name: Link .jmri config from LCRR repo
|
|
ansible.builtin.file:
|
|
src: "{{ jmri_home }}/LCRR/.jmri"
|
|
dest: "{{ jmri_home }}/.jmri"
|
|
state: link
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
force: true
|
|
when: jmri_lcrr_repo | length > 0
|
|
notify: Restart jmri
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# JMRI install / upgrade — version-marker pattern
|
|
# Writes {{ jmri_install_dir }}/.jmri_installed_version after each install.
|
|
# On subsequent runs: read the marker, skip everything if it matches
|
|
# jmri_version. If it differs (or is absent), stop JMRI cleanly, wipe the
|
|
# old install, download the new archive, extract, and write the new marker.
|
|
# To upgrade: bump jmri_version + jmri_build_hash in defaults/main.yml (or
|
|
# host_vars) and re-run the playbook.
|
|
# ---------------------------------------------------------------------------
|
|
|
|
- name: Read installed JMRI version marker (if present)
|
|
ansible.builtin.slurp:
|
|
src: "{{ jmri_install_dir }}/.jmri_installed_version"
|
|
register: jmri_version_marker
|
|
ignore_errors: true
|
|
|
|
- name: Determine whether JMRI install/upgrade is needed
|
|
ansible.builtin.set_fact:
|
|
jmri_needs_install: >-
|
|
{{
|
|
jmri_version_marker is failed or
|
|
(jmri_version_marker.content | b64decode | trim) != jmri_version
|
|
}}
|
|
|
|
- name: Stop JMRI service before upgrade (if running)
|
|
ansible.builtin.systemd:
|
|
name: jmri
|
|
state: stopped
|
|
failed_when: false # service may not exist yet on first install
|
|
when: jmri_needs_install
|
|
|
|
- name: Remove existing JMRI install directory (upgrade path)
|
|
ansible.builtin.file:
|
|
path: "{{ jmri_install_dir }}"
|
|
state: absent
|
|
when: jmri_needs_install
|
|
|
|
- name: Remove stale JMRI archive from /tmp (if version changed)
|
|
ansible.builtin.file:
|
|
path: "/tmp/JMRI-{{ jmri_version }}.tgz"
|
|
state: absent
|
|
when: jmri_needs_install
|
|
|
|
- name: Download JMRI release archive
|
|
ansible.builtin.get_url:
|
|
url: "{{ jmri_download_url }}"
|
|
dest: /tmp/JMRI-{{ jmri_version }}.tgz
|
|
mode: '0644'
|
|
when: jmri_needs_install
|
|
|
|
- name: Create JMRI install directory
|
|
ansible.builtin.file:
|
|
path: "{{ jmri_install_dir }}"
|
|
state: directory
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
mode: '0755'
|
|
|
|
- name: Extract JMRI archive
|
|
ansible.builtin.unarchive:
|
|
src: /tmp/JMRI-{{ jmri_version }}.tgz
|
|
dest: "{{ jmri_install_dir }}"
|
|
remote_src: true
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
extra_opts: ['--strip-components=1']
|
|
when: jmri_needs_install
|
|
|
|
- name: Write installed version marker
|
|
ansible.builtin.copy:
|
|
content: "{{ jmri_version }}\n"
|
|
dest: "{{ jmri_install_dir }}/.jmri_installed_version"
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
mode: '0644'
|
|
when: jmri_needs_install
|
|
|
|
- name: Restore JMRI config from backup
|
|
ansible.builtin.copy:
|
|
src: "{{ jmri_config_src }}/"
|
|
dest: "{{ jmri_home }}/.jmri/"
|
|
owner: "{{ jmri_user }}"
|
|
group: "{{ jmri_group }}"
|
|
mode: '0644'
|
|
directory_mode: '0755'
|
|
when: jmri_config_src | length > 0
|
|
notify: Restart jmri
|
|
|
|
- name: Deploy JmriFaceless systemd unit
|
|
ansible.builtin.template:
|
|
src: jmri.service.j2
|
|
dest: /etc/systemd/system/jmri.service
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
notify:
|
|
- Reload systemd
|
|
- Restart jmri
|
|
|
|
- name: Enable jmri service (monitor manages start/stop — do not start directly)
|
|
ansible.builtin.systemd:
|
|
name: jmri
|
|
enabled: false
|
|
daemon_reload: true
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Phase 3 — X11 remote GUI access (retained for fallback; VNC preferred)
|
|
# ---------------------------------------------------------------------------
|
|
- name: Install xauth (required for SSH X11 forwarding fallback)
|
|
ansible.builtin.apt:
|
|
name: xauth
|
|
state: present
|
|
|
|
- name: Remove old jmri X11 sshd drop-in if present (renamed)
|
|
ansible.builtin.file:
|
|
path: /etc/ssh/sshd_config.d/20-jmri-x11.conf
|
|
state: absent
|
|
notify: Restart sshd
|
|
|
|
- name: Deploy sshd drop-in to enable X11 forwarding (must load before hardening)
|
|
ansible.builtin.copy:
|
|
dest: /etc/ssh/sshd_config.d/09-jmri-x11.conf
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
content: |
|
|
# Allow X11 forwarding for JMRI GUI sessions (jmri role)
|
|
# Must be numbered below 10-mk-labs-hardening.conf — first match wins.
|
|
X11Forwarding yes
|
|
X11UseLocalhost yes
|
|
notify: Restart sshd
|
|
|
|
# ---------------------------------------------------------------------------
|
|
# Phase 4 — Xpra virtual display
|
|
# Replaces TigerVNC. Rootless mode: each JMRI window appears as a native
|
|
# window on the client. Sessions are persistent across disconnects.
|
|
# Connect: xpra attach ssh://jmri@main-street-station/{{ jmri_xpra_display }}
|
|
# ---------------------------------------------------------------------------
|
|
- name: Remove TigerVNC (replaced by Xpra)
|
|
ansible.builtin.apt:
|
|
name: tigervnc-standalone-server
|
|
state: absent
|
|
notify: Reload systemd
|
|
|
|
- name: Disable and stop jmri-vnc service if present
|
|
ansible.builtin.systemd:
|
|
name: jmri-vnc
|
|
enabled: false
|
|
state: stopped
|
|
failed_when: false
|
|
|
|
- name: Remove jmri-vnc systemd unit if present
|
|
ansible.builtin.file:
|
|
path: /etc/systemd/system/jmri-vnc.service
|
|
state: absent
|
|
notify: Reload systemd
|
|
|
|
- name: Remove jmri VNC password directory if present
|
|
ansible.builtin.file:
|
|
path: "{{ jmri_home }}/.vnc"
|
|
state: absent
|
|
|
|
- name: Remove Ubuntu-packaged xpra (too old — v3.x, incompatible with v6 client)
|
|
ansible.builtin.apt:
|
|
name: xpra
|
|
state: absent
|
|
|
|
- name: Install xpra.org apt signing key
|
|
ansible.builtin.get_url:
|
|
url: https://xpra.org/gpg.asc
|
|
dest: /usr/share/keyrings/xpra.asc
|
|
mode: '0644'
|
|
|
|
- name: Add xpra.org upstream apt repository
|
|
ansible.builtin.copy:
|
|
dest: /etc/apt/sources.list.d/xpra.list
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
content: |
|
|
deb [arch=amd64 signed-by=/usr/share/keyrings/xpra.asc] https://xpra.org/ noble main
|
|
|
|
- name: Install Xpra from upstream repo (v6.x)
|
|
ansible.builtin.apt:
|
|
name: xpra
|
|
state: latest
|
|
update_cache: true
|
|
|
|
- name: Deploy jmri-xpra systemd unit
|
|
ansible.builtin.template:
|
|
src: jmri-xpra.service.j2
|
|
dest: /etc/systemd/system/jmri-xpra.service
|
|
owner: root
|
|
group: root
|
|
mode: '0644'
|
|
notify:
|
|
- Reload systemd
|
|
- Restart jmri-xpra
|
|
|
|
- name: Enable and start jmri-xpra service
|
|
ansible.builtin.systemd:
|
|
name: jmri-xpra
|
|
enabled: true
|
|
state: started
|
|
daemon_reload: true
|
|
|
|
- name: Deploy jmri-gui script
|
|
ansible.builtin.template:
|
|
src: jmri-gui.j2
|
|
dest: /usr/local/bin/jmri-gui
|
|
owner: root
|
|
group: root
|
|
mode: '0755'
|
|
|
|
- name: Deploy sudoers drop-in for jmri-gui (wed can manage jmri service)
|
|
ansible.builtin.copy:
|
|
dest: /etc/sudoers.d/jmri-gui
|
|
owner: root
|
|
group: root
|
|
mode: '0440'
|
|
validate: 'visudo -cf %s'
|
|
content: |
|
|
# jmri user can manage its own service (for jmri-gui interactive sessions)
|
|
jmri ALL=(root) NOPASSWD: /usr/bin/systemctl start jmri.service
|
|
jmri ALL=(root) NOPASSWD: /usr/bin/systemctl stop jmri.service
|
|
jmri ALL=(root) NOPASSWD: /opt/jmri-monitor/bin/python3
|
|
# wed retains service control for automation/admin use
|
|
wed ALL=(root) NOPASSWD: /usr/bin/systemctl start jmri.service
|
|
wed ALL=(root) NOPASSWD: /usr/bin/systemctl stop jmri.service
|
|
wed ALL=(root) NOPASSWD: /opt/jmri-monitor/bin/python3
|