commit 6dfaf433dc6d798a49922f88d195218d64ea5511 Author: Joshua M. Boniface Date: Fri Sep 1 15:42:19 2023 -0400 Initial commit of PVC Ansible role diff --git a/README.md b/README.md new file mode 100644 index 0000000..adb4954 --- /dev/null +++ b/README.md @@ -0,0 +1,14 @@ +# PVC Ansible + +A set of Ansible roles to set up a PVC node host. + +## Variables + +A default example set of configuration variables can be found in `group_vars/default/vars.yml + +## Using + +0. Deploy Debian 10 to a set of servers. +0. Create a new cluster group in the `hosts` file. +0. Create a set of vars in `group_vars`. +0. Run the `pvc.yml` playbook against the servers. diff --git a/group_vars/default/vars.yml b/group_vars/default/vars.yml new file mode 100644 index 0000000..3e7a15f --- /dev/null +++ b/group_vars/default/vars.yml @@ -0,0 +1,62 @@ +--- +# Ceph storage +pvc_ceph_storage_secret_uuid: "6e1f4969-f7ea-4be1-9232-e67ce3bfc37e" +pvc_ceph_storage_secret_key: "AQC8y6tWkfUEKxAAI9XKcXlN38Nzbrom899rJw==" +# Database +pvc_dns_database_name: "pvcdns" +pvc_dns_database_user: "pvcdns" +pvc_dns_database_password: "PVCdnsPassw0rd" +pvc_replication_database_user: "replicator" +pvc_replication_database_password: "PVCreplicatorPassw0rd" +pvc_superuser_database_user: "postgres" +pvc_superuser_database_password: "PVCpostgresPassw0rd" +# Coordinators +pvc_nodes: + - hostname: "pvchv1" + is_coordinator: yes + node_id: 1 + router_id: "{{ blsecluster_pvc_clustersubnetsnip }}.1" + cluster_ip: "by-id" + storage_ip: "{{ blsecluster_pvc_storagesubnetsnip }}.25/24" + upstream_ip: "{{ blsecluster_pvc_upstreamsubnetsnip }}.25/24" + ipmi_host: "hv1-lom.{{ blsedomains_mgmtdomain }}" + ipmi_user: "{{ username_ipmi_host }}" + ipmi_password: "{{ passwd_ipmi_host }}" + - hostname: "pvchv2" + is_coordinator: yes + node_id: 2 + router_id: "{{ blsecluster_pvc_clustersubnetsnip }}.2" + cluster_ip: "by-id" + storage_ip: "{{ blsecluster_pvc_storagesubnetsnip }}.26/24" + upstream_ip: "{{ blsecluster_pvc_upstreamsubnetsnip }}.26/24" + ipmi_host: "hv2-lom.{{ blsedomains_mgmtdomain }}" + ipmi_user: "{{ username_ipmi_host }}" + ipmi_password: "{{ passwd_ipmi_host }}" + - hostname: "pvchv3" + is_coordinator: yes + node_id: 3 + router_id: "{{ blsecluster_pvc_clustersubnetsnip }}.3" + cluster_ip: "by-id" + storage_ip: "{{ blsecluster_pvc_storagesubnetsnip }}.27/24" + upstream_ip: "{{ blsecluster_pvc_upstreamsubnetsnip }}.27/24" + ipmi_host: "hv3-lom.{{ blsedomains_mgmtdomain }}" + ipmi_user: "{{ username_ipmi_host }}" + ipmi_password: "{{ passwd_ipmi_host }}" +# Networks +pvc_asn: "{{ blsecluster_pvc_asn }}" +pvc_routers: + - "{{ blsecluster_pvc_upstreamsubnetsnip }}.2" + - "{{ blsecluster_pvc_upstreamsubnetsnip }}.3" +pvc_cluster_device: "bond0" +pvc_cluster_domain: "{{ blsedomains_pvc_clusterdomain }}" +pvc_cluster_subnet: "{{ blsecluster_pvc_clustersubnetv4 }}" +pvc_cluster_floatingip: "{{ blsecluster_pvc_clustersubnetsnip }}.252/24" +pvc_storage_device: "vlan99" +pvc_storage_domain: "{{ blsedomains_pvc_storagedomain }}" +pvc_storage_subnet: "{{ blsecluster_pvc_storagesubnetv4 }}" +pvc_storage_floatingip: "{{ blsecluster_pvc_storagesubnetsnip }}.252/24" +pvc_upstream_device: "vlan100" +pvc_upstream_domain: "{{ blsedomains_pvc_upstreamdomain }}" +pvc_upstream_subnet: "{{ blsecluster_pvc_upstreamsubnetv4 }}" +pvc_upstream_floatingip: "{{ blsecluster_pvc_upstreamsubnetsnip }}.252/24" +pvc_upstream_gatewayip: "{{ blsecluster_pvc_upstreamsubnetsnip }}.1" diff --git a/hosts b/hosts new file mode 100644 index 0000000..ba6dbae --- /dev/null +++ b/hosts @@ -0,0 +1,9 @@ +# PVC hosts file + +[sites:children] +default + +[default] +pvchv1 +pvchv2 +pvchv3 diff --git a/pvc.yml b/pvc.yml new file mode 100644 index 0000000..a5d1b73 --- /dev/null +++ b/pvc.yml @@ -0,0 +1,10 @@ +--- +- hosts: all + remote_user: deploy + become: yes + roles: + - name: base + tags: base + + - name: pvc + tags: pvc diff --git a/roles/base/files/usr/bin/check_mk_agent b/roles/base/files/usr/bin/check_mk_agent new file mode 100755 index 0000000..3bc41e0 --- /dev/null +++ b/roles/base/files/usr/bin/check_mk_agent @@ -0,0 +1,1107 @@ +#!/bin/bash +# Check_MK Agent for Linux +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# Remove locale settings to eliminate localized outputs where possible +export LC_ALL=C +unset LANG + +export MK_LIBDIR=${MK_LIBDIR:-/usr/lib/check_mk_agent} +export MK_CONFDIR=${MK_CONFDIR:-/etc/check_mk} +export MK_VARDIR=${MK_VARDIR:-/var/lib/check_mk_agent} + +# Provide information about the remote host. That helps when data +# is being sent only once to each remote host. +if [ "$REMOTE_HOST" ] ; then + export REMOTE=$REMOTE_HOST +elif [ "$SSH_CLIENT" ] ; then + export REMOTE=${SSH_CLIENT%% *} +fi + +# Make sure, locally installed binaries are found +PATH=$PATH:/usr/local/bin + +# All executables in PLUGINSDIR will simply be executed and their +# ouput appended to the output of the agent. Plugins define their own +# sections and must output headers with '<<<' and '>>>' +PLUGINSDIR=$MK_LIBDIR/plugins + +# All executables in LOCALDIR will by executabled and their +# output inserted into the section <<>>. Please +# refer to online documentation for details about local checks. +LOCALDIR=$MK_LIBDIR/local + +# All files in SPOOLDIR will simply appended to the agent +# output if they are not outdated (see below) +SPOOLDIR=$MK_VARDIR/spool + +# close standard input (for security reasons) and stderr +if [ "$1" = -d ] +then + set -xv +else + exec /dev/null +fi + +# Prefer (relatively) new /usr/bin/timeout from coreutils against +# our shipped waitmax. waitmax is statically linked and crashes on +# some Ubuntu versions recently. +if type timeout >/dev/null 2>&1 ; then + function waitmax () { + timeout "$@" + } + export -f waitmax +fi + +if [ -f $MK_CONFDIR/encryption.cfg ] ; then + source $MK_CONFDIR/encryption.cfg +fi + +if [ "$ENCRYPTED" == "yes" ] ; then + echo -n "00" # protocol version + exec > >(openssl enc -aes-256-cbc -k $PASSPHRASE -nosalt) +fi + + +# +# CHECK SECTIONS +# + +function section_mem() +{ + echo '<<>>' + egrep -v '^Swap:|^Mem:|total:' < /proc/meminfo +} + +function section_cpu() +{ + echo '<<>>' + if [ $(uname -m) = "armv7l" ]; then + CPU_REGEX='^processor' + else + CPU_REGEX='^CPU|^processor' + fi + echo "$(cat /proc/loadavg) $(grep -E $CPU_REGEX < /proc/cpuinfo | wc -l)" +} + + +function run_mrpe() { + local descr=$1 + shift + local cmdline="$@" + + echo '<<>>' + + PLUGIN=${cmdline%% *} + OUTPUT=$(eval "$cmdline") + + echo -n "(${PLUGIN##*/}) $descr $? $OUTPUT" | tr \\n \\1 + echo +} + +export -f run_mrpe + +# Runs a command asynchronous by use of a cache file. Usage: +# run_cached [-s] NAME MAXAGE +# -s creates the section header <<<$NAME>>> +# -m mrpe-mode: stores exit code with the cache +# NAME is the name of the section (also used as cache file name) +# MAXAGE is the maximum cache livetime in seconds +function run_cached () { + local NOW=$(date +%s) + local section= + local mrpe=0 + local append_age=0 + if [ "$1" = -s ] ; then local section="echo '<<<$2:cached($NOW,$3)>>>' ; " ; shift ; fi + if [ "$1" = -m ] ; then local mrpe=1 ; shift ; fi + if [ "$1" = -a ] ; then local append_age=1 ; shift ; fi + local NAME=$1 + local MAXAGE=$2 + shift 2 + local CMDLINE="$section$@" + + if [ ! -d $MK_VARDIR/cache ]; then mkdir -p $MK_VARDIR/cache ; fi + if [ "$mrpe" = 1 ] ; then + CACHEFILE="$MK_VARDIR/cache/mrpe_$NAME.cache" + else + CACHEFILE="$MK_VARDIR/cache/$NAME.cache" + fi + + # Check if the creation of the cache takes suspiciously long and kill the + # process if the age (access time) of $CACHEFILE.new is twice the MAXAGE. + # Output the evantually already cached section anyways and start the cache + # update again. + if [ -e "$CACHEFILE.new" ] ; then + local CF_ATIME=$(stat -c %X "$CACHEFILE.new") + if [ $((NOW - CF_ATIME)) -ge $((MAXAGE * 2)) ] ; then + # Kill the process still accessing that file in case + # it is still running. This avoids overlapping processes! + fuser -k -9 "$CACHEFILE.new" >/dev/null 2>&1 + rm -f "$CACHEFILE.new" + fi + fi + + + # Check if cache file exists and is recent enough + if [ -s "$CACHEFILE" ] ; then + local MTIME=$(stat -c %Y "$CACHEFILE") + local AGE=$((NOW - MTIME)) + if [ $AGE -le $MAXAGE ] ; then local USE_CACHEFILE=1 ; fi + # Output the file in any case, even if it is + # outdated. The new file will not yet be available + if [ $append_age -eq 1 ] ; then + # insert the cached-string before the pipe (first -e) + # or, if no pipe found (-e t) append it (third -e), + # but only once and on the second line (2!b) (first line is section header, + # all further lines are long output) + cat "$CACHEFILE" | sed -e "2s/|/ (Cached: ${AGE}\/${MAXAGE}s)|/" -e t -e "2s/$/ (Cached: ${AGE}\/${MAXAGE}s)/" + else + cat "$CACHEFILE" + fi + fi + + # Cache file outdated and new job not yet running? Start it + if [ -z "$USE_CACHEFILE" ] && [ ! -e "$CACHEFILE.new" ] ; then + # When the command fails, the output is throws away ignored + if [ $mrpe -eq 1 ] ; then + echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; run_mrpe $NAME $CMDLINE && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup /bin/bash >/dev/null 2>&1 & + else + echo "set -o noclobber ; exec > \"$CACHEFILE.new\" || exit 1 ; $CMDLINE && mv \"$CACHEFILE.new\" \"$CACHEFILE\" || rm -f \"$CACHEFILE\" \"$CACHEFILE.new\"" | nohup /bin/bash >/dev/null 2>&1 & + fi + fi +} + +# Make run_cached available for subshells (plugins, local checks, etc.) +export -f run_cached + +# Implements Real-Time Check feature of the Check_MK agent which can send +# some section data in 1 second resolution. Useful for fast notifications and +# detailed graphing (if you configure your RRDs to this resolution). +function run_real_time_checks() +{ + PIDFILE=$MK_VARDIR/real_time_checks.pid + echo $$ > $PIDFILE + + . $MK_CONFDIR/real_time_checks.cfg + + if [ "$PASSPHRASE" != "" ] ; then + # new mechanism to set the passphrase has priority + RTC_SECRET=$PASSPHRASE + fi + + while true; do + # terminate when pidfile is gone or other Real-Time Check process started or configured timeout + if [ ! -e $PIDFILE ] || [ $(<$PIDFILE) -ne $$ ] || [ $RTC_TIMEOUT -eq 0 ]; then + exit 1 + fi + + for SECTION in $RTC_SECTIONS; do + if [ "$ENCRYPTED_RT" != "no" ] ; then + PROTOCOL=00 + else + PROTOCOL=99 + fi + # Be aware of maximum packet size. Maybe we need to check the size of the section + # output and do some kind of nicer error handling. + # 2 bytes: protocol version, 10 bytes: timestamp, rest: encrypted data + # dd is used to concatenate the output of all commands to a single write/block => udp packet + { echo -n $PROTOCOL ; + date +%s | tr -d '\n' ; + if [ "$ENCRYPTED_RT" != "no" ] ; then + export RTC_SECRET=$RTC_SECRET ; section_$SECTION | openssl enc -aes-256-cbc -pass env:RTC_SECRET -nosalt ; + else + section_$SECTION ; + fi + } | dd bs=9999 iflag=fullblock 2>/dev/null >/dev/udp/$REMOTE/$RTC_PORT + done + + sleep 1 + RTC_TIMEOUT=$((RTC_TIMEOUT-1)) + done +} + +echo '<<>>' +echo Version: 1.4.0p11 +echo AgentOS: linux +echo Hostname: $HOSTNAME +echo AgentDirectory: $MK_CONFDIR +echo DataDirectory: $MK_VARDIR +echo SpoolDirectory: $SPOOLDIR +echo PluginsDirectory: $PLUGINSDIR +echo LocalDirectory: $LOCALDIR + +# If we are called via xinetd, try to find only_from configuration +if [ -n "$REMOTE_HOST" ] +then + echo -n 'OnlyFrom: ' + echo $(sed -n '/^service[[:space:]]*check_mk/,/}/s/^[[:space:]]*only_from[[:space:]]*=[[:space:]]*\(.*\)/\1/p' /etc/xinetd.d/* | head -n1) +fi + +# Print out Partitions / Filesystems. (-P gives non-wrapped POSIXed output) +# Heads up: NFS-mounts are generally supressed to avoid agent hangs. +# If hard NFS mounts are configured or you have too large nfs retry/timeout +# settings, accessing those mounts from the agent would leave you with +# thousands of agent processes and, ultimately, a dead monitored system. +# These should generally be monitored on the NFS server, not on the clients. + +echo '<<>>' +# The exclusion list is getting a bit of a problem. -l should hide any remote FS but seems +# to be all but working. +excludefs="-x smbfs -x cifs -x iso9660 -x udf -x nfsv4 -x nfs -x mvfs -x zfs -x prl_fs" +df -PTlk $excludefs | sed 1d + +# df inodes information +echo '<<>>' +echo '[df_inodes_start]' +df -PTli $excludefs | sed 1d +echo '[df_inodes_end]' + +# Filesystem usage for ZFS +if type zfs > /dev/null 2>&1 ; then + echo '<<>>' + zfs get -Hp name,quota,used,avail,mountpoint,type -t filesystem,volume || \ + zfs get -Hp name,quota,used,avail,mountpoint,type + echo '[df]' + df -PTlk -t zfs | sed 1d +fi + +# Check NFS mounts by accessing them with stat -f (System +# call statfs()). If this lasts more then 2 seconds we +# consider it as hanging. We need waitmax. +if type waitmax >/dev/null +then + STAT_VERSION=$(stat --version | head -1 | cut -d" " -f4) + STAT_BROKE="5.3.0" + + echo '<<>>' + sed -n '/ nfs4\? /s/[^ ]* \([^ ]*\) .*/\1/p' < /proc/mounts | + sed 's/\\040/ /g' | + while read MP + do + if [ ! -r $MP ]; then + echo "$MP Permission denied" + elif [ $STAT_VERSION != $STAT_BROKE ]; then + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" || \ + echo "$MP hanging 0 0 0 0" + else + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" && \ + printf '\n'|| echo "$MP hanging 0 0 0 0" + fi + done + + echo '<<>>' + sed -n '/ cifs\? /s/[^ ]* \([^ ]*\) .*/\1/p' < /proc/mounts | + sed 's/\\040/ /g' | + while read MP + do + if [ ! -r $MP ]; then + echo "$MP Permission denied" + elif [ $STAT_VERSION != $STAT_BROKE ]; then + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" || \ + echo "$MP hanging 0 0 0 0" + else + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" && \ + printf '\n'|| echo "$MP hanging 0 0 0 0" + fi + done +fi + +# Check mount options. Filesystems may switch to 'ro' in case +# of a read error. +echo '<<>>' +grep ^/dev < /proc/mounts + +# processes including username, without kernel processes +echo '<<>>' +ps ax -o user:32,vsz,rss,cputime,etime,pid,command --columns 10000 | sed -e 1d -e 's/ *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) *\([^ ]*\) */(\1,\2,\3,\4\/\5,\6) /' + +# Memory usage +section_mem + +# Load and number of processes +section_cpu + +# Uptime +echo '<<>>' +cat /proc/uptime + + +# New variant: Information about speed and state in one section +if type ip > /dev/null +then + echo '<<>>' + echo "[start_iplink]" + ip link + echo "[end_iplink]" +fi + +echo '<<>>' +sed 1,2d /proc/net/dev +if type ethtool > /dev/null +then + for eth in $(sed -e 1,2d < /proc/net/dev | cut -d':' -f1 | sort) + do + echo "[$eth]" + ethtool $eth | egrep '(Speed|Duplex|Link detected|Auto-negotiation):' + echo -en "\tAddress: " ; cat /sys/class/net/$eth/address ; echo + done +fi + + +# Current state of bonding interfaces +if [ -e /proc/net/bonding ] ; then + echo '<<>>' + pushd /proc/net/bonding > /dev/null + head -v -n 1000 * + popd > /dev/null +fi + +# Same for Open vSwitch bonding +if type ovs-appctl > /dev/null ; then + echo '<<>>' + for bond in $(ovs-appctl bond/list | sed -e 1d | rev | cut -f3 | rev) ; do + echo "[$bond]" + ovs-appctl bond/show $bond + done +fi + + +# Number of TCP connections in the various states +echo '<<>>' +if type waitmax >/dev/null ; then + THIS=$(waitmax -s 1 10 cat /proc/net/tcp /proc/net/tcp6 2>/dev/null | awk ' /:/ { c[$4]++; } END { for (x in c) { print x, c[x]; } }') + if [ $? == 0 ] ; then + echo "$THIS" + else + ss -ant |grep -v ^State | awk ' /:/ { c[$1]++; } END { for (x in c) { print x, c[x]; } }' |sed -e 's/^ESTAB/01/g;s/^SYN-SENT/02/g;s/^SYN-RECV/03/g;s/^FIN-WAIT-1/04/g;s/^FIN-WAIT-2/05/g;s/^TIME-WAIT/06/g;s/^CLOSED/07/g;s/^CLOSE-WAIT/08/g;s/^LAST-ACK/09/g;s/^LISTEN/0A/g;s/^CLOSING/0B/g;' + fi +fi + +# Linux Multipathing +if type multipath >/dev/null ; then + if [ -f /etc/multipath.conf ] ; then + echo '<<>>' + multipath -l + fi +fi + +# Performancecounter Platten +echo '<<>>' +date +%s +egrep ' (x?[shv]d[a-z]*|cciss/c[0-9]+d[0-9]+|emcpower[a-z]+|dm-[0-9]+|VxVM.*|mmcblk.*|dasd[a-z]*|nvme[0-9]+n[0-9]+) ' < /proc/diskstats +if type dmsetup >/dev/null ; then + echo '[dmsetup_info]' + dmsetup info -c --noheadings --separator ' ' -o name,devno,vg_name,lv_name +fi +if [ -d /dev/vx/dsk ] ; then + echo '[vx_dsk]' + stat -c "%t %T %n" /dev/vx/dsk/*/* +fi + + +# Performancecounter Kernel +echo '<<>>' +date +%s +cat /proc/vmstat /proc/stat + +# Hardware sensors via IPMI (need ipmitool) +if type ipmitool > /dev/null +then + run_cached -s "ipmi:sep(124)" 300 "waitmax 300 ipmitool sensor list | grep -v 'command failed' | egrep -v '^[^ ]+ na ' | grep -v ' discrete '" + # readable discrete sensor states + run_cached -s "ipmi_discrete:sep(124)" 300 "waitmax 300 ipmitool sdr elist compact" +fi + + +# IPMI data via ipmi-sensors (of freeipmi). Please make sure, that if you +# have installed freeipmi that IPMI is really support by your hardware. +if type ipmi-sensors >/dev/null +then + echo '<<>>' + # Newer ipmi-sensors version have new output format; Legacy format can be used + if ipmi-sensors --help | grep -q legacy-output; then + IPMI_FORMAT="--legacy-output" + else + IPMI_FORMAT="" + fi + if ipmi-sensors --help | grep -q " \-\-groups"; then + IPMI_GROUP_OPT="-g" + else + IPMI_GROUP_OPT="-t" + fi + + # At least with ipmi-sensors 0.7.16 this group is Power_Unit instead of "Power Unit" + run_cached -s ipmi_sensors 300 "for class in Temperature Power_Unit Fan + do + ipmi-sensors $IPMI_FORMAT --sdr-cache-directory /var/cache $IPMI_GROUP_OPT \"$class\" | sed -e 's/ /_/g' -e 's/:_\?/ /g' -e 's@ \([^(]*\)_(\([^)]*\))@ \2_\1@' + # In case of a timeout immediately leave loop. + if [ $? = 255 ] ; then break ; fi + done" +fi + +# RAID status of Linux software RAID +echo '<<>>' +cat /proc/mdstat + +# RAID status of Linux RAID via device mapper +if type dmraid >/dev/null && DMSTATUS=$(dmraid -r) +then + echo '<<>>' + + # Output name and status + dmraid -s | grep -e ^name -e ^status + + # Output disk names of the RAID disks + DISKS=$(echo "$DMSTATUS" | cut -f1 -d\:) + + for disk in $DISKS ; do + device=$(cat /sys/block/$(basename $disk)/device/model ) + status=$(echo "$DMSTATUS" | grep ^${disk}) + echo "$status Model: $device" + done +fi + +# RAID status of LSI controllers via cfggen +if type cfggen > /dev/null ; then + echo '<<>>' + cfggen 0 DISPLAY | egrep '(Target ID|State|Volume ID|Status of volume)[[:space:]]*:' | sed -e 's/ *//g' -e 's/:/ /' +fi + +# RAID status of LSI MegaRAID controller via MegaCli. You can download that tool from: +# http://www.lsi.com/downloads/Public/MegaRAID%20Common%20Files/8.02.16_MegaCLI.zip +if type MegaCli >/dev/null ; then + MegaCli_bin="MegaCli" +elif type MegaCli64 >/dev/null ; then + MegaCli_bin="MegaCli64" +elif type megacli >/dev/null ; then + MegaCli_bin="megacli" +else + MegaCli_bin="unknown" +fi + +if [ "$MegaCli_bin" != "unknown" ]; then + echo '<<>>' + for part in $($MegaCli_bin -EncInfo -aALL -NoLog < /dev/null \ + | sed -rn 's/:/ /g; s/[[:space:]]+/ /g; s/^ //; s/ $//; s/Number of enclosures on adapter ([0-9]+).*/adapter \1/g; /^(Enclosure|Device ID|adapter) [0-9]+$/ p'); do + [ $part = adapter ] && echo "" + [ $part = 'Enclosure' ] && echo -ne "\ndev2enc" + echo -n " $part" + done + echo + $MegaCli_bin -PDList -aALL -NoLog < /dev/null | egrep 'Enclosure|Raw Size|Slot Number|Device Id|Firmware state|Inquiry|Adapter' + echo '<<>>' + $MegaCli_bin -LDInfo -Lall -aALL -NoLog < /dev/null | egrep 'Size|State|Number|Adapter|Virtual' + echo '<<>>' + $MegaCli_bin -AdpBbuCmd -GetBbuStatus -aALL -NoLog < /dev/null | grep -v Exit +fi + +# RAID status of 3WARE disk controller (by Radoslaw Bak) +if type tw_cli > /dev/null ; then + for C in $(tw_cli show | awk 'NR < 4 { next } { print $1 }'); do + echo '<<<3ware_info>>>' + tw_cli /$C show all | egrep 'Model =|Firmware|Serial' + echo '<<<3ware_disks>>>' + tw_cli /$C show drivestatus | egrep 'p[0-9]' | sed "s/^/$C\//" + echo '<<<3ware_units>>>' + tw_cli /$C show unitstatus | egrep 'u[0-9]' | sed "s/^/$C\//" + done +fi + +# RAID controllers from areca (Taiwan) +# cli64 can be found at ftp://ftp.areca.com.tw/RaidCards/AP_Drivers/Linux/CLI/ +if type cli64 >/dev/null ; then + run_cached -s arc_raid_status 300 "cli64 rsf info | tail -n +3 | head -n -2" +fi + +# VirtualBox Guests. Section must always been output. Otherwise the +# check would not be executed in case no guest additions are installed. +# And that is something the check wants to detect +echo '<<>>' +if type VBoxControl >/dev/null 2>&1 && lsmod | grep vboxguest >/dev/null 2>&1; then + VBoxControl -nologo guestproperty enumerate | cut -d, -f1,2 + [ ${PIPESTATUS[0]} = 0 ] || echo "ERROR" +fi + +# OpenVPN Clients. Currently we assume that the configuration # is in +# /etc/openvpn. We might find a safer way to find the configuration later. +if [ -e /etc/openvpn/openvpn-status.log ] ; then + echo '<<>>' + sed -n -e '/CLIENT LIST/,/ROUTING TABLE/p' < /etc/openvpn/openvpn-status.log | sed -e 1,3d -e '$d' +fi + +# Time synchronization with NTP +if type ntpq > /dev/null 2>&1 ; then + # remove heading, make first column space separated + run_cached -s ntp 30 "waitmax 5 ntpq -np | sed -e 1,2d -e 's/^\(.\)/\1 /' -e 's/^ /%/' || true" +fi + +# Time synchronization with Chrony +if type chronyc > /dev/null 2>&1 ; then + # Force successful exit code. Otherwise section will be missing if daemon not running + run_cached -s chrony 30 "waitmax 5 chronyc -n tracking || true" +fi + +if type nvidia-settings >/dev/null && [ -S /tmp/.X11-unix/X0 ] +then + echo '<<>>' + for var in GPUErrors GPUCoreTemp + do + DISPLAY=:0 waitmax 2 nvidia-settings -t -q $var | sed "s/^/$var: /" + done +fi + +if [ -e /proc/drbd ]; then + echo '<<>>' + cat /proc/drbd +fi + +# Status of CUPS printer queues +if type lpstat > /dev/null 2>&1; then + # Systemd starts cupsd on demand: Instead of checking that the cupsd is + # running we check that the cups service exists and is enabled. + if pgrep -f "\bcupsd" > /dev/null 2>&1 \ + || (type systemctl >/dev/null 2>&1 && systemctl is-enabled cups.service); then + # first define a function to check cups + function cups_queues () { + CPRINTCONF=/etc/cups/printers.conf + if [ -r "$CPRINTCONF" ] ; then + LOCAL_PRINTERS=$(grep -E "<(Default)?Printer .*>" $CPRINTCONF | awk '{print $2}' | sed -e 's/>//') + lpstat -p | while read LINE + do + PRINTER=$(echo $LINE | awk '{print $2}') + if echo "$LOCAL_PRINTERS" | grep -q "$PRINTER"; then + echo $LINE + fi + done + echo '---' + lpstat -o | while read LINE + do + PRINTER=${LINE%%-*} + if echo $LOCAL_PRINTERS | grep -q $PRINTER; then + echo $LINE + fi + done + else + lpstat -p + echo '---' + lpstat -o | sort + fi + } + # Make cups_queues available for subshell + export -f cups_queues + # Use cups_queues function with run_cached and cache time of 5 mins + run_cached -s cups_queues 300 "cups_queues" + fi +fi + +# Heartbeat monitoring +# Different handling for heartbeat clusters with and without CRM +# for the resource state +if [ -S /var/run/heartbeat/crm/cib_ro -o -S /var/run/crm/cib_ro ] || pgrep crmd > /dev/null 2>&1; then + echo '<<>>' + TZ=UTC crm_mon -1 -r | grep -v ^$ | sed 's/^ //; /^\sResource Group:/,$ s/^\s//; s/^\s/_/g' +fi +if type cl_status > /dev/null 2>&1; then + echo '<<>>' + cl_status rscstatus + + echo '<<>>' + for NODE in $(cl_status listnodes); do + if [ $NODE != $(echo $HOSTNAME | tr 'A-Z' 'a-z') ]; then + STATUS=$(cl_status nodestatus $NODE) + echo -n "$NODE $STATUS" + for LINK in $(cl_status listhblinks $NODE 2>/dev/null); do + echo -n " $LINK $(cl_status hblinkstatus $NODE $LINK)" + done + echo + fi + done +fi + +# Postfix mailqueue monitoring +# Determine the number of mails and their size in several postfix mail queues +function read_postfix_queue_dirs { + postfix_queue_dir=$1 + if [ -n "$postfix_queue_dir" ]; then + echo '<<>>' + if [ ! -z "$2" ]; then + echo "[[[${2}]]]" + fi + for queue in deferred active + do + count=$(find $postfix_queue_dir/$queue -type f | wc -l) + size=$(du -s $postfix_queue_dir/$queue | awk '{print $1 }') + if [ -z "$size" ]; then + size=0 + fi + if [ -z "$count" ]; then + echo "Mail queue is empty" + else + echo "QUEUE_${queue} $size $count" + fi + done + fi +} + +# Postfix mailqueue monitoring +# Determine the number of mails and their size in several postfix mail queues +if type postconf >/dev/null ; then + # Check if multi_instance_directories exists in main.cf and is not empty + # always takes the last entry, multiple entries possible + multi_instances_dirs=$(postconf -c /etc/postfix 2>/dev/null | grep ^multi_instance_directories | sed 's/.*=[[:space:]]*//g') + if [ ! -z "$multi_instances_dirs" ]; then + for queue_dir in $multi_instances_dirs + do + if [ -n "$queue_dir" ]; then + postfix_queue_dir=$(postconf -c $queue_dir 2>/dev/null | grep ^queue_directory | sed 's/.*=[[:space:]]*//g') + read_postfix_queue_dirs $postfix_queue_dir $queue_dir + fi + done + + else + postfix_queue_dir=$(postconf -h queue_directory 2>/dev/null) + read_postfix_queue_dirs $postfix_queue_dir + fi + +elif [ -x /usr/sbin/ssmtp ] ; then + echo '<<>>' + mailq 2>&1 | sed 's/^[^:]*: \(.*\)/\1/' | tail -n 6 + +fi + +# Postfix status monitoring. Can handle multiple instances. +if type postfix >/dev/null ; then + echo "<<>>" + for i in $(ls -d /var/spool/postfix*) + do + if [ -e "$i/pid/master.pid" ]; then + postfix_pid=$(cat $i/pid/master.pid | sed 's/ //g') # handle possible spaces in output + if readlink -- "/proc/$postfix_pid/exe" | grep -q ".*postfix/\(sbin/\)\?master.*"; then + echo "$i:the Postfix mail system is running:PID:$postfix_pid" | sed 's/\/var\/spool\///g' + else + echo "$i:PID file exists but instance is not running!" | sed 's/\/var\/spool\///g' + fi + else + echo "$i:the Postfix mail system is not running" | sed 's/\/var\/spool\///g' + fi + done +fi + +# Check status of qmail mailqueue +if type qmail-qstat >/dev/null +then + echo "<<>>" + qmail-qstat +fi + +# Nullmailer queue monitoring +if type nullmailer-send >/dev/null && [ -d /var/spool/nullmailer/queue ] +then + echo '<<>>' + COUNT=$(find /var/spool/nullmailer/queue -type f | wc -l) + SIZE=$(du -s /var/spool/nullmailer/queue | awk '{print $1 }') + echo "$SIZE $COUNT" +fi + +# Check status of OMD sites and Check_MK Notification spooler +if type omd >/dev/null +then + run_cached -s omd_status 60 "omd status --bare --auto || true" + echo '<<>>' + for statefile in /omd/sites/*/var/log/mknotifyd.state ; do + if [ -e "$statefile" ] ; then + site=${statefile%/var/log*} + site=${site#/omd/sites/} + echo "[$site]" + grep -v '^#' < $statefile + fi + done + + echo '<<>>' + for statsfile in /omd/sites/*/var/log/apache/stats; do + if [ -e "$statsfile" ] ; then + site=${statsfile%/var/log*} + site=${site#/omd/sites/} + echo "[$site]" + cat $statsfile + > $statsfile + # prevent next section to fail caused by a missing newline at the end of the statsfile + echo + fi + done +fi + + +# Welcome the ZFS check on Linux +# We do not endorse running ZFS on linux if your vendor doesnt support it ;) +# check zpool status +if type zpool >/dev/null; then + echo "<<>>" + zpool status -x +fi + + +# Veritas Cluster Server +# Software is always installed in /opt/VRTSvcs. +# Secure mode must be off to allow root to execute commands +if [ -x /opt/VRTSvcs/bin/haclus ] +then + echo "<<>>" + vcshost=$(hostname | cut -d. -f1) + /opt/VRTSvcs/bin/haclus -display -localclus | grep -e ClusterName -e ClusState + /opt/VRTSvcs/bin/hasys -display -attribute SysState + /opt/VRTSvcs/bin/hagrp -display -sys $vcshost -attribute State -localclus + /opt/VRTSvcs/bin/hares -display -sys $vcshost -attribute State -localclus +fi + + +# Fileinfo-Check: put patterns for files into /etc/check_mk/fileinfo.cfg +if [ -r "$MK_CONFDIR/fileinfo.cfg" ] ; then + echo '<<>>' + date +%s + for line in $(cat "$MK_CONFDIR/fileinfo.cfg") + do + # only work on lines containing files, skip e.g. comments and blank lines + if [ ${line:0:1} = "/" ]; then + stat -c "%n|%s|%Y" "${line}" 2>/dev/null + + if [ $? -ne 0 ]; then + echo "$line|missing|$(date +%s)" + fi + fi + done +fi + +# Get stats about OMD monitoring cores running on this machine. +# Since cd is a shell builtin the check does not affect the performance +# on non-OMD machines. +if cd /omd/sites +then + echo '<<>>' + for site in * + do + if [ -S "/omd/sites/$site/tmp/run/live" ] ; then + echo "[$site]" + echo -e "GET status" | \ + waitmax 3 /omd/sites/$site/bin/unixcat /omd/sites/$site/tmp/run/live + fi + done + echo '<<>>' + for site in * + do + if [ -S "/omd/sites/$site/tmp/run/mkeventd/status" ] ; then + echo "[\"$site\"]" + echo -e "GET status\nOutputFormat: json" \ + | waitmax 3 /omd/sites/$site/bin/unixcat /omd/sites/$site/tmp/run/mkeventd/status + fi + done +fi + +# Collect states of configured Check_MK site backup jobs +if ls /omd/sites/*/var/check_mk/backup/*.state >/dev/null 2>&1; then + echo "<<>>" + for F in /omd/sites/*/var/check_mk/backup/*.state; do + SITE=${F#/*/*/*} + SITE=${SITE%%/*} + + JOB_IDENT=${F%.state} + JOB_IDENT=${JOB_IDENT##*/} + + if [ "$JOB_IDENT" != "restore" ]; then + echo "[[[site:$SITE:$JOB_IDENT]]]" + cat $F + echo + fi + done +fi + +# Collect states of configured CMA backup jobs +if type mkbackup >/dev/null && ls /var/lib/mkbackup/*.state >/dev/null 2>&1; then + echo "<<>>" + for F in /var/lib/mkbackup/*.state; do + JOB_IDENT=${F%.state} + JOB_IDENT=${JOB_IDENT##*/} + + if [ "$JOB_IDENT" != "restore" ]; then + echo "[[[system:$JOB_IDENT]]]" + cat $F + echo + fi + done +fi + +# Get statistics about monitored jobs. Below the job directory there +# is a sub directory per user that ran a job. That directory must be +# owned by the user so that a symlink or hardlink attack for reading +# arbitrary files can be avoided. +if pushd $MK_VARDIR/job >/dev/null; then + echo '<<>>' + for username in * + do + if [ -d "$username" ] && cd "$username" ; then + if [ $EUID -eq 0 ]; then + su "$username" -c "head -n -0 -v *" + else + head -n -0 -v * + fi + cd .. + fi + done + popd > /dev/null +fi + +# Gather thermal information provided e.g. by acpi +# At the moment only supporting thermal sensors +if ls /sys/class/thermal/thermal_zone* >/dev/null 2>&1; then + echo '<<>>' + for F in /sys/class/thermal/thermal_zone*; do + echo -n "${F##*/} " + if [ ! -e $F/mode ] ; then echo -n "- " ; fi + cat $F/{mode,type,temp,trip_point_*} | tr \\n " " + echo + done +fi + +# Libelle Business Shadow +if type trd >/dev/null; then + echo "<<>>" + trd -s +fi + +# HTTP Accelerator Statistics +if type varnishstat >/dev/null; then + echo "<<>>" + varnishstat -1 +fi + +# Proxmox Cluster +if type pvecm > /dev/null 2>&1 ; then + echo "<<>>" + pvecm status + echo "<<>>" + pvecm nodes +fi + +# Start new liveupdate process in background on each agent execution. Starting +# a new live update process will terminate the old one automatically after +# max. 1 sec. +if [ -e $MK_CONFDIR/real_time_checks.cfg ]; then + if [ -z $REMOTE ]; then + echo "ERROR: \$REMOTE not specified. Not starting Real-Time Checks." >&2 + elif ! type openssl >/dev/null; then + echo "ERROR: openssl command is missing. Not starting Real-Time Checks." >&2 + else + run_real_time_checks >/dev/null & + fi +fi + +# MK's Remote Plugin Executor +if [ -e "$MK_CONFDIR/mrpe.cfg" ] +then + grep -Ev '^[[:space:]]*($|#)' "$MK_CONFDIR/mrpe.cfg" | \ + while read descr cmdline + do + interval= + args="-m" + # NOTE: Due to an escaping-related bug in some old bash versions + # (3.2.x), we have to use an intermediate variable for the pattern. + pattern='\(([^\)]*)\)[[:space:]](.*)' + if [[ $cmdline =~ $pattern ]] + then + parameters=${BASH_REMATCH[1]} + cmdline=${BASH_REMATCH[2]} + + # split multiple parameter assignments + for par in $(echo $parameters | tr ":" "\n") + do + # split each assignment + key=$(echo $par | cut -d= -f1) + value=$(echo $par | cut -d= -f2) + + if [ "$key" = "interval" ] ; then + interval=$value + elif [ "$key" = "appendage" ] ; then + args="$args -a" + fi + done + fi + + if [ -z "$interval" ] + then + run_mrpe $descr "$cmdline" + else + run_cached $args $descr $interval "$cmdline" + fi + done +fi + +# MK's runas Executor +if [ -e "$MK_CONFDIR/runas.cfg" ] +then + grep -Ev '^[[:space:]]*($|#)' "$MK_CONFDIR/runas.cfg" | \ + while read type user include + do + if [ -d $include -o \( "$type" == "mrpe" -a -f $include \) ] ; then + PREFIX="" + if [ "$user" != "-" ] ; then + PREFIX="su $user -c " + fi + + # mrpe includes + if [ "$type" == "mrpe" ] ; then + grep -Ev '^[[:space:]]*($|#)' "$include" | \ + while read descr cmdline + do + interval= + # NOTE: Due to an escaping-related bug in some old bash + # versions (3.2.x), we have to use an intermediate variable + # for the pattern. + pattern='\(([^\)]*)\)[[:space:]](.*)' + if [[ $cmdline =~ $pattern ]] + then + parameters=${BASH_REMATCH[1]} + cmdline=${BASH_REMATCH[2]} + + # split multiple parameter assignments + for par in $(echo $parameters | tr ":" "\n") + do + # split each assignment + IFS='=' read key value <<< $par + if [ "$key" = "interval" ] + then + interval=$value + # no other parameters supported currently + fi + done + fi + + if [ -n "$PREFIX" ] ; then + cmdline="$PREFIX\'$cmdline\'" + fi + if [ -z "$interval" ] + then + run_mrpe $descr "$cmdline" + else + run_cached -m $descr $interval "$cmdline" + fi + done + + # local and plugin includes + elif [ "$type" == "local" -o "$type" == "plugin" ] ; then + if [ "$type" == "local" ] ; then + echo "<<>>" + fi + + find $include -executable -type f | \ + while read filename + do + if [ -n "$PREFIX" ] ; then + cmdline="$PREFIX\"$filename\"" + else + cmdline=$filename + fi + + $cmdline + done + fi + fi + done +fi + +function is_valid_plugin () { + # NOTE: Due to an escaping-related bug in some old bash versions + # (3.2.x), we have to use an intermediate variable for the pattern. + pattern='\.dpkg-(new|old|temp)$' + [[ -f "$1" && -x "$1" && ! "$1" =~ $pattern ]] && true || false +} + + +# Local checks +echo '<<>>' +if cd $LOCALDIR ; then + for skript in $(ls) ; do + if is_valid_plugin "$skript" ; then + ./$skript + fi + done + # Call some plugins only every X'th second + for skript in [1-9]*/* ; do + if is_valid_plugin "$skript" ; then + run_cached local_${skript//\//\\} ${skript%/*} "$skript" + fi + done +fi + +# Plugins +if cd $PLUGINSDIR ; then + for skript in $(ls) ; do + if is_valid_plugin "$skript" ; then + ./$skript + fi + done + # Call some plugins only every Xth second + for skript in [1-9]*/* ; do + if is_valid_plugin "$skript" ; then + run_cached plugins_${skript//\//\\} ${skript%/*} "$skript" + fi + done +fi + +# Agent output snippets created by cronjobs, etc. +if [ -d "$SPOOLDIR" ] +then + pushd "$SPOOLDIR" > /dev/null + now=$(date +%s) + + for file in * + do + test "$file" = "*" && break + # output every file in this directory. If the file is prefixed + # with a number, then that number is the maximum age of the + # file in seconds. If the file is older than that, it is ignored. + maxage="" + part="$file" + + # Each away all digits from the front of the filename and + # collect them in the variable maxage. + while [ "${part/#[0-9]/}" != "$part" ] + do + maxage=$maxage${part:0:1} + part=${part:1} + done + + # If there is at least one digit, than we honor that. + if [ "$maxage" ] ; then + mtime=$(stat -c %Y "$file") + if [ $((now - mtime)) -gt $maxage ] ; then + continue + fi + fi + + # Output the file + cat "$file" + done + popd > /dev/null +fi diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/backup b/roles/base/files/usr/lib/check_mk_agent/plugins/backup new file mode 100755 index 0000000..878ed7c --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/backup @@ -0,0 +1,11 @@ +#!/bin/bash + +# Backup check for Check_MK +# Installed by BLSE 2.0 ansible + +SHARELIST=( $( cat /var/backups/shares ) ) + +echo "<<>>" +for SHARE in ${SHARELIST[@]}; do + echo "${SHARE} $( cat ${SHARE}/.backup )" +done diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/cephfsmounts b/roles/base/files/usr/lib/check_mk_agent/plugins/cephfsmounts new file mode 100755 index 0000000..19d82ac --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/cephfsmounts @@ -0,0 +1,15 @@ +echo '<<>>' +sed -n '/ ceph\? /s/[^ ]* \([^ ]*\) .*/\1/p' < /proc/mounts | + sed 's/\\040/ /g' | + while read MP + do + if [ ! -r $MP ]; then + echo "$MP Permission denied" + elif [ $STAT_VERSION != $STAT_BROKE ]; then + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" || \ + echo "$MP hanging 0 0 0 0" + else + waitmax -s 9 2 stat -f -c "$MP ok %b %f %a %s" "$MP" && \ + printf '\n'|| echo "$MP hanging 0 0 0 0" + fi + done diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/dpkg b/roles/base/files/usr/lib/check_mk_agent/plugins/dpkg new file mode 100755 index 0000000..ead76d0 --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/dpkg @@ -0,0 +1,33 @@ +#!/bin/bash + +# Apt and dpkg status check for Check_MK +# Installed by BLSE 2.0 ansible + +TMP_DPKG="$( COLUMNS=200 dpkg --list )" +TMP_AWK="$( awk ' + { if (NR>5) { + if ($1 != "ii") bad_package[$2]=$1; + }} + END { + print NR-5; + bad_package_count=asort(bad_package,junk) + if (bad_package_count) { + for (package in bad_package) + print package "[" bad_package[package] "]" + exit 1 + } + } +' <<<"$TMP_DPKG" )" + +DEBIAN_VERSION="$( cat /etc/debian_version )" +TOTAL_PACKAGES=$( head --lines=1 <<<"${TMP_AWK}" ) +UPGRADABLE_PACKAGES=( $( apt list --upgradable 2>/dev/null | grep -v '^Listing' | awk '{ gsub(/\]/,"",$NF); print $1 "[" $NF "<>" $2 "]" }' ) ) +INCONSISTENT_PACKAGES=( $( tail --lines=+2 <<<"${TMP_AWK}" ) ) +OLD_CONFIG_FILES=( $( ionice -c3 find /etc -type f -a \( -name '*.dpkg-*' -o -name '*.ucf-*' \) 2>/dev/null ) ) + +echo "<<>>" +echo "debian_version ${DEBIAN_VERSION}" +echo "total_packages ${TOTAL_PACKAGES}" +echo "upgradable_packages ${#UPGRADABLE_PACKAGES[*]} ${UPGRADABLE_PACKAGES[*]}" +echo "inconsistent_packages ${#INCONSISTENT_PACKAGES[*]} ${INCONSISTENT_PACKAGES[*]}" +echo "obsolete_configuration_files ${#OLD_CONFIG_FILES[*]} ${OLD_CONFIG_FILES[*]}" diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/entropy b/roles/base/files/usr/lib/check_mk_agent/plugins/entropy new file mode 100755 index 0000000..35a5965 --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/entropy @@ -0,0 +1,16 @@ +#!/bin/bash + +# Entropy availability check for Check_MK +# Installed by BLSE 2.0 ansible + +if [ -e /proc/sys/kernel/random/entropy_avail ]; then + + echo '<<>>' + + echo -n "entropy_avail " + cat /proc/sys/kernel/random/entropy_avail + + echo -n "poolsize " + cat /proc/sys/kernel/random/poolsize + +fi diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/freshness b/roles/base/files/usr/lib/check_mk_agent/plugins/freshness new file mode 100755 index 0000000..3cc7aab --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/freshness @@ -0,0 +1,52 @@ +#!/bin/bash + +# Open file handle freshness check for Check_MK +# Installed by BLSE 2.0 ansible + +OK=0 +WARNING=1 + +FRESHNESS="$( lsof -Fcftn / 2>/dev/null | grep -v '/tmp' | \ +awk ' +{ + field=substr($0,1,1); + data=substr($0,2); + if (field=="f") { + file_descriptor=data; + } else if (field=="t") { + file_type=data; + } else if (field=="c") { + command_name=data; + } else if (field=="n" && file_descriptor=="DEL" && file_type=="REG") { + name=data; + file[command_name]++; + } +} +END { + for (name in file) { + error++; + # Skip these problematic programs + if (name=="systemd-udevd") { continue; } + if (name=="pulseaudio") { continue; } + if (name=="light-locker") { continue; } + if (name=="at-spi-bus-laun") { continue; } + if (name=="node") { continue; } + if (error_name) { error_name=error_name " " }; + error_name=error_name name; + } + if (error_name) { + print error_name; + exit error; + } else { + exit; + } +}' )"; + +echo "<<>>" +if [ "$FRESHNESS" ]; then + echo "Applications needing restart: $FRESHNESS" + exit $WARNING +else + echo "No applications needing restart" + exit $OK +fi diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/kernelversion b/roles/base/files/usr/lib/check_mk_agent/plugins/kernelversion new file mode 100755 index 0000000..d0beb2e --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/kernelversion @@ -0,0 +1,14 @@ +#!/bin/bash +OK=0 +WARNING=1 + +echo "<<>>" +ACTIVE="$( uname -v | awk '{ print $4" "$5 }' )" +ONDISK="$( strings /vmlinuz | grep 'Debian' | head -1 | awk '{ print $6" "$7 }' )" +echo ${ACTIVE} +echo ${ONDISK} +if [[ ${ACTIVE} != ${ONDISK} ]]; then + exit $WARNING +else + exit $OK +fi diff --git a/roles/base/files/usr/lib/check_mk_agent/plugins/ownership b/roles/base/files/usr/lib/check_mk_agent/plugins/ownership new file mode 100755 index 0000000..f1e129c --- /dev/null +++ b/roles/base/files/usr/lib/check_mk_agent/plugins/ownership @@ -0,0 +1,68 @@ +#!/bin/bash + +# File ownership check for Check_MK +# Installed by BLSE 2.0 ansible + +UID_MAX=299 +# http://www.debian.org/doc/debian-policy/ch-opersys.html +# 0-99: Globally allocated by the Debian project +# 100-199: (BLSE) Dynamically allocated system users and groups +# 200-299: (BLSE) BLSE users and groups +# 300-499: (BLSE) reserved +# 500-599: (BLSE) system administrators +# 600-999: (BLSE) reserved +# 64045: (BLSE) ceph + +function is_element_of { + local TO_FIND=$1 + shift + + for ARRAY_ELEMENT in $* + do + if test $TO_FIND = $ARRAY_ELEMENT + then + return 0 + fi + done + return 1 +} + +OK=0 +WARNING=1 + +FILESYSTEMs=(/ /var/log) +MOUNTs=($(awk '{print $2}' '/proc/mounts')) + +FILEs=() +for FILESYSTEM in ${FILESYSTEMs[@]}; do + while IFS= read -r -d $'\0' FILE + do + if ! is_element_of "$FILE" ${FILESYSTEMs[*]}; then + if is_element_of $FILE ${MOUNTs[*]}; then + continue + fi + fi + FILEs+=($FILE) + done < <( find ${FILESYSTEM} -xdev -uid +$UID_MAX -not -uid +64000 -not -uid 2000 \ + -not \( -type d -a \( -path /media -o -path /mnt \) \) \ + -not \( -name '.*.swp' -a -mtime -3 \) \ + -not \( -path '*/.git' -o -path '*/.git/*' \) \ + -not \( -path '*.dirtrack.Storable' \) \ + -not \( -path '/home/*' \) \ + -not \( -path '/tmp/*' \) \ + -not \( -path '/var/home/*' \) \ + -not \( -path '/var/log/gitlab/*' \) \ + -print0 2>/dev/null ) +done + +echo "<<>>" + +if ! test ${#FILEs[*]} -eq 0; then + echo -n "${#FILEs[*]} file(s) found with invalid ownership (must be UID <299): " + echo "${FILEs[*]}" + exit $WARNING +else + echo "All files have valid ownership" + exit $OK +fi + diff --git a/roles/base/files/usr/lib/openssh/ssh-keysign b/roles/base/files/usr/lib/openssh/ssh-keysign new file mode 100755 index 0000000..8eba9f8 Binary files /dev/null and b/roles/base/files/usr/lib/openssh/ssh-keysign differ diff --git a/roles/base/handlers/main.yml b/roles/base/handlers/main.yml new file mode 100644 index 0000000..d722357 --- /dev/null +++ b/roles/base/handlers/main.yml @@ -0,0 +1,36 @@ +--- +- name: restart rsyslog + service: + name: rsyslog + state: restarted + +- name: restart xinetd + service: + name: xinetd + state: restarted + +- name: restart postfix + service: + name: postfix + state: restarted + +- name: restart ntp + service: + name: ntp + state: restarted + +- name: restart ssh + service: + name: ssh + state: restarted + +- name: restart fail2ban + service: + name: fail2ban + state: restarted + +- name: generate locales + command: locale-gen + +- name: newaliases + command: newaliases diff --git a/roles/base/tasks/main.yml b/roles/base/tasks/main.yml new file mode 100644 index 0000000..a8ee312 --- /dev/null +++ b/roles/base/tasks/main.yml @@ -0,0 +1,719 @@ +--- +# +# First run check +# +- name: first run check + shell: "echo 'bootstrapped' > /etc/pvc-install" + register: newhost + args: + creates: "/etc/pvc-install" + +# +# Install custom fact scripts +# +- name: create facts directory + file: + dest: "/etc/ansible/facts.d" + state: directory + recurse: yes + +- name: install custom facts + template: + src: "etc/ansible/facts.d/{{ item }}.fact.j2" + dest: "/etc/ansible/facts.d/{{ item }}.fact" + mode: 0755 + register: installed_facts + with_items: + - host_id + - host_group + - dhcp_status + +- name: regather local facts + setup: + gather_subset: "!all,!any,local" + when: installed_facts.changed + +- debug: + var: ansible_local.host_group + verbosity: 1 + +- debug: + var: ansible_local.host_id + verbosity: 1 + +- debug: + var: ansible_local.dhcp_status + verbosity: 1 + +# +# Debian Buster pre-release tweaks +# +- name: add proper VERSION line in os-release (Debian 10.X testing only) + lineinfile: + dest: "/etc/os-release" + line: 'VERSION="10 (buster)"' + state: present + register: installed_facts + when: + - ansible_distribution_major_version == "buster/sid" + +- name: regather distribution facts + setup: + when: + - installed_facts.changed + - ansible_distribution_major_version == "buster/sid" + +- debug: + var: ansible_distribution_release + verbosity: 1 + when: + - ansible_distribution_major_version == "buster/sid" + +# +# Configure APT +# + +- name: install apt config files + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + with_items: + - { src: "etc/apt/apt.conf.d/10norecommends.j2", dest: "/etc/apt/apt.conf.d/10norecommends" } + - { src: "etc/apt/apt.conf.d/50unattended-upgrades.j2", dest: "/etc/apt/apt.conf.d/50unattended-upgrades" } + - { src: "etc/apt/preferences.d/pins.j2", dest: "/etc/apt/preferences.d/pins" } + - { src: "etc/apt/sources.list.{{ ansible_machine }}.j2", dest: "/etc/apt/sources.list" } + tags: apt + +# +# Safe apt upgrades (on first install only) +# + +- name: apt update + apt: + update-cache: yes + when: newhost.changed + +- name: aptitude safe upgrade with autoremove + apt: + update_cache: yes + autoremove: yes + upgrade: safe + when: newhost.changed + +- name: install dbus + apt: + name: + - dbus + state: latest + when: newhost.changed + +- name: clean out apt cache + file: + path: "/var/cache/apt/archives" + state: absent + when: newhost.changed + +# +# Purge unneeded packages +# + +- name: remove unneeded packages + apt: + name: + - exim4 + - exim4-base + - exim4-config + - exim4-daemon-light + - nano + - joe + state: absent + purge: yes + autoremove: yes + +# +# Install common packages +# + +- name: set override debconf selections + shell: 'echo "{{ item }}" | debconf-set-selections' + with_items: + - "wireshark-common wireshark-common/install-setuid boolean true" + +- name: install common packages (all arch) + apt: + name: + - debconf-utils + - iptables + - locales + - acpid + - acpi-support-base + - rsync + - bash + - bash-completion + - net-tools + - check-mk-agent + - dns-root-data + - bind9-host + - dnsutils + - postfix + - ntp + - openssh-client + - openssh-server + - libpam-systemd + - fail2ban + - ca-certificates + - openssl + - sudo + - rsyslog + - logrotate + - man + - less + - vim + - git + - nmap + - netcat-openbsd + - htop + - psmisc + - dstat + - iotop + - lsof + - jnettop + - iperf + - sysstat + - binutils + - deborphan + - wget + - curl + - gawk + - mmv + - pv + - bc + - reptyr + - sharutils + - tcptraceroute + - nethogs + - strace + - tshark + - acl + - bzip2 + - haveged + - unattended-upgrades + - linux-image-amd64 + - linux-headers-amd64 + +# +# System configuration +# + +# capabilities +- name: set ping capabilities + capabilities: + path: /bin/ping + capability: cap_net_raw=ep + +# locale +- name: install locale config files + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + notify: + - generate locales + with_items: + - { src: "etc/default/locale.j2", dest: "/etc/default/locale" } + - { src: "etc/locale.gen.j2", dest: "/etc/locale.gen" } + +- name: set timezone + file: + src: /usr/share/zoneinfo/Canada/Eastern + dest: /etc/localtime + state: link + force: yes + +# dns +- name: write the hosts config + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + with_items: + - { src: "etc/hosts.j2", dest: "/etc/hosts" } + tags: dns + +- name: write the resolver configs + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + with_items: + - { src: "etc/dhcp/dhclient-enter-hooks.d/noresolv.j2", dest: "/etc/dhcp/dhclient-enter-hooks.d/noresolv" } + - { src: "etc/resolv.conf.j2", dest: "/etc/resolv.conf" } + tags: dns + +# acpi +- name: install sysctl tweaks + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + with_items: + - { src: "etc/sysctl.d/pvc.conf.j2", dest: "/etc/sysctl.d/pvc.conf" } + +- name: activate sysctl tweaks + shell: "sysctl -p {{ item }}" + with_items: + - /etc/sysctl.d/pvc.conf + +# syslog +- name: install rsyslog and logrotate configs + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + notify: + - restart rsyslog + with_items: + - { src: "etc/rsyslog.conf.j2", dest: "/etc/rsyslog.conf" } + - { src: "etc/logrotate.d/rsyslog.j2", dest: "/etc/logrotate.d/rsyslog" } + tags: rsyslog + +- name: set journalctl persistence + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0644 + with_items: + - { src: "etc/systemd/journald.conf.j2", dest: "/etc/systemd/journald.conf" } + tags: rsyslog + +# cron +- name: install crontab + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0755 + with_items: + - { src: "etc/crontab.j2", dest: "/etc/crontab" } + +# mta +- name: install postfix generic config + template: + src: "etc/postfix/main.cf.j2" + dest: "/etc/postfix/main.cf" + notify: + - restart postfix + +- name: touch the postfix aliases file + file: + dest: /etc/postfix/aliases + state: touch + +# +# Local alias maps +# +- name: install local alias maps for various users + lineinfile: + dest: "/etc/aliases" + regexp: "^{{ item }}:" + line: "{{ item }}: {{ username_email_root }}" + state: present + with_items: + - root + - postmaster + - amavis + - clamav + notify: + - newaliases + +# ntp +- name: write the NTP config file + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + notify: + - restart ntp + with_items: + - { src: "etc/ntp.conf.j2", dest: "/etc/ntp.conf" } + tags: ntp + +# ssl +- name: ensure haveged is running + service: + name: haveged + state: started + +- name: generate diffie-hellman parameters + command: openssl dhparam -out /etc/ssl/dhparams.pem 2048 + args: + creates: /etc/ssl/dhparams.pem + +- name: correct permissions on dhparams + file: + dest: /etc/ssl/dhparams.pem + mode: 0440 + +# ssh +- name: write the sshd_config files + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + notify: + - restart ssh + with_items: + - { src: 'etc/ssh/ssh_config.j2', dest: '/etc/ssh/ssh_config' } + - { src: 'etc/ssh/sshd_config.j2', dest: '/etc/ssh/sshd_config' } + - { src: 'etc/ssh/shosts.equiv.j2', dest: '/etc/ssh/shosts.equiv' } + - { src: 'etc/ssh/ssh_known_hosts.j2', dest: '/etc/ssh/ssh_known_hosts' } + tags: ssh + +- name: write sshd pam.d config + template: + src: "etc/pam.d/sshd.j2" + dest: "/etc/pam.d/sshd" + tags: ssh + +- name: remove unneeded SSH keys (leave only RSA and ED25519) + file: + name: "{{ item }}" + state: "absent" + with_items: + - /etc/ssh/ssh_host_dsa_key + - /etc/ssh/ssh_host_dsa_key.pub + - /etc/ssh/ssh_host_ecdsa_key + - /etc/ssh/ssh_host_ecdsa_key.pub + notify: + - restart ssh + tags: ssh + +- name: set permissions on rsa and ed25519 host keys (just in case they're wrong) + file: + name: "/etc/ssh/{{ item.name }}" + mode: "{{ item.mode }}" + with_items: + - { name: 'ssh_host_rsa_key', mode: '600' } + - { name: 'ssh_host_rsa_key.pub', mode: '644' } + - { name: 'ssh_host_ed25519_key', mode: '600' } + - { name: 'ssh_host_ed25519_key.pub', mode: '644' } + tags: ssh + +# sudo +- name: write the sudoers file + template: + src: "etc/sudoers.j2" + dest: "/etc/sudoers" + mode: 0440 + +# bash +- name: write the bash.bashrc config file + template: + src: "etc/bash.bashrc.j2" + dest: "/etc/bash.bashrc" + +# motd +- name: ensure update-motd and w scripts are present + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0755 + with_items: + - { src: "usr/local/sbin/update-motd.sh.j2", dest: "/usr/local/sbin/update-motd.sh" } + - { src: "etc/profile.d/w.sh.j2", dest: "/etc/profile.d/w.sh" } + tags: motd + +- name: install update-motd crontab + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0644 + with_items: + - { src: "etc/cron.d/update-motd.j2", dest: "/etc/cron.d/update-motd" } + tags: motd + +- name: ensure /etc/motd is absent + file: + dest: "/etc/motd" + state: absent + tags: motd + +# dpkg +- name: install dpkg-cleanup script + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0755 + with_items: + - { src: "usr/local/sbin/dpkg-cleanup.sh.j2", dest: "/usr/local/sbin/dpkg-cleanup.sh" } + tags: dpkg + +# fail2ban +- name: install fail2ban configurations + template: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + mode: 0644 + notify: restart fail2ban + with_items: + - { src: "etc/fail2ban/action.d/route.conf.j2", dest: "/etc/fail2ban/action.d/route.conf" } + - { src: "etc/fail2ban/filter.d/sshd.conf.j2", dest: "/etc/fail2ban/filter.d/sshd.conf" } + - { src: "etc/fail2ban/jail.d/sshd.conf.j2", dest: "/etc/fail2ban/jail.d/sshd.conf" } + - { src: "etc/fail2ban/jail.d/sshd.local.j2", dest: "/etc/fail2ban/jail.d/sshd.local" } + tags: fail2ban + +# +# Configure users +# + +# common +- name: ensure /var/home exists + file: + state: directory + dest: /var/home + tags: users + +- name: ensure group media exists + group: + name: media + gid: 9000 + state: present + tags: users + +# root +- name: set Root password + user: + name: root + password: "{{ passwdhash_root }}" + tags: users + +- name: remove Root known_hosts + file: + state: absent + dest: /root/.ssh/known_hosts + tags: users + +- name: write vimrc to root homedir + template: + src: var/home/user/vimrc.j2 + dest: /root/.vimrc + mode: 0600 + tags: users + +- name: create vimdir + file: + state: directory + dest: /root/.vim + mode: 0700 + tags: users + +- name: write htoprc to homedir + template: + src: var/home/user/config/htop/htoprc.j2 + dest: /root/.htoprc + mode: 0600 + tags: users + +# backup +- name: ensure backup user has shell + user: + name: backup + shell: /bin/sh + tags: users + +- name: create backup .ssh directory + file: + path: /var/backups/.ssh + state: directory + owner: backup + group: root + mode: 0700 + tags: users + +- name: create backup authorized_keys file + template: + src: var/backups/ssh/authorized_keys.j2 + dest: /var/backups/.ssh/authorized_keys + owner: backup + group: root + mode: 0640 + tags: users + +- name: write the sudoers file + template: + src: etc/sudoers.d/sudoers-backup.j2 + dest: /etc/sudoers.d/backup + tags: users + +- name: install the post-backup timestamp script + template: + src: var/backups/timestamp.sh.j2 + dest: /var/backups/timestamp.sh + mode: 0755 + tags: users + +- name: touch shares file + file: + dest: /var/backups/shares + state: touch + owner: backup + tags: users + +# deploy +- name: ensure user deploy exists + user: + name: deploy + uid: 200 + group: operator + shell: /bin/bash + home: /var/home/deploy + createhome: yes + move_home: yes + state: present + append: yes + tags: users + +- name: ensure homedir has right permissions + file: + dest: /var/home/deploy + state: directory + owner: deploy + group: operator + mode: 0700 + tags: users + +- name: ensure .ssh directory exists + file: + dest: /var/home/deploy/.ssh + state: directory + owner: deploy + group: operator + mode: 0700 + tags: users + +- name: add authorized keys + authorized_key: + user: "deploy" + key: "{{ item.key }}" + with_items: "{{ admin_users }}" + tags: users + +# admin_users +- name: ensure user exists + user: + name: "{{ item.name }}" + uid: "{{ item.uid }}" + group: operator + groups: sudo,adm,media,wireshark + shell: /bin/bash + home: "/var/home/{{ item.name }}" + createhome: yes + state: present + append: yes + with_items: "{{ admin_users }}" + tags: users + +- name: ensure homedir has right permissions + file: + dest: "/var/home/{{ item.name }}" + state: directory + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +- name: ensure .ssh directory exists + file: + dest: "/var/home/{{ item.name }}/.ssh" + state: directory + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +- name: add authorized keys + authorized_key: + user: "{{ item.name }}" + key: "{{ item.key }}" + with_items: "{{ admin_users }}" + tags: users + +- name: write bashrc to homedir + template: + src: var/home/user/bashrc.j2 + dest: "/var/home/{{ item.name }}/.bashrc" + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +- name: write bash_logout to homedir + template: + src: var/home/user/bash_logout.j2 + dest: "/var/home/{{ item.name }}/.bash_logout" + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +- name: ensure htop config directory exists + file: + dest: "/var/home/{{ item.name }}/.config/htop" + state: directory + owner: "{{ item.name }}" + group: operator + mode: 0755 + with_items: "{{ admin_users }}" + tags: users + +- name: write htoprc to homedir + template: + src: var/home/user/config/htop/htoprc.j2 + dest: "/var/home/{{ item.name }}/.config/htop/htoprc" + owner: "{{ item.name }}" + group: operator + mode: 0644 + with_items: "{{ admin_users }}" + tags: users + +- name: write profile to homedir + template: + src: var/home/user/profile.j2 + dest: "/var/home/{{ item.name }}/.profile" + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +- name: write vimrc to homedir + template: + src: var/home/user/vimrc.j2 + dest: "/var/home/{{ item.name }}/.vimrc" + owner: "{{ item.name }}" + group: operator + mode: 0600 + with_items: "{{ admin_users }}" + tags: users + +- name: create vimdir + file: + state: directory + dest: /var/home/{{ item.name }}/.vim + owner: "{{ item.name }}" + group: operator + mode: 0700 + with_items: "{{ admin_users }}" + tags: users + +# +# Verify and enable services +# + +- name: verify and enable services + service: + name: "{{ item }}" + state: started + enabled: yes + with_items: + - acpid + - rsyslog + - postfix + - ntp + - ssh diff --git a/roles/base/templates/etc/ansible/facts.d/dhcp_status.fact.j2 b/roles/base/templates/etc/ansible/facts.d/dhcp_status.fact.j2 new file mode 100644 index 0000000..46c7953 --- /dev/null +++ b/roles/base/templates/etc/ansible/facts.d/dhcp_status.fact.j2 @@ -0,0 +1,5 @@ +#!/bin/bash +# Ansible fact - dhcp_status +# {{ ansible_managed }} +DHCP_STATUS="$( grep -o 'dhcp' /etc/network/interfaces | uniq )" +echo "\"${DHCP_STATUS}\"" diff --git a/roles/base/templates/etc/ansible/facts.d/host_group.fact.j2 b/roles/base/templates/etc/ansible/facts.d/host_group.fact.j2 new file mode 100644 index 0000000..13efb07 --- /dev/null +++ b/roles/base/templates/etc/ansible/facts.d/host_group.fact.j2 @@ -0,0 +1,5 @@ +#!/bin/bash +# Ansible fact - host_group +# {{ ansible_managed }} +HOST_GROUP="$( hostname -s | sed 's/[0-9]*//g' )" +echo "\"${HOST_GROUP}\"" diff --git a/roles/base/templates/etc/ansible/facts.d/host_id.fact.j2 b/roles/base/templates/etc/ansible/facts.d/host_id.fact.j2 new file mode 100644 index 0000000..90e87ed --- /dev/null +++ b/roles/base/templates/etc/ansible/facts.d/host_id.fact.j2 @@ -0,0 +1,8 @@ +#!/bin/bash +# Ansible fact - host_id +# {{ ansible_managed }} +HOST_ID="$( hostname -s | grep -o '[0-9]\+' )" +if [[ -z ${HOST_ID} ]]; then + HOST_ID="0" +fi +echo "\"${HOST_ID}\"" diff --git a/roles/base/templates/etc/apt/apt.conf.d/10norecommends.j2 b/roles/base/templates/etc/apt/apt.conf.d/10norecommends.j2 new file mode 100644 index 0000000..56709a6 --- /dev/null +++ b/roles/base/templates/etc/apt/apt.conf.d/10norecommends.j2 @@ -0,0 +1,5 @@ +# apt configuration: disable recommends +# {{ ansible_managed }} + +APT::Install-Recommends "0"; +APT::Install-Suggests "0"; diff --git a/roles/base/templates/etc/apt/apt.conf.d/50unattended-upgrades.j2 b/roles/base/templates/etc/apt/apt.conf.d/50unattended-upgrades.j2 new file mode 100644 index 0000000..0f8e64e --- /dev/null +++ b/roles/base/templates/etc/apt/apt.conf.d/50unattended-upgrades.j2 @@ -0,0 +1,37 @@ +# Unattended upgrades configuration +# {{ ansible_managed }} + +Unattended-Upgrade::Origins-Pattern { + "origin=Debian,codename=${distro_codename},label=Debian"; + "origin=Debian,codename=${distro_codename},label=Debian-Security"; +}; + +Unattended-Upgrade::Package-Blacklist { + # "libc6$"; + # "libc6-dev$"; + # "libc6-i686$"; +}; + +# General configurations +Unattended-Upgrade::AutoFixInterruptedDpkg "true"; +Unattended-Upgrade::MinimalSteps "true"; +Unattended-Upgrade::InstallOnShutdown "false"; +Unattended-Upgrade::Mail "root@bonilan.net"; +Unattended-Upgrade::MailOnlyOnError "true"; +Unattended-Upgrade::Remove-Unused-Kernel-Packages "true"; +Unattended-Upgrade::Remove-New-Unused-Dependencies "true"; +Unattended-Upgrade::Remove-Unused-Dependencies "true"; +Unattended-Upgrade::SyslogEnable "true"; +Unattended-Upgrade::SyslogFacility "daemon"; +Unattended-Upgrade::Verbose "false"; +Unattended-Upgrade::Debug "false"; + +# Reboot configurations - skip cephX and hvX +{% if 'hv' in group_names or 'ceph' in group_names %} +Unattended-Upgrade::Automatic-Reboot "false"; +{% else %} +Unattended-Upgrade::Automatic-Reboot "true"; +Unattended-Upgrade::Automatic-Reboot-WithUsers "true"; +{% set reboot_time_minute = 2 * ansible_local.host_id|int %} +Unattended-Upgrade::Automatic-Reboot-Time "04:{{ '%02d' % reboot_time_minute }}"; +{% endif %} diff --git a/roles/base/templates/etc/apt/preferences.d/pins.j2 b/roles/base/templates/etc/apt/preferences.d/pins.j2 new file mode 100644 index 0000000..f8668bd --- /dev/null +++ b/roles/base/templates/etc/apt/preferences.d/pins.j2 @@ -0,0 +1,12 @@ +# apt configuration: pinning preferences +# {{ ansible_managed }} + +Package: * +Pin: release a={{ ansible_distribution_release }} +Pin-Priority: 999 + +{% if 'base' in group_names %} +Package: * +Pin: release a={{ ansible_distribution_release }}-backports +Pin-Priority: -1 +{% endif %} diff --git a/roles/base/templates/etc/apt/sources.list.x86_64.j2 b/roles/base/templates/etc/apt/sources.list.x86_64.j2 new file mode 100644 index 0000000..fbb5ec5 --- /dev/null +++ b/roles/base/templates/etc/apt/sources.list.x86_64.j2 @@ -0,0 +1,14 @@ +# apt configuration: main sources list +# {{ ansible_managed }} + +deb http://debian.mirror.rafal.ca/debian {{ ansible_distribution_release }} main contrib non-free +deb-src http://debian.mirror.rafal.ca/debian {{ ansible_distribution_release }} main contrib + +deb http://security.debian.org/ {{ ansible_distribution_release }}/updates main contrib +deb-src http://security.debian.org/ {{ ansible_distribution_release }}/updates main contrib + +deb http://debian.mirror.rafal.ca/debian/ {{ ansible_distribution_release }}-updates main contrib +deb-src http://debian.mirror.rafal.ca/debian/ {{ ansible_distribution_release }}-updates main contrib + +deb https://repo.bonifacelabs.net/debian/ {{ ansible_distribution_release }}-updates main +deb-src https://repo.bonifacelabs.net/debian/ {{ ansible_distribution_release }}-updates main diff --git a/roles/base/templates/etc/bash.bashrc.j2 b/roles/base/templates/etc/bash.bashrc.j2 new file mode 100755 index 0000000..b8449e8 --- /dev/null +++ b/roles/base/templates/etc/bash.bashrc.j2 @@ -0,0 +1,126 @@ +# System-wide .bashrc file for interactive bash(1) shells. +# {{ ansible_managed }} + +# To enable the settings / commands in this file for login shells as well, +# this file has to be sourced in /etc/profile. + +# Fix the preceeding space stupidity +export HISTCONTROL=ignorespace + +# If not running interactively, don't do anything +[ -z "$PS1" ] && return + +# check the window size after each command and, if necessary, +# update the values of LINES and COLUMNS. +shopt -s checkwinsize + +# set variable identifying the chroot you work in (used in the prompt below) +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +#------------------------------------------------------------------------------ +# Returncode. +#------------------------------------------------------------------------------ +function returncode +{ + returncode=$? + if [ $returncode != 0 ]; then + echo "[$returncode]" + else + echo "" + fi +} + +alias ll='ls -al' + +use_color=false + +# Set colorful PS1 only on colorful terminals. +# dircolors --print-database uses its own built-in database +# instead of using /etc/DIR_COLORS. Try to use the external file +# first to take advantage of user additions. Use internal bash +# globbing instead of external grep binary. +safe_term=${TERM//[^[:alnum:]]/?} # sanitize TERM +match_lhs="" +[[ -f ~/.dir_colors ]] && match_lhs="${match_lhs}$(<~/.dir_colors)" +[[ -f /etc/DIR_COLORS ]] && match_lhs="${match_lhs}$(/dev/null \ + && match_lhs=$(dircolors --print-database) +[[ $'\n'${match_lhs} == *$'\n'"TERM "${safe_term}* ]] && use_color=true + +if ${use_color} ; then + # Enable colors for ls, etc. Prefer ~/.dir_colors #64489 + if type -P dircolors >/dev/null ; then + if [[ -f ~/.dir_colors ]] ; then + eval $(dircolors -b ~/.dir_colors) + elif [[ -f /etc/DIR_COLORS ]] ; then + eval $(dircolors -b /etc/DIR_COLORS) + else + eval $(dircolors) + fi + fi + + if [[ ${EUID} == 0 ]] ; then + PS1='\[\033[0;31m\]$(returncode)\[\033[0;37m\]\[\033[0;35m\]${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\H\[\033[01;34m\] \w \$\[\033[00m\] ' + elif [[ ${UID} == 200 ]] ; then + PS1='\[\033[0;31m\]$(returncode)\[\033[0;37m\]\[\033[0;35m\]${debian_chroot:+($debian_chroot)}\[\033[01;31m\]\u@\H\[\033[01;34m\] \w \$\[\033[00m\] ' + else + PS1='\[\033[0;31m\]$(returncode)\[\033[0;37m\]\[\033[0;35m\]${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\H\[\033[01;34m\] \w \$\[\033[00m\] ' + fi + + alias ls='ls --color=auto' + alias grep='grep --colour=auto' + alias fgrep='fgrep --colour=auto' + alias egrep='egrep --colour=auto' + alias ll='ls -lF' + alias la='ls -A' + alias l='ls -CF' +else + if [[ ${EUID} == 0 ]] ; then + # show root@ when we don't have colors + PS1='\[$(returncode)\]\u@\H \w \$ ' + else + PS1='\[$(returncode)\]\u@\H \w \$ ' + fi +fi + +# Try to keep environment pollution down, EPA loves us. +unset use_color safe_term match_lhs + +# Commented out, don't overwrite xterm -T "title" -n "icontitle" by default. +# If this is an xterm set the title to user@host:dir +#case "$TERM" in +#xterm*|rxvt*) +# PROMPT_COMMAND='echo -ne "\033]0;${USER}@${HOSTNAME}: ${PWD}\007"' +# ;; +#*) +# ;; +#esac + +# enable bash completion in interactive shells +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +# if the command-not-found package is installed, use it +if [ -x /usr/lib/command-not-found -o -x /usr/share/command-not-found/command-not-found ]; then + function command_not_found_handle { + # check because c-n-f could've been removed in the meantime + if [ -x /usr/lib/command-not-found ]; then + /usr/bin/python /usr/lib/command-not-found -- "$1" + return $? + elif [ -x /usr/share/command-not-found/command-not-found ]; then + /usr/bin/python /usr/share/command-not-found/command-not-found -- "$1" + return $? + else + printf "%s: command not found\n" "$1" >&2 + return 127 + fi + } +fi diff --git a/roles/base/templates/etc/check_mk/logwatch.cfg.j2 b/roles/base/templates/etc/check_mk/logwatch.cfg.j2 new file mode 100644 index 0000000..c45f12b --- /dev/null +++ b/roles/base/templates/etc/check_mk/logwatch.cfg.j2 @@ -0,0 +1,58 @@ +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2013 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# {{ ansible_managed }} +# +# check_mk is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# ails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# logwatch.cfg +# This file configures mk_logwatch. Define your logfiles +# and patterns to be looked for here. + +# Name one or more logfiles +/var/log/system.log /var/log/daemon.log +# Patterns are indented with one space are prefixed with: +# C: Critical messages +# W: Warning messages +# I: ignore these lines (OK) +# The first match decided. Lines that do not match any pattern +# are ignored + C Fail event detected on md device + I mdadm.*: Rebuild.*event detected + W mdadm\[ + W ata.*hard resetting link + W ata.*soft reset failed (.*FIS failed) + W device-mapper: thin:.*reached low water mark + C device-mapper: thin:.*no free space + +/var/log/auth.log + W sshd.*Corrupted MAC on input + +/var/log/kern.log + C panic + C Oops + W generic protection rip + W .*Unrecovered read error - auto reallocate failed + +# Globbing patterns are allowed: +# /sapdata/*/saptrans.log +# C ORA- diff --git a/roles/base/templates/etc/cron.d/update-motd.j2 b/roles/base/templates/etc/cron.d/update-motd.j2 new file mode 100644 index 0000000..79f1e2b --- /dev/null +++ b/roles/base/templates/etc/cron.d/update-motd.j2 @@ -0,0 +1,4 @@ +# cron file for motd +# {{ ansible_managed }} + +* * * * * root /bin/sh /usr/local/sbin/update-motd.sh &>/dev/null diff --git a/roles/base/templates/etc/crontab.j2 b/roles/base/templates/etc/crontab.j2 new file mode 100644 index 0000000..82d9da2 --- /dev/null +++ b/roles/base/templates/etc/crontab.j2 @@ -0,0 +1,12 @@ +# /etc/crontab: system-wide crontab +# {{ ansible_managed }} + +SHELL=/bin/sh +PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin + +# m h dom mon dow user command +00 * * * * root cd / && run-parts --report /etc/cron.hourly +05 0 * * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.daily ) +15 0 * * 7 root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.weekly ) +30 0 1 * * root test -x /usr/sbin/anacron || ( cd / && run-parts --report /etc/cron.monthly ) +# diff --git a/roles/base/templates/etc/default/locale.j2 b/roles/base/templates/etc/default/locale.j2 new file mode 100644 index 0000000..3944c9a --- /dev/null +++ b/roles/base/templates/etc/default/locale.j2 @@ -0,0 +1,4 @@ +LANGUAGE=en_CA.UTF-8 +LC_ALL=en_CA.UTF-8 +LANG=en_CA.UTF-8 +LC_TYPE=en_CA.UTF-8 diff --git a/roles/base/templates/etc/dhcp/dhclient-enter-hooks.d/noresolv.j2 b/roles/base/templates/etc/dhcp/dhclient-enter-hooks.d/noresolv.j2 new file mode 100644 index 0000000..b0cc5ab --- /dev/null +++ b/roles/base/templates/etc/dhcp/dhclient-enter-hooks.d/noresolv.j2 @@ -0,0 +1,6 @@ +#!/bin/sh +# Disasble resolv.conf generation from DHCP +# {{ ansible_managed }} +make_resolv_conf() { + : +} diff --git a/roles/base/templates/etc/fail2ban/action.d/route.conf.j2 b/roles/base/templates/etc/fail2ban/action.d/route.conf.j2 new file mode 100644 index 0000000..19b02c4 --- /dev/null +++ b/roles/base/templates/etc/fail2ban/action.d/route.conf.j2 @@ -0,0 +1,15 @@ +# fail2ban action - route + +[Definition] +actionban = ip route add +actionunban = ip route del +actioncheck = +actionstart = +actionstop = + +[Init] + +# Option: blocktype +# Note: Type can be blackhole, unreachable and prohibit. Unreachable and prohibit correspond to the ICMP reject messages. +# Values: STRING +blocktype = blackhole diff --git a/roles/base/templates/etc/fail2ban/filter.d/sshd.conf.j2 b/roles/base/templates/etc/fail2ban/filter.d/sshd.conf.j2 new file mode 100644 index 0000000..df4f1ed --- /dev/null +++ b/roles/base/templates/etc/fail2ban/filter.d/sshd.conf.j2 @@ -0,0 +1,50 @@ +# Fail2Ban filter for openssh +# {{ ansible_managed }} + +[INCLUDES] + +# Read common prefixes. If any customizations available -- read them from +# common.local +before = common.conf + +[Definition] + +_daemon = sshd + +failregex = ^%(__prefix_line)s(?:error: PAM: )?[aA]uthentication (?:failure|error|failed) for .* from ( via \S+)?\s*$ + ^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from \s*$ + ^%(__prefix_line)sFailed \S+ for (?Pinvalid user )?(?P(?P\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from (?: port \d+)?(?: ssh\d*)?(?(cond_user):|(?:(?:(?! from ).)*)$) + ^%(__prefix_line)sROOT LOGIN REFUSED.* FROM \s*$ + ^%(__prefix_line)s[iI](?:llegal|nvalid) user .*? from (?: port \d+)?\s*$ + ^%(__prefix_line)sUser .+ from not allowed because not listed in AllowUsers\s*$ + ^%(__prefix_line)sUser .+ from not allowed because listed in DenyUsers\s*$ + ^%(__prefix_line)sUser .+ from not allowed because not in any group\s*$ + ^%(__prefix_line)srefused connect from \S+ \(\)\s*$ + ^%(__prefix_line)s(?:error: )?Received disconnect from : 3: .*: Auth fail(?: \[preauth\])?$ + ^%(__prefix_line)sUser .+ from not allowed because a group is listed in DenyGroups\s*$ + ^%(__prefix_line)sUser .+ from not allowed because none of user's groups are listed in AllowGroups\s*$ + ^(?P<__prefix>%(__prefix_line)s)User .+ not allowed because account is locked(?P=__prefix)(?:error: )?Received disconnect from : 11: .+ \[preauth\]$ + ^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\](?P=__prefix)(?:error: )?Connection closed by \[preauth\]$ + ^(?P<__prefix>%(__prefix_line)s)Connection from port \d+(?: on \S+ port \d+)?(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$ + ^%(__prefix_line)s(error: )?maximum authentication attempts exceeded for .* from (?: port \d*)?(?: ssh\d*)? \[preauth\]$ + ^%(__prefix_line)spam_unix\(sshd:auth\):\s+authentication failure;\s*logname=\S*\s*uid=\d*\s*euid=\d*\s*tty=\S*\s*ruser=\S*\s*rhost=\s.*$ + ^%(__prefix_line)sUnable to negotiate with .*$ + +ignoreregex = + +[Init] + +# "maxlines" is number of log lines to buffer for multi-line regex searches +maxlines = 10 + +journalmatch = _SYSTEMD_UNIT=sshd.service + _COMM=sshd + +# DEV Notes: +# +# "Failed \S+ for .*? from ..." failregex uses non-greedy catch-all because +# it is coming before use of which is not hard-anchored at the end as well, +# and later catch-all's could contain user-provided input, which need to be greedily +# matched away first. +# +# Author: Cyril Jaquier, Yaroslav Halchenko, Petr Voralek, Daniel Black + diff --git a/roles/base/templates/etc/fail2ban/jail.d/sshd.conf.j2 b/roles/base/templates/etc/fail2ban/jail.d/sshd.conf.j2 new file mode 100644 index 0000000..87e4c97 --- /dev/null +++ b/roles/base/templates/etc/fail2ban/jail.d/sshd.conf.j2 @@ -0,0 +1,30 @@ +# Fail2Ban configuration file +# +# Author: Wolfgang Karall (based on sshd.conf from Cyril Jaquier) +# + +[INCLUDES] + +# Read common prefixes. If any customizations available -- read them from +# common.local +before = common.conf + + +[Definition] + +_daemon = sshd + +# Option: failregex +# Notes.: regex to match the password failures messages in the logfile. The +# host must be matched by a group named "host". The tag "" can +# be used for standard IP/hostname matching and is only an alias for +# (?:::f{4,6}:)?(?P[\w\-.^_]+) +# Values: TEXT +# +failregex = ^%(__prefix_line)sUnable to negotiate with .*$ + +# Option: ignoreregex +# Notes.: regex to ignore. If this regex matches, the line is ignored. +# Values: TEXT +# +ignoreregex = ^%(__prefix_line)sDid not receive identification string from .*$ diff --git a/roles/base/templates/etc/fail2ban/jail.d/sshd.local.j2 b/roles/base/templates/etc/fail2ban/jail.d/sshd.local.j2 new file mode 100644 index 0000000..1ba69dd --- /dev/null +++ b/roles/base/templates/etc/fail2ban/jail.d/sshd.local.j2 @@ -0,0 +1,11 @@ +[DEFAULT] +maxretry = 3 +bantime = 14400 +ignoreip = 127.0.0.0/8 10.0.0.0/8 198.55.48.48/28 + +[ssh] +enabled = true +filter = sshd +action = route +logpath = /var/log/auth.log + diff --git a/roles/base/templates/etc/hosts.j2 b/roles/base/templates/etc/hosts.j2 new file mode 100644 index 0000000..8bc1830 --- /dev/null +++ b/roles/base/templates/etc/hosts.j2 @@ -0,0 +1,6 @@ +# Local system hosts file +# {{ ansible_managed }} + +::1 localhost ip6-localhost ip6-loopback +ff02::1 ip6-allmodes +ff02::2 ip6-allrouters diff --git a/roles/base/templates/etc/locale.gen.j2 b/roles/base/templates/etc/locale.gen.j2 new file mode 100644 index 0000000..531364e --- /dev/null +++ b/roles/base/templates/etc/locale.gen.j2 @@ -0,0 +1,4 @@ +# Locales configuration file +# {{ ansible_managed }} + +en_CA.UTF-8 UTF-8 diff --git a/roles/base/templates/etc/logrotate.d/rsyslog-loghost.j2 b/roles/base/templates/etc/logrotate.d/rsyslog-loghost.j2 new file mode 100644 index 0000000..243ecf7 --- /dev/null +++ b/roles/base/templates/etc/logrotate.d/rsyslog-loghost.j2 @@ -0,0 +1,23 @@ +# Logrotate configuration for loghost archives +# {{ ansible_managed }} + +/srv/log/kern.log +/srv/log/daemon.log +/srv/log/haproxy.log +/srv/log/auth.log +/srv/log/cron.log +/srv/log/mail.log +/srv/log/boot.log +/srv/log/system.log +{ + weekly + missingok + copytruncate + dateext + notifempty + sharedscripts + postrotate + /usr/lib/rsyslog/rsyslog-rotate &>/dev/null + /usr/local/sbin/loghost-archive.sh &>/dev/null + endscript +} diff --git a/roles/base/templates/etc/logrotate.d/rsyslog.j2 b/roles/base/templates/etc/logrotate.d/rsyslog.j2 new file mode 100644 index 0000000..6ae2f58 --- /dev/null +++ b/roles/base/templates/etc/logrotate.d/rsyslog.j2 @@ -0,0 +1,23 @@ +# Logrotate configuration for standard log files +# {{ ansible_managed }} + +/var/log/kern.log +/var/log/daemon.log +/var/log/haproxy.log +/var/log/auth.log +/var/log/cron.log +/var/log/mail.log +/var/log/boot.log +/var/log/system.log +{ + rotate {{ logrotate_keepcount }} + {{ logrotate_interval }} + missingok + notifempty + compress + delaycompress + sharedscripts + postrotate + /usr/lib/rsyslog/rsyslog-rotate &>/dev/null + endscript +} diff --git a/roles/base/templates/etc/ntp.conf.j2 b/roles/base/templates/etc/ntp.conf.j2 new file mode 100644 index 0000000..747031b --- /dev/null +++ b/roles/base/templates/etc/ntp.conf.j2 @@ -0,0 +1,27 @@ +# Main NTP configuration +# {{ ansible_managed }} + +driftfile /var/lib/ntp/ntp.drift + +statistics loopstats peerstats clockstats + +filegen loopstats file loopstats type day enable +filegen peerstats file peerstats type day enable +filegen clockstats file clockstats type day enable + +{% if 'remote' in group_names or 'remote-jellyfin' in group_names %} +server time.nrc.ca +server time.chu.nrc.ca + +restrict -4 default kod notrap nomodify nopeer +restrict -6 default kod notrap nomodify nopeer +{% else %} +disable auth +multicastclient 224.0.0.1 +multicastclient ff05::101 + +restrict -4 default notrap nomodify +restrict -6 default notrap nomodify +{% endif %} +restrict 127.0.0.1 +restrict ::1 diff --git a/roles/base/templates/etc/pam.d/sshd.j2 b/roles/base/templates/etc/pam.d/sshd.j2 new file mode 100644 index 0000000..46d0f3c --- /dev/null +++ b/roles/base/templates/etc/pam.d/sshd.j2 @@ -0,0 +1,54 @@ +# PAM configuration for the Secure Shell service +# {{ ansible_managed }} + +# Standard Un*x authentication. +@include common-auth + +# Disallow non-root logins when /etc/nologin exists. +account required pam_nologin.so + +# Uncomment and edit /etc/security/access.conf if you need to set complex +# access limits that are hard to express in sshd_config. +# account required pam_access.so + +# Standard Un*x authorization. +@include common-account + +# SELinux needs to be the first session rule. This ensures that any +# lingering context has been cleared. Without this it is possible that a +# module could execute code in the wrong domain. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so close + +# Set the loginuid process attribute. +session required pam_loginuid.so + +# Create a new session keyring. +session optional pam_keyinit.so force revoke + +# Standard Un*x session setup and teardown. +@include common-session + +# Print the message of the day upon successful login. +session optional pam_motd.so motd=/run/pvc-motd.dynamic +session optional pam_motd.so noupdate + +# Print the status of the user's mailbox upon successful login. +#session optional pam_mail.so standard noenv # [1] + +# Set up user limits from /etc/security/limits.conf. +session required pam_limits.so + +# Read environment variables from /etc/environment and +# /etc/security/pam_env.conf. +session required pam_env.so # [1] +# In Debian 4.0 (etch), locale-related environment variables were moved to +# /etc/default/locale, so read that as well. +session required pam_env.so user_readenv=1 envfile=/etc/default/locale + +# SELinux needs to intervene at login time to ensure that the process starts +# in the proper default security context. Only sessions which are intended +# to run in the user's context should be run after this. +session [success=ok ignore=ignore module_unknown=ignore default=bad] pam_selinux.so open + +# Standard Un*x password updating. +@include common-password diff --git a/roles/base/templates/etc/postfix/main.cf.j2 b/roles/base/templates/etc/postfix/main.cf.j2 new file mode 100644 index 0000000..24db7c0 --- /dev/null +++ b/roles/base/templates/etc/postfix/main.cf.j2 @@ -0,0 +1,17 @@ +# Postfix main configuration for non-MTA hosts +# {{ ansible_managed }} + +smtpd_banner = $myhostname ESMTP $mail_name (Debian/GNU) +biff = no +append_dot_mydomain = no +readme_directory = no +smtpd_use_tls=no + +alias_maps = hash:/etc/postfix/aliases +alias_database = hash:/etc/postfix/aliases +mydestination = +mynetworks = 127.0.0.0/8 [::ffff:127.0.0.0]/104 [::1]/128 +mailbox_size_limit = 0 +recipient_delimiter = + +inet_interfaces = 127.0.0.1 +inet_protocols = ipv4 diff --git a/roles/base/templates/etc/profile.d/w.sh.j2 b/roles/base/templates/etc/profile.d/w.sh.j2 new file mode 100644 index 0000000..1c0aafb --- /dev/null +++ b/roles/base/templates/etc/profile.d/w.sh.j2 @@ -0,0 +1,7 @@ +#!/bin/sh + +# Message of the day script to print active users +# {{ ansible_managed }} + +export PROCPS_FROMLEN=36 PROCPS_USERLEN=12 +w diff --git a/roles/base/templates/etc/resolv.conf.j2 b/roles/base/templates/etc/resolv.conf.j2 new file mode 100644 index 0000000..6744c90 --- /dev/null +++ b/roles/base/templates/etc/resolv.conf.j2 @@ -0,0 +1,5 @@ +# DNS resolver configuration +# {{ ansible_managed }} + +options timeout:1 attempts:3 rotate +nameserver 1.1.1.1 diff --git a/roles/base/templates/etc/rsyslog.conf.j2 b/roles/base/templates/etc/rsyslog.conf.j2 new file mode 100644 index 0000000..669b9bb --- /dev/null +++ b/roles/base/templates/etc/rsyslog.conf.j2 @@ -0,0 +1,37 @@ +# Main rsyslog configuration +# {{ ansible_managed }} + +#### #### +#### MODULES #### +#### #### + +module(load="imuxsock") # provides support for local system logging (e.g. via logger command) +module(load="imklog") # provides kernel logging support (previously done by rklogd) + +$ModLoad imudp +$UDPServerAddress ::1 +$UDPServerRun 514 + +#### #### +#### GLOBAL DIRECTIVES #### +#### #### + +$PreserveFQDN on +$ActionFileDefaultTemplate RSYSLOG_TraditionalFileFormat + +#### #### +#### RULES #### +#### #### + +ruleset(name="local") { + kern.* /var/log/kern.log + auth,authpriv.* /var/log/auth.log + cron.* /var/log/cron.log + daemon,user.* /var/log/daemon.log + mail.* /var/log/mail.log + local5.* /var/log/nginx.log + local6.* /var/log/haproxy.log + local7.* /var/log/boot.log + *.info;kern,daemon,user,auth,authpriv,cron,mail,local6.none,local7.none /var/log/system.log +} +$DefaultRuleset local diff --git a/roles/base/templates/etc/ssh/shosts.equiv.j2 b/roles/base/templates/etc/ssh/shosts.equiv.j2 new file mode 100644 index 0000000..5bb3663 --- /dev/null +++ b/roles/base/templates/etc/ssh/shosts.equiv.j2 @@ -0,0 +1,3 @@ +# SSH remote allowed hosts +# {{ ansible_managed }} + diff --git a/roles/base/templates/etc/ssh/ssh_config.j2 b/roles/base/templates/etc/ssh/ssh_config.j2 new file mode 100644 index 0000000..b8f5230 --- /dev/null +++ b/roles/base/templates/etc/ssh/ssh_config.j2 @@ -0,0 +1,43 @@ +# Default SSH client configuration +# {{ ansible_managed }} + +Host * +# ForwardAgent no +# ForwardX11 no +# ForwardX11Trusted yes +# RhostsRSAAuthentication no +# RSAAuthentication yes +# PasswordAuthentication yes +# EnableSSHKeysign yes +# HostbasedAuthentication yes +# GSSAPIAuthentication no +# GSSAPIDelegateCredentials no +# GSSAPIKeyExchange no +# GSSAPITrustDNS no +# BatchMode no +# CheckHostIP yes +# AddressFamily any +# ConnectTimeout 0 +# StrictHostKeyChecking ask +# IdentityFile ~/.ssh/identity +# IdentityFile ~/.ssh/id_rsa +# IdentityFile ~/.ssh/id_dsa +# Port 22 +# Protocol 2,1 +# Cipher 3des +# Ciphers aes128-ctr,aes192-ctr,aes256-ctr,arcfour256,arcfour128,aes128-cbc,3des-cbc +# MACs hmac-md5,hmac-sha1,umac-64@openssh.com,hmac-ripemd160 +# EscapeChar ~ +# Tunnel no +# TunnelDevice any:any +# PermitLocalCommand no +# VisualHostKey no +# ProxyCommand ssh -q -W %h:%p gateway.example.com +# PreferredAuthentications hostbased,pubkey + SendEnv LANG LC_* + HashKnownHosts yes + GSSAPIAuthentication yes + GSSAPIDelegateCredentials no + PubkeyAuthentication yes + HostbasedAuthentication yes + EnableSSHKeysign yes diff --git a/roles/base/templates/etc/ssh/ssh_known_hosts.j2 b/roles/base/templates/etc/ssh/ssh_known_hosts.j2 new file mode 100644 index 0000000..5bb3663 --- /dev/null +++ b/roles/base/templates/etc/ssh/ssh_known_hosts.j2 @@ -0,0 +1,3 @@ +# SSH remote allowed hosts +# {{ ansible_managed }} + diff --git a/roles/base/templates/etc/ssh/sshd_config.j2 b/roles/base/templates/etc/ssh/sshd_config.j2 new file mode 100644 index 0000000..9f95029 --- /dev/null +++ b/roles/base/templates/etc/ssh/sshd_config.j2 @@ -0,0 +1,43 @@ +# Main SSH daemon configuraton +# {{ ansible_managed }} + +Port 22 +ListenAddress :: +ListenAddress 0.0.0.0 +Protocol 2 +HostKey /etc/ssh/ssh_host_ed25519_key +UsePrivilegeSeparation yes +SyslogFacility AUTH +LogLevel INFO +LoginGraceTime 120 +UsePAM yes +StrictModes yes +X11Forwarding no +PrintMotd no +PrintLastLog yes +TCPKeepAlive yes +AcceptEnv LANG LC_* + +MACs hmac-sha2-512,hmac-sha2-256 +Ciphers aes256-ctr,aes192-ctr,aes128-ctr + +KexAlgorithms curve25519-sha256@libssh.org,ecdh-sha2-nistp521,ecdh-sha2-nistp384,ecdh-sha2-nistp256,diffie-hellman-group-exchange-sha256 +Ciphers chacha20-poly1305@openssh.com,aes256-gcm@openssh.com,aes128-gcm@openssh.com,aes256-ctr,aes192-ctr,aes128-ctr +MACs hmac-sha2-512-etm@openssh.com,hmac-sha2-256-etm@openssh.com,umac-128-etm@openssh.com,hmac-sha2-512,hmac-sha2-256,umac-128@openssh.com + +PubkeyAuthentication yes +PermitEmptyPasswords no +ChallengeResponseAuthentication no +PasswordAuthentication no +{% if 'hv' in group_names %} +HostbasedAuthentication yes +HostbasedUsesNameFromPacketOnly yes +IgnoreRhosts no +PermitRootLogin yes +{% else %} +HostbasedAuthentication no +IgnoreRhosts yes +PermitRootLogin no +{% endif %} + +Subsystem sftp /usr/lib/openssh/sftp-server -f AUTH -l INFO diff --git a/roles/base/templates/etc/sudoers.d/sudoers-backup.j2 b/roles/base/templates/etc/sudoers.d/sudoers-backup.j2 new file mode 100644 index 0000000..e54e7a2 --- /dev/null +++ b/roles/base/templates/etc/sudoers.d/sudoers-backup.j2 @@ -0,0 +1,5 @@ +# sudoers configuraton for BackupPC +# {{ ansible_managed }} + +Cmnd_Alias BACKUPS = /usr/bin/rsync, /var/backups/timestamp.sh +backup ALL=(root) NOPASSWD: BACKUPS diff --git a/roles/base/templates/etc/sudoers.j2 b/roles/base/templates/etc/sudoers.j2 new file mode 100644 index 0000000..76db387 --- /dev/null +++ b/roles/base/templates/etc/sudoers.j2 @@ -0,0 +1,12 @@ +# sudoers configuraton; per-host declarations go in /etc/sudoers.d +# {{ ansible_managed }} + +Defaults env_reset +Defaults mail_badpass +Defaults secure_path="/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin" + +root ALL=(ALL:ALL) NOPASSWD: ALL +deploy ALL=(ALL:ALL) NOPASSWD: /bin/sh +%sudo ALL=(ALL:ALL) NOPASSWD: ALL + +#includedir /etc/sudoers.d diff --git a/roles/base/templates/etc/sysctl.d/pvc.conf.j2 b/roles/base/templates/etc/sysctl.d/pvc.conf.j2 new file mode 100644 index 0000000..8ac3f64 --- /dev/null +++ b/roles/base/templates/etc/sysctl.d/pvc.conf.j2 @@ -0,0 +1,47 @@ +# General sysctl parameters for BLSE2 +# {{ ansible_managed }} + +# Reduce swappiness +vm.swappiness = 1 + +# enable Spoof protection (reverse-path filter) +# Turn on Source Address Verification in all interfaces to +# prevent some spoofing attacks +net.ipv4.conf.default.rp_filter = 1 +net.ipv4.conf.all.rp_filter = 1 + +# Ignore ICMP broadcasts +net.ipv4.icmp_echo_ignore_broadcasts = 1 + +# Ignore bogus ICMP errors +net.ipv4.icmp_ignore_bogus_error_responses = 1 + +# Do not accept ICMP redirects (prevent MITM attacks) +net.ipv4.conf.all.accept_redirects = 0 +{% if not 'rpi' in group_names %} +net.ipv6.conf.all.accept_redirects = 0 +{% endif %} + +# Do not send ICMP redirects (we are not a router) +net.ipv4.conf.all.send_redirects = 0 + +# Do not accept IP source route packets (we are not a router) +net.ipv4.conf.all.accept_source_route = 0 +{% if not 'rpi' in group_names %} +net.ipv6.conf.all.accept_source_route = 0 +{% endif %} + +# Don't log Martian Packets +net.ipv4.conf.all.log_martians = 0 + +# Explicit Congestion Notification (ECN) +net.ipv4.tcp_ecn = 1 + +# number of seconds the kernel waits before rebooting on a panic +kernel.panic = 60 + +# Panic on an OOPS +kernel.panic_on_oops = 1 + +# Restrict dmesg +kernel.dmesg_restrict = 1 diff --git a/roles/base/templates/etc/systemd/journald.conf.j2 b/roles/base/templates/etc/systemd/journald.conf.j2 new file mode 100644 index 0000000..03f2d6a --- /dev/null +++ b/roles/base/templates/etc/systemd/journald.conf.j2 @@ -0,0 +1,4 @@ +# Journald configuration +# {{ ansible_managed }} +[Journal] +Storage=persistent diff --git a/roles/base/templates/etc/systemd/system/check_mk.socket b/roles/base/templates/etc/systemd/system/check_mk.socket new file mode 100644 index 0000000..0e15ba4 --- /dev/null +++ b/roles/base/templates/etc/systemd/system/check_mk.socket @@ -0,0 +1,10 @@ +# systemd socket definition file +[Unit] +Description=Check_MK Agent Socket + +[Socket] +ListenStream=6556 +Accept=true + +[Install] +WantedBy=sockets.target diff --git a/roles/base/templates/etc/systemd/system/check_mk@.service b/roles/base/templates/etc/systemd/system/check_mk@.service new file mode 100644 index 0000000..a721813 --- /dev/null +++ b/roles/base/templates/etc/systemd/system/check_mk@.service @@ -0,0 +1,12 @@ +# systemd service definition file +[Unit] +Description=Check_MK + +[Service] +ExecStart=/usr/bin/check_mk_agent +KillMode=process + +User=root +Group=root + +StandardInput=socket diff --git a/roles/base/templates/root/vimrc.j2 b/roles/base/templates/root/vimrc.j2 new file mode 100644 index 0000000..cd5a9d8 --- /dev/null +++ b/roles/base/templates/root/vimrc.j2 @@ -0,0 +1,5 @@ +set showcmd +set number +set cursorline +syntax on +set mouse= diff --git a/roles/base/templates/usr/local/sbin/dpkg-cleanup.sh.j2 b/roles/base/templates/usr/local/sbin/dpkg-cleanup.sh.j2 new file mode 100755 index 0000000..705b7cd --- /dev/null +++ b/roles/base/templates/usr/local/sbin/dpkg-cleanup.sh.j2 @@ -0,0 +1,18 @@ +#!/bin/bash + +# dpkg-cleanup.sh - Remove obsolete packages and config files +# {{ ansible_managed }} + +# Phase 1 - purge `rc` packages +PACKAGE_LIST=( $( dpkg --list | awk '/^rc/{ print $2 } /^ri/{ print $2 }' ) ) +apt purge -y ${PACKAGE_LIST[@]} + +# Phase 2 - autoremove packages +apt autoremove --purge -y + +# Phase 3 - clean archives +apt clean + +# Phase 4 - find and remove obsolete config files +OLD_FILES_LIST=( $( find /etc -type f -a \( -name '*.dpkg-*' -o -name '*.ucf-*' \) 2>/dev/null ) ) +rm -f ${OLD_FILES_LIST[@]} diff --git a/roles/base/templates/usr/local/sbin/loghost-archive.sh.j2 b/roles/base/templates/usr/local/sbin/loghost-archive.sh.j2 new file mode 100755 index 0000000..f3888d9 --- /dev/null +++ b/roles/base/templates/usr/local/sbin/loghost-archive.sh.j2 @@ -0,0 +1,23 @@ +#!/bin/bash + +# Archive old logs on loghost +# {{ ansible_managed }} + +LOGPATH=/srv/log +ARCHIVEPATH=${LOGPATH}/archive/ +test -d $ARCHIVEPATH || mkdir -p $ARCHIVEPATH +for LOGFILE in \ + kern.log \ + daemon.log \ + haproxy.log \ + auth.log \ + cron.log \ + mail.log \ + boot.log \ + system.log +do + test -d ${ARCHIVEPATH}/${LOGFILE} || mkdir -p ${ARCHIVEPATH}/${LOGFILE} + mv ${LOGPATH}/${LOGFILE}-* ${ARCHIVEPATH}/${LOGFILE} + gzip ${ARCHIVEPATH}/${LOGFILE}/* + find ${ARCHIVEPATH}/${LOGFILE} -type f -ctime +90 -exec rm {} \; +done diff --git a/roles/base/templates/usr/local/sbin/update-motd.sh.j2 b/roles/base/templates/usr/local/sbin/update-motd.sh.j2 new file mode 100755 index 0000000..1261237 --- /dev/null +++ b/roles/base/templates/usr/local/sbin/update-motd.sh.j2 @@ -0,0 +1,26 @@ +#!/bin/sh + +# Update dynamic MOTD file +# {{ ansible_managed }} + +set -o errexit + +TMPFILE=$(mktemp) +TGTFILE=/run/pvc-motd.dynamic +DEBVER="({{ ansible_lsb.description }})" + +echo >> $TMPFILE +echo "\033[01;34mParallel Virtual Cluster \033[01;36m${DEBVER}\033[0m" >> $TMPFILE +echo -n "> \033[01;32m$(hostname)\033[0m" >> $TMPFILE +if test -f /etc/hostdesc; then + echo " - $( cat /etc/hostdesc )" >> $TMPFILE +else + echo >> $TMPFILE +fi +# Get machine information +echo "> \033[1;37mPVC node\033[0m on \033[1;31m$( + /usr/sbin/dmidecode | grep -A1 'Chassis Information' | tail -1 | awk -F':' '{print $2}' | tr -s ' ' + )\033[0m hardware" >> $TMPFILE +echo "> $(/bin/uname -srvmo)" >> $TMPFILE + +mv $TMPFILE $TGTFILE || rm $TMPFILE diff --git a/roles/base/templates/var/backups/ssh/authorized_keys.j2 b/roles/base/templates/var/backups/ssh/authorized_keys.j2 new file mode 100644 index 0000000..80c426f --- /dev/null +++ b/roles/base/templates/var/backups/ssh/authorized_keys.j2 @@ -0,0 +1,4 @@ +# backup user authorized_keys +# {{ ansible_managed }} + +ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQCnndMxkLF+Trm7Zpo59daJbH6C6SbInl8f1PAizxtUkWg8skP5EXkUc0eguos+5o6BG1VL0c8SWBnl4smvZL075l2wC3+cJeDUIyxC6aue6vualFMPj5p0h4gJWrX+L5r1b1hxnR3r5Mqx7/2W9K35/u3M6TPnRXn0XjGN93j8dsywfDOuU4xH+w0INM4iNeEne4l2SEAVA0Sm7nGNss4X18iwjnxyKgqUB+HtG2WHyEPr/Uv5OiEC+4n4LvkMRMpupx33U5ZH7pgyfFKJJsIObBf4nC4xUUZyCG2FlHiWzX0Ua9xxwz9OJIeqlwfYsLFrHEbPS5KpAXukEjshKGY1 backuppc@base.bonilan.net diff --git a/roles/base/templates/var/backups/timestamp.sh.j2 b/roles/base/templates/var/backups/timestamp.sh.j2 new file mode 100755 index 0000000..48b4ff4 --- /dev/null +++ b/roles/base/templates/var/backups/timestamp.sh.j2 @@ -0,0 +1,11 @@ +#!/bin/bash + +# Writes timestamps on successful BackupPC completion and updates dynamic share inventory for this host +# {{ ansible_managed }} + +OK="$1" +SHARE="$2" +grep -F "${SHARE}" /var/backups/shares || echo "${SHARE}" >> /var/backups/shares +if [[ ${OK} -eq 1 ]]; then + /bin/date +%s > ${SHARE}/.backup +fi diff --git a/roles/base/templates/var/home/user/bash_logout.j2 b/roles/base/templates/var/home/user/bash_logout.j2 new file mode 100755 index 0000000..0859925 --- /dev/null +++ b/roles/base/templates/var/home/user/bash_logout.j2 @@ -0,0 +1,7 @@ +# BLSE 2.0 bash_logout file +# {{ ansible_managed }} + +# when leaving the console clear the screen to increase privacy +if [ "$SHLVL" = 1 ]; then + [ -x /usr/bin/clear_console ] && /usr/bin/clear_console -q +fi diff --git a/roles/base/templates/var/home/user/bashrc.j2 b/roles/base/templates/var/home/user/bashrc.j2 new file mode 100755 index 0000000..2488ad0 --- /dev/null +++ b/roles/base/templates/var/home/user/bashrc.j2 @@ -0,0 +1,144 @@ +#!/bin/bash + +# BLSE 2.0 bashrc file +# {{ ansible_managed }} + +# +# GENERAL SETTINGS +# + +# Before anything, see if we're running interactively. If not, skip everything here. +[[ $- == *i* ]] || return + +# Ensure bash completion is enabled if installed +if ! shopt -oq posix; then + if [ -f /usr/share/bash-completion/bash_completion ]; then + . /usr/share/bash-completion/bash_completion + elif [ -f /etc/bash_completion ]; then + . /etc/bash_completion + fi +fi + +# Some other tweaks +[ -x /usr/bin/lesspipe ] && eval "$(SHELL=/bin/sh lesspipe)" +if [ -z "${debian_chroot:-}" ] && [ -r /etc/debian_chroot ]; then + debian_chroot=$(cat /etc/debian_chroot) +fi + +# Set history limits and values +shopt -s cdspell +shopt -s dirspell +shopt -s dotglob +shopt -s histreedit +shopt -s histverify +shopt -s histappend +PROMPT_COMMAND="history -a;$PROMPT_COMMAND" +HISTCONTROL=ignoreboth +HISTSIZE=25000 +HISTFILESIZE=25000 + +# +# BASH SETTINGS +# + +# Set a shiny Linux Mint-style PS1 with spaces for easy double-click-select +git_branch() { + git branch 2> /dev/null | sed -e '/^[^*]/d' -e 's/* \(.*\)/git:\1 /' +} +export PS1='${debian_chroot:+($debian_chroot)}\[\033[01;32m\]\u@\[\033[01;32m\]\H\[\033[01;34m\] \[\e[35m\]$(git_branch)\[\033[01;34m\]\w \$\[\033[00m\] ' + +# Sensible PATH (find things in *sbin* as non-root user) +export PATH="/usr/lib/check_mk_agent/plugins:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/local/games:/usr/games" + +# Set PATH to include ~/Scripts if it exists +if [ -d ~/Scripts ]; then + export PATH=~/Scripts:$PATH +fi + +# Set editor to vim +export EDITOR=/usr/bin/vim + +# Force SCREEN to xterm due to Debian weirdness +export SCREEN="xterm" + + +# +# ALIASES +# + +# Coloured command aliases +alias ls='ls --color=always' +alias dir='dir --color=always' +alias vdir='vdir --color=always' +alias grep='grep --color=always' +alias fgrep='fgrep --color=always' +alias egrep='egrep --color=always' +alias xzgrep='xzgrep --color=always' +alias less='less -r' + +# Convenient ls aliases +alias ll='ls -alh' +alias la='ls -A' +alias l='ls -lh' + +# Always-sudo commands, because fuck typing sudo all the time +alias service='sudo service' +alias systemctl='sudo systemctl' +alias journalctl='sudo journalctl' +alias dmesg='sudo dmesg' +alias apt='sudo apt' +alias dpkg='sudo dpkg' +alias find='sudo find' +alias htop='sudo htop' +alias powertop='sudo powertop' +alias jnettop='sudo jnettop' +alias wavemon='sudo wavemon' +alias parted='sudo parted' +alias fdisk='sudo fdisk' +alias gdisk='sudo gdisk' +alias chroot='sudo chroot' +alias mount='sudo mount' +alias umount='sudo umount' +alias virsh='sudo virsh -c qemu:///system' +alias ceph='sudo ceph' +alias rbd='sudo rbd' +alias mysql='sudo mysql' +alias zpool='sudo zpool' +alias zfs='sudo zfs' +alias crm='sudo crm' + +# Cool aliases +alias cccp='sudo rsync -auv --progress' +alias untmp='sudo umount /tmp/tmp.*{/*/*,/*,} 2>/dev/null' +alias txz='tar -p --same-owner -I pxz' +alias stxz='sudo tar -p --same-owner -I pxz' +alias zkcli='sudo /usr/share/zookeeper/bin/zkCli.sh' +alias hatop='sudo hatop -s /var/lib/haproxy/admin.sock' +alias patronictl='sudo patronictl -c /etc/patroni/config.yml -d zookeeper://localhost:2181' +alias repo='sudo reprepro -b /srv/debrepo' +alias beet='sudo -u debian-deluged beet --config=/srv/deluged/config.beets/config.yaml' +{% if 'mon' in group_names %} +alias icli='sudo -u monitor icli --status-file /omd/sites/monitor/tmp/nagios/status.dat --config /omd/sites/monitor/var/nagios/objects.cache -z \!o' + +# +# Show monitoring stats +# + +icli +{% endif %} + +# +# SOURCE OTHER SCRIPTS +# + +test -f ~/.bashrc.d/* && . ~/.bashrc.d/* + +# +# NICE AND CLEAN +# + +echo + +# +# END OF FILE +# diff --git a/roles/base/templates/var/home/user/config/htop/htoprc.j2 b/roles/base/templates/var/home/user/config/htop/htoprc.j2 new file mode 100644 index 0000000..f0311f0 --- /dev/null +++ b/roles/base/templates/var/home/user/config/htop/htoprc.j2 @@ -0,0 +1,25 @@ +# htop config file +# {{ ansible_managed }} +fields=0 48 17 18 38 39 40 2 46 47 49 1 +sort_key=46 +sort_direction=1 +hide_threads=0 +hide_kernel_threads=0 +hide_userland_threads=0 +shadow_other_users=0 +show_thread_names=1 +highlight_base_name=1 +highlight_megabytes=1 +highlight_threads=1 +tree_view=0 +header_margin=1 +detailed_cpu_time=1 +cpu_count_from_zero=0 +update_process_names=1 +account_guest_in_cpu_meter=1 +color_scheme=0 +delay=15 +left_meters=LeftCPUs2 Blank CPU Blank Blank Memory Swap +left_meter_modes=1 2 1 2 2 1 1 +right_meters=RightCPUs2 Blank LoadAverage Tasks Blank Hostname Clock Uptime Blank +right_meter_modes=1 2 2 2 2 2 2 2 2 diff --git a/roles/base/templates/var/home/user/profile.j2 b/roles/base/templates/var/home/user/profile.j2 new file mode 100644 index 0000000..41bbb9a --- /dev/null +++ b/roles/base/templates/var/home/user/profile.j2 @@ -0,0 +1,16 @@ +# {{ ansible_managed }} + +EDITOR=/usr/bin/vim + +# if running bash +if [ -n "$BASH_VERSION" ]; then + # include .bashrc if it exists + if [ -f "$HOME/.bashrc" ]; then + . "$HOME/.bashrc" + fi +fi + +# set PATH so it includes user's private bin if it exists +if [ -d "$HOME/bin" ] ; then + PATH="$HOME/bin:$PATH" +fi diff --git a/roles/base/templates/var/home/user/vimrc.j2 b/roles/base/templates/var/home/user/vimrc.j2 new file mode 100644 index 0000000..cdd8303 --- /dev/null +++ b/roles/base/templates/var/home/user/vimrc.j2 @@ -0,0 +1,13 @@ +set showcmd +set number +set cursorline +set autoindent +set expandtab +set tabstop=4 +set viminfo='100,<1000,s1000,h +hi CursorLine term=bold cterm=bold guibg=Grey40 +syntax on +set ruler +set directory=~/.vim +set mouse= +autocmd Filetype gitcommit setlocal spell textwidth=72 diff --git a/roles/pvc/README.md b/roles/pvc/README.md new file mode 100644 index 0000000..ec601d3 --- /dev/null +++ b/roles/pvc/README.md @@ -0,0 +1,13 @@ +# package-pvc + +This package configures the PVC virtual cluster system. + +# Supplemental variables + +## Configurable + +### `ceph_storage_secret_key`: The Ceph storage secret key in base64 format. +* Should be obtained from Ceph cluster. + +### `ceph_storage_secret_uuid`: A UUID for the Ceph secret in libvirt. +* Should be unique per cluster. diff --git a/roles/pvc/defaults/main.yml b/roles/pvc/defaults/main.yml new file mode 100644 index 0000000..0c0dc59 --- /dev/null +++ b/roles/pvc/defaults/main.yml @@ -0,0 +1,57 @@ +--- +# Ceph storage +ceph_storage_secret_key: "" +ceph_storage_secret_uuid: "" +# Database +pvc_dns_database_name: "pvcdns" +pvc_dns_database_user: "pvcdns" +pvc_dns_database_password: "PVCdnsPassw0rd" +# Coordinators +pvc_nodes: + - hostname: "pvc1" + is_coordinator: yes + node_id: 1 + router_id: "10.0.0.1" + cluster_ip: "by-id" + storage_ip: "by-id" + upstream_ip: "" + ipmi_host: "pvc1-lom" + ipmi_user: "" + ipmi_password: "" + - hostname: "pvc2" + is_coordinator: yes + node_id: 2 + router_id: "10.0.0.2" + cluster_ip: "by-id" + storage_ip: "by-id" + upstream_ip: "" + ipmi_host: "pvc2-lom" + ipmi_user: "" + ipmi_password: "" + - hostname: "pvc3" + is_coordinator: yes + node_id: 3 + router_id: "10.0.0.3" + cluster_ip: "by-id" + storage_ip: "by-id" + upstream_ip: "" + ipmi_host: "pvc3-lom" + ipmi_user: "" + ipmi_password: "" +# Networks +pvc_asn: "65001" +pvc_routers: + - "" +pvc_cluster_device: "eth0" +pvc_cluster_domain: "pvc.local" +pvc_cluster_subnet: "10.0.0.0/24" +pvc_cluster_floatingip: "10.0.0.251/24" +pvc_storage_device: "eth1" +pvc_storage_domain: "pvc.storage" +pvc_storage_subnet: "10.0.1.0/24" +pvc_storage_floatingip: "10.0.1.251/24" +pvc_upstream_device: "eth2" +pvc_upstream_domain: "" +pvc_upstream_subnet: "" +pvc_upstream_floatingip: "" +pvc_upstream_gatewayip: "" diff --git a/roles/pvc/files/mariadb/mk_mysql.agents b/roles/pvc/files/mariadb/mk_mysql.agents new file mode 100755 index 0000000..569bb19 --- /dev/null +++ b/roles/pvc/files/mariadb/mk_mysql.agents @@ -0,0 +1,90 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2014 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + +# gets optional socket as argument +function do_query() { + INSTANCE=$(echo $1|awk -v FS="=" '{print $2}') + COUNT=$(ps -efww | grep [/]usr/sbin/mysqld | grep socket | wc -l) + if [ $COUNT -gt 1 ] + then + INSTANCE_NAME=$(ps -efww|grep socket|grep "${INSTANCE}"|grep "[u]ser" | sed -ne 's/.*socket=\([^.]*\).*/\1/p') + INSTANCE_NAME="[[${INSTANCE_NAME##*/}]]" + else + INSTANCE_NAME="[[$(ps -efww|grep socket|grep "${INSTANCE}"|grep "[u]ser" | sed -ne 's/.*user=\([^ ]*\).*/\1/p')]]" + fi + + + + # Check if mysqld is running and root password setup + echo "<<>>" + echo $INSTANCE_NAME + mysqladmin --defaults-extra-file=/root/.my.cnf $1 ping 2>&1 + + if [ $? -eq 0 ]; then + + echo "<<>>" + echo $INSTANCE_NAME + mysql --defaults-extra-file=/root/.my.cnf $1 -sN \ + -e "show global status ; show global variables ;" + + echo "<<>>" + echo $INSTANCE_NAME + mysql --defaults-extra-file=/root/.my.cnf $1 -sN \ + -e "SELECT table_schema, sum(data_length + index_length), sum(data_free) + FROM information_schema.TABLES GROUP BY table_schema" + + echo "<<>>" + echo $INSTANCE_NAME + mysql --defaults-extra-file=/root/.my.cnf $1 -s \ + -e "show slave status\G" + + fi + +} + +if which mysqladmin >/dev/null +then + mysql_sockets=$(fgrep socket /root/.my.cnf|sed -ne 's/.*socket=\([^ ]*\).*/\1/p') + if [ -z "$mysql_sockets" ] ; then + mysql_sockets=$(ps -efww | grep mysqld | grep "[s]ocket" | sed -ne 's/.*socket=\([^ ]*\).*/\1/p') + fi + if [ -z "$mysql_sockets" ] ; then + do_query "" + else + for socket in $mysql_sockets ; do + do_query "--socket="$socket + done + fi + #echo "<<>>" + #mysql -V + + echo "<<>>" + ps -efww|grep mysqld|while read LINE; do echo $LINE|grep "[u]ser" | sed -ne 's/.*user=\([^ ]*\).*/\1/p'; echo $LINE|grep mysqld | grep "[p]ort"|sed -ne 's/.*port=\([^ ]*\).*/\1/p' ; done|xargs -n2 + + #echo "<<>>" + #mysql --defaults-extra-file=/root/.my.cnf $1 -s \ + # -e "show INSTANCES" + +fi diff --git a/roles/pvc/files/patroni/postgres b/roles/pvc/files/patroni/postgres new file mode 100755 index 0000000..5aad043 --- /dev/null +++ b/roles/pvc/files/patroni/postgres @@ -0,0 +1,485 @@ +#!/bin/bash +# +------------------------------------------------------------------+ +# | ____ _ _ __ __ _ __ | +# | / ___| |__ ___ ___| | __ | \/ | |/ / | +# | | | | '_ \ / _ \/ __| |/ / | |\/| | ' / | +# | | |___| | | | __/ (__| < | | | | . \ | +# | \____|_| |_|\___|\___|_|\_\___|_| |_|_|\_\ | +# | | +# | Copyright Mathias Kettner 2015 mk@mathias-kettner.de | +# +------------------------------------------------------------------+ +# +# This file is part of Check_MK. +# The official homepage is at http://mathias-kettner.de/check_mk. +# +# check_mk is free software; you can redistribute it and/or modify it +# under the terms of the GNU General Public License as published by +# the Free Software Foundation in version 2. check_mk is distributed +# in the hope that it will be useful, but WITHOUT ANY WARRANTY; with- +# out even the implied warranty of MERCHANTABILITY or FITNESS FOR A +# PARTICULAR PURPOSE. See the GNU General Public License for more de- +# tails. You should have received a copy of the GNU General Public +# License along with GNU Make; see the file COPYING. If not, write +# to the Free Software Foundation, Inc., 51 Franklin St, Fifth Floor, +# Boston, MA 02110-1301 USA. + + +# TODO postgres_connections output format + + +# .--common funcs--------------------------------------------------------. +# | __ | +# | ___ ___ _ __ ___ _ __ ___ ___ _ __ / _|_ _ _ __ ___ ___ | +# | / __/ _ \| '_ ` _ \| '_ ` _ \ / _ \| '_ \ | |_| | | | '_ \ / __/ __| | +# || (_| (_) | | | | | | | | | | | (_) | | | || _| |_| | | | | (__\__ \ | +# | \___\___/|_| |_| |_|_| |_| |_|\___/|_| |_||_| \__,_|_| |_|\___|___/ | +# | | +# '----------------------------------------------------------------------' + + +function compare_version_greater_equal() { + local GREATER_ONE + GREATER_ONE=$(echo "$1 $2" | awk '{if ($1 >= $2) print $1; else print $2}') + if [ "$GREATER_ONE" == "$1" ] ; then + return 0 + else + return 1 + fi +} + + +#. +# .--section funcs-------------------------------------------------------. +# | _ _ __ | +# | ___ ___ ___| |_(_) ___ _ __ / _|_ _ _ __ ___ ___ | +# | / __|/ _ \/ __| __| |/ _ \| '_ \ | |_| | | | '_ \ / __/ __| | +# | \__ \ __/ (__| |_| | (_) | | | | | _| |_| | | | | (__\__ \ | +# | |___/\___|\___|\__|_|\___/|_| |_| |_| \__,_|_| |_|\___|___/ | +# | | +# '----------------------------------------------------------------------' + + +function postgres_instances() { + echo '<<>>' + # If we have no instances we take db id (pqsql/postgres) because + # ps output may be unreadable + # In case of instances ps output shows them readable + if [ ! -z "${1}" ]; then + echo "[[[${1}]]]" + fi + pgrep -laf bin/postgres +} + + +function postgres_sessions() { + # Postgres 9.2 uses 'query' instead of 'current_query' + local OUTPUT + OUTPUT="$(echo "\echo '<<>>${INSTANCE_SECTION}' + SELECT ( + SELECT column_name + FROM information_schema.columns + WHERE table_name='pg_stat_activity' AND column_name in ('query', 'current_query') + ) = '' as query, count(*) + FROM pg_stat_activity + GROUP BY (query = '');" |\ + sudo -u "$DBUSER" $export_PGPASSFILE $psql -X --variable ON_ERROR_STOP=1 -d $PGDATABASE ${EXTRA_ARGS} -A -t -F' ' 2>/dev/null)" + + echo "$OUTPUT" + # line with number of idle sessions is sometimes missing on Postgres 8.x. This can lead + # to an altogether empty section and thus the check disappearing. + echo "$OUTPUT" | grep -q '^t ' || echo "t 0" +} + + +function postgres_simple_queries() { + # Querytime + # Supports versions >= 8.3, > 9.1 + local QUERYTIME_QUERY + if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then + QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, state AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds, + pid, regexp_replace(query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND (state NOT LIKE 'idle%' OR state IS NULL)) ORDER BY query_start, pid DESC;" + else + QUERYTIME_QUERY="SELECT datname, datid, usename, client_addr, '' AS state, COALESCE(ROUND(EXTRACT(epoch FROM now()-query_start)),0) AS seconds, + procpid as pid, regexp_replace(current_query, E'[\\n\\r\\u2028]+', ' ', 'g' ) AS current_query FROM pg_stat_activity WHERE (query_start IS NOT NULL AND current_query NOT LIKE '%') ORDER BY query_start, procpid DESC;" + fi + + # Number of current connections per database + # We need to output the databases, too. + # This query does not report databases without an active query + local CONNECTIONS_QUERY + if compare_version_greater_equal "$POSTGRES_VERSION" "9.2" ; then + CONNECTIONS_QUERY="SELECT COUNT(datid) AS current, + (SELECT setting AS mc FROM pg_settings WHERE name = 'max_connections') AS mc, + d.datname + FROM pg_database d + LEFT JOIN pg_stat_activity s ON (s.datid = d.oid) WHERE state <> 'idle' + GROUP BY 2,3 + ORDER BY datname;" + else + CONNECTIONS_QUERY="SELECT COUNT(datid) AS current, + (SELECT setting AS mc FROM pg_settings WHERE name = 'max_connections') AS mc, + d.datname + FROM pg_database d + LEFT JOIN pg_stat_activity s ON (s.datid = d.oid) WHERE current_query <> '' + GROUP BY 2,3 + ORDER BY datname;" + fi + + echo "\pset footer off + \echo '<<>>${INSTANCE_SECTION}' + SELECT datid, datname, numbackends, xact_commit, xact_rollback, blks_read, blks_hit, tup_returned, tup_fetched, tup_inserted, tup_updated, tup_deleted, pg_database_size(datname) AS datsize FROM pg_stat_database; + + \echo '<<>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + SELECT datname, granted, mode FROM pg_locks l RIGHT JOIN pg_database d ON (d.oid=l.database) WHERE d.datallowconn; + + \echo '<<>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + $QUERYTIME_QUERY + + \echo '<<>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]' + $CONNECTIONS_QUERY" \ + | sudo -u "$DBUSER" $export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -q -A -F';' +} + + +function postgres_stats() { + # Contains last vacuum time and analyze time + local LASTVACUUM="SELECT current_database() AS datname, nspname AS sname, relname AS tname, + CASE WHEN v IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS vtime, + CASE WHEN g IS NULL THEN -1 ELSE round(extract(epoch FROM v)) END AS atime + FROM (SELECT nspname, relname, GREATEST(pg_stat_get_last_vacuum_time(c.oid), pg_stat_get_last_autovacuum_time(c.oid)) AS v, + GREATEST(pg_stat_get_last_analyze_time(c.oid), pg_stat_get_last_autoanalyze_time(c.oid)) AS g + FROM pg_class c, pg_namespace n + WHERE relkind = 'r' AND n.oid = c.relnamespace AND n.nspname <> 'information_schema' + ORDER BY 3) AS foo;" + + local FIRST= + local QUERY="\pset footer off + BEGIN; + SET statement_timeout=30000; + COMMIT; + + \echo '<<>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]'" + + for db in $DATABASES ; do + QUERY="$QUERY + \c $db + $LASTVACUUM + " + if [ -z $FIRST ] ; then + FIRST=false + QUERY="$QUERY + \pset tuples_only on + " + fi + done + echo "$QUERY" | sudo -u "$DBUSER" $export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';' | grep -v -e 'COMMIT$' -e 'SET$' -e 'BEGIN$' +} + + +function postgres_version() { + # Postgres version an connection time + echo -e "<<>>${INSTANCE_SECTION}" + (TIMEFORMAT='%3R'; time echo "SELECT version() AS v" |\ + sudo -u "$DBUSER" $export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -t -A -F';'; echo -e "<<>>${INSTANCE_SECTION}") 2>&1 +} + + +function postgres_bloat() { + # Bloat index and tables + # Supports versions <9.0, >=9.0 + # This huge query has been gratefully taken from Greg Sabino Mullane's check_postgres.pl + local BLOAT_QUERY + if compare_version_greater_equal "$POSTGRES_VERSION" "9.0" ; then + BLOAT_QUERY="SELECT + current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta, + ROUND(CASE WHEN otta=0 OR sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE sml.relpages/otta::numeric END,1) AS tbloat, + CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages, + CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes, + CASE WHEN relpages < otta THEN 0 ELSE (bs*(relpages-otta))::bigint END AS wastedsize, + iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta, + ROUND(CASE WHEN iotta=0 OR ipages=0 OR ipages=iotta THEN 0.0 ELSE ipages/iotta::numeric END,1) AS ibloat, + CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages, + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, + CASE WHEN ipages < iotta THEN 0 ELSE (bs*(ipages-iotta))::bigint END AS wastedisize, + CASE WHEN relpages < otta THEN + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END + ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint) + ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END + END AS totalwastedbytes + FROM ( + SELECT + nn.nspname AS schemaname, + cc.relname AS tablename, + COALESCE(cc.reltuples,0) AS reltuples, + COALESCE(cc.relpages,0) AS relpages, + COALESCE(bs,0) AS bs, + COALESCE(CEIL((cc.reltuples*((datahdr+ma- + (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta, + COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, + COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols + FROM + pg_class cc + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema' + LEFT JOIN + ( + SELECT + ma,bs,foo.nspname,foo.relname, + (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, + (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 + FROM ( + SELECT + ns.nspname, tbl.relname, hdr, ma, bs, + SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth, + MAX(coalesce(null_frac,0)) AS maxfracsum, + hdr+( + SELECT 1+count(*)/8 + FROM pg_stats s2 + WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname + ) AS nullhdr + FROM pg_attribute att + JOIN pg_class tbl ON att.attrelid = tbl.oid + JOIN pg_namespace ns ON ns.oid = tbl.relnamespace + LEFT JOIN pg_stats s ON s.schemaname=ns.nspname + AND s.tablename = tbl.relname + AND s.inherited=false + AND s.attname=att.attname, + ( + SELECT + (SELECT current_setting('block_size')::numeric) AS bs, + CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\[0-9]+.[0-9]+#\%' for '#') + IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr, + CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma + FROM (SELECT version() AS v) AS foo + ) AS constants + WHERE att.attnum > 0 AND tbl.relkind='r' + GROUP BY 1,2,3,4,5 + ) AS foo + ) AS rs + ON cc.relname = rs.relname AND nn.nspname = rs.nspname + LEFT JOIN pg_index i ON indrelid = cc.oid + LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid + ) AS sml + WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;" + else + BLOAT_QUERY="SELECT + current_database() AS db, schemaname, tablename, reltuples::bigint AS tups, relpages::bigint AS pages, otta, + ROUND(CASE WHEN otta=0 OR sml.relpages=0 OR sml.relpages=otta THEN 0.0 ELSE sml.relpages/otta::numeric END,1) AS tbloat, + CASE WHEN relpages < otta THEN 0 ELSE relpages::bigint - otta END AS wastedpages, + CASE WHEN relpages < otta THEN 0 ELSE bs*(sml.relpages-otta)::bigint END AS wastedbytes, + CASE WHEN relpages < otta THEN '0 bytes'::text ELSE (bs*(relpages-otta))::bigint || ' bytes' END AS wastedsize, + iname, ituples::bigint AS itups, ipages::bigint AS ipages, iotta, + ROUND(CASE WHEN iotta=0 OR ipages=0 OR ipages=iotta THEN 0.0 ELSE ipages/iotta::numeric END,1) AS ibloat, + CASE WHEN ipages < iotta THEN 0 ELSE ipages::bigint - iotta END AS wastedipages, + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta) END AS wastedibytes, + CASE WHEN ipages < iotta THEN '0 bytes' ELSE (bs*(ipages-iotta))::bigint || ' bytes' END AS wastedisize, + CASE WHEN relpages < otta THEN + CASE WHEN ipages < iotta THEN 0 ELSE bs*(ipages-iotta::bigint) END + ELSE CASE WHEN ipages < iotta THEN bs*(relpages-otta::bigint) + ELSE bs*(relpages-otta::bigint + ipages-iotta::bigint) END + END AS totalwastedbytes + FROM ( + SELECT + nn.nspname AS schemaname, + cc.relname AS tablename, + COALESCE(cc.reltuples,0) AS reltuples, + COALESCE(cc.relpages,0) AS relpages, + COALESCE(bs,0) AS bs, + COALESCE(CEIL((cc.reltuples*((datahdr+ma- + (CASE WHEN datahdr%ma=0 THEN ma ELSE datahdr%ma END))+nullhdr2+4))/(bs-20::float)),0) AS otta, + COALESCE(c2.relname,'?') AS iname, COALESCE(c2.reltuples,0) AS ituples, COALESCE(c2.relpages,0) AS ipages, + COALESCE(CEIL((c2.reltuples*(datahdr-12))/(bs-20::float)),0) AS iotta -- very rough approximation, assumes all cols + FROM + pg_class cc + JOIN pg_namespace nn ON cc.relnamespace = nn.oid AND nn.nspname <> 'information_schema' + LEFT JOIN + ( + SELECT + ma,bs,foo.nspname,foo.relname, + (datawidth+(hdr+ma-(case when hdr%ma=0 THEN ma ELSE hdr%ma END)))::numeric AS datahdr, + (maxfracsum*(nullhdr+ma-(case when nullhdr%ma=0 THEN ma ELSE nullhdr%ma END))) AS nullhdr2 + FROM ( + SELECT + ns.nspname, tbl.relname, hdr, ma, bs, + SUM((1-coalesce(null_frac,0))*coalesce(avg_width, 2048)) AS datawidth, + MAX(coalesce(null_frac,0)) AS maxfracsum, + hdr+( + SELECT 1+count(*)/8 + FROM pg_stats s2 + WHERE null_frac<>0 AND s2.schemaname = ns.nspname AND s2.tablename = tbl.relname + ) AS nullhdr + FROM pg_attribute att + JOIN pg_class tbl ON att.attrelid = tbl.oid + JOIN pg_namespace ns ON ns.oid = tbl.relnamespace + LEFT JOIN pg_stats s ON s.schemaname=ns.nspname + AND s.tablename = tbl.relname + AND s.attname=att.attname, + ( + SELECT + (SELECT current_setting('block_size')::numeric) AS bs, + CASE WHEN SUBSTRING(SPLIT_PART(v, ' ', 2) FROM '#\"[0-9]+.[0-9]+#\"%' for '#') + IN ('8.0','8.1','8.2') THEN 27 ELSE 23 END AS hdr, + CASE WHEN v ~ 'mingw32' OR v ~ '64-bit' THEN 8 ELSE 4 END AS ma + FROM (SELECT version() AS v) AS foo + ) AS constants + WHERE att.attnum > 0 AND tbl.relkind='r' + GROUP BY 1,2,3,4,5 + ) AS foo + ) AS rs + ON cc.relname = rs.relname AND nn.nspname = rs.nspname + LEFT JOIN pg_index i ON indrelid = cc.oid + LEFT JOIN pg_class c2 ON c2.oid = i.indexrelid + ) AS sml + WHERE sml.relpages - otta > 0 OR ipages - iotta > 10 ORDER BY totalwastedbytes DESC LIMIT 10;" + fi + + local FIRST= + local QUERY="\pset footer off + \echo '<<>>${INSTANCE_SECTION}' + \echo '[databases_start]' + $ECHO_DATABASES + \echo '[databases_end]'" + + for db in $DATABASES ; do + QUERY="$QUERY + \c $db + $BLOAT_QUERY + " + if [ -z $FIRST ] ; then + FIRST=false + QUERY="$QUERY + \pset tuples_only on + " + fi + done + echo "$QUERY" | sudo -u "$DBUSER" $export_PGPASSFILE $psql -X ${EXTRA_ARGS} -q -A -F';' +} + + +#. +# .--main----------------------------------------------------------------. +# | _ | +# | _ __ ___ __ _(_)_ __ | +# | | '_ ` _ \ / _` | | '_ \ | +# | | | | | | | (_| | | | | | | +# | |_| |_| |_|\__,_|_|_| |_| | +# | | +# '----------------------------------------------------------------------' + + +### postgres.cfg ## +# DBUSER=OS_USER_NAME +# INSTANCE=/home/postgres/db1.env:USER_NAME:/PATH/TO/.pgpass +# INSTANCE=/home/postgres/db2.env:USER_NAME:/PATH/TO/.pgpass + +# TODO @dba USERNAME in .pgpass ? +# INSTANCE=/home/postgres/db2.env:/PATH/TO/.pgpass + + +function postgres_main() { + if [ -z "$DBUSER" ] || [ -z "$PGDATABASE" ] ; then + exit 0 + fi + + EXTRA_ARGS="" + if [ ! -z "$PGUSER" ]; then + EXTRA_ARGS=$EXTRA_ARGS" -U $PGUSER" + fi + if [ ! -z "$PGPORT" ]; then + EXTRA_ARGS=$EXTRA_ARGS" -p $PGPORT" + fi + + if [ ! -z "$PGPASSFILE" ]; then + export_PGPASSFILE="export PGPASSFILE=$PGPASSFILE; " + fi + + DATABASES="$(echo "SELECT datname FROM pg_database WHERE datistemplate = false;" |\ + sudo -u "$DBUSER" $export_PGPASSFILE $psql -X -d $PGDATABASE ${EXTRA_ARGS} -t -A -F';')" + ECHO_DATABASES="$(echo "$DATABASES" | sed 's/^/\\echo /')" + + POSTGRES_VERSION=$(sudo -u "$DBUSER" $psql -X -V -d $PGDATABASE ${EXTRA_ARGS} | egrep -o '[0-9]{1,}\.[0-9]{1,}') + + postgres_sessions + postgres_simple_queries + #postgres_stats + postgres_version + postgres_bloat +} + + +MK_CONFFILE=$MK_CONFDIR/postgres.cfg +if [ -e "$MK_CONFFILE" ]; then + + postgres_instances + + DBUSER=$(grep DBUSER "$MK_CONFFILE" | sed 's/.*=//g') + cat "$MK_CONFFILE" | while read line + do + case $line in + INSTANCE*) + instance=$line + ;; + *) + instance= + ;; + esac + + if [ ! -z "$instance" ]; then + instance_path=$(echo "$instance" | sed 's/.*=\(.*\):.*:.*$/\1/g') + instance_name=$(echo "$instance_path" | sed -e 's/.*\/\(.*\)/\1/g' -e 's/\.env$//g') + if [ ! -z "$instance_name" ]; then + INSTANCE_SECTION="\n[[[$instance_name]]]" + else + INSTANCE_SECTION="" + fi + + psql="/$DBUSER/$(grep "^export PGVERSION=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g')/bin/psql" + + PGUSER=$(echo "$instance" | sed 's/.*=.*:\(.*\):.*$/\1/g') + PGPASSFILE="$(echo "$instance" | sed 's/.*=.*:.*:\(.*\)$/\1/g')" + PGDATABASE=$(grep "^export PGDATABASE=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g') + PGPORT=$(grep "^export PGPORT=" "$instance_path" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g') + + # Fallback + if [ ! -f "$psql" ]; then + psql="$(cat $instance_path | grep "^export PGHOME=" | + sed -e 's/.*=//g' -e 's/\s*#.*$//g')/psql" + fi + + postgres_main + + fi + done + +else + + if id pgsql >/dev/null 2>&1; then + DBUSER=pgsql + elif id postgres >/dev/null 2>&1; then + DBUSER=postgres + else + exit 0 + fi + INSTANCE_SECTION="" + + postgres_instances "$DBUSER" + + psql="psql" + PGDATABASE=postgres + postgres_main + +fi diff --git a/roles/pvc/files/patroni/powerdns-schema.sql b/roles/pvc/files/patroni/powerdns-schema.sql new file mode 100644 index 0000000..e6c6b7c --- /dev/null +++ b/roles/pvc/files/patroni/powerdns-schema.sql @@ -0,0 +1,94 @@ +CREATE TABLE domains ( + id SERIAL PRIMARY KEY, + name VARCHAR(255) NOT NULL, + master VARCHAR(128) DEFAULT NULL, + last_check INT DEFAULT NULL, + type VARCHAR(6) NOT NULL, + notified_serial INT DEFAULT NULL, + account VARCHAR(40) DEFAULT NULL, + CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT))) +); + +CREATE UNIQUE INDEX name_index ON domains(name); + + +CREATE TABLE records ( + id BIGSERIAL PRIMARY KEY, + domain_id INT DEFAULT NULL, + name VARCHAR(255) DEFAULT NULL, + type VARCHAR(10) DEFAULT NULL, + content VARCHAR(65535) DEFAULT NULL, + ttl INT DEFAULT NULL, + prio INT DEFAULT NULL, + disabled BOOL DEFAULT 'f', + ordername VARCHAR(255), + auth BOOL DEFAULT 't', + CONSTRAINT domain_exists + FOREIGN KEY(domain_id) REFERENCES domains(id) + ON DELETE CASCADE, + CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT))) +); + +CREATE INDEX rec_name_index ON records(name); +CREATE INDEX nametype_index ON records(name,type); +CREATE INDEX domain_id ON records(domain_id); +CREATE INDEX recordorder ON records (domain_id, ordername text_pattern_ops); + + +CREATE TABLE supermasters ( + ip INET NOT NULL, + nameserver VARCHAR(255) NOT NULL, + account VARCHAR(40) NOT NULL, + PRIMARY KEY(ip, nameserver) +); + + +CREATE TABLE comments ( + id SERIAL PRIMARY KEY, + domain_id INT NOT NULL, + name VARCHAR(255) NOT NULL, + type VARCHAR(10) NOT NULL, + modified_at INT NOT NULL, + account VARCHAR(40) DEFAULT NULL, + comment VARCHAR(65535) NOT NULL, + CONSTRAINT domain_exists + FOREIGN KEY(domain_id) REFERENCES domains(id) + ON DELETE CASCADE, + CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT))) +); + +CREATE INDEX comments_domain_id_idx ON comments (domain_id); +CREATE INDEX comments_name_type_idx ON comments (name, type); +CREATE INDEX comments_order_idx ON comments (domain_id, modified_at); + + +CREATE TABLE domainmetadata ( + id SERIAL PRIMARY KEY, + domain_id INT REFERENCES domains(id) ON DELETE CASCADE, + kind VARCHAR(32), + content TEXT +); + +CREATE INDEX domainidmetaindex ON domainmetadata(domain_id); + + +CREATE TABLE cryptokeys ( + id SERIAL PRIMARY KEY, + domain_id INT REFERENCES domains(id) ON DELETE CASCADE, + flags INT NOT NULL, + active BOOL, + content TEXT +); + +CREATE INDEX domainidindex ON cryptokeys(domain_id); + + +CREATE TABLE tsigkeys ( + id SERIAL PRIMARY KEY, + name VARCHAR(255), + algorithm VARCHAR(50), + secret VARCHAR(255), + CONSTRAINT c_lowercase_name CHECK (((name)::TEXT = LOWER((name)::TEXT))) +); + +CREATE UNIQUE INDEX namealgoindex ON tsigkeys(name, algorithm); diff --git a/roles/pvc/handlers/main.yml b/roles/pvc/handlers/main.yml new file mode 100644 index 0000000..0a59472 --- /dev/null +++ b/roles/pvc/handlers/main.yml @@ -0,0 +1,25 @@ +--- +- name: restart zookeeper + service: + name: zookeeper + state: restarted + +- name: restart libvirtd + service: + name: libvirtd + state: restarted + +- name: restart frr + service: + name: frr + state: restarted + +- name: restart patroni + service: + name: patroni + state: restarted + +- name: restart pvcd + service: + name: pvcd + state: restarted diff --git a/roles/pvc/tasks/ceph.yml b/roles/pvc/tasks/ceph.yml new file mode 100644 index 0000000..e9d2310 --- /dev/null +++ b/roles/pvc/tasks/ceph.yml @@ -0,0 +1,48 @@ +--- +- name: create ceph group + group: + name: ceph + gid: 64046 + state: present + +- name: install packages + apt: + name: + - ceph-osd + - ceph-mds + - ceph-mon + - ceph-mgr + - radosgw + - libjemalloc2 + state: latest + +- name: install sysctl tweaks + template: + src: ceph/sysctl.conf.j2 + dest: /etc/sysctl.d/pvc-ceph.conf + +- name: activate sysctl tweaks + command: sysctl -p /etc/sysctl.d/pvc-ceph.conf + +- name: install user limits overrides + template: + src: ceph/limits.conf.j2 + dest: /etc/security/limits.d/99-pvc-ceph.conf + +- name: install ceph default config + template: + src: ceph/default.conf.j2 + dest: /etc/default/ceph + +- name: create ceph configuration directory + file: + dest: /etc/ceph + state: directory + +- name: install ceph cluster configurations + template: + src: ceph/{{ item }}.j2 + dest: /etc/ceph/{{ item }} + with_items: + - ceph.conf + - ceph.client.admin.keyring diff --git a/roles/pvc/tasks/frr.yml b/roles/pvc/tasks/frr.yml new file mode 100644 index 0000000..9213237 --- /dev/null +++ b/roles/pvc/tasks/frr.yml @@ -0,0 +1,23 @@ +--- +- name: install frr packages + apt: + name: + - frr + state: latest + +- name: install frr configuration + template: + src: frr/{{ item }}.j2 + dest: /etc/frr/{{ item }} + with_items: + - daemons + - frr.conf + notify: restart frr + ignore_errors: true + +- name: disable services + service: + name: "{{ item }}" + enabled: no + with_items: + - frr diff --git a/roles/pvc/tasks/libvirt.yml b/roles/pvc/tasks/libvirt.yml new file mode 100644 index 0000000..8b349b0 --- /dev/null +++ b/roles/pvc/tasks/libvirt.yml @@ -0,0 +1,43 @@ +--- +- name: install libvirt packages + apt: + name: + - libvirt-daemon-system + - qemu-kvm + - qemu-utils + - qemu-block-extra + - vhostmd + - ceph-common + - libjemalloc2 + state: latest + +- name: install libvirt configuration + template: + src: libvirt/{{ item }}.j2 + dest: /etc/libvirt/{{ item }} + with_items: + - libvirtd.conf + - ceph-secret.xml + notify: restart libvirtd + +- name: define ceph secret + command: virsh secret-define /etc/libvirt/ceph-secret.xml + ignore_errors: true + +- name: set ceph secret value + command: virsh secret-set-value --secret {{ ceph_storage_secret_uuid }} --base64 {{ ceph_storage_secret_key }} + ignore_errors: true + +- name: configure libvirt for listening + replace: + dest: /etc/default/libvirtd + regexp: '#libvirtd_opts=""' + replace: 'libvirtd_opts="--listen"' + notify: restart libvirtd + +- name: disable services + service: + name: "{{ item }}" + enabled: no + with_items: + - libvirtd diff --git a/roles/pvc/tasks/main.yml b/roles/pvc/tasks/main.yml new file mode 100644 index 0000000..85aba17 --- /dev/null +++ b/roles/pvc/tasks/main.yml @@ -0,0 +1,26 @@ +--- +- name: add module blacklist + template: + src: system/blacklist.j2 + dest: /etc/modprobe.d/blacklist.conf + +- include_tasks: ceph.yml + tags: pvc-ceph + +- include_tasks: zookeeper.yml + tags: pvc-zookeeper + +- include_tasks: libvirt.yml + tags: pvc-libvirt + +- include_tasks: frr.yml + tags: pvc-frr + +- include_tasks: patroni.yml + tags: pvc-patroni + +- include_tasks: pvc.yml + tags: pvc-pvc + run_once: true + delegate_to: "{{ item }}" + with_items: "{{ play_hosts }}" diff --git a/roles/pvc/tasks/patroni.yml b/roles/pvc/tasks/patroni.yml new file mode 100644 index 0000000..571bec6 --- /dev/null +++ b/roles/pvc/tasks/patroni.yml @@ -0,0 +1,128 @@ +--- +- name: install patroni packages via apt + apt: + name: + - python-psycopg2 + - python3-kazoo + - patroni + - postgresql-11 + state: latest + update-cache: yes + +- name: first run check + shell: "echo 'bootstrapped' > /etc/postgresql/pvc" + register: newinstance + args: + creates: /etc/postgresql/pvc + +- name: stop and disable postgresql + service: + name: "{{ item }}" + state: stopped + enabled: no + with_items: + - postgresql + - postgresql@11-main + when: newinstance.changed + +- name: remove obsolete database directories + file: + dest: "{{ item }}" + state: absent + with_items: + - /etc/postgresql/11 + - /var/lib/postgresql/11 + when: newinstance.changed + +- name: create patroni database directory + file: + dest: /var/lib/postgresql/patroni/pvc + state: directory + owner: postgres + mode: 0700 + when: newinstance.changed + +- name: install postgresql customization configuration file + template: + src: patroni/postgresql.pvc.conf.j2 + dest: /etc/postgresql/postgresql.pvc.conf + owner: postgres + group: sudo + mode: 0640 + notify: restart patroni + +- name: install patroni configuration file + template: + src: patroni/patroni.yml.j2 + dest: /etc/patroni/config.yml + owner: postgres + group: postgres + mode: 0640 + notify: restart patroni + +- name: install check_mk agent check + copy: + src: patroni/postgres + dest: /usr/lib/check_mk_agent/plugins/postgres + mode: 0755 + +- name: ensure patroni services are enabled and started + service: + name: "{{ item }}.service" + state: started + enabled: yes + with_items: + - patroni + +- name: install initial schema files + copy: + src: "{{ item.src }}" + dest: "{{ item.dest }}" + owner: postgres + group: sudo + mode: 0640 + with_items: + - { src: "patroni/powerdns-schema.sql", dest: "/etc/postgresql/powerdns-schema.sql" } + +- name: set up PVC DNS database on first host + block: + - name: wait 15s for cluster to initialize + pause: + seconds: 15 + + - name: create user for role + postgresql_user: + name: "{{ pvc_dns_database_user }}" + password: "{{ pvc_dns_database_password }}" + state: present + login_host: /run/postgresql + + - name: create database for role + postgresql_db: + name: "{{ pvc_dns_database_name }}" + owner: "{{ pvc_dns_database_user }}" + encoding: utf8 + state: present + login_host: /run/postgresql + + - name: set user privs for role + postgresql_user: + name: "{{ pvc_dns_database_user }}" + db: "{{ pvc_dns_database_name }}" + priv: ALL + login_host: /run/postgresql + + - name: create extensions + postgresql_ext: + name: "{{ item }}" + db: "{{ pvc_dns_database_name }}" + login_host: /run/postgresql + with_items: "{{ extensions }}" + when: extensions is defined + + - name: import dns database schema + command: "psql -U {{ pvc_dns_database_user }} -f /etc/postgresql/powerdns-schema.sql {{ pvc_dns_database_name }}" + + become: yes + become_user: postgres + when: newinstance.changed and ansible_local.host_id == '1' diff --git a/roles/pvc/tasks/pvc.yml b/roles/pvc/tasks/pvc.yml new file mode 100644 index 0000000..26c0585 --- /dev/null +++ b/roles/pvc/tasks/pvc.yml @@ -0,0 +1,43 @@ +--- +- name: install pvc packages + apt: + name: + - pvc-daemon + - pvc-client-cli + - pvc-client-common + state: latest + +- name: install pvc configuration + template: + src: pvc/{{ item }}.j2 + dest: /etc/pvc/{{ item }} + with_items: + - pvcd.yaml + notify: restart pvcd + +- name: verify if cluster has been started + shell: "/usr/share/zookeeper/bin/zkCli.sh stat /nodes 2>&1 | grep -q 'Node does not exist'" + register: cluster_init + failed_when: no + +- name: bootstrap a fresh cluster + shell: /usr/bin/pvc init + when: cluster_init.rc == 0 and ansible_local.host_id == 1 + +- name: stop and disable unneccessary services + service: + name: "{{ item }}" + state: stopped + enabled: no + with_items: + - pdns.service + +- name: start and enable services + service: + name: "{{ item }}" + state: started + enabled: yes + with_items: + - pvc-flush.service + - pvcd.service + - pvcd.target diff --git a/roles/pvc/tasks/zookeeper.yml b/roles/pvc/tasks/zookeeper.yml new file mode 100644 index 0000000..5ebc0a2 --- /dev/null +++ b/roles/pvc/tasks/zookeeper.yml @@ -0,0 +1,26 @@ +--- +- name: install zookeeper packages + apt: + name: + - zookeeperd + - zookeeper-bin + state: latest + +- name: install zookeeper configuration + template: + src: zookeeper/{{ item }}.j2 + dest: /etc/zookeeper/conf/{{ item }} + with_items: + - configuration.xsl + - environment + - log4j.properties + - myid + - zoo.cfg + notify: restart zookeeper + +- name: disable services + service: + name: "{{ item }}" + enabled: no + with_items: + - zookeeper diff --git a/roles/pvc/templates/ceph/default.conf.j2 b/roles/pvc/templates/ceph/default.conf.j2 new file mode 100644 index 0000000..97d658e --- /dev/null +++ b/roles/pvc/templates/ceph/default.conf.j2 @@ -0,0 +1,4 @@ +# Environment file for ceph daemon systemd unit files. +# {{ ansible_managed }} + +LD_PRELOAD=/usr/lib/x86_64-linux-gnu/libjemalloc.so.1 diff --git a/roles/pvc/templates/ceph/limits.conf.j2 b/roles/pvc/templates/ceph/limits.conf.j2 new file mode 100644 index 0000000..c09a082 --- /dev/null +++ b/roles/pvc/templates/ceph/limits.conf.j2 @@ -0,0 +1,4 @@ +# Limits for ceph processes +# {{ ansible_managed }} +ceph soft nproc unlimited +ceph soft nofile unlimited diff --git a/roles/pvc/templates/ceph/sysctl.conf.j2 b/roles/pvc/templates/ceph/sysctl.conf.j2 new file mode 100644 index 0000000..6c8355f --- /dev/null +++ b/roles/pvc/templates/ceph/sysctl.conf.j2 @@ -0,0 +1,4 @@ +# sysctl: tweak settings for Ceph +# {{ ansible_managed }} + +vm.swappiness = 0 diff --git a/roles/pvc/templates/frr/daemons.j2 b/roles/pvc/templates/frr/daemons.j2 new file mode 100644 index 0000000..b605527 --- /dev/null +++ b/roles/pvc/templates/frr/daemons.j2 @@ -0,0 +1,16 @@ +# frr daemon status +# {{ ansible_managed }} +zebra=yes +bgpd=yes +ospfd=no +ospf6d=no +ripd=no +ripngd=no +isisd=no +pimd=no +ldpd=no +nhrpd=no +eigrpd=no +babeld=no +sharpd=no +pbrd=no diff --git a/roles/pvc/templates/frr/frr.conf.j2 b/roles/pvc/templates/frr/frr.conf.j2 new file mode 100644 index 0000000..7399d8d --- /dev/null +++ b/roles/pvc/templates/frr/frr.conf.j2 @@ -0,0 +1,53 @@ +! frr main configuration +! {{ ansible_managed }} +! +frr version 4.0 +frr defaults traditional +hostname cloud-14 +no ipv6 forwarding +username cumulus nopassword +! +service integrated-vtysh-config +! +log syslog informational +! +line vty +! +! BGP EVPN mesh configuration +! +router bgp {{ pvc_asn }} + bgp router-id {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.router_id }}{% endfor %} + + no bgp default ipv4-unicast + ! BGP sessions with route reflectors + neighbor fabric peer-group + neighbor fabric remote-as {{ pvc_asn }} + neighbor fabric capability extended-nexthop +{% for node in pvc_nodes if node.is_coordinator %} + neighbor {{ node.router_id }} peer-group fabric +{% endfor %} + ! BGP sessions with upstream routers + neighbor upstream peer-group + neighbor upstream remote-as {{ pvc_asn }} + neighbor upstream capability extended-nexthop +{% for router in pvc_routers %} + neighbor {{ router }} peer-group upstream +{% endfor %} + ! + address-family l2vpn evpn + neighbor fabric activate + advertise-all-vni + exit-address-family + address-family ipv4 unicast + neighbor fabric activate + neighbor upstream activate + redistribute connected + exit-address-family + address-family ipv6 unicast + neighbor fabric activate + neighbor upstream activate + redistribute connected + exit-address-family + ! + exit +! diff --git a/roles/pvc/templates/libvirt/ceph-secret.xml.j2 b/roles/pvc/templates/libvirt/ceph-secret.xml.j2 new file mode 100644 index 0000000..81afc89 --- /dev/null +++ b/roles/pvc/templates/libvirt/ceph-secret.xml.j2 @@ -0,0 +1,6 @@ + + {{ ceph_storage_secret_uuid }} + + client.libvirt secret + + diff --git a/roles/pvc/templates/libvirt/libvirtd.conf.j2 b/roles/pvc/templates/libvirt/libvirtd.conf.j2 new file mode 100644 index 0000000..dcf1cf3 --- /dev/null +++ b/roles/pvc/templates/libvirt/libvirtd.conf.j2 @@ -0,0 +1,7 @@ +# PVC libvirt daemon configuration file +# {{ ansible_managed }} + +listen_tls = 0 +listen_tcp = 1 +tcp_port = "16509" +auth_tcp = "none" diff --git a/roles/pvc/templates/patroni/patroni.yml.j2 b/roles/pvc/templates/patroni/patroni.yml.j2 new file mode 100644 index 0000000..6ce6fca --- /dev/null +++ b/roles/pvc/templates/patroni/patroni.yml.j2 @@ -0,0 +1,63 @@ +scope: pvcdns +namespace: /patroni +name: {{ ansible_hostname }} + +restapi: + listen: '0.0.0.0:8008' + connect_address: '{{ ansible_fqdn }}:8008' + +zookeeper: + hosts: [ {% for host in groups[ansible_local.host_group] %}'{{ host }}.{{ ansible_domain }}:2181',{% endfor %} ] + +bootstrap: + dcs: + ttl: 30 + loop_wait: 10 + retry_timeout: 10 + maximum_lag_on_failover: 1048576 + postgresql: + use_pg_rewind: true + + initdb: + - encoding: UTF8 + - data-checksums + + pg_hba: + - local all all peer + - host replication replicator 127.0.0.1/32 trust +{% for host in groups[ansible_local.host_group] %} + - host replication replicator {{ host }}.{{ ansible_domain }} trust +{% endfor %} + - host all all 0.0.0.0/0 md5 + + users: + admin: + password: admin + options: + - createrole + - createdb + +postgresql: + listen: '0.0.0.0:5432' + connect_address: '{{ ansible_fqdn }}:5432' + log_destination: 'stderr' + log_min_messages: INFO + custom_conf: /etc/postgresql/postgresql.pvc.conf + bin_dir: /usr/lib/postgresql/11/bin + data_dir: /var/lib/postgresql/patroni/pvc + pgpass: /tmp/pgpass + authentication: + replication: + username: '{{ pvc_replication_database_user }}' + password: '{{ pvc_replication_database_password }}' + superuser: + username: '{{ pvc_superuser_database_user }}' + password: '{{ pvc_superuser_database_password }}' + parameters: + unix_socket_directories: '/run/postgresql' + +tags: + nofailover: false + noloadbalance: false + clonefrom: false + nosync: false diff --git a/roles/pvc/templates/patroni/postgresql.pvc.conf.j2 b/roles/pvc/templates/patroni/postgresql.pvc.conf.j2 new file mode 100644 index 0000000..ab0d4d9 --- /dev/null +++ b/roles/pvc/templates/patroni/postgresql.pvc.conf.j2 @@ -0,0 +1,21 @@ +# Additional PostgreSQL tuning parameters for PVC Patroni instance +# {{ ansible_managed }} + +max_connections = 100 +shared_buffers = 64MB +effective_cache_size = 256MB +dynamic_shared_memory_type = posix + +random_page_cost = 1 +seq_page_cost = 1 + +log_timezone = 'localtime' +datestyle = 'iso, dmy' +timezone = 'localtime' + +lc_messages = 'en_CA.UTF-8' +lc_monetary = 'en_CA.UTF-8' +lc_numeric = 'en_CA.UTF-8' +lc_time = 'en_CA.UTF-8' + +default_text_search_config = 'pg_catalog.english' diff --git a/roles/pvc/templates/pvc/pvcd.yaml.j2 b/roles/pvc/templates/pvc/pvcd.yaml.j2 new file mode 100644 index 0000000..515e923 --- /dev/null +++ b/roles/pvc/templates/pvc/pvcd.yaml.j2 @@ -0,0 +1,75 @@ +--- +# pvcd cluster configuration +# {{ ansible_managed }} +pvc: + node: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.hostname }}{% endfor %} + + functions: + enable_hypervisor: True + enable_networking: True + enable_storage: False + cluster: + coordinators: +{% for node in pvc_nodes if node.is_coordinator %} + - {{ node.hostname }} +{% endfor %} + networks: + cluster: + domain: {{ pvc_cluster_domain }} + network: {{ pvc_cluster_subnet }} + floating_ip: {{ pvc_cluster_floatingip }} + storage: + domain: {{ pvc_storage_domain }} + network: {{ pvc_storage_subnet }} + floating_ip: {{ pvc_storage_floatingip }} + upstream: + domain: {{ pvc_upstream_domain }} + network: {{ pvc_upstream_subnet }} + floating_ip: {{ pvc_upstream_floatingip }} + gateway: {{ pvc_upstream_gatewayip }} + coordinator: + dns: + database: + host: localhost + port: 5432 + name: pvcdns + user: pvcdns + pass: PVCdnsPassw0rd + system: + fencing: + intervals: + keepalive_interval: 5 + fence_intervals: 6 + suicide_intervals: 0 + actions: + successful_fence: migrate + failed_fence: None + ipmi: + host: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.ipmi_host }}{% endfor %} + + user: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.ipmi_user }}{% endfor %} + + pass: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.ipmi_password }}{% endfor %} + + migration: + target_selector: mem + configuration: + directories: + dynamic_directory: "/run/pvc" + log_directory: "/var/log/pvc" + console_log_directory: "/var/log/libvirt" + logging: + file_logging: True + stdout_logging: True + console_log_lines: 1000 + networking: + devices: + cluster: {{ pvc_cluster_device }} + storage: {{ pvc_storage_device }} + upstream: {{ pvc_upstream_device }} + addresses: + cluster: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.cluster_ip }}{% endfor %} + + storage: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.storage_ip }}{% endfor %} + + upstream: {% for node in pvc_nodes if node.hostname == ansible_hostname %}{{ node.upstream_ip }}{% endfor %} diff --git a/roles/pvc/templates/system/blacklist.j2 b/roles/pvc/templates/system/blacklist.j2 new file mode 100644 index 0000000..10d43a1 --- /dev/null +++ b/roles/pvc/templates/system/blacklist.j2 @@ -0,0 +1,11 @@ +# modprobe blacklist +# {{ ansible_managed }} + +# Blacklist GPU drivers +blacklist nouveau +blacklist radeon +blacklist amdgpu +blacklist snd_hda_intel + +# Blacklist HP Proliant management +blacklist hpwdt diff --git a/roles/pvc/templates/zookeeper/configuration.xsl.j2 b/roles/pvc/templates/zookeeper/configuration.xsl.j2 new file mode 100644 index 0000000..0fa2f82 --- /dev/null +++ b/roles/pvc/templates/zookeeper/configuration.xsl.j2 @@ -0,0 +1,25 @@ + + + + + + + + + + + + + + + + + + + + +
namevaluedescription
+ + +
+
diff --git a/roles/pvc/templates/zookeeper/environment.j2 b/roles/pvc/templates/zookeeper/environment.j2 new file mode 100644 index 0000000..84bc73f --- /dev/null +++ b/roles/pvc/templates/zookeeper/environment.j2 @@ -0,0 +1,10 @@ +# {{ ansible_managed }} +ZOOMAIN=org.apache.zookeeper.server.quorum.QuorumPeerMain +ZOOCFGDIR=/etc/zookeeper/conf +ZOOCFG=/etc/zookeeper/conf/zoo.cfg +ZOO_LOG_DIR=/var/log/zookeeper +ZOO_LOG4J_PROP=INFO,ROLLINGFILE +JMXLOCALONLY=false +JAVA_OPTS="" +JAVA=/usr/bin/java +CLASSPATH="/etc/zookeeper/conf:/usr/share/java/jline.jar:/usr/share/java/log4j-1.2.jar:/usr/share/java/xercesImpl.jar:/usr/share/java/xmlParserAPIs.jar:/usr/share/java/netty.jar:/usr/share/java/slf4j-api.jar:/usr/share/java/slf4j-log4j12.jar:/usr/share/java/zookeeper.jar" diff --git a/roles/pvc/templates/zookeeper/log4j.properties.j2 b/roles/pvc/templates/zookeeper/log4j.properties.j2 new file mode 100644 index 0000000..91f7a22 --- /dev/null +++ b/roles/pvc/templates/zookeeper/log4j.properties.j2 @@ -0,0 +1,50 @@ +# ZooKeeper Logging Configuration +# {{ ansible_managed }} + +# Format is " (, )+ + +log4j.rootLogger=${zookeeper.root.logger} + +# Example: console appender only +# log4j.rootLogger=INFO, CONSOLE + +# Example with rolling log file +#log4j.rootLogger=DEBUG, CONSOLE, ROLLINGFILE + +# Example with rolling log file and tracing +#log4j.rootLogger=TRACE, CONSOLE, ROLLINGFILE, TRACEFILE + +# +# Log INFO level and above messages to the console +# +log4j.appender.CONSOLE=org.apache.log4j.ConsoleAppender +log4j.appender.CONSOLE.Threshold=INFO +log4j.appender.CONSOLE.layout=org.apache.log4j.PatternLayout +log4j.appender.CONSOLE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n + +# +# Add ROLLINGFILE to rootLogger to get log file output +# Log DEBUG level and above messages to a log file +log4j.appender.ROLLINGFILE=org.apache.log4j.RollingFileAppender +log4j.appender.ROLLINGFILE.Threshold=DEBUG +log4j.appender.ROLLINGFILE.File=${zookeeper.log.dir}/zookeeper.log + +# Max log file size of 10MB +log4j.appender.ROLLINGFILE.MaxFileSize=10MB +# uncomment the next line to limit number of backup files +#log4j.appender.ROLLINGFILE.MaxBackupIndex=10 + +log4j.appender.ROLLINGFILE.layout=org.apache.log4j.PatternLayout +log4j.appender.ROLLINGFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L] - %m%n + + +# +# Add TRACEFILE to rootLogger to get log file output +# Log DEBUG level and above messages to a log file +log4j.appender.TRACEFILE=org.apache.log4j.FileAppender +log4j.appender.TRACEFILE.Threshold=TRACE +log4j.appender.TRACEFILE.File=${zookeeper.log.dir}/zookeeper_trace.log + +log4j.appender.TRACEFILE.layout=org.apache.log4j.PatternLayout +### Notice we are including log4j's NDC here (%x) +log4j.appender.TRACEFILE.layout.ConversionPattern=%d{ISO8601} - %-5p [%t:%C{1}@%L][%x] - %m%n diff --git a/roles/pvc/templates/zookeeper/myid.j2 b/roles/pvc/templates/zookeeper/myid.j2 new file mode 100644 index 0000000..687b441 --- /dev/null +++ b/roles/pvc/templates/zookeeper/myid.j2 @@ -0,0 +1 @@ +{{ ansible_local.host_id }} diff --git a/roles/pvc/templates/zookeeper/zoo.cfg.j2 b/roles/pvc/templates/zookeeper/zoo.cfg.j2 new file mode 100644 index 0000000..1326781 --- /dev/null +++ b/roles/pvc/templates/zookeeper/zoo.cfg.j2 @@ -0,0 +1,13 @@ +# PVC Zookeeper configuration +# {{ ansible_managed }} + +tickTime=1000 +initLimit=10 +syncLimit=5 +dataDir=/var/lib/zookeeper + +clientPort=2181 + +{% for node in pvc_nodes if node.is_coordinator %} +server.{{ node.node_id }}={{ node.hostname }}:2888:3888 +{% endfor %}