Skip to content

Instantly share code, notes, and snippets.

@jeanfrancoisgratton
Created February 15, 2018 14: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 jeanfrancoisgratton/9b8c4e2ffd0d24995bec9bce8efde469 to your computer and use it in GitHub Desktop.
Save jeanfrancoisgratton/9b8c4e2ffd0d24995bec9bce8efde469 to your computer and use it in GitHub Desktop.
dns updater daemon
#!/usr/bin/env python3
import datetime
import getpass
import ipaddress
import logging
import shutil
import subprocess
import sys
import socket
from P4 import P4, P4Exception
# v1.10 (2018.01.26) : initial version
# ./dnsupd_srv [-c configfile] [-l loglevel] [-?] [-v]
# showHelp() : shows the allowed switches server-side
def showHelp():
print("dnsupdsrv.py [-c conf_file]")
print("-c conf_file : read the config file 'conf_file'")
print("-v: version")
print("-?: this help")
sys.exit(0)
# showVersion() : displays CHANGELOG & version info
def showVersion():
print("dnsupdsrv.py")
print("============")
print("1.10 : 2018.01.26 -> added logging facility")
print("1.00 : 2018.01.14 -> initial version")
sys.exit(0)
# incrementSerial() : increment the DNS serial #
def incrementSerial(ligne):
serialnum = int(ligne[-2:])
cdate = datetime.date.today().strftime('%Y%m%d')
if cdate != ligne[:8]:
return cdate+'01'
else:
++serialnum
if serialnum<10:
return ligne[:8]+'0'+str(serialnum)
else:
return ligne[:8]+str(serialnum)
# changeIPaddressInZone() modifies the serial # and the IP address in zonefile
def changeIPaddressInZone(logger, p4client, p4port, dnsfile, clientroot, newip):
if p4client != '':
p4 = P4()
p4.client = p4client
p4.port = p4port
p4.connect()
p4.run('sync', '-f', dnsfile)
p4.run('edit', dnsfile)
newzonefile = dnsfile.replace('//puppet/', clientroot + '/puppet/')
try:
with open(newzonefile, 'r') as infile:
with open('/tmp/newfile', 'w') as outfile:
for line in infile:
if line.endswith('; serial'):
line = incrementSerial(line) + '\t; serial'
if line.startswith('oslo'):
line = 'oslo\t\t\tIN\tA\t'+newip+'\n'
outfile.write(line)
shutil.move('/tmp/newfile', newzonefile)
except OSError as x:
logger.error('Exception: ' + int(x[0]) + ' => ' + x[1])
if p4client !='':
try:
p4chg = p4.fetch_change()
p4chg._description = 'DNSUPDATER: ' + str(datetime.datetime.now().strftime('%Y/%m/%d %H.%M.%S')) + ": " + newip
p4chg._files = dnsfile
p4.run_submit(p4chg)
except P4Exception as p4x:
logger.error('Perforce error: ' + p4.error)
# readConfigFile() : reads the various params to be used by the daemon
def readConfigFile(logger, configfile = '/opt/dnsupdater/server.conf'):
logger.debug('Entering readConfigFile()')
try:
with open(configfile) as cfgf:
for line in cfgf.readlines():
if not line.startswith('#') and not line.startswith(' ') and not line.startswith('\n'):
if line.startswith('bindport: '):
bindport = int(line.rsplit(' ', 1)[1].strip('\n'))
logger.debug('bindport: ' + str(bindport))
if line.startswith('p4user: '):
p4user = line.rsplit(' ', 1)[1].strip('\n')
logger.debug('Perforce user: ' + p4user)
if line.startswith('p4client: '):
p4client = line.rsplit(' ', 1)[1].strip('\n')
logger.debug('Perforce workspace (p4client): '+ p4client)
if line.startswith('p4port: '):
p4port = line.rsplit(' ', 1)[1].strip('\n')
logger.debug('Perforce server: ' + p4port)
if line.startswith('dnsfile: '):
dnsfile = line.rsplit(' ', 1)[1].strip('\n')
logger.debug('P4 path to zone file (dnsfile): ' + dnsfile)
if line.startswith('clientroot: '):
clientroot = line.rsplit(' ', 1)[1].strip('\n')
logger.debug('P4 client workspace depot root: ' + clientroot)
if line.startswith('loglevel: '):
loglevel = int(line.rsplit(' ', 1)[1].strip('\n'))
logger.debug('log level: ' + str(loglevel))
if loglevel < 1 or loglevel > 5:
loglevel = 3
logger.setLevel(loglevel * 10)
logger.info('Successfully read ' + configfile)
except OSError as msg:
logger.error('Error with file' + configfile + '. Error code = ' + str(msg[0]) + ':' + msg[1])
logger.debug('Leaving readConfigFile()')
def main():
# check who is running this script ? (only devops is allowed)
##if getpass.getuser()!='devops':
## print("This must be run by user 'devops'")
## sys.exit(-1)
# Overridden variables
configfile = newip = p4client = ''
clientroot='/repos'
bindport = 2009
loglevel = 3
dnsfile = '//puppet/code/environments/production/modules/dns/files/famillegratton.net.zone'
p4port='perforce.cloud.famillegratton.net:1818'
shutdown = False
# Set logging facilities (https://docs.python.org/2.3/lib/node304.html)
# Logging levels : 0 = NOTSET, 10 = DEBUG, 20 = INFO, 30 = WARNING, 40 = ERROR, 50 = CRITICAL
logger = logging.getLogger(__name__)
lgf = logging.FileHandler('/var/log/dnsupdater/dnsupdater.log')
lgfformat = logging.Formatter('%(asctime)s %(levelname)s %(message)s', datefmt='%Y.%m.%d %H:%M:%S')
lgf.setFormatter(lgfformat)
logger.addHandler(lgf)
logger.setLevel(logging.INFO)
logger.info('=====================')
logger.info('MARK MARK MARK')
logger.info('=====================')
logger.info('===> Daemon startup')
# Parse command line
if '-c' in sys.argv and len(sys.argv) - sys.argv.index('-c') >= 2:
configfile = sys.argv[sys.argv.index('-c') + 1]
if '-?' in sys.argv or '-h' in sys.argv:
showHelp()
if '-v' in sys.argv:
showVersion()
if '-l' in sys.argv and len(sys.argv) - sys.argv.index('-l') >= 2:
loglevel = int(sys.argv.index('-l')) + 1
if loglevel < 1 or loglevel > 5:
loglevel = 3
# Set logging level past logfile header
# (ie: we wanted a startup message in the logfile, now we log according to config)
logger.setLevel(loglevel * 10)
readConfigFile(logger, configfile)
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
try:
logger.debug('binding on port ' + str(bindport))
s.bind(('', bindport))
except socket.error as msg:
logger.error('Bind failed. Error Code : ' + str(msg[0]) + ' Message ' + msg[1])
sys.exit()
# Start listening on socket
logger.debug('listening.....')
s.listen(1)
# now keep talking with the client
while 1:
connection, client_address = s.accept()
source = client_address[0] + ' tcp/' + str(client_address[1])
logger.info('Connection from ' + source)
try:
while True:
data = connection.recv(256)
if not data:
break
finally:
logger.info('Closing connection from ' + source)
connection.close()
recv = str(data)
if recv.endswith('<-Q'):
logger.warning('Received daemon shutdown request from' + source)
recv=recv[:-3]
shutdown = True
try:
newip = ipaddress.ip_address(recv)
except ValueError:
newip=''
if newip != '':
changeIPaddressInZone(logger, p4client, p4port, dnsfile, clientroot, newip)
with open('/var/log/dnsupdater/ip.log', 'a') as logfile:
try:
logfile.write(datetime.datetime.now().strftime('%Y/%m/%d %H.%M.%S') + ': ' + newip)
except OSError as msg:
logger.warning('Unable to write /var/log/dnsupdater/ip.log : ' + msg[1])
subprocess.call('systemctl reload named')
if shutdown == True:
logger.info('---> Daemon is shutting down')
sys.exit(1)
if __name__ == '__main__':
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment