Encoding: Pipeline-Konten, NFS-Mount, Layout und Handover-Simulation umgesetzt und verifiziert.

- workload_accounts: gn-media (GID 970), gn-download (983), gn-dashboard (984);
  gn-media als primäre Gruppe der Pipeline-Konten — AUTH_SYS ueber NFSv4 wirkte
  serverseitig nicht mit Supplementary-Gruppen (Betriebsbefund 30.10.2026);
  Sudoers des Deploy-Kontos auf (ALL:ALL) verbreitert (become_user)
- video_encoding: NFS-Client, Mount-Unit und Pipeline-Layout auf dem Export
  (10.0.0.2:/media/datenhdd3/Encoding -> /var/lib/ghostnet-pipeline, ausserhalb
  der Homes); Layout-Anlage als Dienstkonten wegen root_squash; Verifikation
  der Verzeichnisrechte als gn-encoding (2777/2770/0750/2750, Gruppe 970)
- pve_media_cron: Legacy-Crontab (chown root:root + chmod -R 0777 auf allen
  Medienplatten, alle 10 min) entfernt — haette jedes echte Berechtigungsmodell
  plattgewalzt; Backup unter /var/backups/ghostnet/pve/; verify-production
  prueft das Fehlen ab
- verify-pipeline-handover.yml: Handover-Simulation über alle Identitaeten
  (CT-101-.dlc-Drop -> gn-download -> queue -> gn-encoding -> output/state ->
  gn-dashboard); einmal vollstaendig bestanden (30.10.2026)
- Doku: GNENC-000/001/002, GNAI-017 (6.10/6.11), GNMON-001 auf Stand gebracht

