Update vacuum automation condition to extend inactivity period from 3 to 4 days and add new APT updates package to documentation. #1552
parent
63e570982e
commit
c1712d83db
@ -0,0 +1,35 @@
|
||||
######################################################################
|
||||
# @CCOSTAN - Follow Me on X
|
||||
# For more info visit https://www.vcloudinfo.com/click-here
|
||||
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
||||
# -------------------------------------------------------------------
|
||||
# Infrastructure Card - APT update status for Docker hosts
|
||||
# Paste this card into the existing Infrastructure dashboard (storage mode).
|
||||
# -------------------------------------------------------------------
|
||||
# Notes: Depends on `config/packages/apt_updates.yaml`.
|
||||
######################################################################
|
||||
|
||||
type: entities
|
||||
title: "APT Updates (Docker Hosts)"
|
||||
show_header_toggle: false
|
||||
entities:
|
||||
- type: section
|
||||
label: docker_10
|
||||
- entity: sensor.docker_10_apt_status
|
||||
- entity: sensor.docker_10_apt_last_check
|
||||
- entity: sensor.docker_10_apt_last_update
|
||||
- entity: sensor.docker_10_apt_reboot_status
|
||||
|
||||
- type: section
|
||||
label: docker_14
|
||||
- entity: sensor.docker_14_apt_status
|
||||
- entity: sensor.docker_14_apt_last_check
|
||||
- entity: sensor.docker_14_apt_last_update
|
||||
- entity: sensor.docker_14_apt_reboot_status
|
||||
|
||||
- type: section
|
||||
label: docker_69
|
||||
- entity: sensor.docker_69_apt_status
|
||||
- entity: sensor.docker_69_apt_last_check
|
||||
- entity: sensor.docker_69_apt_last_update
|
||||
- entity: sensor.docker_69_apt_reboot_status
|
||||
@ -0,0 +1,46 @@
|
||||
######################################################################
|
||||
# @CCOSTAN - Follow Me on X
|
||||
# For more info visit https://www.vcloudinfo.com/click-here
|
||||
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
||||
# -------------------------------------------------------------------
|
||||
# Infrastructure View - APT updates (3-column layout)
|
||||
# Paste this view into the Infrastructure dashboard (storage mode).
|
||||
# -------------------------------------------------------------------
|
||||
# Notes: Uses `sensor.docker_*` entities from `config/packages/apt_updates.yaml`.
|
||||
######################################################################
|
||||
|
||||
title: "Infrastructure - APT Updates"
|
||||
path: infrastructure-apt-updates
|
||||
icon: mdi:package-up
|
||||
type: sections
|
||||
max_columns: 3
|
||||
sections:
|
||||
- type: grid
|
||||
columns: 3
|
||||
cards:
|
||||
- type: entities
|
||||
title: docker_10
|
||||
show_header_toggle: false
|
||||
entities:
|
||||
- entity: sensor.docker_10_apt_status
|
||||
- entity: sensor.docker_10_apt_last_check
|
||||
- entity: sensor.docker_10_apt_last_update
|
||||
- entity: sensor.docker_10_apt_reboot_status
|
||||
|
||||
- type: entities
|
||||
title: docker_14
|
||||
show_header_toggle: false
|
||||
entities:
|
||||
- entity: sensor.docker_14_apt_status
|
||||
- entity: sensor.docker_14_apt_last_check
|
||||
- entity: sensor.docker_14_apt_last_update
|
||||
- entity: sensor.docker_14_apt_reboot_status
|
||||
|
||||
- type: entities
|
||||
title: docker_69
|
||||
show_header_toggle: false
|
||||
entities:
|
||||
- entity: sensor.docker_69_apt_status
|
||||
- entity: sensor.docker_69_apt_last_check
|
||||
- entity: sensor.docker_69_apt_last_update
|
||||
- entity: sensor.docker_69_apt_reboot_status
|
||||
@ -0,0 +1,369 @@
|
||||
######################################################################
|
||||
# @CCOSTAN - Follow Me on X
|
||||
# For more info visit https://www.vcloudinfo.com/click-here
|
||||
# Original Repo : https://github.com/CCOSTAN/Home-AssistantConfig
|
||||
# -------------------------------------------------------------------
|
||||
# APT Updates - Weekly patch reporting for Docker hosts
|
||||
# Receives webhook payloads from docker_10/docker_14/docker_69 after APT runs.
|
||||
# -------------------------------------------------------------------
|
||||
# Notes: Hosts run a weekly Wednesday 12:00 APT job and POST JSON to these webhooks.
|
||||
# Notes: Logbook entry only when `updated: true`; creates Repairs issue if reboot needed.
|
||||
######################################################################
|
||||
|
||||
input_datetime:
|
||||
apt_docker_10_last_check:
|
||||
name: "docker_10 APT last check"
|
||||
has_date: true
|
||||
has_time: true
|
||||
apt_docker_10_last_update:
|
||||
name: "docker_10 APT last update"
|
||||
has_date: true
|
||||
has_time: true
|
||||
apt_docker_14_last_check:
|
||||
name: "docker_14 APT last check"
|
||||
has_date: true
|
||||
has_time: true
|
||||
apt_docker_14_last_update:
|
||||
name: "docker_14 APT last update"
|
||||
has_date: true
|
||||
has_time: true
|
||||
apt_docker_69_last_check:
|
||||
name: "docker_69 APT last check"
|
||||
has_date: true
|
||||
has_time: true
|
||||
apt_docker_69_last_update:
|
||||
name: "docker_69 APT last update"
|
||||
has_date: true
|
||||
has_time: true
|
||||
|
||||
input_text:
|
||||
apt_docker_10_last_result:
|
||||
name: "docker_10 APT last result"
|
||||
max: 255
|
||||
apt_docker_14_last_result:
|
||||
name: "docker_14 APT last result"
|
||||
max: 255
|
||||
apt_docker_69_last_result:
|
||||
name: "docker_69 APT last result"
|
||||
max: 255
|
||||
|
||||
input_boolean:
|
||||
apt_docker_10_reboot_required:
|
||||
name: "docker_10 reboot required"
|
||||
icon: mdi:restart-alert
|
||||
apt_docker_14_reboot_required:
|
||||
name: "docker_14 reboot required"
|
||||
icon: mdi:restart-alert
|
||||
apt_docker_69_reboot_required:
|
||||
name: "docker_69 reboot required"
|
||||
icon: mdi:restart-alert
|
||||
|
||||
template:
|
||||
- sensor:
|
||||
- name: "docker_10 APT status"
|
||||
unique_id: apt_docker_10_status
|
||||
icon: mdi:package-up
|
||||
state: "{{ states('input_text.apt_docker_10_last_result') }}"
|
||||
- name: "docker_10 APT last check"
|
||||
unique_id: apt_docker_10_last_check
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_10_last_check') }}"
|
||||
- name: "docker_10 APT last update"
|
||||
unique_id: apt_docker_10_last_update
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_10_last_update') }}"
|
||||
- name: "docker_10 APT reboot status"
|
||||
unique_id: apt_docker_10_reboot_status
|
||||
icon: >-
|
||||
{{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_10_reboot_required', 'on')
|
||||
else 'mdi:check-circle' }}
|
||||
state: >-
|
||||
{{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_10_reboot_required', 'on')
|
||||
else 'NO REBOOT NEEDED' }}
|
||||
- name: "docker_14 APT status"
|
||||
unique_id: apt_docker_14_status
|
||||
icon: mdi:package-up
|
||||
state: "{{ states('input_text.apt_docker_14_last_result') }}"
|
||||
- name: "docker_14 APT last check"
|
||||
unique_id: apt_docker_14_last_check
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_14_last_check') }}"
|
||||
- name: "docker_14 APT last update"
|
||||
unique_id: apt_docker_14_last_update
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_14_last_update') }}"
|
||||
- name: "docker_14 APT reboot status"
|
||||
unique_id: apt_docker_14_reboot_status
|
||||
icon: >-
|
||||
{{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_14_reboot_required', 'on')
|
||||
else 'mdi:check-circle' }}
|
||||
state: >-
|
||||
{{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_14_reboot_required', 'on')
|
||||
else 'NO REBOOT NEEDED' }}
|
||||
- name: "docker_69 APT status"
|
||||
unique_id: apt_docker_69_status
|
||||
icon: mdi:package-up
|
||||
state: "{{ states('input_text.apt_docker_69_last_result') }}"
|
||||
- name: "docker_69 APT last check"
|
||||
unique_id: apt_docker_69_last_check
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_69_last_check') }}"
|
||||
- name: "docker_69 APT last update"
|
||||
unique_id: apt_docker_69_last_update
|
||||
device_class: timestamp
|
||||
state: "{{ states('input_datetime.apt_docker_69_last_update') }}"
|
||||
- name: "docker_69 APT reboot status"
|
||||
unique_id: apt_docker_69_reboot_status
|
||||
icon: >-
|
||||
{{ 'mdi:restart-alert' if is_state('input_boolean.apt_docker_69_reboot_required', 'on')
|
||||
else 'mdi:check-circle' }}
|
||||
state: >-
|
||||
{{ 'REBOOT REQUIRED' if is_state('input_boolean.apt_docker_69_reboot_required', 'on')
|
||||
else 'NO REBOOT NEEDED' }}
|
||||
|
||||
- binary_sensor:
|
||||
- name: "docker_10 APT reboot required"
|
||||
unique_id: apt_docker_10_reboot_required
|
||||
device_class: problem
|
||||
icon: mdi:restart-alert
|
||||
state: "{{ is_state('input_boolean.apt_docker_10_reboot_required', 'on') }}"
|
||||
- name: "docker_14 APT reboot required"
|
||||
unique_id: apt_docker_14_reboot_required
|
||||
device_class: problem
|
||||
icon: mdi:restart-alert
|
||||
state: "{{ is_state('input_boolean.apt_docker_14_reboot_required', 'on') }}"
|
||||
- name: "docker_69 APT reboot required"
|
||||
unique_id: apt_docker_69_reboot_required
|
||||
device_class: problem
|
||||
icon: mdi:restart-alert
|
||||
state: "{{ is_state('input_boolean.apt_docker_69_reboot_required', 'on') }}"
|
||||
|
||||
automation:
|
||||
- alias: "APT Update Report - docker_10"
|
||||
id: apt_update_report_docker_10
|
||||
description: "Receive docker_10 APT results and update helpers/logbook."
|
||||
mode: queued
|
||||
trigger:
|
||||
- platform: webhook
|
||||
webhook_id: !secret apt_webhook_docker_10
|
||||
allowed_methods:
|
||||
- POST
|
||||
local_only: true
|
||||
variables:
|
||||
payload: "{{ trigger.json | default({}) }}"
|
||||
success: "{{ payload.get('success', true) | bool }}"
|
||||
updated: "{{ payload.get('updated', false) | bool }}"
|
||||
packages: "{{ payload.get('packages', 0) | int(0) }}"
|
||||
reboot_required: "{{ payload.get('reboot_required', false) | bool }}"
|
||||
message: "{{ payload.get('message', '') | string }}"
|
||||
result: >-
|
||||
{% if not success %}
|
||||
ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %}
|
||||
{% elif updated %}
|
||||
UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %}
|
||||
{% else %}
|
||||
NO UPDATES
|
||||
{% endif %}
|
||||
log_message: >-
|
||||
docker_10 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}.
|
||||
action:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_10_last_check
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: input_text.apt_docker_10_last_result
|
||||
data:
|
||||
value: "{{ result }}"
|
||||
- choose:
|
||||
- conditions: "{{ success and reboot_required }}"
|
||||
sequence:
|
||||
- service: input_boolean.turn_on
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_10_reboot_required
|
||||
default:
|
||||
- service: input_boolean.turn_off
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_10_reboot_required
|
||||
- choose:
|
||||
- conditions: "{{ success and updated }}"
|
||||
sequence:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_10_last_update
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "APT"
|
||||
message: "{{ log_message }}"
|
||||
|
||||
- alias: "APT Update Report - docker_14"
|
||||
id: apt_update_report_docker_14
|
||||
description: "Receive docker_14 APT results and update helpers/logbook."
|
||||
mode: queued
|
||||
trigger:
|
||||
- platform: webhook
|
||||
webhook_id: !secret apt_webhook_docker_14
|
||||
allowed_methods:
|
||||
- POST
|
||||
local_only: true
|
||||
variables:
|
||||
payload: "{{ trigger.json | default({}) }}"
|
||||
success: "{{ payload.get('success', true) | bool }}"
|
||||
updated: "{{ payload.get('updated', false) | bool }}"
|
||||
packages: "{{ payload.get('packages', 0) | int(0) }}"
|
||||
reboot_required: "{{ payload.get('reboot_required', false) | bool }}"
|
||||
message: "{{ payload.get('message', '') | string }}"
|
||||
result: >-
|
||||
{% if not success %}
|
||||
ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %}
|
||||
{% elif updated %}
|
||||
UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %}
|
||||
{% else %}
|
||||
NO UPDATES
|
||||
{% endif %}
|
||||
log_message: >-
|
||||
docker_14 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}.
|
||||
action:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_14_last_check
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: input_text.apt_docker_14_last_result
|
||||
data:
|
||||
value: "{{ result }}"
|
||||
- choose:
|
||||
- conditions: "{{ success and reboot_required }}"
|
||||
sequence:
|
||||
- service: input_boolean.turn_on
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_14_reboot_required
|
||||
default:
|
||||
- service: input_boolean.turn_off
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_14_reboot_required
|
||||
- choose:
|
||||
- conditions: "{{ success and updated }}"
|
||||
sequence:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_14_last_update
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "APT"
|
||||
message: "{{ log_message }}"
|
||||
|
||||
- alias: "APT Update Report - docker_69"
|
||||
id: apt_update_report_docker_69
|
||||
description: "Receive docker_69 APT results and update helpers/logbook."
|
||||
mode: queued
|
||||
trigger:
|
||||
- platform: webhook
|
||||
webhook_id: !secret apt_webhook_docker_69
|
||||
allowed_methods:
|
||||
- POST
|
||||
local_only: true
|
||||
variables:
|
||||
payload: "{{ trigger.json | default({}) }}"
|
||||
success: "{{ payload.get('success', true) | bool }}"
|
||||
updated: "{{ payload.get('updated', false) | bool }}"
|
||||
packages: "{{ payload.get('packages', 0) | int(0) }}"
|
||||
reboot_required: "{{ payload.get('reboot_required', false) | bool }}"
|
||||
message: "{{ payload.get('message', '') | string }}"
|
||||
result: >-
|
||||
{% if not success %}
|
||||
ERROR{% if (message | trim) != '' %}: {{ message | trim }}{% endif %}
|
||||
{% elif updated %}
|
||||
UPDATED {{ packages }} PKGS{% if reboot_required %}; REBOOT REQUIRED{% endif %}
|
||||
{% else %}
|
||||
NO UPDATES
|
||||
{% endif %}
|
||||
log_message: >-
|
||||
docker_69 updated {{ packages }} package{% if packages != 1 %}s{% endif %}{% if reboot_required %} (reboot required){% endif %}.
|
||||
action:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_69_last_check
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: input_text.set_value
|
||||
target:
|
||||
entity_id: input_text.apt_docker_69_last_result
|
||||
data:
|
||||
value: "{{ result }}"
|
||||
- choose:
|
||||
- conditions: "{{ success and reboot_required }}"
|
||||
sequence:
|
||||
- service: input_boolean.turn_on
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_69_reboot_required
|
||||
default:
|
||||
- service: input_boolean.turn_off
|
||||
target:
|
||||
entity_id: input_boolean.apt_docker_69_reboot_required
|
||||
- choose:
|
||||
- conditions: "{{ success and updated }}"
|
||||
sequence:
|
||||
- service: input_datetime.set_datetime
|
||||
target:
|
||||
entity_id: input_datetime.apt_docker_69_last_update
|
||||
data:
|
||||
datetime: "{{ now().strftime('%Y-%m-%d %H:%M:%S') }}"
|
||||
- service: script.send_to_logbook
|
||||
data:
|
||||
topic: "APT"
|
||||
message: "{{ log_message }}"
|
||||
|
||||
- alias: "APT Reboot Repairs"
|
||||
id: apt_reboot_repairs
|
||||
description: "Create or clear Repairs issues when Docker hosts need a reboot."
|
||||
mode: queued
|
||||
trigger:
|
||||
- platform: state
|
||||
entity_id:
|
||||
- binary_sensor.docker_10_apt_reboot_required
|
||||
- binary_sensor.docker_14_apt_reboot_required
|
||||
- binary_sensor.docker_69_apt_reboot_required
|
||||
variables:
|
||||
host_name: >-
|
||||
{% if 'docker_10' in trigger.entity_id %}
|
||||
docker_10
|
||||
{% elif 'docker_14' in trigger.entity_id %}
|
||||
docker_14
|
||||
{% else %}
|
||||
docker_69
|
||||
{% endif %}
|
||||
issue_id: >-
|
||||
{% if 'docker_10' in trigger.entity_id %}
|
||||
apt_docker_10_reboot_required
|
||||
{% elif 'docker_14' in trigger.entity_id %}
|
||||
apt_docker_14_reboot_required
|
||||
{% else %}
|
||||
apt_docker_69_reboot_required
|
||||
{% endif %}
|
||||
action:
|
||||
- choose:
|
||||
- conditions: "{{ trigger.to_state.state == 'on' }}"
|
||||
sequence:
|
||||
- service: repairs.create
|
||||
data:
|
||||
issue_id: "{{ issue_id }}"
|
||||
severity: warning
|
||||
persistent: true
|
||||
title: "{{ host_name }} needs reboot"
|
||||
description: >-
|
||||
{{ host_name }} reports a reboot is required after APT updates.
|
||||
Status: {{ states('sensor.' ~ host_name ~ '_apt_status') }}.
|
||||
default:
|
||||
- continue_on_error: true
|
||||
service: repairs.remove
|
||||
data:
|
||||
issue_id: "{{ issue_id }}"
|
||||
@ -0,0 +1,16 @@
|
||||
[Unit]
|
||||
Description=Weekly APT maintenance (Home Assistant webhook)
|
||||
After=network-online.target
|
||||
Wants=network-online.target
|
||||
|
||||
[Service]
|
||||
Type=oneshot
|
||||
ExecStart=/usr/local/sbin/apt_weekly.sh https://HOMEASSISTANT_HOST:8123/api/webhook/REPLACE_WITH_WEBHOOK_ID
|
||||
User=root
|
||||
Group=root
|
||||
Nice=10
|
||||
IOSchedulingClass=best-effort
|
||||
IOSchedulingPriority=7
|
||||
|
||||
[Install]
|
||||
WantedBy=multi-user.target
|
||||
@ -0,0 +1,58 @@
|
||||
#!/usr/bin/env bash
|
||||
set -euo pipefail
|
||||
|
||||
# Weekly APT maintenance for docker hosts (runs Wednesdays at 12:00 local via systemd timer)
|
||||
# Posts results to Home Assistant webhook.
|
||||
|
||||
WEBHOOK_URL="$1"
|
||||
|
||||
if [[ -z "$WEBHOOK_URL" ]]; then
|
||||
echo "Usage: $0 <webhook_url>" >&2
|
||||
exit 1
|
||||
fi
|
||||
|
||||
log() { echo "[$(date --iso-8601=seconds)] $*"; }
|
||||
|
||||
UPDATED=false
|
||||
REBOOT=false
|
||||
MESSAGE=""
|
||||
log "Updating package lists"
|
||||
if ! apt-get update -qq; then
|
||||
MESSAGE="apt-get update failed"
|
||||
curl -sS -X POST -H 'Content-Type: application/json' -d "{\"success\":false,\"updated\":false,\"packages\":0,\"reboot_required\":false,\"message\":\"$MESSAGE\"}" "$WEBHOOK_URL"
|
||||
exit 0
|
||||
fi
|
||||
|
||||
PACKAGES=$(apt list --upgradable 2>/dev/null | tail -n +2 | wc -l)
|
||||
|
||||
if [[ "$PACKAGES" -gt 0 ]]; then
|
||||
log "Applying upgrades ($PACKAGES pending)"
|
||||
if apt-get -y upgrade --with-new-pkgs; then
|
||||
UPDATED=true
|
||||
else
|
||||
MESSAGE="apt-get upgrade failed"
|
||||
fi
|
||||
else
|
||||
log "No packages to upgrade"
|
||||
fi
|
||||
|
||||
log "Autoremoving stale packages"
|
||||
apt-get -y autoremove >/dev/null 2>&1 || true
|
||||
|
||||
if [[ -f /var/run/reboot-required ]]; then
|
||||
REBOOT=true
|
||||
fi
|
||||
|
||||
payload=$(cat <<JSON
|
||||
{
|
||||
"success": $( [[ "$MESSAGE" == "" ]] && echo true || echo false ),
|
||||
"updated": $( $UPDATED && echo true || echo false ),
|
||||
"packages": $PACKAGES,
|
||||
"reboot_required": $( $REBOOT && echo true || echo false ),
|
||||
"message": "${MESSAGE}"
|
||||
}
|
||||
JSON
|
||||
)
|
||||
|
||||
log "Posting results to Home Assistant"
|
||||
curl -sS -X POST -H 'Content-Type: application/json' -d "$payload" "$WEBHOOK_URL" || true
|
||||
@ -0,0 +1,9 @@
|
||||
[Unit]
|
||||
Description=Run APT maintenance weekly on Wednesday at 12:00
|
||||
|
||||
[Timer]
|
||||
OnCalendar=Wed *-*-* 12:00:00
|
||||
Persistent=true
|
||||
|
||||
[Install]
|
||||
WantedBy=timers.target
|
||||
Loading…
Reference in new issue