Created
May 21, 2021 07:03
-
-
Save ctheune/09ab1feba33c8d8d87d7c71c0c78fe76 to your computer and use it in GitHub Desktop.
batou example for managing system stuff with a traditional package manager
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
from batou.component import Component | |
from batou.lib.archive import Extract | |
from batou.lib.download import Download | |
from batou.lib.file import File | |
from batou.utils import Address | |
import os.path | |
import socket | |
import xmlrpc.client | |
class System(Component): | |
license = None | |
sensu_ssh_privkey = None | |
sensu_ssh_pubkey = None | |
def configure(self): | |
directory_password = self.host.data.get('directory-password') | |
directory = xmlrpc.client.ServerProxy( | |
f'https://{self.host.name}:{directory_password}@directory.fcio.net/v2/api') | |
self.ssh_keys = {} | |
users = directory.list_users('services') | |
for user in users: | |
if 'services' not in user['permissions']: | |
continue | |
if 'admins' not in user['permissions']['services']: | |
continue | |
self.ssh_keys[user['uid']] = user['ssh_pubkey'] | |
self.ssh_keys['sensu'] = [ self.sensu_ssh_pubkey ] | |
self += Hostname(self.host.name) | |
self += Timezone('UTC') | |
self += CumulusLicense(self.license) | |
self += UserHasGroup('cumulus', | |
group='systemd-journal') | |
# XXX Validating for syntax would be nice. | |
self += File('/etc/sudoers.d/fcio') | |
self += File('/home/cumulus/.ssh/authorized_keys', | |
owner='cumulus') | |
self += ServiceReload('sshd', | |
deps=[File('/etc/ssh/sshd_config')]) | |
mgmt_address = self.host.data.get('mgmt-address').split('/')[0] | |
self += Telegraf(listen_address=Address(mgmt_address, 9126)) | |
self += Exec('apt update', | |
deps=[File('/etc/apt/sources.list')]) | |
self += AptPackage('monitoring-plugins') | |
self += AptPackage('dstat') | |
self += AptPackage('nagios-plugins-contrib') | |
self += File('/usr/local/bin/check_smon', mode=0o755) | |
self += File('/usr/local/bin/check_journal', mode=0o755) | |
self += File('/usr/local/bin/check_units', mode=0o755) | |
self.provide('sensucheck', | |
dict(name='smon', | |
source=self.host.name, | |
host=mgmt_address, | |
command='/usr/local/bin/check_smon')) | |
self.provide('sensucheck', | |
dict(name='load', | |
source=self.host.name, | |
host=mgmt_address, | |
command=f'/usr/lib/nagios/plugins/check_load -w 3,2,1 -c 4,3,2')) | |
self.provide('sensucheck', | |
dict(name='ntp_time', | |
source=self.host.name, | |
host=mgmt_address, | |
command='/usr//lib/nagios/plugins/check_ntp_time ' | |
'-H 0.de.pool.ntp.org -w 2 -c 10')) | |
self.provide('sensucheck', | |
dict(name='disk', | |
source=self.host.name, | |
host=mgmt_address, | |
command='/usr/lib/nagios/plugins/check_disk -w 80 -c 90 /')) | |
self.provide('sensucheck', | |
dict(name='uptime', | |
source=self.host.name, | |
host=mgmt_address, | |
command='/usr/lib/nagios/plugins/check_uptime -w 60 -c 30')) | |
self.provide('sensucheck', | |
dict(name='failed_units', | |
source=self.host.name, | |
host=mgmt_address, | |
command='/usr/local/bin/check_units')) | |
self.provide('sensucheck', | |
dict(name='journal', | |
source=self.host.name, | |
host=mgmt_address, | |
interval=3600, | |
command='/usr/local/bin/check_journal')) | |
# Reachability via MGMT/SRV network | |
self.provide('sensucheck', | |
dict(name='telegraf_prometheus_out', | |
source=self.host.name, | |
remote=False, | |
command=f'check_http -H {mgmt_address} -p 9126 -u /metrics')) | |
self.provide('sensucheck', | |
dict(name='ssh', | |
source=self.host.name, | |
remote=False, | |
command=f'check_ssh {mgmt_address}')) | |
self.provide('sensucheck', | |
dict(name='ping_to_management', | |
source=self.host.name, | |
remote=False, | |
command=f'check_ping {mgmt_address} -w 30,10% -c 100,20%')) | |
class UserHasGroup(Component): | |
namevar = 'user' | |
group = None | |
def verify(self): | |
assert f'({self.group})' in self.cmd(f'id {self.user}')[0] | |
def update(self): | |
self.cmd(f'usermod -a -G {self.group} {self.user}') | |
@property | |
def namevar_for_breadcrumb(self): | |
return f'{self.user}@{self.group}' | |
class AptPackage(Component): | |
namevar = 'package' | |
def verify(self): | |
self.assert_cmd(f'dpkg -S {self.package}') | |
def update(self): | |
self.cmd(f'apt -qy install {self.package}') | |
# XXX duplicated in network/component.py | |
class ServiceReload(Component): | |
namevar = 'service' | |
action = 'reload' | |
deps = () | |
def configure(self): | |
for d in self.deps: | |
# XXX auauauaua | |
d.template_context = self.parent | |
self += d | |
def verify(self): | |
self.assert_no_subcomponent_changes() | |
def update(self): | |
self.cmd(f'systemctl {self.action} {self.service}') | |
class Hostname(Component): | |
namevar = 'hostname' | |
def configure(self): | |
self += File('/etc/hosts') | |
def verify(self): | |
assert socket.gethostname() == self.hostname | |
def update(self): | |
self.cmd(f'hostnamectl set-hostname {self.hostname}') | |
class Timezone(Component): | |
namevar = 'timezone' | |
def verify(self): | |
with open('/etc/timezone') as f: | |
assert f.read().strip() == self.timezone | |
def update(self): | |
self.cmd(f'timedatectl set-timezone {self.timezone}') | |
class CumulusLicense(Component): | |
namevar = 'license' | |
@property | |
def namevar_for_breadcrumb(self): | |
# Hide the license ... | |
return 'XXX' | |
def configure(self): | |
self.license = self.license.strip() | |
self += File('license', content=self.license) | |
def verify(self): | |
current_license, _ = self.cmd('cl-license', ignore_returncode=True) | |
assert current_license.strip() == self.license | |
self.assert_no_subcomponent_changes() | |
def update(self): | |
self.cmd('cl-license -i license') | |
class Service(Component): | |
namevar = 'service' | |
def configure(self): | |
self += EnabledService(self.service) | |
running = RunningService(self.service) | |
self += running | |
self += ReloadedService(self.service, fresh=[running]) | |
class EnabledService(Component): | |
namevar = 'service' | |
def verify(self): | |
assert os.path.exists( | |
f'/etc/systemd/system/multi-user.target.wants/{self.service}') | |
def update(self): | |
self.cmd('systemctl daemon-reload') | |
self.cmd(f'systemctl enable {self.service}') | |
class RunningService(Component): | |
namevar = 'service' | |
def verify(self): | |
self.assert_cmd(f'systemctl status {self.service}') | |
def update(self): | |
self.cmd(f'systemctl start {self.service}') | |
class ReloadedService(Component): | |
namevar = 'service' | |
fresh = () | |
def verify(self): | |
try: | |
self.parent.parent.assert_no_subcomponent_changes() | |
except AssertionError: | |
# If any of the 'fresh' components have changed then | |
# the service doesn't need a reload any more. | |
for fresh in self.fresh: | |
if fresh.changed: | |
return | |
raise | |
def update(self): | |
self.cmd(f'systemctl reload {self.service}') | |
class User(Component): | |
namevar = 'username' | |
group = 'daemon' | |
def verify(self): | |
self.assert_cmd(f'id {self.username}') | |
def update(self): | |
self.cmd(f'useradd -g {self.group} -r {self.username}') | |
class Telegraf(Component): | |
listen_address = None | |
def configure(self): | |
download = Download( | |
'https://dl.influxdata.com/telegraf/releases/telegraf-1.14.3_linux_amd64.tar.gz', | |
checksum='sha256:ffbee48653b2dacd7aeed1c10021da8adae820df81975ae45456122b961b18d2') | |
self += download | |
self += Extract(download.target, target='/', strip=2) | |
self += File('/etc/telegraf/telegraf.conf') | |
self += File('/lib/systemd/system/telegraf.service', | |
source='/usr/lib/telegraf/scripts/telegraf.service') | |
self += File('/etc/vrf/systemd.conf', source='vrf_systemd.conf') | |
self += User('telegraf') | |
self += Service( | |
'[email protected]') | |
class Exec(Component): | |
namevar = 'cmdline' | |
deps = () | |
touches = () | |
def configure(self): | |
for d in self.deps: | |
d.template_context = self.parent | |
self += d | |
def verify(self): | |
self.assert_no_subcomponent_changes() | |
for x in self.deps: | |
self.assert_file_is_current(x.path, self.touches) | |
def update(self): | |
self.cmd(self.cmdline) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment