Skip to content

Instantly share code, notes, and snippets.

@undeadops
Created January 3, 2019 01:33
Show Gist options
  • Save undeadops/1ea2101fa3001c90f332fdc5e2c6956b to your computer and use it in GitHub Desktop.
Save undeadops/1ea2101fa3001c90f332fdc5e2c6956b to your computer and use it in GitHub Desktop.
Watch upstream dns entries for changes, reload nginx based on changes
#!/usr/bin/env python
# Watch for DNS changes for heroku
import logging
from logging.handlers import SysLogHandler
import socket
import os
import yaml
import sys
import argparse
from datetime import datetime
from subprocess import Popen, PIPE
# Because readability in data file is important
try: import simplejson as json
except ImportError: import json
# Because its not part of stdlib, importing here to gain logging
try:
import dns.resolver
except ImportError, e:
print "Missing DNSPython: %s" % e
print "Install with: apt-get install python-dnspython"
sys.exit(1)
class DNSWatcher:
def __init__(self, quiet=False):
"""
Setup Logging and state file
"""
self.logger = logging.getLogger(name="DNSWatcher")
self.logger.setLevel(logging.INFO)
if quiet:
handler = SysLogHandler(address = '/dev/log')
else:
handler = logging.StreamHandler(sys.stdout)
formatter = logging.Formatter('DNSWatcher: %(message)s')
handler.setFormatter(formatter)
self.logger.addHandler(handler)
# Do I need to reload nginx
self.reload_nginx = False
self.logger.info("DNS Watcher Is Starting...")
try:
with open('/etc/dnswatcher.cfg') as fh:
self.cfg = yaml.load(fh)
except IOError, e:
self.logger.critical("Config Missing: /etc/dnswatcher.cfg")
sys.exit(2)
self.data = self._load_datafile()
if self.cfg['heroku-hosts'] != None:
self._proccess_hosts()
def _reload_nginx(self):
"""
Reload Nginx
"""
self.logger.warning("Reloading Nginx")
process = Popen(['/usr/sbin/service','nginx','reload'], stdout=PIPE)
(output, err) = process.communicate()
exit_code = process.wait()
if exit_code != 0:
self.logger.critical("ERROR Reloading Nginx! I hope its still up...")
else:
self.logger.warning("Success! Reload of Nginx complete.")
def _load_datafile(self):
"""
Look for datafile, create if nessisary
"""
if os.path.isfile('/var/cache/dnswatcher'):
with open('/var/cache/dnswatcher', 'r') as fh:
data = json.load(fh)
fh.close()
else:
data = {}
return data
def _write_datafile(self):
"""
Write DNS Lookup result to datafile
"""
try:
with open('/var/cache/dnswatcher', 'w') as fh:
json.dump(self.data, fh, indent=1)
fh.close()
except IOError, e:
self.logger.critical("Error writing data file: %s" % e)
sys.exit(2)
def _query_dns(self, host):
"""
Perform DNS lookup, return IP's
"""
result = dns.resolver.query(host, 'A')
host_ips = []
for r in result:
host_ips.append(r.to_text())
self.logger.info("%s -> %s" % (host, host_ips))
return host_ips
def _compare_hosts_dns(self,host,cur_ips):
"""
Perform Comparison of current host ips with stored
"""
data = {}
try:
prev_ips = self.data[host]['ips']
except KeyError:
data[host] = {'updated': datetime.now().strftime("%c")}
data[host] = {'ips': cur_ips }
self.data.update(data)
prev_ips = None
self.logger.info("Adding new host lookup: %s" % host)
# I know these will be a list of 3 IP's always...
if prev_ips != None:
diff = list(set(cur_ips) - set(prev_ips))
if len(diff) > 0:
self.logger.info("Oy! DNS Changes afoot for host: %s" % host)
self.logger.warning("New IPs: %s | Old IPs: %s" % (cur_ips, prev_ips))
data[host] = {'updated': datetime.now().strftime("%c")}
data[host] = {'ips': cur_ips }
self.data.update(data)
self.reload_nginx = True
def _proccess_hosts(self):
"""
Load heroku hosts,
"""
for host in self.cfg['heroku-hosts']:
ips = self._query_dns(host)
self._compare_hosts_dns(host,ips)
self._write_datafile()
if self.reload_nginx == True:
self.logger.info("Oy! Reloading Nginx!")
self._reload_nginx()
def main():
"""
Watch for DNS changes in Heroku
Reload Nginx if there are changes
"""
parser = argparse.ArgumentParser(description='Check for IP Changes on Heroku Hosts')
parser.add_argument('--quiet', action='store_true', help='Send Logging to Syslog')
args = parser.parse_args()
dns = DNSWatcher(quiet=args.quiet)
if __name__ == '__main__':
main()
@undeadops
Copy link
Author

Example /etc/dnswatcher.cfg

---
# What Hosts to watch
heroku-hosts:
  - host1.example.com
  - host2.example.com

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment