Skip to content

Instantly share code, notes, and snippets.

@holly
Created March 13, 2016 09:04
Show Gist options
  • Save holly/4a5925c256c71fdf5116 to your computer and use it in GitHub Desktop.
Save holly/4a5925c256c71fdf5116 to your computer and use it in GitHub Desktop.
script for dnsmasq master/slave
#!/usr/bin/env python3
from argparse import ArgumentParser
from subprocess import Popen, PIPE, STDOUT
import shlex
import hashlib
import time
import warnings
import os, sys, io
import signal
import re
DELAY = 1
DNSMASQ_ADDN_HOSTS = "/etc/dnsmasq.hosts.d"
DNSMASQ_RESTART_COMMAND = "sudo systemctl restart dnsmasq"
RSYNC_CMD = "/usr/bin/rsync"
RSYNC_OPTIONS = ["--delete", "-a", "-x", "--rsync-path", "sudo " + RSYNC_CMD];
SSH_CMD = "/usr/bin/ssh"
SSH_OPTIONS = ["-o", "StrictHostKeyChecking=no", "-o", "UserKnownHostsFile=/dev/null", "-o", "ConnectTimeout=30"]
REMOTE_COMMAND_TIMEOUT = 10
VERSION = "1.0"
parser = ArgumentParser(description="foo")
parser.add_argument('--version', '-v', action='version', version='%(prog)s ' + VERSION)
parser.add_argument('--slave-servers', action='store', required=True, nargs='*', help='dnsmasq slave servers')
parser.add_argument('--ssh-user', '-u', action='store', default=os.environ.get("USER"), metavar='SSH_USER', help='dnsmasq server execute ssh account name')
parser.add_argument('--ssh-key', '-i', action='store', metavar='SSH_PRIVATE_KEY', help='dnsmasq server execute ssh key')
parser.add_argument('--remote-command-timeout', '-t', type=int, action='store', metavar='REMOTE_COMMAND_TIMEOUT', default=REMOTE_COMMAND_TIMEOUT, help='remote dnsmasq server execution timeout seconds')
parser.add_argument('--delay', action='store', type=int, metavar='DELAY SECONDS', default=DELAY, help='check delay seconds')
parser.add_argument('--dnsmasq-addn-hosts', action='store', metavar='DNSMASQ_ADDN_HOSTS', default=DNSMASQ_ADDN_HOSTS, help='dnsmasq dns server addn-hosts(file or directory)')
def checksum_file(path):
m = hashlib.md5()
with open(path) as f:
m.update(f.read().encode("utf-8"))
return m.hexdigest()
def addn_hosts_meta(addn_hosts):
meta = []
if os.path.isfile(addn_hosts):
meta.append({ addn_hosts: checksum_file(addn_hosts) })
elif os.path.isdir(addn_hosts):
for entry in os.listdir(addn_hosts):
if re.match('^.*\.(swp|bk|git)$', entry):
continue
path = os.path.join(addn_hosts, entry)
meta.append({ os.path.join(path): checksum_file(path) })
else:
sys.exit("dnsmasq-addn-hosts has to be a file or directory")
return meta
def make_rsync_command(server, args):
# ssh option
ssh_opts = ["ssh"]
ssh_opts.extend(SSH_OPTIONS)
ssh_opts.append("-l")
ssh_opts.append(shlex.quote(args.ssh_user))
if args.ssh_key:
ssh_opts.append("-i")
ssh_opts.append(shlex.quote(args.ssh_key))
ssh_opts_str = " ".join(ssh_opts)
rsync_cmds = [RSYNC_CMD]
rsync_cmds.extend(RSYNC_OPTIONS)
rsync_cmds.append("-e")
rsync_cmds.append(ssh_opts_str)
addn_hosts = args.dnsmasq_addn_hosts
if os.path.isdir(addn_hosts) and addn_hosts[-1] != "/":
addn_hosts = addn_hosts + "/"
rsync_cmds.append(addn_hosts)
rsync_cmds.append(server + ':' + addn_hosts)
return rsync_cmds
def make_ssh_command(server, args, remote_command=None):
ssh_cmds = [SSH_CMD]
ssh_cmds.extend(SSH_OPTIONS)
ssh_cmds.append("-l")
ssh_cmds.append(shlex.quote(args.ssh_user))
if args.ssh_key:
ssh_cmds.append("-i")
ssh_cmds.append(shlex.quote(args.ssh_key))
ssh_cmds.append(server)
ssh_cmds.append(remote_command)
return ssh_cmds
def main():
args = parser.parse_args()
addn_hosts = args.dnsmasq_addn_hosts
before_meta = addn_hosts_meta(addn_hosts)
while True:
time.sleep(args.delay)
after_meta = addn_hosts_meta(addn_hosts)
if before_meta == after_meta:
print("{0} is not updated. skip".format(addn_hosts))
continue
# local(master) restart dnsmasq
p = Popen(shlex.split(DNSMASQ_RESTART_COMMAND), universal_newlines=True, stderr=STDOUT)
outs, errs = p.communicate(timeout=args.remote_command_timeout)
print("{0}: {1} execute command. returncode:{2}".format("localhost", DNSMASQ_RESTART_COMMAND, p.returncode))
if p.returncode != 0:
sys.exit("dnsmasq master update failure. returncode:{0} message:{1}".format(p.returncode, errs))
for server in args.slave_servers:
rsync_command = make_rsync_command(server, args)
ssh_command = make_ssh_command(server, args, DNSMASQ_RESTART_COMMAND)
for command in [rsync_command, ssh_command]:
p = Popen(command, universal_newlines=True, stderr=STDOUT)
outs, errs = p.communicate(timeout=args.remote_command_timeout)
print("{0}: {1} execute command. returncode:{2}".format(server, command[0], p.returncode))
if p.returncode != 0:
sys.exit("dnsmasq slave update failure. returncode:{0} message:{1}".format(p.returncode, errs))
before_meta = after_meta
if __name__ == "__main__":
main()
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment