Initial commit of PVC Ansible role

This commit is contained in:
Joshua Boniface 2023-09-01 15:42:19 -04:00
commit 6dfaf433dc
92 changed files with 4709 additions and 0 deletions

14
README.md Normal file
View File

@ -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.

View File

@ -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"

9
hosts Normal file
View File

@ -0,0 +1,9 @@
# PVC hosts file
[sites:children]
default
[default]
pvchv1
pvchv2
pvchv3

10
pvc.yml Normal file
View File

@ -0,0 +1,10 @@
---
- hosts: all
remote_user: deploy
become: yes
roles:
- name: base
tags: base
- name: pvc
tags: pvc

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,11 @@
#!/bin/bash
# Backup check for Check_MK
# Installed by BLSE 2.0 ansible
SHARELIST=( $( cat /var/backups/shares ) )
echo "<<<backup>>>"
for SHARE in ${SHARELIST[@]}; do
echo "${SHARE} $( cat ${SHARE}/.backup )"
done

View File

@ -0,0 +1,15 @@
echo '<<<cephfsmounts>>>'
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

View File

@ -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 "<<<dpkg>>>"
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[*]}"

View File

@ -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 '<<<entropy_avail>>>'
echo -n "entropy_avail "
cat /proc/sys/kernel/random/entropy_avail
echo -n "poolsize "
cat /proc/sys/kernel/random/poolsize
fi

View File

@ -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 "<<<freshness>>>"
if [ "$FRESHNESS" ]; then
echo "Applications needing restart: $FRESHNESS"
exit $WARNING
else
echo "No applications needing restart"
exit $OK
fi

View File

@ -0,0 +1,14 @@
#!/bin/bash
OK=0
WARNING=1
echo "<<<kernelversion>>>"
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

View File

@ -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 "<<<file_ownership>>>"
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

Binary file not shown.

View File

@ -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

719
roles/base/tasks/main.yml Normal file
View File

@ -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

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Ansible fact - dhcp_status
# {{ ansible_managed }}
DHCP_STATUS="$( grep -o 'dhcp' /etc/network/interfaces | uniq )"
echo "\"${DHCP_STATUS}\""

View File

@ -0,0 +1,5 @@
#!/bin/bash
# Ansible fact - host_group
# {{ ansible_managed }}
HOST_GROUP="$( hostname -s | sed 's/[0-9]*//g' )"
echo "\"${HOST_GROUP}\""

View File

@ -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}\""

View File

@ -0,0 +1,5 @@
# apt configuration: disable recommends
# {{ ansible_managed }}
APT::Install-Recommends "0";
APT::Install-Suggests "0";

View File

@ -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 %}

View File

@ -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 %}

View File

@ -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

View File

@ -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}$(</etc/DIR_COLORS)"
[[ -z ${match_lhs} ]] \
&& type -P dircolors >/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

View File

@ -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-

View File

@ -0,0 +1,4 @@
# cron file for motd
# {{ ansible_managed }}
* * * * * root /bin/sh /usr/local/sbin/update-motd.sh &>/dev/null

View File

@ -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 )
#

View File

@ -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

View File

@ -0,0 +1,6 @@
#!/bin/sh
# Disasble resolv.conf generation from DHCP
# {{ ansible_managed }}
make_resolv_conf() {
:
}

View File

@ -0,0 +1,15 @@
# fail2ban action - route
[Definition]
actionban = ip route add <blocktype> <ip>
actionunban = ip route del <blocktype> <ip>
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

View File

@ -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 <HOST>( via \S+)?\s*$
^%(__prefix_line)s(?:error: PAM: )?User not known to the underlying authentication module for .* from <HOST>\s*$
^%(__prefix_line)sFailed \S+ for (?P<cond_inv>invalid user )?(?P<user>(?P<cond_user>\S+)|(?(cond_inv)(?:(?! from ).)*?|[^:]+)) from <HOST>(?: port \d+)?(?: ssh\d*)?(?(cond_user):|(?:(?:(?! from ).)*)$)
^%(__prefix_line)sROOT LOGIN REFUSED.* FROM <HOST>\s*$
^%(__prefix_line)s[iI](?:llegal|nvalid) user .*? from <HOST>(?: port \d+)?\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not listed in AllowUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because listed in DenyUsers\s*$
^%(__prefix_line)sUser .+ from <HOST> not allowed because not in any group\s*$
^%(__prefix_line)srefused connect from \S+ \(<HOST>\)\s*$
^%(__prefix_line)s(?:error: )?Received disconnect from <HOST>: 3: .*: Auth fail(?: \[preauth\])?$
^%(__prefix_line)sUser .+ from <HOST> not allowed because a group is listed in DenyGroups\s*$
^%(__prefix_line)sUser .+ from <HOST> 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<SKIPLINES>(?P=__prefix)(?:error: )?Received disconnect from <HOST>: 11: .+ \[preauth\]$
^(?P<__prefix>%(__prefix_line)s)Disconnecting: Too many authentication failures for .+? \[preauth\]<SKIPLINES>(?P=__prefix)(?:error: )?Connection closed by <HOST> \[preauth\]$
^(?P<__prefix>%(__prefix_line)s)Connection from <HOST> port \d+(?: on \S+ port \d+)?<SKIPLINES>(?P=__prefix)Disconnecting: Too many authentication failures for .+? \[preauth\]$
^%(__prefix_line)s(error: )?maximum authentication attempts exceeded for .* from <HOST>(?: 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=<HOST>\s.*$
^%(__prefix_line)sUnable to negotiate with <HOST> .*$
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 <HOST>..." failregex uses non-greedy catch-all because
# it is coming before use of <HOST> 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

View File

@ -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 "<HOST>" can
# be used for standard IP/hostname matching and is only an alias for
# (?:::f{4,6}:)?(?P<host>[\w\-.^_]+)
# Values: TEXT
#
failregex = ^%(__prefix_line)sUnable to negotiate with <HOST> .*$
# 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 .*$

View File

@ -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

View File

@ -0,0 +1,6 @@
# Local system hosts file
# {{ ansible_managed }}
::1 localhost ip6-localhost ip6-loopback
ff02::1 ip6-allmodes
ff02::2 ip6-allrouters

View File

@ -0,0 +1,4 @@
# Locales configuration file
# {{ ansible_managed }}
en_CA.UTF-8 UTF-8

View File

@ -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
}

View File

@ -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
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,5 @@
# DNS resolver configuration
# {{ ansible_managed }}
options timeout:1 attempts:3 rotate
nameserver 1.1.1.1

View File

@ -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

View File

@ -0,0 +1,3 @@
# SSH remote allowed hosts
# {{ ansible_managed }}

View File

@ -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

View File

@ -0,0 +1,3 @@
# SSH remote allowed hosts
# {{ ansible_managed }}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
# Journald configuration
# {{ ansible_managed }}
[Journal]
Storage=persistent

View File

@ -0,0 +1,10 @@
# systemd socket definition file
[Unit]
Description=Check_MK Agent Socket
[Socket]
ListenStream=6556
Accept=true
[Install]
WantedBy=sockets.target

View File

@ -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

View File

@ -0,0 +1,5 @@
set showcmd
set number
set cursorline
syntax on
set mouse=

View File

@ -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[@]}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
#

View File

@ -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

View File

@ -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

View File

@ -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

13
roles/pvc/README.md Normal file
View File

@ -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.

View File

@ -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: ""

View File

@ -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 "<<<mysql_ping>>>"
echo $INSTANCE_NAME
mysqladmin --defaults-extra-file=/root/.my.cnf $1 ping 2>&1
if [ $? -eq 0 ]; then
echo "<<<mysql>>>"
echo $INSTANCE_NAME
mysql --defaults-extra-file=/root/.my.cnf $1 -sN \
-e "show global status ; show global variables ;"
echo "<<<mysql_capacity>>>"
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 "<<<mysql_slave>>>"
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_version>>>"
#mysql -V
echo "<<<mysql_port>>>"
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_instances>>>"
#mysql --defaults-extra-file=/root/.my.cnf $1 -s \
# -e "show INSTANCES"
fi

485
roles/pvc/files/patroni/postgres Executable file
View File

@ -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 '<<<postgres_instances>>>'
# 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 '<<<postgres_sessions>>>${INSTANCE_SECTION}'
SELECT (
SELECT column_name
FROM information_schema.columns
WHERE table_name='pg_stat_activity' AND column_name in ('query', 'current_query')
) = '<IDLE>' as query, count(*)
FROM pg_stat_activity
GROUP BY (query = '<IDLE>');" |\
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 '<IDLE>%') 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 <> '<IDLE>'
GROUP BY 2,3
ORDER BY datname;"
fi
echo "\pset footer off
\echo '<<<postgres_stat_database:sep(59)>>>${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 '<<<postgres_locks:sep(59)>>>${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 '<<<postgres_query_duration:sep(59)>>>${INSTANCE_SECTION}'
\echo '[databases_start]'
$ECHO_DATABASES
\echo '[databases_end]'
$QUERYTIME_QUERY
\echo '<<<postgres_connections:sep(59)>>>${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 '<<<postgres_stats:sep(59)>>>${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 "<<<postgres_version:sep(1)>>>${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 "<<<postgres_conn_time>>>${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 '<<<postgres_bloat:sep(59)>>>${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

View File

@ -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);

View File

@ -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

48
roles/pvc/tasks/ceph.yml Normal file
View File

@ -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

23
roles/pvc/tasks/frr.yml Normal file
View File

@ -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

View File

@ -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

26
roles/pvc/tasks/main.yml Normal file
View File

@ -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 }}"

128
roles/pvc/tasks/patroni.yml Normal file
View File

@ -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'

43
roles/pvc/tasks/pvc.yml Normal file
View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -0,0 +1,4 @@
# Limits for ceph processes
# {{ ansible_managed }}
ceph soft nproc unlimited
ceph soft nofile unlimited

View File

@ -0,0 +1,4 @@
# sysctl: tweak settings for Ceph
# {{ ansible_managed }}
vm.swappiness = 0

View File

@ -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

View File

@ -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
!

View File

@ -0,0 +1,6 @@
<secret ephemeral='no' private='no'>
<uuid>{{ ceph_storage_secret_uuid }}</uuid>
<usage type='ceph'>
<name>client.libvirt secret</name>
</usage>
</secret>

View File

@ -0,0 +1,7 @@
# PVC libvirt daemon configuration file
# {{ ansible_managed }}
listen_tls = 0
listen_tcp = 1
tcp_port = "16509"
auth_tcp = "none"

View File

@ -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

View File

@ -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'

View File

@ -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 %}

View File

@ -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

View File

@ -0,0 +1,25 @@
<!-- {{ ansible_managed }} -->
<?xml version="1.0"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform" version="1.0">
<xsl:output method="html"/>
<xsl:template match="configuration">
<html>
<body>
<table border="1">
<tr>
<td>name</td>
<td>value</td>
<td>description</td>
</tr>
<xsl:for-each select="property">
<tr>
<td><a name="{name}"><xsl:value-of select="name"/></a></td>
<td><xsl:value-of select="value"/></td>
<td><xsl:value-of select="description"/></td>
</tr>
</xsl:for-each>
</table>
</body>
</html>
</xsl:template>
</xsl:stylesheet>

View File

@ -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"

View File

@ -0,0 +1,50 @@
# ZooKeeper Logging Configuration
# {{ ansible_managed }}
# Format is "<default threshold> (, <appender>)+
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

View File

@ -0,0 +1 @@
{{ ansible_local.host_id }}

View File

@ -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 %}