Skip to content

Instantly share code, notes, and snippets.

@ctheune
Created May 21, 2021 07:03
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ctheune/09ab1feba33c8d8d87d7c71c0c78fe76 to your computer and use it in GitHub Desktop.
Save ctheune/09ab1feba33c8d8d87d7c71c0c78fe76 to your computer and use it in GitHub Desktop.
batou example for managing system stuff with a traditional package manager
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(
'telegraf@mgmt.service')
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