chore(install): sync playbooks and installers with pve audit
This commit is contained in:
@@ -0,0 +1,66 @@
|
||||
---
|
||||
- name: Deploy pfSense ActivityWatch poller
|
||||
hosts: aw_pfsense_pollers
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
aw_pfsense_install_root: "/opt/aw-pfsense"
|
||||
aw_pfsense_config_dir: "/etc/aw-pfsense"
|
||||
aw_pfsense_service_name: "aw-pfsense-poller.service"
|
||||
|
||||
tasks:
|
||||
- name: Install required packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- python3
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure directories exist
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ aw_pfsense_install_root }}"
|
||||
- "{{ aw_pfsense_config_dir }}"
|
||||
|
||||
- name: Install pfSense poller script
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.py"
|
||||
dest: "{{ aw_pfsense_install_root }}/pfsense-aw-poller.py"
|
||||
mode: "0755"
|
||||
|
||||
- name: Install systemd service
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_repo_root }}/pfsense/pfsense-aw-poller.service"
|
||||
dest: "/etc/systemd/system/{{ aw_pfsense_service_name }}"
|
||||
mode: "0644"
|
||||
notify:
|
||||
- Reload systemd
|
||||
|
||||
- name: Write pfSense poller config
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ aw_pfsense_config_dir }}/poller.json"
|
||||
mode: "0600"
|
||||
content: "{{ aw_pfsense_poller_config | to_nice_json }}"
|
||||
notify:
|
||||
- Restart pfSense poller
|
||||
|
||||
- name: Enable and start pfSense poller
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ aw_pfsense_service_name }}"
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
handlers:
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart pfSense poller
|
||||
ansible.builtin.systemd:
|
||||
name: "{{ aw_pfsense_service_name }}"
|
||||
state: restarted
|
||||
@@ -0,0 +1,229 @@
|
||||
---
|
||||
- name: Deploy AWatch-rus server
|
||||
hosts: aw_server
|
||||
become: true
|
||||
gather_facts: true
|
||||
|
||||
vars:
|
||||
aw_release_root: "/opt/activitywatch/releases"
|
||||
aw_release_dir: "{{ aw_release_root }}/{{ aw_server_version }}"
|
||||
aw_archive_path: "/tmp/activitywatch-{{ aw_server_version }}.zip"
|
||||
aw_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||
aw_ru_patch_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-ru-patch.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||
aw_sw_cleanup_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-sw-cleanup.js') | hash('sha1') | truncate(12, true, '') }}"
|
||||
aw_host_groups_cache_bust: "{{ lookup('file', aw_repo_root + '/aw-server/aw-host-groups.json') | hash('sha1') | truncate(12, true, '') }}"
|
||||
aw_worktime_classes: "{{ lookup('file', aw_repo_root + '/aw-server/settings/classes-worktime.json') | from_json }}"
|
||||
aw_default_views: "{{ lookup('file', aw_repo_root + '/aw-server/settings/views-default.json') | from_json }}"
|
||||
|
||||
tasks:
|
||||
- name: Install base packages
|
||||
ansible.builtin.apt:
|
||||
name:
|
||||
- curl
|
||||
- unzip
|
||||
state: present
|
||||
update_cache: true
|
||||
|
||||
- name: Ensure service account exists
|
||||
ansible.builtin.user:
|
||||
name: "{{ aw_server_user }}"
|
||||
group: "{{ aw_server_group }}"
|
||||
home: "{{ aw_server_data_dir }}"
|
||||
shell: /usr/sbin/nologin
|
||||
system: true
|
||||
create_home: false
|
||||
|
||||
- name: Ensure required directories
|
||||
ansible.builtin.file:
|
||||
path: "{{ item }}"
|
||||
state: directory
|
||||
owner: "{{ aw_server_user }}"
|
||||
group: "{{ aw_server_group }}"
|
||||
mode: "0755"
|
||||
loop:
|
||||
- "{{ aw_release_root }}"
|
||||
- "{{ aw_release_dir }}"
|
||||
- "{{ aw_server_webui_dir }}"
|
||||
- "{{ aw_server_data_dir }}"
|
||||
- "{{ aw_server_log_dir }}"
|
||||
- /etc/activitywatch
|
||||
- "{{ aw_bootstrap_dir }}"
|
||||
|
||||
- name: Download ActivityWatch release archive
|
||||
ansible.builtin.get_url:
|
||||
url: "{{ aw_server_download_url }}"
|
||||
dest: "{{ aw_archive_path }}"
|
||||
mode: "0644"
|
||||
|
||||
- name: Unpack ActivityWatch release
|
||||
ansible.builtin.unarchive:
|
||||
src: "{{ aw_archive_path }}"
|
||||
dest: "{{ aw_release_dir }}"
|
||||
remote_src: true
|
||||
extra_opts: ["-o"]
|
||||
|
||||
- name: Discover extracted AW directory
|
||||
ansible.builtin.find:
|
||||
paths: "{{ aw_release_dir }}"
|
||||
file_type: directory
|
||||
patterns: "activitywatch*"
|
||||
register: aw_release_find
|
||||
|
||||
- name: Set release extracted path
|
||||
ansible.builtin.set_fact:
|
||||
aw_release_extracted: "{{ (aw_release_find.files | sort(attribute='path') | map(attribute='path') | list | first) }}"
|
||||
|
||||
- name: Verify extracted directory exists
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- aw_release_extracted is defined
|
||||
- aw_release_extracted | length > 0
|
||||
fail_msg: "Cannot locate extracted ActivityWatch release directory."
|
||||
|
||||
- name: Sync release content to /opt/activitywatch
|
||||
ansible.builtin.command:
|
||||
cmd: "rsync -a --delete {{ aw_release_extracted }}/ /opt/activitywatch/"
|
||||
|
||||
- name: Copy bootstrap files from repository
|
||||
ansible.builtin.copy:
|
||||
src: "{{ item.src }}"
|
||||
dest: "{{ item.dest }}"
|
||||
mode: "{{ item.mode }}"
|
||||
loop:
|
||||
- { src: "{{ aw_repo_root }}/aw-server/activitywatch-server.service", dest: "/etc/systemd/system/activitywatch-server.service", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-ru-patch.js", dest: "{{ aw_server_webui_dir }}/js/ru-patch-v5.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-sw-cleanup.js", dest: "{{ aw_server_webui_dir }}/js/sw-cleanup.js", mode: "0644" }
|
||||
- { src: "{{ aw_repo_root }}/aw-server/aw-host-groups.json", dest: "{{ aw_server_webui_dir }}/js/aw-host-groups.json", mode: "0644" }
|
||||
notify:
|
||||
- Reload systemd
|
||||
- Restart activitywatch
|
||||
|
||||
- name: Copy WebUI index template from installed distribution
|
||||
ansible.builtin.copy:
|
||||
remote_src: true
|
||||
src: "/opt/activitywatch/aw-webui/index.html"
|
||||
dest: "{{ aw_server_webui_dir }}/index.html"
|
||||
mode: "0644"
|
||||
|
||||
- name: Insert RU patch scripts into index.html
|
||||
ansible.builtin.replace:
|
||||
path: "{{ aw_server_webui_dir }}/index.html"
|
||||
regexp: '</head>'
|
||||
replace: '<script src="/js/sw-cleanup.js?v={{ aw_sw_cleanup_cache_bust }}"></script></head>'
|
||||
|
||||
- name: Insert RU patch loader before body end
|
||||
ansible.builtin.replace:
|
||||
path: "{{ aw_server_webui_dir }}/index.html"
|
||||
regexp: '</body>'
|
||||
replace: '<script defer="defer" src="/js/ru-patch-v5.js?v={{ aw_ru_patch_cache_bust }}"></script></body>'
|
||||
|
||||
- name: Write /etc/activitywatch/aw-server.env
|
||||
ansible.builtin.copy:
|
||||
dest: /etc/activitywatch/aw-server.env
|
||||
mode: "0640"
|
||||
content: |
|
||||
AW_SERVER_HOST={{ aw_server_bind_host }}
|
||||
AW_SERVER_PORT={{ aw_server_port }}
|
||||
AW_DATA_DIR={{ aw_server_data_dir }}
|
||||
AW_LOG_DIR={{ aw_server_log_dir }}
|
||||
AW_WEBUI_DIR={{ aw_server_webui_dir }}
|
||||
AW_SERVER_USER={{ aw_server_user }}
|
||||
AW_SERVER_GROUP={{ aw_server_group }}
|
||||
|
||||
- name: Enable and start service
|
||||
ansible.builtin.systemd:
|
||||
name: activitywatch-server.service
|
||||
enabled: true
|
||||
state: restarted
|
||||
daemon_reload: true
|
||||
|
||||
- name: Wait for API
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/info"
|
||||
method: GET
|
||||
status_code: 200
|
||||
register: aw_api
|
||||
retries: 10
|
||||
delay: 3
|
||||
until: aw_api.status == 200
|
||||
|
||||
- name: Apply baseline worktime settings (classes)
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/classes"
|
||||
method: POST
|
||||
body: "{{ aw_worktime_classes }}"
|
||||
body_format: json
|
||||
status_code: 201
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Apply baseline views (include DLP and worktime)
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/views"
|
||||
method: POST
|
||||
body: "{{ aw_default_views }}"
|
||||
body_format: json
|
||||
status_code: 201
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Derive worktime durationDefault from aw_worktime_from/to
|
||||
ansible.builtin.set_fact:
|
||||
aw_worktime_from_h: "{{ (aw_worktime_from | default('08:00')).split(':')[0] | int }}"
|
||||
aw_worktime_from_m: "{{ (aw_worktime_from | default('08:00')).split(':')[1] | int }}"
|
||||
aw_worktime_to_h: "{{ (aw_worktime_to | default('17:00')).split(':')[0] | int }}"
|
||||
aw_worktime_to_m: "{{ (aw_worktime_to | default('17:00')).split(':')[1] | int }}"
|
||||
aw_worktime_duration_default_derived: >-
|
||||
{{
|
||||
(
|
||||
(
|
||||
((aw_worktime_to_h | int) * 60 + (aw_worktime_to_m | int)) -
|
||||
((aw_worktime_from_h | int) * 60 + (aw_worktime_from_m | int))
|
||||
) * 60
|
||||
)
|
||||
}}
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Normalize derived durationDefault for overnight shifts
|
||||
ansible.builtin.set_fact:
|
||||
aw_worktime_duration_default_effective: >-
|
||||
{{
|
||||
(aw_worktime_duration_default_derived | int)
|
||||
if (aw_worktime_duration_default_derived | int) > 0
|
||||
else ((aw_worktime_duration_default_derived | int) + 86400)
|
||||
}}
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Validate derived durationDefault is sane
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- aw_worktime_duration_default_effective | int > 0
|
||||
- aw_worktime_duration_default_effective | int <= 86400
|
||||
fail_msg: "Invalid worktime window: {{ aw_worktime_from }}..{{ aw_worktime_to }}"
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Apply baseline worktime period (startOfDay)
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/startOfDay"
|
||||
method: POST
|
||||
body: "{{ aw_worktime_start_of_day }}"
|
||||
body_format: json
|
||||
status_code: 200
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
- name: Apply baseline worktime period (durationDefault seconds)
|
||||
ansible.builtin.uri:
|
||||
url: "http://127.0.0.1:{{ aw_server_port }}/api/0/settings/durationDefault"
|
||||
method: POST
|
||||
body: "{{ aw_worktime_duration_default_effective }}"
|
||||
body_format: json
|
||||
status_code: 200
|
||||
when: aw_apply_worktime_settings | default(false) | bool
|
||||
|
||||
handlers:
|
||||
- name: Reload systemd
|
||||
ansible.builtin.systemd:
|
||||
daemon_reload: true
|
||||
|
||||
- name: Restart activitywatch
|
||||
ansible.builtin.systemd:
|
||||
name: activitywatch-server.service
|
||||
state: restarted
|
||||
@@ -0,0 +1,24 @@
|
||||
aw_server_version: "v0.13.2"
|
||||
aw_server_download_url: "https://github.com/ActivityWatch/activitywatch/releases/download/v0.13.2/activitywatch-v0.13.2-linux-x86_64.zip"
|
||||
aw_server_bind_host: "0.0.0.0"
|
||||
aw_server_port: 5600
|
||||
aw_server_webui_dir: "/opt/activitywatch/webui-ru"
|
||||
aw_server_data_dir: "/var/lib/activitywatch"
|
||||
aw_server_log_dir: "/var/log/activitywatch"
|
||||
aw_server_user: "activitywatch"
|
||||
aw_server_group: "activitywatch"
|
||||
|
||||
aw_repo_root: "/home/igor/tmp/AWatch-rus"
|
||||
|
||||
# Optional: apply a baseline worktime-focused categorization and views via AW settings API.
|
||||
# WARNING: this overwrites existing server-side settings/classes/views.
|
||||
aw_apply_worktime_settings: false
|
||||
|
||||
# Optional defaults for the worktime period in Web UI.
|
||||
# startOfDay controls day-boundary and default report window start.
|
||||
# durationDefault controls default time range (seconds) shown in UI.
|
||||
#
|
||||
# Recommended: set worktime window explicitly and let the playbook derive duration.
|
||||
aw_worktime_from: "08:00"
|
||||
aw_worktime_to: "17:00"
|
||||
aw_worktime_start_of_day: "{{ aw_worktime_from }}"
|
||||
@@ -0,0 +1,30 @@
|
||||
aw_pfsense_poller_config:
|
||||
poll_interval_seconds: 60
|
||||
aw:
|
||||
server_host: "10.10.10.13"
|
||||
server_port: 5600
|
||||
hostname: "PFSENSE-EDGE01"
|
||||
pulse_time_seconds: 120
|
||||
timeout_seconds: 15
|
||||
pfsense:
|
||||
name: "pfSense Edge 01"
|
||||
host: "10.10.10.1"
|
||||
scheme: "https"
|
||||
verify_tls: false
|
||||
timeout_seconds: 15
|
||||
headers:
|
||||
X-API-Key: "replace-me"
|
||||
X-API-Secret: "replace-me"
|
||||
endpoints:
|
||||
- name: "system-status"
|
||||
path: "/api/v2/status/system"
|
||||
bucket_prefix: "aw-pfsense-health"
|
||||
bucket_type: "aw.pfsense.health"
|
||||
- name: "interfaces"
|
||||
path: "/api/v2/interface"
|
||||
bucket_prefix: "aw-pfsense-interfaces"
|
||||
bucket_type: "aw.pfsense.interfaces"
|
||||
- name: "gateways"
|
||||
path: "/api/v2/status/gateways"
|
||||
bucket_prefix: "aw-pfsense-gateways"
|
||||
bucket_type: "aw.pfsense.gateways"
|
||||
@@ -0,0 +1,38 @@
|
||||
proxmox_ct_matrix:
|
||||
- id: "203"
|
||||
hostname: "activitywatch-user1"
|
||||
storage: "local-lvm"
|
||||
template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
rootfs_size: "8G"
|
||||
cores: "2"
|
||||
memory: "2048"
|
||||
swap: "512"
|
||||
bridge: "vmbr10"
|
||||
ip: "10.20.30.13/24"
|
||||
gw: "10.20.30.1"
|
||||
vlan: ""
|
||||
nameserver: "1.1.1.1 8.8.8.8"
|
||||
searchdomain: "example.internal"
|
||||
password: "CHANGE_ME"
|
||||
unprivileged: "1"
|
||||
onboot: "1"
|
||||
features: "nesting=1,keyctl=1"
|
||||
|
||||
- id: "204"
|
||||
hostname: "activitywatch-user2"
|
||||
storage: "local-lvm"
|
||||
template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
rootfs_size: "8G"
|
||||
cores: "2"
|
||||
memory: "2048"
|
||||
swap: "512"
|
||||
bridge: "vmbr10"
|
||||
ip: "10.20.30.14/24"
|
||||
gw: "10.20.30.1"
|
||||
vlan: ""
|
||||
nameserver: "1.1.1.1 8.8.8.8"
|
||||
searchdomain: "example.internal"
|
||||
password: "CHANGE_ME"
|
||||
unprivileged: "1"
|
||||
onboot: "1"
|
||||
features: "nesting=1,keyctl=1"
|
||||
@@ -0,0 +1,18 @@
|
||||
proxmox_ct_id: "203"
|
||||
proxmox_ct_hostname: "activitywatch-server"
|
||||
proxmox_ct_storage: "local-lvm"
|
||||
proxmox_ct_template: "local:vztmpl/debian-12-standard_12.7-1_amd64.tar.zst"
|
||||
proxmox_ct_rootfs_size: "8G"
|
||||
proxmox_ct_cores: "2"
|
||||
proxmox_ct_memory: "2048"
|
||||
proxmox_ct_swap: "512"
|
||||
proxmox_ct_bridge: "vmbr10"
|
||||
proxmox_ct_ip: "10.20.30.13/24"
|
||||
proxmox_ct_gw: "10.20.30.1"
|
||||
proxmox_ct_vlan: ""
|
||||
proxmox_ct_nameserver: "1.1.1.1 8.8.8.8"
|
||||
proxmox_ct_searchdomain: "example.internal"
|
||||
proxmox_ct_password: "CHANGE_ME"
|
||||
proxmox_ct_unprivileged: "1"
|
||||
proxmox_ct_onboot: "1"
|
||||
proxmox_ct_features: "nesting=1,keyctl=1"
|
||||
@@ -0,0 +1,8 @@
|
||||
[proxmox]
|
||||
pve-main ansible_host=192.168.10.2 ansible_user=root ansible_port=22
|
||||
|
||||
[aw_server]
|
||||
aw-ct ansible_host=10.20.30.13 ansible_user=root ansible_port=22
|
||||
|
||||
[aw_windows]
|
||||
win-node1 ansible_host=192.168.100.21 ansible_user=Administrator ansible_password=CHANGE_ME ansible_connection=winrm ansible_winrm_transport=ntlm ansible_port=5985 ansible_winrm_server_cert_validation=ignore
|
||||
@@ -0,0 +1,40 @@
|
||||
---
|
||||
- name: Provision single Proxmox CT and deploy AWatch-rus
|
||||
hosts: proxmox
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
proxmox_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||
aw_bootstrap_files:
|
||||
- install_aw_server.sh
|
||||
- apply_webui_ru_patch.sh
|
||||
- activitywatch-server.service
|
||||
- aw-server.env.example
|
||||
- aw-ru-patch.js
|
||||
- aw-sw-cleanup.js
|
||||
- aw-host-groups.json
|
||||
- settings/classes-worktime.json
|
||||
- settings/views-default.json
|
||||
|
||||
tasks:
|
||||
- name: Execute single-CT provisioning workflow
|
||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||
vars:
|
||||
ct_id: "{{ proxmox_ct_id }}"
|
||||
ct_hostname: "{{ proxmox_ct_hostname }}"
|
||||
ct_storage: "{{ proxmox_ct_storage }}"
|
||||
ct_template: "{{ proxmox_ct_template }}"
|
||||
ct_rootfs_size: "{{ proxmox_ct_rootfs_size }}"
|
||||
ct_cores: "{{ proxmox_ct_cores }}"
|
||||
ct_memory: "{{ proxmox_ct_memory }}"
|
||||
ct_swap: "{{ proxmox_ct_swap }}"
|
||||
ct_bridge: "{{ proxmox_ct_bridge }}"
|
||||
ct_ip: "{{ proxmox_ct_ip }}"
|
||||
ct_gw: "{{ proxmox_ct_gw }}"
|
||||
ct_vlan: "{{ proxmox_ct_vlan | default('') }}"
|
||||
ct_nameserver: "{{ proxmox_ct_nameserver | default('') }}"
|
||||
ct_searchdomain: "{{ proxmox_ct_searchdomain | default('') }}"
|
||||
ct_password: "{{ proxmox_ct_password }}"
|
||||
ct_unprivileged: "{{ proxmox_ct_unprivileged }}"
|
||||
ct_onboot: "{{ proxmox_ct_onboot }}"
|
||||
ct_features: "{{ proxmox_ct_features }}"
|
||||
+50
@@ -0,0 +1,50 @@
|
||||
---
|
||||
- name: Provision Proxmox CT matrix and deploy AWatch-rus with RU patch
|
||||
hosts: proxmox
|
||||
gather_facts: false
|
||||
|
||||
vars:
|
||||
proxmox_bootstrap_dir: "/tmp/aw-rus-bootstrap"
|
||||
aw_bootstrap_files:
|
||||
- install_aw_server.sh
|
||||
- apply_webui_ru_patch.sh
|
||||
- activitywatch-server.service
|
||||
- aw-server.env.example
|
||||
- aw-ru-patch.js
|
||||
- aw-sw-cleanup.js
|
||||
- aw-host-groups.json
|
||||
- settings/classes-worktime.json
|
||||
- settings/views-default.json
|
||||
|
||||
tasks:
|
||||
- name: Validate CT matrix is provided
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- proxmox_ct_matrix is defined
|
||||
- proxmox_ct_matrix | length > 0
|
||||
fail_msg: "Define proxmox_ct_matrix in group_vars/proxmox-matrix.yml"
|
||||
|
||||
- name: Execute provisioning workflow for each CT
|
||||
ansible.builtin.include_tasks: tasks/provision_ct_and_deploy_aw.yml
|
||||
vars:
|
||||
ct_id: "{{ item.id }}"
|
||||
ct_hostname: "{{ item.hostname }}"
|
||||
ct_storage: "{{ item.storage }}"
|
||||
ct_template: "{{ item.template }}"
|
||||
ct_rootfs_size: "{{ item.rootfs_size }}"
|
||||
ct_cores: "{{ item.cores }}"
|
||||
ct_memory: "{{ item.memory }}"
|
||||
ct_swap: "{{ item.swap }}"
|
||||
ct_bridge: "{{ item.bridge }}"
|
||||
ct_ip: "{{ item.ip }}"
|
||||
ct_gw: "{{ item.gw }}"
|
||||
ct_vlan: "{{ item.vlan | default('') }}"
|
||||
ct_nameserver: "{{ item.nameserver | default('') }}"
|
||||
ct_searchdomain: "{{ item.searchdomain | default('') }}"
|
||||
ct_password: "{{ item.password }}"
|
||||
ct_unprivileged: "{{ item.unprivileged }}"
|
||||
ct_onboot: "{{ item.onboot }}"
|
||||
ct_features: "{{ item.features }}"
|
||||
loop: "{{ proxmox_ct_matrix }}"
|
||||
loop_control:
|
||||
label: "ct={{ item.id }} host={{ item.hostname }} ip={{ item.ip }}"
|
||||
@@ -0,0 +1,217 @@
|
||||
---
|
||||
- name: Validate required per-CT variables
|
||||
ansible.builtin.assert:
|
||||
that:
|
||||
- ct_id is defined
|
||||
- ct_hostname is defined
|
||||
- ct_storage is defined
|
||||
- ct_template is defined
|
||||
- ct_rootfs_size is defined
|
||||
- ct_cores is defined
|
||||
- ct_memory is defined
|
||||
- ct_swap is defined
|
||||
- ct_bridge is defined
|
||||
- ct_ip is defined
|
||||
- ct_gw is defined
|
||||
- ct_password is defined
|
||||
- ct_unprivileged is defined
|
||||
- ct_onboot is defined
|
||||
- ct_features is defined
|
||||
- aw_repo_root is defined
|
||||
- aw_server_version is defined
|
||||
- aw_server_download_url is defined
|
||||
- aw_server_bind_host is defined
|
||||
- aw_server_port is defined
|
||||
- aw_server_webui_dir is defined
|
||||
- aw_server_data_dir is defined
|
||||
- aw_server_log_dir is defined
|
||||
- aw_server_user is defined
|
||||
- aw_server_group is defined
|
||||
fail_msg: "Missing required variables for CT provisioning/deploy."
|
||||
|
||||
- name: Build CT network string
|
||||
ansible.builtin.set_fact:
|
||||
ct_net0: >-
|
||||
name=eth0,bridge={{ ct_bridge }},ip={{ ct_ip }},gw={{ ct_gw }}{% if (ct_vlan | default('') | string | length) > 0 %},tag={{ ct_vlan }}{% endif %}
|
||||
|
||||
- name: Check whether CT already exists
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- status
|
||||
- "{{ ct_id }}"
|
||||
register: ct_status_check
|
||||
failed_when: false
|
||||
changed_when: false
|
||||
|
||||
- name: Create CT when absent
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- create
|
||||
- "{{ ct_id }}"
|
||||
- "{{ ct_template }}"
|
||||
- --hostname
|
||||
- "{{ ct_hostname }}"
|
||||
- --cores
|
||||
- "{{ ct_cores }}"
|
||||
- --memory
|
||||
- "{{ ct_memory }}"
|
||||
- --swap
|
||||
- "{{ ct_swap }}"
|
||||
- --rootfs
|
||||
- "{{ ct_storage }}:{{ ct_rootfs_size }}"
|
||||
- --password
|
||||
- "{{ ct_password }}"
|
||||
- --unprivileged
|
||||
- "{{ ct_unprivileged }}"
|
||||
- --onboot
|
||||
- "{{ ct_onboot }}"
|
||||
- --features
|
||||
- "{{ ct_features }}"
|
||||
- --net0
|
||||
- "{{ ct_net0 }}"
|
||||
- --nameserver
|
||||
- "{{ ct_nameserver | default('') }}"
|
||||
- --searchdomain
|
||||
- "{{ ct_searchdomain | default('') }}"
|
||||
- --ostype
|
||||
- debian
|
||||
when: ct_status_check.rc != 0
|
||||
|
||||
- name: Check current CT runtime state
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- status
|
||||
- "{{ ct_id }}"
|
||||
register: ct_runtime_status
|
||||
changed_when: false
|
||||
|
||||
- name: Start CT when stopped
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- start
|
||||
- "{{ ct_id }}"
|
||||
when: "'stopped' in ct_runtime_status.stdout"
|
||||
|
||||
- name: Ensure bootstrap directory on Proxmox host
|
||||
ansible.builtin.file:
|
||||
path: "{{ proxmox_bootstrap_dir }}"
|
||||
state: directory
|
||||
mode: "0700"
|
||||
|
||||
- name: Copy AW bootstrap files to Proxmox host temp
|
||||
ansible.builtin.copy:
|
||||
src: "{{ aw_repo_root }}/aw-server/{{ item }}"
|
||||
dest: "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
||||
mode: "0644"
|
||||
loop: "{{ aw_bootstrap_files }}"
|
||||
|
||||
- name: Bootstrap CT OS dependencies
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- exec
|
||||
- "{{ ct_id }}"
|
||||
- --
|
||||
- bash
|
||||
- -lc
|
||||
- |
|
||||
set -euo pipefail
|
||||
export DEBIAN_FRONTEND=noninteractive
|
||||
apt-get update
|
||||
apt-get install -y curl ca-certificates bash unzip xz-utils jq rsync openssh-server
|
||||
mkdir -p /root/bootstrap /etc/activitywatch
|
||||
systemctl enable ssh || true
|
||||
systemctl restart ssh || true
|
||||
|
||||
- name: Push bootstrap files into CT
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- push
|
||||
- "{{ ct_id }}"
|
||||
- "{{ proxmox_bootstrap_dir }}/{{ item }}"
|
||||
- "/root/bootstrap/{{ item }}"
|
||||
loop: "{{ aw_bootstrap_files }}"
|
||||
|
||||
- name: Write AW server env file on Proxmox host temp
|
||||
ansible.builtin.copy:
|
||||
dest: "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||
mode: "0600"
|
||||
content: |
|
||||
AW_SERVER_VERSION={{ aw_server_version }}
|
||||
AW_SERVER_DOWNLOAD_URL={{ aw_server_download_url }}
|
||||
AW_SERVER_BIND_HOST={{ aw_server_bind_host }}
|
||||
AW_SERVER_PORT={{ aw_server_port }}
|
||||
AW_SERVER_WEBUI_DIR={{ aw_server_webui_dir }}
|
||||
AW_SERVER_DATA_DIR={{ aw_server_data_dir }}
|
||||
AW_SERVER_LOG_DIR={{ aw_server_log_dir }}
|
||||
AW_SERVER_USER={{ aw_server_user }}
|
||||
AW_SERVER_GROUP={{ aw_server_group }}
|
||||
|
||||
- name: Push AW server env into CT
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- push
|
||||
- "{{ ct_id }}"
|
||||
- "{{ proxmox_bootstrap_dir }}/aw-server.env"
|
||||
- /etc/activitywatch/aw-server.env
|
||||
|
||||
- name: Set mode for env inside CT
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- exec
|
||||
- "{{ ct_id }}"
|
||||
- --
|
||||
- chmod
|
||||
- "0600"
|
||||
- /etc/activitywatch/aw-server.env
|
||||
|
||||
- name: Install server and apply RU patch inside CT
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- exec
|
||||
- "{{ ct_id }}"
|
||||
- --
|
||||
- bash
|
||||
- -lc
|
||||
- |
|
||||
set -euo pipefail
|
||||
chmod +x /root/bootstrap/install_aw_server.sh /root/bootstrap/apply_webui_ru_patch.sh
|
||||
bash /root/bootstrap/install_aw_server.sh
|
||||
bash /root/bootstrap/apply_webui_ru_patch.sh
|
||||
systemctl restart activitywatch-server.service
|
||||
|
||||
- name: Validate AW API from inside CT
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- exec
|
||||
- "{{ ct_id }}"
|
||||
- --
|
||||
- bash
|
||||
- -lc
|
||||
- "curl -fsS http://127.0.0.1:{{ aw_server_port }}/api/0/info >/dev/null"
|
||||
|
||||
- name: Validate RU patch hooks in index
|
||||
ansible.builtin.command:
|
||||
argv:
|
||||
- pct
|
||||
- exec
|
||||
- "{{ ct_id }}"
|
||||
- --
|
||||
- bash
|
||||
- -lc
|
||||
- "grep -q 'ru-patch-v5.js' {{ aw_server_webui_dir }}/index.html && grep -q 'sw-cleanup.js' {{ aw_server_webui_dir }}/index.html"
|
||||
|
||||
- name: Show final endpoint
|
||||
ansible.builtin.debug:
|
||||
msg:
|
||||
- "CT {{ ct_id }} is provisioned and configured."
|
||||
- "ActivityWatch endpoint: http://{{ ct_ip | regex_replace('/[0-9]+$', '') }}:{{ aw_server_port }}"
|
||||
Reference in New Issue
Block a user