[roster-dns-management] r1152 committed - Refactoring dnsconfigsync to swap BIND directories atomically via syml...

1 view
Skip to first unread message

roster-dns...@googlecode.com

unread,
Jul 19, 2013, 4:56:54 PM7/19/13
to roster-...@googlegroups.com
Revision: 1152
Author: J.S.P...@gmail.com
Date: Fri Jul 19 13:56:34 2013
Log: Refactoring dnsconfigsync to swap BIND directories atomically via
symlinks. This resolves issue 241.
http://code.google.com/p/roster-dns-management/source/detail?r=1152

Modified:
/trunk/roster-config-manager/roster_config_manager/config_lib.py
/trunk/roster-config-manager/roster_config_manager/tree_exporter.py
/trunk/roster-config-manager/scripts/dnsconfigsync
/trunk/test/dnsconfigsync_test.py
/trunk/test/dnsexportconfig_test.py

=======================================
--- /trunk/roster-config-manager/roster_config_manager/config_lib.py Fri
May 10 09:51:01 2013
+++ /trunk/roster-config-manager/roster_config_manager/config_lib.py Fri
Jul 19 13:56:34 2013
@@ -136,6 +136,10 @@
Inputs:
audit_log_id: id of the audit log of a compressed dns tree

+ Outputs:
+ audit_log_id: int - the same as the input, unless the input is None.
+ In which case, the most recent audit log will be returned.
+
Raises:
ExporterFileError Compressed files will not be extracted to
/root/config.
"""
@@ -155,6 +159,7 @@
raise ExporterFileError('Could not extract the DNS tree %s/%s
to %s.' % (
self.backup_dir, filename, self.root_config_dir))
tar_file.close()
+ return audit_log_id

def TarDnsTree(self, audit_log_id):
"""Compresses the uncompressed Roster Tree in the root configuration
=======================================
--- /trunk/roster-config-manager/roster_config_manager/tree_exporter.py Wed
Jul 10 11:35:06 2013
+++ /trunk/roster-config-manager/roster_config_manager/tree_exporter.py Fri
Jul 19 13:56:34 2013
@@ -285,18 +285,16 @@
zone_file_handle.close()

# Write named conf files
- named_conf_file = '%s/named.conf.a' % named_directory.rstrip('/')
- named_conf_binary_file = '%s/named.conf.b' % (
- named_directory.rstrip('/'))
+ named_conf_file = os.path.join(named_directory, 'named.conf.a')
+ named_conf_binary_file =
os.path.join(named_directory, 'named.conf.b')
named_conf_a_file_string = self.MakeNamedConf(data, cooked_data,

dns_server_set, 'db',
bind_dir)
named_conf_b_file_string = self.MakeNamedConf(data, cooked_data,

dns_server_set, 'aa',
bind_dir,
binary=True)
- root_hint_file = '%s/named/named.ca' %
named_directory.rstrip('/')
+ root_hint_file = os.path.join(named_directory, 'named/named.ca')
root_hint_file_string = open(self.root_hint_file, 'r').read()
-
root_hint_file_handle = open(root_hint_file, 'w')
named_conf_binary_file_handle = open(named_conf_binary_file, 'w')
named_conf_file_handle = open(named_conf_file, 'w')
=======================================
--- /trunk/roster-config-manager/scripts/dnsconfigsync Fri May 10 09:51:01
2013
+++ /trunk/roster-config-manager/scripts/dnsconfigsync Fri Jul 19 13:56:34
2013
@@ -43,6 +43,8 @@
import tarfile
import iscpy
import re
+import time
+import datetime
from fabric import api as fabric_api
from fabric import exceptions as fabric_exceptions
from fabric import state as fabric_state
@@ -57,186 +59,361 @@
#BIND default
DEFAULT_RNDC_PORT = 953

-def PushFiles(dns_server, config_lib_instance, rndc_exec='rndc',
- ssh_id_file=None, dns_server_rndc_port=None):
- """Pushes files to a remote directory over SSH, and refreshes rndc.
- Inputs:
- dns_server: name of the dns_server to push files to.
- config_lib_instance: ConfigLib object
- rndc_exec: string of path to rndc executable
- ssh_id_file: string of path to SSH private key file
- dns_server_rndc_port: int of port to connect rndc to
- Raises:
- ExporterNoFileError: Could not fnid a DNS tree in the root_config_dir
- ServerCheckError:
- DNS server does not exist
- A server check has not been run on a DNS server
- Failed to move compressed BIND tree to DNS server
- Failed to untar compressed BIND tree on DNS server
- A named.conf check failed on the DNS server
- A zone did not pass named-checkzone
- Failed to compile zone
- Lost SSH connection to DNS server
- Unable to connect via SSH to DNS server
- Failed to reload DNS server via rndc reload
- """
- root_config_dir = config_lib_instance.root_config_dir
- if( not os.path.exists(root_config_dir) or
- len(os.listdir(root_config_dir)) < 1 ):
- raise config_lib.ExporterNoFileError('Could not find a DNS tree
in %s.' %
- root_config_dir)
-
- dns_server_directory = '%s/%s' % (root_config_dir, dns_server)
- if( not os.path.exists(dns_server_directory) ):
- raise config_lib.ServerCheckError('DNS server %s does not exist. %s' %
- dns_server)
-
- dns_server_info = config_lib_instance.GetDnsServerInfo(dns_server)
- if( not dns_server_rndc_port ):
- rndc_port_found = False
- named_conf_string = open(
- os.path.join(root_config_dir,
dns_server, 'named.conf.a'), 'r').read()
- named_conf_dict = iscpy.ParseISCString(named_conf_string)
- if( 'controls' in named_conf_dict ):
- named_conf_dict = named_conf_dict['controls']
- for sub_dict in named_conf_dict:
- for key in sub_dict:
- if( 'inet' in key and 'port' in key ):
- #Turns 'inet 192.168.what.ever port 1234 allow' into
- #['inet 192.168.what.ever ', ' 1234 allow']
- port_split_list = key.split('port')
+class ConfigSyncer:
+ def __init__(self, dns_server, config_lib_instance, rndc_exec='rndc',
+ ssh_id_file=None, dns_server_rndc_port=None, id=None):
+ """
+ Inputs:
+ dns_server: name of the dns_server to push files to.
+ config_lib_instance: ConfigLib object
+ rndc_exec: string of path to rndc executable
+ ssh_id_file: string of path to SSH private key file
+ dns_server_rndc_port: int of port to connect rndc to
+ id: int of audit log id of files we're sending over
+ Raises:
+ ExporterNoFileError: Could not find a DNS tree in the root_config_dir
+ ServerCheckError:
+ DNS server does not exist
+ A server check has not been run on a DNS server
+ """
+ self.dns_server = dns_server
+ self.config_lib_instance = config_lib_instance
+ self.root_config_dir = config_lib_instance.root_config_dir
+ self.rndc_exec = rndc_exec
+ self.ssh_id_file = ssh_id_file
+ self.dns_server_rndc_port = dns_server_rndc_port
+ self.id = int(id)

- if len(port_split_list) > 1:
- #Results in '1234 allow'
- port_segment = port_split_list[1].strip(' ')
+ if( not os.path.exists(self.root_config_dir) or
+ len(os.listdir(self.root_config_dir)) < 1 ):
+ raise config_lib.ExporterNoFileError('Could not find a DNS tree
in %s.' %
+ self.root_config_dir)
+
+ self.dns_server_directory = '%s/%s' % (self.root_config_dir,
self.dns_server)
+ if( not os.path.exists(self.dns_server_directory) ):
+ raise config_lib.ServerCheckError('DNS server %s does not
exist. %s' %
+ dns_server)
+
+ self.dns_server_info = config_lib_instance.GetDnsServerInfo(dns_server)
+ self.bind_dir =
self.dns_server_info['server_info']['bind_dir'].rstrip('/')
+ self.test_dir =
self.dns_server_info['server_info']['test_dir'].rstrip('/')
+ self.ssh_host = '%s@%s:22' % (
+ self.dns_server_info['server_info']['server_user'],
+ self.dns_server_info['server_info']['server_name'])
+ self.checkzone = ('named-checkzone' in self.dns_server_info['tools']
and
+ self.dns_server_info['tools']['named-checkzone'])
+ self.checkconf = ('named-checkconf' in self.dns_server_info['tools']
and
+ self.dns_server_info['tools']['named-checkconf'])
+ self.compilezone = ('named-compilezone' in
self.dns_server_info['tools'] and
+ self.dns_server_info['tools']['named-compilezone'])
+ self.tar = ('tar' in self.dns_server_info['tools'] and
+ self.dns_server_info['tools']['tar'])

- #Results in ' allow'
- to_strip = ''.join(re.split('\A[0-9]{1,5}', port_segment))
+ if( not self.dns_server_rndc_port ):
+ rndc_port_found = False
+ named_conf_string = open(os.path.join(
+ self.root_config_dir, dns_server, 'named.conf.a'), 'r').read()
+ named_conf_dict = iscpy.ParseISCString(named_conf_string)
+ if( 'controls' in named_conf_dict ):
+ named_conf_dict = named_conf_dict['controls']
+ for sub_dict in named_conf_dict:
+ for key in sub_dict:
+ if( 'inet' in key and 'port' in key ):
+ #Turns 'inet 192.168.what.ever port 1234 allow' into
+ #['inet 192.168.what.ever ', ' 1234 allow']
+ port_split_list = key.split('port')

- #Results in '1234'
- port_string = port_segment.strip(to_strip)
+ if len(port_split_list) > 1:
+ #Results in '1234 allow'
+ port_segment = port_split_list[1].strip(' ')

- if port_string.isdigit():
- dns_server_rndc_port = int(port_string)
- rndc_port_found = True
+ #Results in ' allow'
+ to_strip = ''.join(re.split('\A[0-9]{1,5}', port_segment))

- if( not rndc_port_found ):
- print 'rndc port not found in named.conf. Using default of %s' % (
- DEFAULT_RNDC_PORT)
- dns_server_rndc_port = DEFAULT_RNDC_PORT
+ #Results in '1234'
+ port_string = port_segment.strip(to_strip)

- dns_server_info['server_info']['bind_dir'] =
dns_server_info['server_info'][
- 'bind_dir'].rstrip('/')
- dns_server_info['server_info']['test_dir'] =
dns_server_info['server_info'][
- 'test_dir'].rstrip('/')
- if( dns_server_info['server_info']['bind_version'].lower() ==
- 'undetermined' ):
- raise config_lib.ServerCheckError('A server check has not been run
on %s.' %
- dns_server)
- ssh_host = '%s@%s:22' % (dns_server_info['server_info']['server_user'],
- dns_server_info['server_info']['server_name'])
-
- checkzone = ('named-checkzone' in dns_server_info['tools'] and
- dns_server_info['tools']['named-checkzone'])
- checkconf = ('named-checkconf' in dns_server_info['tools'] and
- dns_server_info['tools']['named-checkconf'])
- compilezone = ('named-compilezone' in dns_server_info['tools'] and
- dns_server_info['tools']['named-compilezone'])
- tar = ('tar' in dns_server_info['tools'] and
dns_server_info['tools']['tar'])
- try:
- if( ssh_id_file is not None ):
- fabric_api.env.key_filename = ssh_id_file
- fabric_api.env.warn_only = True
- for out in fabric_state.output:
- fabric_state.output[out] = False
- fabric_api.env.host_string = ssh_host
- fabric_api.run('rm -rf %s/*' %
dns_server_info['server_info']['test_dir'])
+ if port_string.isdigit():
+ self.dns_server_rndc_port = int(port_string)
+ rndc_port_found = True

