Skip to content
Snippets Groups Projects

Compare revisions

Changes are shown as if the source revision was being merged into the target revision. Learn more about comparing revisions.

Source

Select target project
No results found

Target

Select target project
  • infrastruktur/ansible-warpzone
  • specki/ansible-warpzone
2 results
Show changes
Commits on Source (21)
Showing
with 1408 additions and 246 deletions
FROM node:14-alpine
FROM node:19-alpine
RUN apk update \
&& apk upgrade \
......
......@@ -22,8 +22,16 @@
- docker-compose.yml
- Dockerfile
- syncrepl_exporter.yml
register: config
- name: start openldap docker
- name: "stop {{ servicename}} docker"
docker_compose:
project_src: /srv/ldap/
project_src: "{{ basedir }}"
state: absent
when: config.changed
- name: "start {{ servicename}} docker"
docker_compose:
project_src: "{{ basedir }}"
state: present
......@@ -52,4 +52,4 @@
docker_compose:
project_src: "{{ basedir }}"
state: present
\ No newline at end of file
# Globale Variablen für alle produktiven Server
# SMTP Settings
smtp_domain: enteentelos.com
smtp_host: mailserver.enteentelos.com
smtp_port: 587
noreply_email_user: noreply@enteentelos.com
......@@ -53,11 +53,24 @@ alert:
crit: 4
containers:
- { name: "dockerstats_app_1" }
- { name: "esphome_app_1" }
- { name: "esphome-dev_app_1" }
- { name: "fridgeserver_app_1" }
- { name: "grafana_app_1" }
- { name: "graylog_graylog_1" }
- { name: "graylog_mongodb_1" }
- { name: "graylog_opensearch_1" }
- { name: "heimdall_app_1" }
- { name: "homeassistant_app_1" }
- { name: "homeassistant_influxdb_1" }
- { name: "mqtt_app_1" }
- { name: "mqtt_influxdb_1" }
- { name: "mqtt_telegraf_1" }
- { name: "mqtt_tgbinary_1" }
- { name: "mqtt_tgfloat_1" }
- { name: "nodered_app_1" }
- { name: "unifi_app_1" }
- { name: "omada_app_1" }
- { name: "tasmoadmin_app_1" }
- { name: "traefik_app_1" }
disks:
- { mountpoint: "/", warn: "5 GB", crit: "1 GB" }
- { mountpoint: "/srv", warn: "5 GB", crit: "1 GB" }
......
......@@ -86,35 +86,35 @@ alert:
# Definition von Borgbackup Repositories
borgbackup_repos:
warpsrvint:
# warpsrvint:
# URL des Repos
repo: "ssh://warpzone@192.168.0.201:22/data/warpzone/verwaltung"
# # URL des Repos
# repo: "ssh://warpzone@192.168.0.201:22/data/warpzone/verwaltung"
# Repo-spezifische Optionen zum Aufruf von Borgbackup
# z.B. bei Sicherungen zu rsync.net ist --remote-path=borg1 erforderlich
options: ""
# # Repo-spezifische Optionen zum Aufruf von Borgbackup
# # z.B. bei Sicherungen zu rsync.net ist --remote-path=borg1 erforderlich
# options: ""
# Compression Options, z,b. "zlib,5, "zstd,5"
compression: "zlib,5"
# # Compression Options, z,b. "zlib,5, "zstd,5"
# compression: "zlib,5"
# Prune Optionen
prune: "--keep-within=2d --keep-daily=7 --keep-weekly=4 --keep-monthly=6"
# # Prune Optionen
# prune: "--keep-within=2d --keep-daily=7 --keep-weekly=4 --keep-monthly=6"
# Backup Schedule
weekday: "*"
hour: "10"
minute: "30"
# Zusätzliche Verzeichnisse, die nur in diesem Backup gesichtert werden sollen
# directories:
# Monitoring
alert: true
warning_age: 26
critical_age: 50
warning_count: 10
critical_count: 5
# # Backup Schedule
# weekday: "*"
# hour: "10"
# minute: "30"
# # Zusätzliche Verzeichnisse, die nur in diesem Backup gesichtert werden sollen
# # directories:
# # Monitoring
# alert: true
# warning_age: 26
# critical_age: 50
# warning_count: 10
# critical_count: 5
borgbase:
......
......@@ -36,5 +36,6 @@ alert:
crit: 8
disks:
- { mountpoint: "/", warn: "10 GB", crit: "3 GB" }
- { mountpoint: "/mnt/data", warn: "10 GB", crit: "3 GB" }
# btrfs currently no data from node exporter
# - { mountpoint: "/mnt/data", warn: "10 GB", crit: "3 GB" }
......@@ -33,8 +33,6 @@ webserver_domains:
- "warpzone.ms"
- "api.warpzone.ms"
# - "auth.warpzone.ms"
- "autodiscover.warpzone.ms"
- "autoconfig.warpzone.ms"
- "gitlab.warpzone.ms"
- "matrix.warpzone.ms"
- "mailserver.warpzone.ms"
......@@ -65,7 +63,6 @@ administratorenteam:
docker:
# Interne Docker-Netzwerke
internal_networks:
- mail
- web
# Monitoring aktivieren
......@@ -74,12 +71,9 @@ alert:
warn: 8
crit: 16
containers:
- { name: "autodiscover_warpzonems_1" }
- { name: "autodiscover_lists_warpzonems_1" }
- { name: "autodiscover_member_warpzonems_1" }
- { name: "coturn_coturn_1" }
- { name: "dockerstats_app_1" }
- { name: "dokuwiki_app_1" }
- { name: "coturn_coturn_1" }
- { name: "gitlab_app_1" }
- { name: "gitlab_dind_1" }
- { name: "gitlab_runner_1" }
......@@ -93,35 +87,29 @@ alert:
- { name: "keycloak_sync-group-active_1" }
- { name: "ldap_openldap_1" }
- { name: "ldap_phpldapadmin_1" }
- { name: "mail_dovecot-mailcow_1" }
- { name: "mail_dockerapi-mailcow_1" }
- { name: "mail_ipv6nat-mailcow_1" }
- { name: "mail_mailman-core" }
- { name: "mail_mailman-db" }
- { name: "mail_mailman-nginx" }
- { name: "mail_mailman-web" }
- { name: "mail_memcached-mailcow_1" }
- { name: "mail_mysql-mailcow_1" }
- { name: "mail_netfilter-mailcow_1" }
- { name: "mail_nginx-mailcow_1" }
- { name: "mail_olefy-mailcow_1" }
- { name: "mail_ofelia-mailcow_1" }
- { name: "mail_postfix-mailcow_1" }
- { name: "mail_postfix-exporter_1" }
- { name: "mail_php-fpm-mailcow_1" }
- { name: "mail_redis-mailcow_1" }
- { name: "mail_rspamd-mailcow_1" }
- { name: "mail_traefik-certdumper_1" }
- { name: "mail_unbound-mailcow_1" }
- { name: "mail_watchdog-mailcow_1" }
- { name: "mail_admin_1" }
- { name: "mail_antispam_1" }
- { name: "mail_certdumper_1" }
- { name: "mail_db_1" }
- { name: "mail_front_1" }
- { name: "mail_imap_1" }
- { name: "mail_oletools_1" }
- { name: "mail_redis_1" }
- { name: "mail_resolver_1" }
- { name: "mail_smtp_1" }
- { name: "mail_webmail_1" }
- { name: "mail_mailman-core_1" }
- { name: "mail_mailman-web_1" }
- { name: "mail_mailman-nginx_1" }
- { name: "matrix_ma1sd_1" }
- { name: "matrix_db_1" }
- { name: "matrix_synapse_1" }
- { name: "matterbridge_cw_1" }
- { name: "matterbridge_wz_1" }
- { name: "matterbridge_web_1" }
- { name: "matterbridge_restarter_1" }
- { name: "matrix_ma1sd_1" }
- { name: "matrix_db_1" }
- { name: "matrix_synapse_1" }
- { name: "traefik_app_1" }
- { name: "vpnserver_app_1" }
- { name: "warpapi_app_1" }
- { name: "wordpress_app_1" }
- { name: "wordpress_db_1" }
......@@ -138,35 +126,35 @@ alert:
# Definition von Borgbackup Repositories
borgbackup_repos:
warpsrvint:
# warpsrvint:
# URL des Repos
repo: "ssh://warpzone@192.168.0.201:22/data/warpzone/webserver"
# # URL des Repos
# repo: "ssh://warpzone@192.168.0.201:22/data/warpzone/webserver"
# Repo-spezifische Optionen zum Aufruf von Borgbackup
# z.B. bei Sicherungen zu rsync.net ist --remote-path=borg1 erforderlich
options: ""
# # Repo-spezifische Optionen zum Aufruf von Borgbackup
# # z.B. bei Sicherungen zu rsync.net ist --remote-path=borg1 erforderlich
# options: ""
# Compression Options, z,b. "zlib,5, "zstd,5"
compression: "zlib,5"
# # Compression Options, z,b. "zlib,5, "zstd,5"
# compression: "zlib,5"
# Prune Optionen
prune: "--keep-within=2d --keep-daily=7 --keep-weekly=4 --keep-monthly=6"
# # Prune Optionen
# prune: "--keep-within=2d --keep-daily=7 --keep-weekly=4 --keep-monthly=6"
# Backup Schedule
weekday: "*"
hour: "6"
minute: "0"
# Zusätzliche Verzeichnisse, die nur in diesem Backup gesichtert werden sollen
# directories:
# Monitoring
alert: true
warning_age: 26
critical_age: 50
warning_count: 10
critical_count: 5
# # Backup Schedule
# weekday: "*"
# hour: "6"
# minute: "0"
# # Zusätzliche Verzeichnisse, die nur in diesem Backup gesichtert werden sollen
# # directories:
# # Monitoring
# alert: true
# warning_age: 26
# critical_age: 50
# warning_count: 10
# critical_count: 5
borgbase:
......
# Nameskonvention für Server: Pratchett Name/Charaktere
# Namensliste: https://wiki.lspace.org/List_of_Pratchett_characters
# Nächste freie Namen: vimes, cake, colon, detritus, dibbler, dorfl, gaspode, quirm, cherry, nobby, ramkin, ron, shoe, slant, angua, vetinary, bursar, coin, dean, hex, hix, worblehat, luggage. mustrum, rincewind, wrangler, stibbons, whitlow
# Nächste freie Namen: vimes, cake, colon, detritus, dibbler, dorfl, gaspode, quirm, cherry, nobby, ramkin, ron, shoe, slant, angua, vetinary, bursar, coin, dean, worblehat, luggage. mustrum, rincewind, wrangler, stibbons, whitlow
[test]
[prod]
# Interner Server Warpzone
# Umgebaute Watchguard im Serverschrank
# https://wiki.warpzone.ms/intern:warpzone_internal_it_infrastructure#host_fuer_interne_dienste_watchguard_xtm_505
# Interner Proxmox-Server
# Für Verbindungen über den Webserver als Jumphost folgende Parameter ergänzen:
# ansible_ssh_common_args='-o ForwardAgent=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="ssh -W %h:%p -q 159.69.57.51"'
# Interner Proxmox-Server (neu ab 09-2022)
weatherwax ansible_ssh_host=192.168.0.200
# Server für interne Dienste (neu ab 09-2022)
# Server für interne Dienste
# Container auf dem internen Proxmox Server
# Wichtige Optionen: Nesting = Yes, keyctl = enabled
ogg ansible_ssh_host=192.168.0.201
......@@ -44,4 +42,4 @@ hex ansible_ssh_host=10.111.10.100
# Virtueller Server für Infrastruktur-Dienste auf Veranstaltungen / Camps
# Container auf dem warpzone.remote Proxmox-Server
# Wichtige Optionen: Nesting = Yes, keyctl = enabled
hix ansible_ssh_host=10.111.10.101
\ No newline at end of file
hix ansible_ssh_host=10.111.10.101
---
# Nameskonvention für Server: Pratchett Name/Charaktere
# Namensliste: https://wiki.lspace.org/List_of_Pratchett_characters
# Nächste freie Namen: vimes, cake, colon, detritus, dibbler, dorfl, gaspode, quirm, cherry, nobby, ramkin, ron, shoe, slant, angua, vetinary, bursar, coin, dean, worblehat, luggage. mustrum, rincewind, wrangler, stibbons, whitlow
prod:
children:
pyhsical:
hosts:
# Interner Proxmox-Server
# Für Verbindungen über den Webserver als Jumphost folgende Parameter ergänzen:
# ansible_ssh_common_args='-o ForwardAgent=yes -o StrictHostKeyChecking=no -o UserKnownHostsFile=/dev/null -o ProxyCommand="ssh -W %h:%p -q 159.69.57.51"'
weatherwax:
ansible_ssh_host: 192.168.0.200
# Externe Server Warpzone
# Öffentlicher Root Server Warpzone bei Hetzner
tiffany:
ansible_ssh_host: 159.69.57.15
vms:
children:
weatherwax-vms:
hosts:
# Server für interne Dienste
# Container auf dem internen Proxmox Server
# Wichtige Optionen: Nesting = Yes, keyctl = enabled
ogg:
ansible_ssh_host: 192.168.0.201
# Server für VPN Verbindung zum Webserver
# Container auf dem internen Proxmox Server
# Wichtige Optionen: Nesting = Yes, keyctl = enabled
carrot:
ansible_ssh_host: 192.168.0.202
tiffany-vms:
hosts:
# Öffentlicher Webserver Warpzone
# VM auf Tiffany
webserver:
ansible_ssh_host: 159.69.57.51
# Vorstands-VM
# VM auf Tiffany
# Auch erreichbar unter verwaltung.warpzone.ms
verwaltung:
ansible_ssh_host: 195.201.179.60
event:
children:
physical:
hosts:
# Physischer Server für Veranstaltungen / Camps
# warpzone.remote Proxmox-Server
hex:
ansible_ssh_host: 10.111.10.100
vms:
hosts:
# Virtueller Server für Infrastruktur-Dienste auf Veranstaltungen / Camps
# Container auf dem warpzone.remote Proxmox-Server
# Wichtige Optionen: Nesting = Yes, keyctl = enabled
hix:
ansible_ssh_host: 10.111.10.101
<Files *.php>
deny from all
apiVersion: 1
providers:
# <string> an unique provider name
- name: 'Pixelflut'
# <int> org id. will default to orgId 1 if not specified
# orgId: 1
# <string, required> name of the dashboard folder. Required
folder: 'Pixelflut'
# <string> folder UID. will be automatically generated if not specified
# folderUid: ''
# <string, required> provider type. Required
type: file
# <bool> disable dashboard deletion
# disableDeletion: false
# <bool> enable dashboard editing
editable: true
# <int> how often Grafana will scan for changed dashboards
updateIntervalSeconds: 10
# <bool> allow updating provisioned dashboards from the UI
allowUiUpdates: true
options:
# <string, required> path to dashboard files on disk. Required
path: /etc/grafana/provisioning/dashboards/pixelflut
apiVersion: 1
datasources:
- name: MQTT_Flux
type: influxdb
access: proxy
url: http://{{ int_ip4 }}:{{ mqtt_influxdb_port }}
jsonData:
version: Flux
organization: mqtt
defaultBucket: mqtt
tlsSkipVerify: true
secureJsonData:
token: {{ influxdb_token }}
- name: MQTT_InfluxQL
type: influxdb
access: proxy
url: http://{{ int_ip4 }}:{{ mqtt_influxdb_port }}
# This database should be mapped to a bucket
database: mqtt
jsonData:
httpMode: GET
httpHeaderName1: 'Authorization'
secureJsonData:
httpHeaderValue1: 'Token {{ influxdb_token }}'
{% if inventory_hostname == 'hix' %}
- name: Prometheus
type: prometheus
access: proxy
url: http://{{ int_ip4 }}:9090
{% endif %}
{% if inventory_hostname == 'ogg' %}
- name: Pixelflut
type: prometheus
access: proxy
url: http://pixelflut.warpzone.lan:9090
uuid: P0FAC05DE14135586
{% endif %}
......@@ -10,6 +10,9 @@
- { role: all/common, tags: common }
- { role: all/sysctl, tags: sysctl }
##################################################
# Test Server
##################################################
##################################################
# Produktive Server
......@@ -26,6 +29,7 @@
remote_user: root
roles:
- { role: common/proxmox, tags: proxmox }
- { role: common/prometheus-node, tags: prometheus-node }
- { role: common/cronapt, tags: cronapt }
......@@ -33,6 +37,7 @@
remote_user: root
roles:
- { role: common/cronapt, tags: cronapt }
- { role: common/prometheus-node, tags: prometheus-node }
- { role: common/wireguard, tags: wireguard }
......@@ -148,7 +153,8 @@
}
- {
role: common/docker_ldap, tags: ldap,
servicename: "ldap",
servicename: ldap,
basedir: /srv/ldap,
domain: "ldap.warpzone.ms"
}
- {
......@@ -159,11 +165,6 @@
domain_default: "www.warpzone.ms",
matrix_federation: true
}
- {
role: webserver/docker_autodiscover, tags: autodiscover,
servicename: autodiscover,
basedir: /srv/autodiscover
}
- {
role: webserver/docker_coturn, tags: coturn,
servicename: "coturn",
......@@ -172,7 +173,9 @@
- {
role: webserver/docker_dokuwiki, tags: dokuwiki,
servicename: "dokuwiki",
domain: "wiki.warpzone.ms"
domain: "wiki.warpzone.ms",
basedir: /srv/dokuwiki,
healthchecks_url: "https://hc-ping.com/038adcfe-05bf-45b4-919b-88b69aab8844"
}
- {
role: webserver/docker_gitlab, tags: gitlab,
......@@ -187,6 +190,8 @@
domain: icinga.warpzone.ms,
api_port: 5665,
mysql_port: 33306,
matrix_notification_domain: "matrix.warpzone.ms",
matrix_notification_room: "!iYefxbySFEfFQfUGEK:matrix.warpzone.ms"
}
- {
role: webserver/docker_hackmd, tags: hackmd,
......@@ -202,10 +207,15 @@
}
- {
role: webserver/docker_mail, tags: mail,
servicename: mail,
basedir: /srv/mail,
domain: "warpzone.ms",
mailserver: "mailserver.warpzone.ms",
listserver: "listserver.warpzone.ms"
}
- {
role: webserver/docker_matterbridge, tags: matterbridge,
servicename: matterbridge,
basedir: /srv/matterbridge,
domain: "www.warpzone.ms"
}
......
---
- name: "create folder struct for {{ servicename }}"
file:
path: "{{ item }}"
state: "directory"
with_items:
- "{{ basedir }}"
- name: deploy {{ servicename }} config
template:
dest: "{{ basedir }}/{{ item }}"
src: "{{ item }}"
with_items:
- docker-compose.yml
register: config
# Start containers
- name: "stop {{ servicename }} docker"
docker_compose:
project_src: "{{ basedir }}"
state: absent
when: config.changed
- name: "start {{ servicename }} docker"
docker_compose:
project_src: "{{ basedir }}"
state: present
version: '2.1'
services:
{% for domain in mail_domains %}
{{ domain }}:
image: monogramm/autodiscover-email-settings:1.4.0
restart: always
environment:
- DOMAIN={{ mail_domains[domain].maildomain }}
- IMAP_HOST={{ mail_domains[domain].mxserver }}
- IMAP_PORT=993
- IMAP_SOCKET=SSL
- POP_HOST={{ mail_domains[domain].mxserver }}
- POP_PORT=995
- POP_SOCKET=SSL
- SMTP_HOST={{ mail_domains[domain].mxserver }}
- SMTP_PORT=587
- SMTP_SOCKET=STARTTLS
labels:
- traefik.enable=true
- traefik.http.routers.{{ servicename }}-{{ domain }}.rule=Host(`autodiscover.{{ mail_domains[domain].maildomain }}`) || Host(`autoconfig.{{ mail_domains[domain].maildomain }}`)
- traefik.http.routers.{{ servicename }}-{{ domain }}.entrypoints=websecure
- traefik.http.services.{{ servicename }}-{{ domain }}.loadbalancer.server.port=8000
networks:
- default
- web
{% endfor %}
networks:
web:
external: true
......@@ -9,23 +9,19 @@
path: "{{item}}"
state: "directory"
with_items:
- /srv/dokuwiki/
- /srv/dokuwiki/data
- /srv/dokuwiki/pdftemplate
- "{{ basedir }}"
- "{{ basedir }}/data"
- "{{ basedir }}/pdftemplate"
- name: Docker Compose Konfig-Datei erstellen
template:
src: "{{item}}"
dest: "/srv/dokuwiki/{{item}}"
dest: "{{ basedir }}/{{item}}"
with_items:
- docker-compose.yml
- Dockerfile
- sendmail_plenum.py
- name: start dokuwiki docker
docker_compose:
project_src: /srv/dokuwiki/
state: present
register: config
- name: Cronjob für Mailversand Plenumsmail
cron:
......@@ -33,5 +29,16 @@
weekday: "0"
hour: "20"
minute: "0"
job: "/usr/bin/python3 /srv/dokuwiki/sendmail_plenum.py"
job: "/usr/bin/python3 {{ basedir }}/sendmail_plenum.py"
disabled: false
- name: "stop {{ servicename}} docker"
docker_compose:
project_src: "{{ basedir }}"
state: absent
when: config.changed
- name: "start {{ servicename}} docker"
docker_compose:
project_src: "{{ basedir }}"
state: present
......@@ -3,6 +3,7 @@ version: "3"
services:
app:
# values set in configuration: noreply_email_user - noreply_email_pass - smtp_host - smtp_port
build: .
image: "dokuwiki--{{ ansible_date_time.date }}--{{ ansible_date_time.hour }}-{{ ansible_date_time.minute }}-{{ ansible_date_time.second }}"
restart: always
......@@ -16,11 +17,8 @@ services:
- traefik.http.services.{{ servicename }}.loadbalancer.server.port=80
networks:
- default
- mail
- web
networks:
mail:
external: true
web:
external: true
......@@ -2,73 +2,76 @@ import smtplib
import datetime
from email.message import EmailMessage
from email.utils import formatdate
with open("/srv/dokuwiki/data/data/pages/intern/diskussionsthemen.txt") as fp:
raw = fp.read().split('\n')
# Extract Themen
firstline = 0
lastline = 0
for index, line in enumerate(raw):
# Start marker
if "====== 1." in line:
firstline = index + 1
continue
import requests
def do_work():
with open("/srv/dokuwiki/data/data/pages/intern/diskussionsthemen.txt") as fp:
raw = fp.read().split('\n')
# Extract Themen
firstline = 0
lastline = 0
for index, line in enumerate(raw):
# Start marker
if "====== 1." in line:
firstline = index + 1
continue
# End marker
if "====== 2." in line:
lastline = index
break
# End marker
if "====== 2." in line:
lastline = index
break
# Remove empty lines from topics
topics = []
for extracted in raw[firstline:lastline-1]:
if extracted != '':
topics.append(extracted)
# There are no topics - just exit
if (len(topics) == 0):
exit()
# calculate date of next tuesday
d = datetime.date.today()
while d.strftime('%a') != 'Tue':
d += datetime.timedelta(1)
# mail template
mail = f"""
Liebe Zonies,
es gibt wieder Themen die im Plenum besprochen werden sollen. Der nächste
Reguläre Termin ist am nächsten Dienstag ({str(d)}) um 20:00.
Im Wiki (https://wiki.warpzone.ms/intern:diskussionsthemen#diskussionsthemen_naechstes_plenum_automatische_einladung)
stehen folgende Themen:
{chr(10).join(topics)}
Die Teilnahme ist natürlich auch remote möglich unter
https://jitsi.dorf-post.de/warpzone-plenum möglich.
Viele Grüße und bis Dienstag,
sendmail_plenum.py
"""
msg = EmailMessage()
msg['Subject'] = f'Aktuelle Plenumsthemen für Dienstag ({str(d)} 20:00)'
msg['From'] = '{{noreply_email_user}}'
msg['To'] = 'intern@warpzone.ms'
msg['Date'] = formatdate(localtime=True)
msg.set_content(mail)
server = smtplib.SMTP('{{smtp_host}}', {{smtp_port}})
server.starttls()
server.login("{{noreply_email_user}}", "{{noreply_email_pass}}")
server.send_message(msg)
server.quit()
# Remove empty lines from topics
topics = []
for extracted in raw[firstline:lastline-1]:
if extracted != '':
topics.append(extracted)
# There are no topics - just exit
if (len(topics) == 0):
exit()
# calculate date of next tuesday
d = datetime.date.today()
while d.strftime('%a') != 'Tue':
d += datetime.timedelta(1)
# mail template
mail = f"""
Liebe Zonies,
es gibt wieder Themen die im Plenum besprochen werden sollen. Der nächste
Reguläre Termin ist am nächsten Dienstag ({str(d)}) um 20:00.
Im Wiki (https://wiki.warpzone.ms/intern:diskussionsthemen#diskussionsthemen_naechstes_plenum_automatische_einladung)
stehen folgende Themen:
{chr(10).join(topics)}
Die Teilnahme ist natürlich auch remote möglich unter
https://jitsi.dorf-post.de/warpzone-plenum möglich.
Viele Grüße und bis Dienstag,
sendmail_plenum.py
"""
msg = EmailMessage()
msg['Subject'] = f'Aktuelle Plenumsthemen für Dienstag ({str(d)} 20:00)'
msg['From'] = '{{noreply_email_user}}'
msg['To'] = 'intern@warpzone.ms'
msg['Date'] = formatdate(localtime=True)
msg.set_content(mail)
server = smtplib.SMTP('{{smtp_host}}', {{smtp_port}})
server.starttls()
server.login("{{noreply_email_user}}", "{{noreply_email_pass}}")
server.send_message(msg)
server.quit()
return True
success = False
try:
success = do_work()
finally:
requests.get("{{ healthchecks_url }}" if success else "{{ healthchecks_url }}" + "/fail")