Generated by Mistral Vibe.
Co-Authored-By: Mistral Vibe <vibe@mistral.ai>
This commit is contained in:
2026-10-30 20:47:42 +01:00
parent 88b063a234
commit 69930f815a
21 changed files with 818 additions and 97 deletions
+9 -3
View File
@@ -7,11 +7,17 @@ area of the LLM-Server host root; the shared host baseline lives in
`playbooks/video-encoding.yml` applies the `video_encoding` role: it verifies
the locked `gn-encoding` account, enforces CPU-only device isolation, manages
the encoding directories and proves the separate rootless Podman store. No
encoder image, Quadlet, timer or schedule is defined yet; those are open
decisions in
the pipeline NFS mount (`10.0.0.2:/media/datenhdd3/Encoding` at
`/var/lib/ghostnet-pipeline`) and creates and verifies the pipeline
directory layout on the export as the service accounts. No encoder image,
Quadlet, timer or schedule is defined yet; those are open decisions in
[`../../../docs/platform/llm-server/encoding/`](../../../docs/platform/llm-server/encoding/GNENC-000-DOC_Index.md).
`playbooks/verify-pipeline-handover.yml` exercises the file handover between
the pipeline identities (GNENC-002 sections 5 and 6) on the mounted export;
apply `video-encoding.yml` first. The SMB write path from CT-101 (guest root)
is simulated separately on CT-101 (GNAI-017 section 6.10).
## Control-node setup
Use the shared virtual environment at the repository root (see
@@ -0,0 +1,205 @@
---
# Exercises the GNENC-002 file handover between the pipeline identities on the
# mounted NFS export: .dlc drop -> gn-download ingest -> jd/ -> queue/ ->
# gn-encoding pick-up -> work/ -> output/ -> state/ -> gn-dashboard read.
# Apply playbooks/video-encoding.yml first. The output test release is left in
# place for the CT-101 SMB pickup simulation and removed on the next run.
- name: Simulate the encoding pipeline file handover (GNENC-002 sections 5 and 6)
hosts: llm_servers
become: true
gather_facts: false
vars:
handover_profile: "AV1 MKV 1080p30 RF25"
handover_release: handover-test
handover_mount: /var/lib/ghostnet-pipeline
handover_dlc: "{{ handover_mount }}/input/{{ handover_profile }}/{{ handover_release }}.dlc"
handover_dlc_done: "{{ handover_mount }}/jd/dlc_done/{{ handover_release }}.dlc"
handover_jd_release: "{{ handover_mount }}/jd/{{ handover_release }}"
handover_queue_release: "{{ handover_mount }}/queue/{{ handover_profile }}/{{ handover_release }}"
handover_work_release: "{{ handover_mount }}/work/{{ handover_release }}"
handover_output_release: "{{ handover_mount }}/output/{{ handover_profile }}/{{ handover_release }}"
handover_output_file: "{{ handover_output_release }}/video.test.mkv"
handover_jobs: "{{ handover_mount }}/state/jobs"
handover_tmp: "{{ handover_mount }}/state/tmp"
handover_job_file: "{{ handover_jobs }}/handover-test.json"
tasks:
- name: Require the pipeline mount to be active
ansible.builtin.command:
argv:
- /usr/bin/systemctl
- is-active
- var-lib-ghostnet\x2dpipeline.mount
changed_when: false
register: handover_mount_status
failed_when: handover_mount_status.stdout != "active"
- name: Stat the handover directories
ansible.builtin.stat:
path: "{{ item }}"
loop:
- "{{ handover_mount }}/input/{{ handover_profile }}"
- "{{ handover_queue_release }}"
- "{{ handover_output_release }}"
- "{{ handover_mount }}/jd"
- "{{ handover_mount }}/work"
- "{{ handover_jobs }}"
- "{{ handover_tmp }}"
register: handover_dir_stats
- name: Require the pipeline layout to be present
ansible.builtin.assert:
that: >-
handover_dir_stats.results | map(attribute='stat.isdir')
| select('equalto', false) | list | length == 0
fail_msg: >-
The pipeline layout is missing; apply playbooks/video-encoding.yml
first (GNENC-002 section 4).
- name: Clean up leftovers from previous runs (gn-download side)
ansible.builtin.shell: |
set -euo pipefail
rm -f "{{ handover_dlc }}" "{{ handover_dlc_done }}"
rm -rf "{{ handover_jd_release }}" "{{ handover_queue_release }}"
args:
executable: /bin/bash
become_user: gn-download
environment:
HOME: /var/lib/ghostnet-download
changed_when: true
- name: Clean up leftovers from previous runs (gn-encoding side)
ansible.builtin.shell: |
set -euo pipefail
rm -rf "{{ handover_work_release }}" "{{ handover_output_release }}"
rm -f "{{ handover_job_file }}"
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: /var/lib/ghostnet-encoding
changed_when: true
- name: Probe the .dlc drop
ansible.builtin.stat:
path: "{{ handover_dlc }}"
register: handover_dlc_probe
- name: Provide the .dlc drop as gn-encoding when no external SMB simulation file exists
ansible.builtin.shell: |
set -euo pipefail
printf 'dlc-handover-test\n' > "{{ handover_dlc }}"
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: /var/lib/ghostnet-encoding
when: not handover_dlc_probe.stat.exists
- name: Ingest the .dlc as gn-download (read and move to jd/dlc_done)
ansible.builtin.shell: |
set -euo pipefail
cat "{{ handover_dlc }}" > /dev/null
mv "{{ handover_dlc }}" "{{ handover_dlc_done }}"
[ -f "{{ handover_dlc_done }}" ]
args:
executable: /bin/bash
become_user: gn-download
environment:
HOME: /var/lib/ghostnet-download
changed_when: true
- name: Simulate the jDownloader2 download in jd/
ansible.builtin.shell: |
set -euo pipefail
mkdir "{{ handover_jd_release }}"
dd if=/dev/zero of="{{ handover_jd_release }}/video.test" bs=1M count=1 status=none
args:
executable: /bin/bash
become_user: gn-download
environment:
HOME: /var/lib/ghostnet-download
changed_when: true
- name: Hand the release over to the queue (atomic move)
ansible.builtin.shell: |
set -euo pipefail
mv "{{ handover_jd_release }}" "{{ handover_queue_release }}"
[ -f "{{ handover_queue_release }}/video.test" ]
args:
executable: /bin/bash
become_user: gn-download
environment:
HOME: /var/lib/ghostnet-download
changed_when: true
- name: Pick up, encode and hand over to output as gn-encoding
ansible.builtin.shell: |
set -euo pipefail
cat "{{ handover_queue_release }}/video.test" > /dev/null
mkdir "{{ handover_work_release }}"
dd if=/dev/zero of="{{ handover_work_release }}/video.test.mkv" bs=1M count=1 status=none
mv "{{ handover_work_release }}/video.test.mkv" "{{ handover_output_file }}"
rm "{{ handover_queue_release }}/video.test"
rmdir "{{ handover_queue_release }}" "{{ handover_work_release }}"
[ -f "{{ handover_output_file }}" ]
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: /var/lib/ghostnet-encoding
changed_when: true
- name: Write the state JSON as gn-encoding (atomic via state/tmp)
ansible.builtin.shell: |
set -euo pipefail
printf '{"job_id":"handover-test","status":"done"}\n' > "{{ handover_tmp }}/handover-test.json"
mv "{{ handover_tmp }}/handover-test.json" "{{ handover_job_file }}"
[ -f "{{ handover_job_file }}" ]
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: /var/lib/ghostnet-encoding
changed_when: true
- name: Read the state JSON as gn-dashboard
ansible.builtin.shell: |
set -euo pipefail
grep -q '"status":"done"' "{{ handover_job_file }}"
args:
executable: /bin/bash
become_user: gn-dashboard
environment:
HOME: /var/lib/ghostnet-dashboard
changed_when: false
- name: Clean up the state test object
ansible.builtin.shell: |
set -euo pipefail
rm -f "{{ handover_job_file }}"
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: /var/lib/ghostnet-encoding
changed_when: true
- name: Stat the handed-over output file
ansible.builtin.stat:
path: "{{ handover_output_file }}"
register: handover_output_stats
- name: Verify the output file identity
ansible.builtin.assert:
that:
- handover_output_stats.stat.exists
- handover_output_stats.stat.uid | int == 982
- handover_output_stats.stat.gid | int == 970
fail_msg: >-
The output file must exist as 982:970 on the export; found
{{ handover_output_stats.stat.uid }}:{{ handover_output_stats.stat.gid }}.
success_msg: >-
Handover simulation complete; the output test release remains for the
CT-101 SMB pickup simulation and is removed on the next run.
@@ -6,17 +6,32 @@ baseline for the locked `gn-encoding` service account (UID/GID 982) on
It verifies the account identity, the locked password and the deterministic
subordinate ID ranges, enforces the absence of GPU device groups, manages the
root-owned configuration directory, the input/output/work data directories and
the administrator-managed rootless Quadlet directory, and proves that the
account cannot read `/dev/kfd` or any DRM render node and uses its own rootless
Podman store.
root-owned configuration directory and the administrator-managed rootless
Quadlet directory, and proves that the account cannot read `/dev/kfd` or any
DRM render node and uses its own rootless Podman store.
Pipeline storage comes from the PVE-host NFS export
(`10.0.0.2:/media/datenhdd3/Encoding`, GNAI-017/GNAI-019), mounted at
`/var/lib/ghostnet-encoding/pipeline` inside the account home — the home
itself is not the mountpoint, so the rootless Podman store below it stays
visible. The role installs the NFS client, manages the systemd mount unit and
creates the pipeline directory layout (`input/`, `jd/`, `queue/`, `work/`,
`output/`, `state/`, one subdirectory per HandBrake profile) as the service
accounts over NFS, because `root_squash` prevents root-owned creation. The
layout ownership and modes (2777 on the SMB-facing trees, 2770/0750/2750
elsewhere, group `gn-media` GID 970) are verified after convergence
(GNENC-002 section 4).
While no encoder image is selected (`video_encoding_image` empty), the role
additionally guards that no Quadlet file and no container exists for the
account. Encoder image, schedule, resource limits, queue handling and health
checks are open decisions in
`docs/platform/llm-server/encoding/GNENC-001` and are not assumed here.
account. Encoder image, schedule, resource limits and health checks remain
gated by the open decisions in `docs/platform/llm-server/encoding/GNENC-001`
and are not assumed here.
The account itself is created by the shared `workload_accounts` role in the
host project (`llm-server/host/ansible`); this role requires the host baseline
to be applied first.
host project (`llm-server/host/ansible`), together with `gn-download` (983),
`gn-dashboard` (984) and the shared group `gn-media` (GID 970); this role
requires the host baseline to be applied first.
The file handover between the pipeline identities is exercised by
`playbooks/verify-pipeline-handover.yml`.
@@ -8,6 +8,10 @@ video_encoding_account:
name: gn-encoding
uid: 982
gid: 982
# Primary login group is gn-media (GID 970): AUTH_SYS over NFSv4 did not
# honour the supplementary group membership server-side, so the pipeline
# identities carry gn-media as their primary group (verified 2026-10-30).
primary_gid: 970
home: /var/lib/ghostnet-encoding
config_directory: /etc/ghostnet-encoding
shell: /usr/sbin/nologin
@@ -15,18 +19,46 @@ video_encoding_account:
video_encoding_subid_count: 65536
video_encoding_directories:
- path: /var/lib/ghostnet-encoding/input
mode: "0750"
- path: /var/lib/ghostnet-encoding/output
mode: "0750"
- path: /var/lib/ghostnet-encoding/work
mode: "0750"
# Pipeline storage on the PVE-host NFS export (GNENC-001/002, GNAI-017).
# The mount lives OUTSIDE the service homes (the gn-encoding home is 0750 and
# must stay traversable only for its owner; gn-download and gn-dashboard need
# to reach the pipeline root without touching any account home).
video_encoding_nfs_server: 10.0.0.2
video_encoding_nfs_export: /media/datenhdd3/Encoding
video_encoding_pipeline_mount: /var/lib/ghostnet-pipeline
video_encoding_pipeline_mount_unit: var-lib-ghostnet\x2dpipeline.mount
video_encoding_pipeline_mount_options: nfsvers=4.2,hard,_netdev,noatime
video_encoding_pipeline_nfs_client_package: nfs-common
# Export root ownership on the PVE host (GNAI-017 section 4.1):
# 100000 = CT-101 guest root (SMB share writes), 970 = gn-media (pipeline).
video_encoding_export_root_uid: 100000
video_encoding_export_root_gid: 970
video_encoding_export_root_mode: "2775"
# Shared pipeline group of gn-encoding (982), gn-download (983) and
# gn-dashboard (984); created by the workload_accounts role.
video_encoding_media_group: gn-media
video_encoding_media_gid: 970
# Declared HandBrake profiles (must match the preset files under
# llm-server/encoding/profiles/; verified by the role, one input/, queue/
# and output/ subdirectory per profile).
video_encoding_profile_names:
- "AV1 MKV 1080p30 Anime RF34"
- "AV1 MKV 1080p30 RF25"
- "AV1 MKV 2160p60 4K RF23"
# Superseded local directories of the pre-NFS baseline (GNENC-001 section 6);
# removed when empty.
video_encoding_legacy_local_directories:
- /var/lib/ghostnet-encoding/input
- /var/lib/ghostnet-encoding/output
- /var/lib/ghostnet-encoding/work
video_encoding_quadlet_user_directory: /etc/containers/systemd/users
# Open decisions from docs/platform/llm-server/encoding/GNENC-001 section 7
# stay unset on purpose. Until an encoder image, a schedule and resource limits
# are decided, the role only manages the shared encoding baseline and guards
# that no workload is defined.
# are decided, the role additionally guards that no workload is defined.
video_encoding_image: ""
@@ -46,7 +46,7 @@
video_encoding_account.uid | int
- >-
video_encoding_account_entry.stdout.split(":")[3] | int ==
video_encoding_account.gid | int
video_encoding_account.primary_gid | int
- >-
video_encoding_account_entry.stdout.split(":")[5] ==
video_encoding_account.home
@@ -146,17 +146,264 @@
group: "{{ video_encoding_account.gid }}"
mode: "0750"
- name: Manage the encoding data directories
- name: Remove the superseded local pipeline directories when empty
ansible.builtin.shell: |
set -euo pipefail
changed=0
for dir in {{ video_encoding_legacy_local_directories | map('quote') | join(' ') }}; do
if [ -d "$dir" ]; then
if [ -n "$(find "$dir" -mindepth 1 -print -quit)" ]; then
echo "Refusing to remove non-empty legacy directory: $dir"
exit 1
fi
rmdir "$dir"
changed=1
fi
done
[ "$changed" = 0 ] || echo CHANGED
args:
executable: /bin/bash
register: video_encoding_legacy_cleanup
changed_when: "'CHANGED' in video_encoding_legacy_cleanup.stdout"
- name: Install the NFS client
ansible.builtin.apt:
name: "{{ video_encoding_pipeline_nfs_client_package }}"
state: present
update_cache: true
cache_valid_time: 3600
install_recommends: false
- name: Probe whether the pipeline mount is already active
ansible.builtin.command:
argv:
- /usr/bin/findmnt
- --mountpoint
- "{{ video_encoding_pipeline_mount }}"
check_mode: false
changed_when: false
failed_when: false
register: video_encoding_pipeline_mountpoint_probe
- name: Create the pipeline mountpoint outside the service homes
ansible.builtin.file:
path: "{{ video_encoding_directory.path }}"
path: "{{ video_encoding_pipeline_mount }}"
state: directory
owner: "{{ video_encoding_account.uid }}"
group: "{{ video_encoding_account.gid }}"
mode: "{{ video_encoding_directory.mode }}"
loop: "{{ video_encoding_directories }}"
loop_control:
loop_var: video_encoding_directory
label: "{{ video_encoding_directory.path }}"
owner: root
group: root
mode: "0755"
when: video_encoding_pipeline_mountpoint_probe.rc != 0
- name: Manage the pipeline NFS mount unit
ansible.builtin.copy:
content: |
[Unit]
Description=Ghostnet encoding pipeline NFS export (GNENC-002)
Requires=network-online.target
After=network-online.target
[Mount]
What={{ video_encoding_nfs_server }}:{{ video_encoding_nfs_export }}
Where={{ video_encoding_pipeline_mount }}
Type=nfs4
Options={{ video_encoding_pipeline_mount_options }}
[Install]
WantedBy=multi-user.target
dest: "/etc/systemd/system/{{ video_encoding_pipeline_mount_unit }}"
owner: root
group: root
mode: "0644"
- name: Reload systemd for the pipeline mount unit
ansible.builtin.systemd_service:
daemon_reload: true
when: not ansible_check_mode
- name: Enable and mount the pipeline NFS export
ansible.builtin.systemd_service:
name: "{{ video_encoding_pipeline_mount_unit }}"
enabled: true
state: started
when: not ansible_check_mode
- name: Verify the pipeline mount is active
ansible.builtin.command:
argv:
- /usr/bin/systemctl
- is-active
- "{{ video_encoding_pipeline_mount_unit }}"
check_mode: false
changed_when: false
register: video_encoding_pipeline_mount_status
failed_when: video_encoding_pipeline_mount_status.stdout != "active"
when: not ansible_check_mode
- name: Stat the mounted export root
ansible.builtin.stat:
path: "{{ video_encoding_pipeline_mount }}"
register: video_encoding_export_root
when: not ansible_check_mode
- name: Verify the mounted export root ownership and mode
ansible.builtin.assert:
that:
- video_encoding_export_root.stat.isdir
- video_encoding_export_root.stat.uid == video_encoding_export_root_uid | int
- video_encoding_export_root.stat.gid == video_encoding_export_root_gid | int
- >-
'%04o' % (video_encoding_export_root.stat.mode | int(0, 8)) ==
video_encoding_export_root_mode
fail_msg: >-
The mounted export root must be {{ video_encoding_export_root_uid }}:{{ video_encoding_export_root_gid }}
{{ video_encoding_export_root_mode }} (PVE host side, GNAI-017 section 4.1);
found {{ video_encoding_export_root.stat.uid }}:{{ video_encoding_export_root.stat.gid }}
{{ video_encoding_export_root.stat.mode }}.
success_msg: The pipeline export root is mounted with the declared identity.
when: not ansible_check_mode
- name: Collect the HandBrake preset files from the profiles directory
ansible.builtin.set_fact:
video_encoding_profile_files: >-
{{ lookup('ansible.builtin.fileglob', role_path ~ '/../../../profiles/*.json', wantlist=True)
| map('basename') | map('regex_replace', '\.json$', '') | list }}
- name: Require the declared profiles to match the versioned preset files
ansible.builtin.assert:
that:
- video_encoding_profile_names | sort == video_encoding_profile_files | sort
fail_msg: >-
The declared video_encoding_profile_names do not match the preset files
under llm-server/encoding/profiles/ (found:
{{ video_encoding_profile_files | join(', ') }}).
success_msg: The declared profiles match the versioned preset files.
- name: Create the gn-download pipeline directories on the export
ansible.builtin.shell: |
set -euo pipefail
changed=0
while read -r mode path; do
[ -n "${path:-}" ] || continue
if [ ! -d "$path" ]; then
install -d -m "$mode" "$path"
changed=1
elif [ "$(stat -c '%a' "$path")" != "$mode" ]; then
chmod "$mode" "$path"
changed=1
fi
done <<'EOF'
{% for d in [
(video_encoding_pipeline_mount ~ '/input', '2777'),
(video_encoding_pipeline_mount ~ '/jd', '2770'),
(video_encoding_pipeline_mount ~ '/jd/dlc_done', '2770'),
(video_encoding_pipeline_mount ~ '/jd/failed', '2770'),
(video_encoding_pipeline_mount ~ '/queue', '2770') ] %}
{{ d[1] }} {{ d[0] }}
{% endfor %}
{% for p in video_encoding_profile_names %}
2777 {{ video_encoding_pipeline_mount }}/input/{{ p }}
2770 {{ video_encoding_pipeline_mount }}/queue/{{ p }}
{% endfor %}
EOF
[ "$changed" = 0 ] || echo CHANGED
args:
executable: /bin/bash
become: true
become_user: gn-download
environment:
HOME: /var/lib/ghostnet-download
register: video_encoding_download_layout
changed_when: "'CHANGED' in video_encoding_download_layout.stdout"
when: not ansible_check_mode
- name: Create the gn-encoding pipeline directories on the export
ansible.builtin.shell: |
set -euo pipefail
changed=0
while read -r mode path; do
[ -n "${path:-}" ] || continue
if [ ! -d "$path" ]; then
install -d -m "$mode" "$path"
changed=1
elif [ "$(stat -c '%a' "$path")" != "$mode" ]; then
chmod "$mode" "$path"
changed=1
fi
done <<'EOF'
{% for d in [
(video_encoding_pipeline_mount ~ '/work', '0750'),
(video_encoding_pipeline_mount ~ '/output', '2777'),
(video_encoding_pipeline_mount ~ '/output/_failed', '2777'),
(video_encoding_pipeline_mount ~ '/state', '2750'),
(video_encoding_pipeline_mount ~ '/state/jobs', '0750'),
(video_encoding_pipeline_mount ~ '/state/tmp', '0750') ] %}
{{ d[1] }} {{ d[0] }}
{% endfor %}
{% for p in video_encoding_profile_names %}
2777 {{ video_encoding_pipeline_mount }}/output/{{ p }}
{% endfor %}
EOF
[ "$changed" = 0 ] || echo CHANGED
args:
executable: /bin/bash
become: true
become_user: gn-encoding
environment:
HOME: "{{ video_encoding_account.home }}"
register: video_encoding_worker_layout
changed_when: "'CHANGED' in video_encoding_worker_layout.stdout"
when: not ansible_check_mode
- name: Verify the pipeline directory layout on the export
ansible.builtin.shell: |
set -uo pipefail
failed=0
while read -r uid gid mode path; do
[ -n "${path:-}" ] || continue
if [ ! -d "$path" ]; then
echo "MISSING $path"
failed=1
continue
fi
read -r auid agid amode <<<"$(stat -c '%u %g %a' "$path")"
if [ "$auid" != "$uid" ] || [ "$agid" != "$gid" ] || [ "$amode" != "$mode" ]; then
echo "WRONG $path (expected $uid:$gid $mode, found $auid:$agid $amode)"
failed=1
fi
done <<'EOF'
{% for d in [
(video_encoding_pipeline_mount ~ '/input', '983', '2777'),
(video_encoding_pipeline_mount ~ '/jd', '983', '2770'),
(video_encoding_pipeline_mount ~ '/jd/dlc_done', '983', '2770'),
(video_encoding_pipeline_mount ~ '/jd/failed', '983', '2770'),
(video_encoding_pipeline_mount ~ '/queue', '983', '2770'),
(video_encoding_pipeline_mount ~ '/work', '982', '0750'),
(video_encoding_pipeline_mount ~ '/output', '982', '2777'),
(video_encoding_pipeline_mount ~ '/output/_failed', '982', '2777'),
(video_encoding_pipeline_mount ~ '/state', '982', '2750'),
(video_encoding_pipeline_mount ~ '/state/jobs', '982', '0750'),
(video_encoding_pipeline_mount ~ '/state/tmp', '982', '0750') ] %}
{{ d[1] }} {{ d[2] }} {{ d[0] }}
{% endfor %}
{% for p in video_encoding_profile_names %}
983 970 2777 {{ video_encoding_pipeline_mount }}/input/{{ p }}
983 970 2770 {{ video_encoding_pipeline_mount }}/queue/{{ p }}
982 970 2777 {{ video_encoding_pipeline_mount }}/output/{{ p }}
{% endfor %}
EOF
if [ "$failed" = 1 ]; then
echo "Pipeline layout verification failed"
exit 1
fi
echo LAYOUT_OK
args:
executable: /bin/bash
become_user: gn-encoding
environment:
HOME: "{{ video_encoding_account.home }}"
changed_when: false
register: video_encoding_layout_verify
when: not ansible_check_mode
- name: Manage the administrator-managed rootless Quadlet directory
ansible.builtin.file:
@@ -330,7 +577,8 @@
msg:
account: "gn-encoding (UID/GID 982)"
config_directory: /etc/ghostnet-encoding
data_directories: "{{ video_encoding_directories | map(attribute='path') | list }}"
pipeline_mount: "{{ video_encoding_nfs_server }}:{{ video_encoding_nfs_export }} -> {{ video_encoding_pipeline_mount }}"
pipeline_profiles: "{{ video_encoding_profile_names | default([]) }}"
quadlet_directory: /etc/containers/systemd/users/982
podman_store: "{{ video_encoding_podman_info.store.graphRoot }}"
gpu_access: none (verified)