- # Prepare and push BIND tree to server
- if( tar ):
- config_lib_instance.TarDnsBindFiles(dns_server)
- result = fabric_api.put('%s/%s.tar.bz2' %
- (dns_server_directory, dns_server),
- '%s/%s.tar.bz2' % (dns_server_info['server_info']['test_dir'],
- dns_server))
+ if( not rndc_port_found ):
+ print 'rndc port not found in named.conf. Using default of %s' % (
+ DEFAULT_RNDC_PORT)
+ self.dns_server_rndc_port = DEFAULT_RNDC_PORT
+
+ if( self.dns_server_info['server_info']['bind_version'].lower() ==
+ 'undetermined' ):
+ raise config_lib.ServerCheckError('A server check has not been run
on %s.' %
+ self.dns_server)
+
+ def run(self):
+ """Pushes files to a remote directory over SSH, and refreshes rndc.
+ Raises:
+ ServerCheckError:
+ Lost SSH connection to DNS server
+ Unable to connect via SSH to DNS server
+ """
+ try:
+ if( self.ssh_id_file is not None ):
+ fabric_api.env.key_filename = self.ssh_id_file
+ fabric_api.env.warn_only = True
+ for out in fabric_state.output:
+ fabric_state.output[out] = False
+ fabric_api.env.host_string = self.ssh_host
+ fabric_api.run('rm -rf %s/*' % self.test_dir)
+
+ retry_methods = [self.MoveFilesAndUnTar, self.ReplaceBindDir,
+ self.ReloadBindServer]
+ for method in retry_methods:
+ tries = 0
+ success = False
+ while not success:
+ try:
+ tries += 1
+ method()
+ success = True
+ except (fabric_exceptions.NetworkError, ssh.SSHException,
+ config_lib.ServerCheckError) as e:
+ if tries == options.tries:
+ raise e
+ except fabric_exceptions.NetworkError:
+ raise config_lib.ServerCheckError('Could not connect to %s via
SSH.' %
+ self.dns_server)
+ except ssh.SSHException:
+ raise config_lib.ServerCheckError('Lost connection to %s.' %
+ self.dns_server)
+ finally:
+ fabric_network.disconnect_all()
+
+ def ReloadBindServer(self):
+ """Reloads BIND server via rndc reload
+ Raises:
+ ServerCheckError:
+ Failed to reload DNS server via rndc reload
+ """
+ rndc_exec = '%s -p %s reload' % (
+ self.rndc_exec, self.dns_server_rndc_port)
+
+ result = fabric_api.run(rndc_exec)
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError('Failed to reload %s BIND '
+ 'server: %s.' % (self.dns_server, result))
+
+ def CreateSymlink(self, symlink, target,
write_redirect_named_conf=False):
+ """Creates a symlink on the remote DNS server, with the option to
+ write the "dummy" redirect named.conf
+
+ Inputs:
+ symlink: string - path of the symlink to create
+ target: string - path of folder for symlink to point to
+ write_redirect_named_conf: boolean - write the redirect named.conf
+
+ Raises:
+ ServerCheckError:
+ Failed to create named.conf in the named directory
+ Failed to create symlink
+ """
+ if( write_redirect_named_conf ):
+ #symlink_named_conf is the "redirect" named.conf file. It simply
includes
+ #the contents of the named.conf that is "inside" the symlink
directory.
+ #After changing the symlink target, this file is now different, and
thus
+ #we have "updated" the "real" named.conf without touching the
+ #named.conf that BIND looks at, which is the symlink_named_conf.
+ symlink_named_conf = os.path.join(symlink, 'named.conf')
+
+ #This comes out to be something like:
+ #include "/etc/bind/named/named.conf";
+ symlink_named_conf_string = 'include "%s";' % symlink_named_conf
+
+ #This comes out to (staying in sync with the above example):
+ #/etc/bind/named.conf
+ bind_dir_named_conf = os.path.join(self.bind_dir, 'named.conf')
+
+ echo_command = "echo '%s' > %s" % (symlink_named_conf_string,
+ bind_dir_named_conf)
+
+ result = fabric_api.run(echo_command)
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError(
+ 'Failed to create named.conf in directory %s on server %s' % (
+ self.bind_dir, self.dns_server))
+
+ result = fabric_api.run('ln -s %s %s' % (target, symlink))
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError(
+ 'Failed to symlink %s to %s on server %s' % (target,
+ symlink, self.dns_server))
+
+ def ReplaceBindDir(self):
+ """Moves the contents of test_dir into bind_dir
+ Raises:
+ ServerCheckError:
+ BIND directory does not exist on remote DNS server
+ Failed to move BIND files from test_dir to bind_dir
+ Failed to remove bind_dir and create a symlink in its place
+ Failed to change symlink target
+ Unknown symlink directory type
+ """
+
+ symlink_dir = os.path.join(self.bind_dir, 'named')
+ new_bind_dir_real = '%s_%s_%s' % (symlink_dir.rstrip('/'), self.id,
+ datetime.datetime.now().strftime('%d_%m_%yT%H_%M_%S'))
+
+ #Making sure self.bind_dir exists
+ result = fabric_api.run('file %s' % self.bind_dir)
+ if( result.stdout == "%s: cannot open `%s' (No such file or
directory)" % (
+ self.bind_dir, self.bind_dir) ):
+ raise config_lib.ServerCheckError(
+ 'BIND directory %s does not exist on server %s' % (self.bind_dir,
+ self.dns_server))
+
+ #OK, it exists. Now let's move the test_dir there.
+ test_dir_named_dir = os.path.join(self.test_dir, 'named')
+ result = fabric_api.run('mv %s %s' % (test_dir_named_dir,
new_bind_dir_real))
+ if( result.return_code != 0 ):
+ #check to see if it exists
+ raise config_lib.ServerCheckError(
+ 'Failed moving %s to %s on server %s' % (test_dir_named_dir,
+ new_bind_dir_real, self.dns_server))
+
+ result = fabric_api.run('file %s' % symlink_dir)
+ if( result.stdout == "%s: cannot open `%s' (No such file or
directory)" % (
+ symlink_dir, symlink_dir) ):
+ self.CreateSymlink(symlink_dir, new_bind_dir_real,
+ write_redirect_named_conf=True)
+
+ #The BIND directory is an actual directory, not a symlink
+ elif( result.stdout == '%s: directory' % symlink_dir ):
+ result = fabric_api.run('rm -rf %s' % symlink_dir )
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError(
+ 'Failed to remove directory %s on server %s' % (symlink_dir,
+ self.dn_server))
+ self.CreateSymlink(symlink_dir, new_bind_dir_real,
+ write_redirect_named_conf=True)
+
+ #The BIND directory is a symlink
+ elif( 'symbolic link to' in result.stdout ):
+ #Turns "/some_dir/: symbolic link to `/some_other_dir'"
+ real_bind_dir = result.stdout.split('symbolic link to')[1].lstrip('
`')[:-1]
+ temp_symlink = '%s_link' % new_bind_dir_real.rstrip('/')
+
+ self.CreateSymlink(temp_symlink, new_bind_dir_real)
+
+ #This is key. This must (and does) run atomically.
+ result = fabric_api.run('mv -T %s %s' % (temp_symlink, symlink_dir))
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError(
+ 'Failed to change %s symlink target from %s to %s on
server %s' % (
+ self.bind_dir, real_bind_dir, new_bind_dir_real,
self.dns_server))
+
+ result = fabric_api.run('rm -rf %s' % real_bind_dir)
+ if( result.return_code != 0 ):
+ print('WARNING: Failed to remove %s on server %s. This can be
removed '
+ 'manually. However, it will not affect BIND operation.' % (
+ real_bind_dir, self.dns_server))
+ else:
+ raise config_lib.ServerCheckError('Unknown error in determing file
type '
+ 'of %s on server %s' %
(symlink_dir,
+ self.dns_server))
+
+ def MoveFilesAndUnTar(self):
+ """Moves files to remote server, untars, and runs checks
+ Raises:
+ ServerCheckError:
+ Failed to move compressed/uncompressed BIND tree to DNS server
+ Failed to untar compressed BIND tree on DNS server
+ A named.conf check failed on the DNS server
+ A zone did not pass named-checkzone
+ Failed to compile zone
+ """
+ if( self.tar ):
+ self.config_lib_instance.TarDnsBindFiles(self.dns_server)
+
+ tar_file_name = '%s.tar.bz2' % self.dns_server
+ local_tar_file_path = os.path.join(
+ self.dns_server_directory, tar_file_name)
+ remote_tar_file_path = os.path.join(self.test_dir, tar_file_name)
+
+ result = fabric_api.run('ls %s' % self.test_dir)
+ if( result.return_code != 0 ):
+ result = fabric_api.run('mkdir %s' % self.test_dir)
+ if( result.return_code != 0 ):
+ raise config_lib.ServerCheckError('Failed to move compressed
BIND tree'
+ ' to server %s' %
self.dns_server)
+
+ result = fabric_api.put(local_tar_file_path, remote_tar_file_path)
if( not result.succeeded ):
raise config_lib.ServerCheckError('Failed to move compressed BIND
tree '
- 'to server %s.' % dns_server)
- with fabric_api.cd(dns_server_info['server_info']['test_dir']):
- result = fabric_api.run('tar -xf %s/%s.tar.bz2' % (dns_server_info[
- 'server_info']['test_dir'], dns_server))
- os.remove('%s/%s/%s.tar.bz2' % (root_config_dir, dns_server,
dns_server))
+ 'to server %s.' %
self.dns_server)
+ with fabric_api.cd(self.test_dir):
+ result = fabric_api.run('tar -xf %s' % remote_tar_file_path)
+ os.remove(os.path.join(
+ self.root_config_dir, self.dns_server, tar_file_name))
if( result.return_code != 0 ):
raise config_lib.ServerCheckError('Failed to untar compressed
BIND '
- 'tree on server %s.' % dns_server)
- result = fabric_api.run('ls %s' %
- dns_server_info['server_info']['test_dir'])
+ 'tree on server %s.' % self.dns_server)
+ result = fabric_api.run('ls %s' % self.test_dir)
if( 'named.conf.a' not in result ):
raise config_lib.ServerCheckError('Failed to untar compressed
BIND '
- 'tree on server %s.' % dns_server)
- fabric_api.run('rm %s/%s.tar.bz2' % (dns_server_info['server_info'][
- 'test_dir'], dns_server))
+ 'tree on server %s.' % self.dns_server)
+ fabric_api.run('rm %s' % remote_tar_file_path)
else:
- result = fabric_api.put('%s/*' % dns_server_directory,
- dns_server_info['server_info']['test_dir'])
+ result = fabric_api.put('%s/*' % self.dns_server_directory,
self.test_dir)
if( not result.succeeded ):
raise config_lib.ServerCheckError('Failed in moving BIND tree
files '
- 'to server %s.' % dns_server)
- fabric_api.run('rm %s/%s.info' % (dns_server_info['server_info'][
- 'test_dir'], dns_server))
+ 'to server %s.' % self.dns_server)

- # Check and prepare named.conf file
- result = fabric_api.run('ls %s/named' % dns_server_info[
- 'server_info']['bind_dir'])
- if( result.return_code!=0 ):
- fabric_api.run('mkdir %s/named' % dns_server_info['server_info'][
- 'bind_dir'])
+ dns_server_info_file = '%s.info' % self.dns_server
+ fabric_api.run('rm %s' % os.path.join(self.test_dir,
dns_server_info_file))
+
+ remote_named_dir = os.path.join(self.bind_dir, 'named')
+ result = fabric_api.run('ls %s' % remote_named_dir)
+ if( result.return_code != 0 ):
+ fabric_api.run('mkdir %s' % remote_named_dir)

conf_keep = ''
conf_remove = ''
- if( checkconf and compilezone ):
- result = fabric_api.run('named-checkconf %s/named.conf.b' %
- dns_server_info['server_info']['test_dir'])
+ if( self.checkconf and self.compilezone ):
+ result = fabric_api.run('named-checkconf %s/named.conf.b' %
self.test_dir)
if( result.return_code != 0 ):
raise config_lib.ServerCheckError('Binary named.conf check failed '
- 'on %s.' % dns_server)
- elif( checkconf ):
- result = fabric_api.run('named-checkconf %s/named.conf.a' %
- dns_server_info['server_info']['test_dir'])
+ 'on %s.' % self.dns_server)
+ elif( self.checkconf ):
+ result = fabric_api.run('named-checkconf %s/named.conf.a' %
self.test_dir)
if( result.return_code != 0 ):
raise config_lib.ServerCheckError('Named.conf check failed
on %s.' %
- dns_server)
+ self.dns_server)

- if( compilezone ):
- conf_keep = '%s/named.conf.b' %
dns_server_info['server_info']['test_dir']
- conf_remove = '%s/named.conf.a' % dns_server_info['server_info'][
- 'test_dir']
+ if( self.compilezone ):
+ conf_keep = os.path.join(self.test_dir, 'named.conf.b')
+ conf_remove = os.path.join(self.test_dir, 'named.conf.a')
else:
- conf_keep = '%s/named.conf.a' %
dns_server_info['server_info']['test_dir']
- conf_remove = '%s/named.conf.b' % dns_server_info['server_info'][
- 'test_dir']
+ conf_keep = os.path.join(self.test_dir, 'named.conf.a')
+ conf_remove = os.path.join(self.test_dir, 'named.conf.b')
fabric_api.run('rm %s' % conf_remove)
- fabric_api.run('mv %s %s/named.conf' % (conf_keep,
- dns_server_info['server_info']['test_dir']))
+ #Moves the named.conf into the named folder
+ fabric_api.run('mv %s %s' % (conf_keep,
+ os.path.join(self.test_dir, 'named', 'named.conf')))

# Check and compile zone files
- if( checkzone or compilezone ):
- zone_dict = config_lib_instance.GetZoneList(dns_server)
+ if( self.checkzone or self.compilezone ):
+ zone_dict = self.config_lib_instance.GetZoneList(self.dns_server)
for view in zone_dict:
- view_dir = '%s/named/%s' % (
- dns_server_info['server_info']['test_dir'], view)
+ view_dir = os.path.join(self.test_dir, 'named', view)
for zone_origin in zone_dict[view]:
- zone_file_name = '%s/%s' % (view_dir,
zone_dict[view][zone_origin])
- zone_tool_arguments = config_lib_instance.GetNamedZoneToolArgs(\
- dns_server, view, zone_dict[view][zone_origin])
- if( checkzone ):
+ zone_file_name = os.path.join(view_dir,
zone_dict[view][zone_origin])
+ zone_tool_arguments =
self.config_lib_instance.GetNamedZoneToolArgs(
+ self.dns_server, view, zone_dict[view][zone_origin])
+ if( self.checkzone ):
result = fabric_api.run('named-checkzone %s %s %s' % (
zone_tool_arguments, zone_origin, zone_file_name))
if( result.return_code != 0 ):
raise config_lib.ServerCheckError('Zone %s did not pass the '
'check.' % zone_origin)

- if( compilezone ):
+ if( self.compilezone ):
zone_binary_file_name = '%s.aa' % zone_file_name.rstrip('.db')
result = fabric_api.run('named-compilezone -F raw %s
-o %s %s %s' % (
zone_tool_arguments, zone_binary_file_name, zone_origin,
@@ -246,46 +423,13 @@
raise config_lib.ServerCheckError('Failed to compile
zone %s.' %
zone_origin)

- if( checkzone ):
+ if( self.checkzone ):
result = fabric_api.run('named-checkzone -f raw %s %s %s' % (
zone_tool_arguments, zone_origin, zone_binary_file_name))
if( result.return_code != 0 ):
raise config_lib.ServerCheckError('Zone check on binary
zone '
'%s failed.' % zone_origin)

- # Move files in BIND tree to BIND directory
- result = fabric_api.run('cp %s/named.conf %s/named.conf' % (
- dns_server_info['server_info']['test_dir'],
- dns_server_info['server_info']['bind_dir']))
- if( result.return_code != 0 ):
- raise config_lib.ServerCheckError('Failed to move files from '
- 'test directory to bind directory on server %s.' % dns_server)
- try:
- result = fabric_api.run('cp -rf %s/named/* %s/named/ &' % (
- dns_server_info['server_info']['test_dir'],
- dns_server_info['server_info']['bind_dir']), pty=False)
- except ssh.SSHException:
- raise config_lib.ServerCheckError('Lost SSH connection to %s while '
- 'moving files from test directory to BIND directory.' %
dns_server)
- fabric_api.run('rm -rf %s/* &' %
- dns_server_info['server_info']['test_dir'], pty=False)
-
- rndc_exec = '%s -p %s' % (
- rndc_exec, dns_server_rndc_port)
-
- # Reload BIND server
- result = fabric_api.run('%s reload' % rndc_exec)
- if( result.return_code != 0 ):
- raise config_lib.ServerCheckError('Failed to reload %s BIND '
- 'server: %s.' % (dns_server, result))
- except fabric_exceptions.NetworkError:
- raise config_lib.ServerCheckError('Could not connect to %s via SSH.' %
- dns_server)
- except ssh.SSHException:
- raise config_lib.ServerCheckError('Lost connection to %s.' %
dns_server)
- finally:
- fabric_network.disconnect_all()
-
def main(args):
"""Collects command line arguments. Exports tree.

@@ -330,6 +474,10 @@
'named.conf will be parsed to find one. If one can
not '
'be found, 953 will be used.', metavar='<rndc-port>',
default=None)
+ parser.add_option('--ssh-failure-retries', action='store', dest='tries',
+ help='Number of times to retry config syncing should '
+ 'an SSH error (e.g. timeout) occur. Defaults to 3.',
+ default=3)

(globals()["options"], args) = parser.parse_args(args)

@@ -347,7 +495,7 @@
if( not options.export_config ):
if( os.path.exists(root_config_dir) ):
shutil.rmtree(root_config_dir)
- config_lib_instance.UnTarDnsTree(options.id)
+ options.id = config_lib_instance.UnTarDnsTree(options.id)

if( options.rndc_key ):
if( options.rndc_key.startswith('./')
@@ -363,9 +511,9 @@
options.rndc_exec, os.path.expanduser(options.rndc_conf))

try:
- PushFiles(options.dns_server, config_lib_instance,
+ ConfigSyncer(options.dns_server, config_lib_instance,
rndc_exec=options.rndc_exec, ssh_id_file=options.ssh_id,
- dns_server_rndc_port=options.rndc_port)
+ dns_server_rndc_port=options.rndc_port, id=options.id).run()
except config_lib.ConfigManagerError as err:
print('ERROR: %s' % err.message)
sys.exit(1)
=======================================
--- /trunk/test/dnsconfigsync_test.py Mon Mar 18 08:38:32 2013
+++ /trunk/test/dnsconfigsync_test.py Fri Jul 19 13:56:34 2013
@@ -57,6 +57,7 @@
import time
import unittest
import tarfile
+import glob
from fabric import api as fabric_api
from fabric import network as fabric_network
from fabric import state as fabric_state
@@ -72,8 +73,8 @@
SCHEMA_FILE = '../roster-core/data/database_schema.sql'
DATA_FILE = 'test_data/test_data.sql'
SSH_ID = 'test_data/roster_id_dsa'
-TESTDIR = u'%s/test_data/unittest_dir/' % os.getcwd()
-BINDDIR = u'%s/test_data/named/' % os.getcwd()
+TESTDIR = unicode(os.path.join(os.getcwd(), 'test_data/unittest_dir/'))
+BINDDIR = unicode(os.path.join(os.getcwd(), 'test_data/bind_dir/'))
SSH_USER = unicode(getpass.getuser())
TEST_DNS_SERVER = u'localhost'
NS_IP_ADDRESS = '127.0.0.1'
@@ -92,8 +93,8 @@
' algorithm hmac-md5;\n'
' secret "yTB86M+Ai8vKJYGYo2ossQ==";\n'
' };')
-RNDC_CONF = '%s/test_data/named/rndc.conf' % os.getcwd()
-RNDC_KEY = '%s/test_data/named/rndc.key' % os.getcwd()
+RNDC_CONF = unicode(os.path.join(os.getcwd(), 'test_data', 'rndc.conf'))
+RNDC_KEY = unicode(os.path.join(os.getcwd(), 'test_data', 'rndc.key'))


class TestCheckConfig(unittest.TestCase):
@@ -152,20 +153,6 @@
if( not os.path.exists(BINDDIR) ):
os.mkdir(BINDDIR)

- def tearDown(self):
- fabric_api.local('killall named', capture=True)
- fabric_network.disconnect_all()
- time.sleep(1) # Wait for disk to settle
- if( os.path.exists(self.lockfile) ):
- os.remove(self.lockfile)
- if( os.path.exists(self.root_config_dir) ):
- shutil.rmtree(self.root_config_dir)
- if( os.path.exists(self.backup_dir) ):
- shutil.rmtree(self.backup_dir)
- if( os.path.exists(TESTDIR) ):
- shutil.rmtree(TESTDIR)
-
- def testNull(self):
self.core_instance.MakeView(u'test_view')
self.core_instance.MakeZone(u'sub.university.lcl', u'master',
u'sub.university.lcl.',
view_name=u'test_view')
@@ -178,22 +165,32 @@
self.assertEqual(output.read(),
'Loading in test_data/test_zone.db\n'
'17 records loaded from zone test_data/test_zone.db\n'
- '17 total records added\n')
+ '17 total records added\n')
output.close()

- self.core_instance.MakeDnsServer(TEST_DNS_SERVER, SSH_USER, BINDDIR,
TESTDIR)
+ self.core_instance.MakeDnsServer(
+ TEST_DNS_SERVER, SSH_USER, BINDDIR, TESTDIR)
self.core_instance.MakeDnsServerSet(u'set1')
self.core_instance.MakeDnsServerSetAssignments(TEST_DNS_SERVER,
u'set1')
self.core_instance.MakeDnsServerSetViewAssignments(u'test_view', 1,
u'set1')
self.core_instance.MakeNamedConfGlobalOption(
- u'set1', u'include "%srndc.key"; options {
pid-file "%snamed.pid";};\n'
- 'controls { inet 127.0.0.1 port %d allow{localhost;} keys
{rndc-key;};};' % (BINDDIR, BINDDIR, self.rndc_port)) # So we can test
+ u'set1',
+ u'include "%s"; options { pid-file "%s";};\n'
+ 'controls { inet 127.0.0.1 port %d allow{localhost;} '
+ 'keys {rndc-key;};};' % (
+ RNDC_KEY,
+ os.path.join(BINDDIR, 'named.pid'),
+ self.rndc_port)) # So we can test
self.core_instance.MakeViewToACLAssignments(u'test_view', u'set1',
u'any', 1)
self.tree_exporter_instance.ExportAllBindTrees()
# Copy blank named.conf to start named with
- shutil.copyfile('test_data/named.blank.conf', '%s/named.conf' %
BINDDIR.rstrip('/'))
- named_file_contents = open('%s/named.conf' %
BINDDIR.rstrip('/'), 'r').read()
+
+ bind_dir_named_conf = os.path.join(BINDDIR, 'named.conf')
+
+ shutil.copyfile('test_data/named.blank.conf',
+ bind_dir_named_conf)
+ named_file_contents = open(bind_dir_named_conf, 'r').read()
named_file_contents = named_file_contents.replace('RNDC_KEY', RNDC_KEY)
named_file_contents = named_file_contents.replace('NAMED_DIR', BINDDIR)
named_file_contents =
named_file_contents.replace('NAMED_PID', '%snamed.pid' % BINDDIR)
@@ -206,58 +203,96 @@
out = fabric_api.local('/usr/sbin/named -p %s -u %s
-c %s/named.conf' % (
self.port, SSH_USER, BINDDIR.rstrip('/')), capture=True)
time.sleep(2)
- # Start of testing tool functionality
- command_string = (
+
+ self.command_string = (
'python %s -d %s --rndc-key %s --rndc-conf %s --rndc-port %s '
'--ssh-id %s --config-file %s ' % (
EXEC, TEST_DNS_SERVER, RNDC_KEY, RNDC_CONF, self.rndc_port,
SSH_ID,
CONFIG_FILE))
+ self.audit_id, filename =
self.config_lib_instance.FindNewestDnsTreeFilename()

- audit_id, filename =
self.config_lib_instance.FindNewestDnsTreeFilename()
- self.config_lib_instance.UnTarDnsTree(audit_id)
+
+ def tearDown(self):
+ fabric_api.local('killall named', capture=True)
+ fabric_network.disconnect_all()
+ time.sleep(1) # Wait for disk to settle
+ if( os.path.exists(self.lockfile) ):
+ os.remove(self.lockfile)
+ if( os.path.exists(self.root_config_dir) ):
+ shutil.rmtree(self.root_config_dir)
+ if( os.path.exists(self.backup_dir) ):
+ shutil.rmtree(self.backup_dir)
+ if( os.path.exists(TESTDIR) ):
+ shutil.rmtree(TESTDIR)
+
+ bind_dirs = glob.glob('%s*' % BINDDIR.rstrip('/'))
+ for bind_dir in bind_dirs:
+ if( os.path.islink(bind_dir) ):
+ os.unlink(bind_dir)
+ else:
+ shutil.rmtree(bind_dir)
+
+ def testConfigSync(self):
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
dns_server_info =
self.config_lib_instance.GetDnsServerInfo(TEST_DNS_SERVER)
dns_server_info['server_info']['bind_version'] = '9.8.1-P1'
- dns_server_info['server_info']['bind_dir'] = '/wrong/dir/'
- dns_server_info['server_info']['test_dir'] = '/wrong/test/dir/'
+ dns_server_info['server_info']['bind_dir'] = '/wrong/bind_dir/'
+ dns_server_info['server_info']['test_dir'] = '/wrong/test_dir/'
dns_server_info['tools'] = {}
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
+ self.config_lib_instance.TarDnsTree(self.audit_id)

- command = os.popen(command_string)
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
- self.assertTrue('ERROR: Failed in moving BIND tree files to server
localhost.' in lines)
-
- self.config_lib_instance.UnTarDnsTree(audit_id)
+ command.close()
+ self.assertTrue(
+ 'ERROR: Failed in moving BIND tree files to server localhost.' in
lines)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
dns_server_info['tools']['tar'] = True
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
+ self.config_lib_instance.TarDnsTree(self.audit_id)

- command = os.popen(command_string)
+
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
- self.assertTrue('ERROR: Failed to move compressed BIND tree to server
localhost.' in lines)
+ command.close()
+ self.assertTrue(
+ 'ERROR: Failed to move compressed BIND tree '
+ 'to server localhost' in lines)

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
dns_server_info['server_info']['test_dir'] = TESTDIR
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
+ self.config_lib_instance.TarDnsTree(self.audit_id)

- # Bind directory raise
- command = os.popen(command_string)
- lines = command.read().split('\n')
- self.assertTrue('ERROR: Failed to move files from test directory to
bind directory on server localhost.' in lines)
+ #Bind directory raise
+ command = os.popen(self.command_string)
+ output = command.read()
+ command.close()
+ self.assertEqual(output,
+ 'ERROR: BIND directory /wrong/bind_dir does not exist on server
localhost\n')

- # Make sure can run successfully without any tools
+ #Make sure can run successfully without any tools
dns_server_info['server_info']['bind_dir'] = BINDDIR
- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
+ self.config_lib_instance.TarDnsTree(self.audit_id)

- command = os.popen(command_string)
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
- #self.assertEqual(lines, [''])
- self.assertTrue(os.path.exists('%s/named.conf' % BINDDIR))
+ command.close()
+ self.assertEqual(lines, [''])
+ self.assertTrue(os.path.exists(os.path.join(BINDDIR, 'named.conf')))

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
+ dns_server_info =
self.config_lib_instance.GetDnsServerInfo(TEST_DNS_SERVER)
+ dns_server_info['server_info']['bind_version'] = '9.8.1-P1'
+ dns_server_info['server_info']['bind_dir'] = BINDDIR
+ dns_server_info['server_info']['test_dir'] = TESTDIR
+ self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
+
shutil.move('%s/localhost/named.conf.a' % self.root_config_dir,
'%s/localhost/named.conf.a.old' % self.root_config_dir)
shutil.move('%s/localhost/named.conf.b' % self.root_config_dir,
@@ -268,23 +303,30 @@
f = open('%s/localhost/named.conf.b' % self.root_config_dir, 'w')
f.write('bad named.conf')
f.close()
+ dns_server_info =
self.config_lib_instance.GetDnsServerInfo(TEST_DNS_SERVER)
dns_server_info['tools']['named-checkconf'] = True
dns_server_info['tools']['named-compilezone'] = False
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
- command = os.popen(command_string)
- lines = command.read().strip('\n')
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+
+ if( not os.path.exists(TESTDIR) ):
+ os.mkdir(TESTDIR)
+
+ command = os.popen(self.command_string)
+ lines = command.read().split('\n')
+ command.close()
self.assertTrue('ERROR: Named.conf check failed on localhost.' in
lines)

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
dns_server_info['tools']['named-compilezone'] = True
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
- command = os.popen(command_string)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
+ command.close()
self.assertTrue('ERROR: Binary named.conf check failed on localhost.'
in lines)

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
os.remove('%s/localhost/named.conf.a' % self.root_config_dir)
os.remove('%s/localhost/named.conf.b' % self.root_config_dir)
shutil.move('%s/localhost/named.conf.a.old' % self.root_config_dir,
@@ -297,32 +339,62 @@
f = open('%s/localhost/named/test_view/sub.university.lcl.db' %
self.root_config_dir, 'w')
f.write('bad zone\n$ORIGIN sub.university.lcl.')
f.close()
- self.config_lib_instance.TarDnsTree(audit_id)
- command = os.popen(command_string)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
+ command.close()
self.assertTrue('ERROR: Failed to compile zone sub.university.lcl.' in
lines)

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
dns_server_info['tools']['named-checkzone'] = True
self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
- self.config_lib_instance.TarDnsTree(audit_id)
- command = os.popen(command_string)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
+ command.close()
self.assertTrue('ERROR: Zone sub.university.lcl did not pass the
check.' in lines)

- self.config_lib_instance.UnTarDnsTree(audit_id)
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
shutil.move('%s/sub.university.lcl.db.old' % self.backup_dir,
'%s/localhost/named/test_view/sub.university.lcl.db' %
self.root_config_dir)
- self.config_lib_instance.TarDnsTree(audit_id)
- command = os.popen(command_string)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+
+ for bind_dir in glob.glob('%s*' % BINDDIR.rstrip('/')):
+ if( os.path.isdir(bind_dir) ):
+ shutil.rmtree(bind_dir)
+ elif( os.path.islink(bind_dir) ):
+ os.unlink(bind_dir)
+ os.mkdir(BINDDIR)
+
+ command = os.popen(self.command_string)
lines = command.read().split('\n')
+ command.close()

- result = os.popen('rndc -c %s -k %s -p %s reload' % (
- RNDC_CONF, RNDC_KEY, self.rndc_port)).read()
- self.assertEqual(result, 'server reload successful\n')
-
self.assertTrue(os.path.exists('%s/named/test_view/sub.university.lcl.aa' %
BINDDIR))
+ self.assertEqual(lines, [''])
+
self.assertTrue(os.path.exists(os.path.join(BINDDIR, 'named/test_view/sub.university.lcl.aa')))
+ self.assertTrue(os.path.exists('%s/named.conf' % BINDDIR))
self.assertTrue(os.path.exists('%s/named/named.ca' % BINDDIR))
+ self.assertTrue(os.path.exists('%s/named/named.conf' % BINDDIR))
+
+ self.core_instance.MakeRecord(u'a', u'desktop-2',
u'sub.university.lcl',
+ { u'assignment_ip': u'192.168.1.105' }, view_name=u'test_view')
+ self.tree_exporter_instance.ExportAllBindTrees()
+
+ #Remember, 1 for the new A record, and 1 for the export
+ self.audit_id += 2
+
+ self.config_lib_instance.UnTarDnsTree(self.audit_id)
+ self.config_lib_instance.WriteDnsServerInfo(dns_server_info)
+ self.config_lib_instance.TarDnsTree(self.audit_id)
+
+ command = os.popen(self.command_string)
+ lines = command.read().split('\n')
+ command.close()
+ self.assertEqual(lines, [''])
+
self.assertTrue(os.path.exists('%s/named.conf' % BINDDIR))
+ self.assertTrue(os.path.exists('%s/named/named.ca' % BINDDIR))
+ self.assertTrue(os.path.exists('%s/named/named.conf' % BINDDIR))

if( __name__ == '__main__' ):
unittest.main()
=======================================
--- /trunk/test/dnsexportconfig_test.py Fri May 10 09:51:01 2013
+++ /trunk/test/dnsexportconfig_test.py Fri Jul 19 13:56:34 2013
@@ -70,7 +70,7 @@
DATA_FILE = 'test_data/test_data.sql'
TESTDIR = u'%s/test_data/unittest_dir/' % os.getcwd()
#BINDDIR = u'/etc/bind/'
-BINDDIR = u'%s/test_data/named/' % os.getcwd()
+BINDDIR = u'%s/test_data/bind_dir/' % os.getcwd()
THREADING_DIR = os.path.join(BINDDIR, 'threading')
TEST_DNS_SERVER = u'localhost'
SSH_ID = 'test_data/roster_id_dsa'
Reply all
Reply to author
Forward
0 new messages