feat: add JMRI headless server role and main-street-station host
- New ansible/roles/jmri role: installs OpenJDK 21 headless, creates jmri service user, downloads JMRI 5.10, deploys JmriFaceless systemd unit - Handles dialout group membership for serial device access - Config restore task for post-reinstall recovery from GitHub backup - host_vars/main-street-station: profile_id and serial device (TODO: fill in) - Inventory: jmri_server group with main-street-station at 192.168.10.45 - Playbook: day1_deploy_jmri.yml (linux-baseline + jmri)
This commit is contained in:
16
ansible/host_vars/main-street-station/main.yml
Normal file
16
ansible/host_vars/main-street-station/main.yml
Normal file
@@ -0,0 +1,16 @@
|
|||||||
|
---
|
||||||
|
# Host-specific vars for main-street-station (JMRI headless server)
|
||||||
|
# LCRR - Lake Country Railroad, Milwaukee Road Oct 1956, HO scale
|
||||||
|
|
||||||
|
# JMRI profile ID — find with: ls ~/.jmri/profiles/ on the old box
|
||||||
|
# Format: <name>.<8-char-hex> e.g. LCRR.3d3f1dfc
|
||||||
|
# TODO: fill in after restoring config from GitHub backup
|
||||||
|
jmri_profile_id: ""
|
||||||
|
|
||||||
|
# USB serial device for NCE command station
|
||||||
|
# Verify after install: ls -la /dev/ttyUSB* /dev/ttyACM*
|
||||||
|
jmri_serial_device: /dev/ttyUSB0
|
||||||
|
|
||||||
|
# Path to JMRI config backup for restore task (leave empty to skip)
|
||||||
|
# Point at a local checkout of the LCRR GitHub repo
|
||||||
|
jmri_config_src: ""
|
||||||
@@ -80,6 +80,13 @@ honcho_server:
|
|||||||
ansible_user: wed
|
ansible_user: wed
|
||||||
ansible_become: true
|
ansible_become: true
|
||||||
|
|
||||||
|
jmri_server:
|
||||||
|
hosts:
|
||||||
|
main-street-station:
|
||||||
|
ansible_host: 192.168.10.45
|
||||||
|
ansible_user: wed
|
||||||
|
ansible_become: true
|
||||||
|
|
||||||
papermc_server:
|
papermc_server:
|
||||||
# ansible-galaxy role install engonzal.papermc
|
# ansible-galaxy role install engonzal.papermc
|
||||||
hosts:
|
hosts:
|
||||||
|
|||||||
24
ansible/playbooks/day1_deploy_jmri.yml
Normal file
24
ansible/playbooks/day1_deploy_jmri.yml
Normal file
@@ -0,0 +1,24 @@
|
|||||||
|
---
|
||||||
|
# ============================================================================
|
||||||
|
# day1_deploy_jmri.yml
|
||||||
|
# ----------------------------------------------------------------------------
|
||||||
|
# Deploys JMRI JmriFaceless headless server on main-street-station.
|
||||||
|
# Applies linux-baseline first, then the jmri role.
|
||||||
|
#
|
||||||
|
# Usage:
|
||||||
|
# ansible-playbook playbooks/day1_deploy_jmri.yml
|
||||||
|
# ansible-playbook playbooks/day1_deploy_jmri.yml -e target=main-street-station
|
||||||
|
#
|
||||||
|
# Prerequisites:
|
||||||
|
# 1. Host is in inventory under jmri_server group
|
||||||
|
# 2. jmri_profile_id is set in host_vars/main-street-station.yml
|
||||||
|
# 3. SSH access as 'wed' with sudo
|
||||||
|
# ============================================================================
|
||||||
|
|
||||||
|
- name: Deploy JMRI headless server
|
||||||
|
hosts: "{{ target | default('jmri_server') }}"
|
||||||
|
become: true
|
||||||
|
gather_facts: true
|
||||||
|
roles:
|
||||||
|
- linux-baseline
|
||||||
|
- jmri
|
||||||
25
ansible/roles/jmri/defaults/main.yml
Normal file
25
ansible/roles/jmri/defaults/main.yml
Normal file
@@ -0,0 +1,25 @@
|
|||||||
|
---
|
||||||
|
# JMRI version to install
|
||||||
|
jmri_version: "5.10"
|
||||||
|
jmri_install_dir: /opt/JMRI
|
||||||
|
jmri_download_url: "https://github.com/JMRI/JMRI/releases/download/v{{ jmri_version }}/JMRI.{{ jmri_version }}+Rca461bd266.tgz"
|
||||||
|
|
||||||
|
# Service user
|
||||||
|
jmri_user: jmri
|
||||||
|
jmri_group: jmri
|
||||||
|
jmri_home: /home/jmri
|
||||||
|
|
||||||
|
# Profile — override per-host in inventory
|
||||||
|
# Find on the old box: ls ~/.jmri/profiles/
|
||||||
|
jmri_profile_id: ""
|
||||||
|
|
||||||
|
# Config restore source (path to the backed-up .jmri directory)
|
||||||
|
jmri_config_src: ""
|
||||||
|
|
||||||
|
# Ports (for documentation / firewall rules)
|
||||||
|
jmri_json_port: 12080
|
||||||
|
jmri_withrottle_port: 12090
|
||||||
|
|
||||||
|
# USB serial device for NCE (set per-host in inventory)
|
||||||
|
# e.g. /dev/ttyUSB0 or /dev/ttyACM0
|
||||||
|
jmri_serial_device: /dev/ttyUSB0
|
||||||
9
ansible/roles/jmri/handlers/main.yml
Normal file
9
ansible/roles/jmri/handlers/main.yml
Normal file
@@ -0,0 +1,9 @@
|
|||||||
|
---
|
||||||
|
- name: Reload systemd
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
daemon_reload: true
|
||||||
|
|
||||||
|
- name: Restart jmri
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: jmri
|
||||||
|
state: restarted
|
||||||
13
ansible/roles/jmri/meta/main.yml
Normal file
13
ansible/roles/jmri/meta/main.yml
Normal file
@@ -0,0 +1,13 @@
|
|||||||
|
---
|
||||||
|
galaxy_info:
|
||||||
|
role_name: jmri
|
||||||
|
author: JARVIS
|
||||||
|
description: Deploy JMRI JmriFaceless headless server as a systemd service
|
||||||
|
license: MIT
|
||||||
|
min_ansible_version: "2.12"
|
||||||
|
platforms:
|
||||||
|
- name: Ubuntu
|
||||||
|
versions:
|
||||||
|
- jammy
|
||||||
|
- noble
|
||||||
|
dependencies: []
|
||||||
87
ansible/roles/jmri/tasks/main.yml
Normal file
87
ansible/roles/jmri/tasks/main.yml
Normal file
@@ -0,0 +1,87 @@
|
|||||||
|
---
|
||||||
|
- name: Install Java runtime (headless)
|
||||||
|
ansible.builtin.apt:
|
||||||
|
name: openjdk-21-jre-headless
|
||||||
|
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: /usr/sbin/nologin
|
||||||
|
system: true
|
||||||
|
create_home: true
|
||||||
|
state: present
|
||||||
|
|
||||||
|
- name: Add JMRI user to dialout group (serial device access)
|
||||||
|
ansible.builtin.user:
|
||||||
|
name: "{{ jmri_user }}"
|
||||||
|
groups: dialout
|
||||||
|
append: true
|
||||||
|
|
||||||
|
- name: Check if JMRI is already installed at correct version
|
||||||
|
ansible.builtin.stat:
|
||||||
|
path: "{{ jmri_install_dir }}/JmriFaceless"
|
||||||
|
register: jmri_binary
|
||||||
|
|
||||||
|
- name: Download JMRI release archive
|
||||||
|
ansible.builtin.get_url:
|
||||||
|
url: "{{ jmri_download_url }}"
|
||||||
|
dest: /tmp/JMRI-{{ jmri_version }}.tgz
|
||||||
|
mode: '0644'
|
||||||
|
when: not jmri_binary.stat.exists
|
||||||
|
|
||||||
|
- 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: not jmri_binary.stat.exists
|
||||||
|
|
||||||
|
- 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 and start JMRI service
|
||||||
|
ansible.builtin.systemd:
|
||||||
|
name: jmri
|
||||||
|
enabled: true
|
||||||
|
state: started
|
||||||
|
daemon_reload: true
|
||||||
35
ansible/roles/jmri/templates/jmri.service.j2
Normal file
35
ansible/roles/jmri/templates/jmri.service.j2
Normal file
@@ -0,0 +1,35 @@
|
|||||||
|
[Unit]
|
||||||
|
Description=JMRI Server (JmriFaceless)
|
||||||
|
Documentation=https://www.jmri.org/
|
||||||
|
After=network.target
|
||||||
|
# Give USB devices time to enumerate after boot
|
||||||
|
After=dev-{{ jmri_serial_device | basename }}.device
|
||||||
|
Wants=dev-{{ jmri_serial_device | basename }}.device
|
||||||
|
|
||||||
|
[Service]
|
||||||
|
Type=simple
|
||||||
|
User={{ jmri_user }}
|
||||||
|
Group={{ jmri_group }}
|
||||||
|
WorkingDirectory={{ jmri_install_dir }}
|
||||||
|
|
||||||
|
# JmriFaceless requires a profile ID
|
||||||
|
# Find yours: ls ~/.jmri/profiles/
|
||||||
|
ExecStart={{ jmri_install_dir }}/JmriFaceless --profile={{ jmri_profile_id }}
|
||||||
|
|
||||||
|
# Restart on failure, not on clean exit (e.g. intentional shutdown from Jython)
|
||||||
|
Restart=on-failure
|
||||||
|
RestartSec=10
|
||||||
|
|
||||||
|
# Give hardware time to settle before retry
|
||||||
|
TimeoutStartSec=30
|
||||||
|
|
||||||
|
# Logging — view with: journalctl -u jmri -f
|
||||||
|
StandardOutput=journal
|
||||||
|
StandardError=journal
|
||||||
|
SyslogIdentifier=jmri
|
||||||
|
|
||||||
|
# Serial device access
|
||||||
|
SupplementaryGroups=dialout
|
||||||
|
|
||||||
|
[Install]
|
||||||
|
WantedBy=multi-user.target
|
||||||
Reference in New Issue
Block a user