# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
import unittest
+import shutil
import socket
import io
from isc.testutils.tsigctx_mock import MockTSIGContext
TEST_MASTER_IPV6_ADDRINFO = (socket.AF_INET6, socket.SOCK_STREAM,
socket.IPPROTO_TCP, '',
(TEST_MASTER_IPV6_ADDRESS, 53))
+
+TESTDATA_SRCDIR = os.getenv("TESTDATASRCDIR")
+TESTDATA_OBJDIR = os.getenv("TESTDATAOBJDIR")
# XXX: This should be a non priviledge port that is unlikely to be used.
# If some other process uses this port test will fail.
TEST_MASTER_PORT = '53535'
def add_rrset(self, rrset):
self.diffs.append(('add', rrset))
- def remove_rrset(self, rrset):
+ def delete_rrset(self, rrset):
self.diffs.append(('remove', rrset))
def commit(self):
# method in the superclass
self.xfrin_started_master_addr = master_addrinfo[2][0]
self.xfrin_started_master_port = master_addrinfo[2][1]
- return Xfrin.xfrin_start(self, zone_name, rrclass, db_file,
+ return Xfrin.xfrin_start(self, zone_name, rrclass, None,
master_addrinfo, tsig_key,
check_soa)
class MockXfrinConnection(XfrinConnection):
def __init__(self, sock_map, zone_name, rrclass, db_file, shutdown_event,
master_addr):
- super().__init__(sock_map, zone_name, rrclass, db_file, shutdown_event,
- master_addr)
+ super().__init__(sock_map, zone_name, rrclass, MockDataSourceClient(),
+ db_file, shutdown_event, master_addr)
self.query_data = b''
self.reply_data = b''
self.force_time_out = False
self.qid = None
self.response_generator = None
- # The following three implement a simplified mock of DataSourceClient
- # and ZoneFinder classes for testing purposes.
- def _get_datasrc_client(self, rrclass):
- return MockDataSourceClient()
-
def _asyncore_loop(self):
if self.force_close:
self.handle_close()
c.close()
def test_init_chclass(self):
- c = XfrinConnection({}, TEST_ZONE_NAME, RRClass.CH(), TEST_DB_FILE,
- threading.Event(), TEST_MASTER_IPV4_ADDRINFO)
+ c = MockXfrinConnection({}, TEST_ZONE_NAME, RRClass.CH(), TEST_DB_FILE,
+ threading.Event(), TEST_MASTER_IPV4_ADDRINFO)
axfrmsg = c._create_query(RRType.AXFR())
self.assertEqual(axfrmsg.get_question()[0].get_class(),
RRClass.CH())
self._create_broken_response_data()
self.assertEqual(XFRIN_FAIL, self.conn.do_xfrin(False, True))
+class TestIXFRSessionWithSQLite3(TestXfrinConnection):
+ '''Tests for IXFR sessions using an SQLite3 DB.
+
+ These are provided mainly to confirm the implementation actually works
+ in an environment closer to actual operational environments. So we
+ only check a few common cases; other details are tested using mock
+ data sources.
+
+ '''
+ def setUp(self):
+ self.sqlite3db_src = TESTDATA_SRCDIR + '/example.com.sqlite3'
+ self.sqlite3db_obj = TESTDATA_SRCDIR + '/example.com.sqlite3.copy'
+ super().setUp()
+ if os.path.exists(self.sqlite3db_obj):
+ os.unlink(self.sqlite3db_obj)
+ shutil.copyfile(self.sqlite3db_src, self.sqlite3db_obj)
+ self.conn._datasrc_client = DataSourceClient(self.sqlite3db_obj)
+
+ def tearDown(self):
+ if os.path.exists(self.sqlite3db_obj):
+ os.unlink(self.sqlite3db_obj)
+
+ def get_zone_serial(self):
+ result, finder = self.conn._datasrc_client.find_zone(TEST_ZONE_NAME)
+ self.assertEqual(DataSourceClient.SUCCESS, result)
+ result, soa = finder.find(TEST_ZONE_NAME, RRType.SOA(),
+ None, ZoneFinder.FIND_DEFAULT)
+ self.assertEqual(ZoneFinder.SUCCESS, result)
+ self.assertEqual(1, soa.get_rdata_count())
+ return get_soa_serial(soa.get_rdata()[0])
+
+ def test_do_xfrin_sqlite3(self):
+ def create_ixfr_response():
+ self.conn.reply_data = self.conn.create_response_data(
+ questions=[Question(TEST_ZONE_NAME, TEST_RRCLASS,
+ RRType.IXFR())],
+ answers=[soa_rrset, begin_soa_rrset, soa_rrset, soa_rrset])
+ self.conn.response_generator = create_ixfr_response
+
+ self.assertEqual(1230, self.get_zone_serial())
+ self.assertEqual(XFRIN_OK, self.conn.do_xfrin(False, True))
+ self.assertEqual(1234, self.get_zone_serial())
+
class TestXfrinRecorder(unittest.TestCase):
def setUp(self):
self.recorder = XfrinRecorder()
# This is the beginning state of one difference sequence (changes
# for one SOA update). We need to create a new Diff object now.
conn._diff = Diff(conn._datasrc_client, conn._zone_name)
- conn._diff.remove_data(rr)
+ conn._diff.delete_data(rr)
self.set_xfrstate(conn, XfrinIXFRDelete())
return True
conn._current_serial = get_soa_serial(rr.get_rdata()[0])
self.set_xfrstate(conn, XfrinIXFRAddSOA())
return False
- conn._diff.remove_data(rr)
+ conn._diff.delete_data(rr)
return True
class XfrinIXFRAddSOA(XfrinState):
'''Do xfrin in this class. '''
def __init__(self,
- sock_map, zone_name, rrclass, db_file, shutdown_event,
- master_addrinfo, tsig_key = None, verbose = False,
- idle_timeout = 60):
- ''' idle_timeout: max idle time for read data from socket.
- db_file: specify the data source file.
- check_soa: when it's true, check soa first before sending xfr query
+ sock_map, zone_name, rrclass, datasrc_client, db_file,
+ shutdown_event, master_addrinfo, tsig_key = None,
+ verbose=False, idle_timeout=60):
+ '''Constructor of the XfirnConnection class.
+
+ idle_timeout: max idle time for read data from socket.
+ datasrc_client: the data source client object used for the XFR session.
+ This will eventually replace db_file completely.
+ db_file: specify the data source file (should soon be deprecated).
+
'''
asyncore.dispatcher.__init__(self, map=sock_map)
# Data source handlers
self._db_file = db_file # temporary for sqlite3 specific code
- self._datasrc_client = self._get_datasrc_client(rrclass)
+ self._datasrc_client = datasrc_client
self.create_socket(master_addrinfo[0], master_addrinfo[1])
self._sock_map = sock_map
def __create_tsig_ctx(self, key):
return TSIGContext(key)
- def _get_datasrc_client(self, rrclass):
- # TODO: create a DataSourceClient assuming the sqlite3 backend for now
- return None
-
def __set_xfrstate(self, new_state):
self.__state = new_state
shutdown_event, master_addrinfo, check_soa, verbose,
tsig_key):
xfrin_recorder.increment(zone_name)
+
+ # Create a data source client used in this XFR session. Right now we
+ # still assume an sqlite3-based data source, and use both the old and new
+ # data source APIs. We also need to use a mock client for tests.
+ # For a temporary workaround to deal with these situations, we skip the
+ # creation when the given file is none (the test case). Eventually
+ # this code will be much cleaner.
+ datasrc_client = None
+ if db_file is not None:
+ datasrc_client = DataSourceClient(db_file)
+
+ # Create a TCP connection for the XFR session and perform the operation.
sock_map = {}
- conn = XfrinConnection(sock_map, zone_name, rrclass, db_file,
- shutdown_event, master_addrinfo,
+ conn = XfrinConnection(sock_map, zone_name, rrclass, datasrc_client,
+ db_file, shutdown_event, master_addrinfo,
tsig_key, verbose)
ret = XFRIN_FAIL
if conn.connect_to_master():
while not self._shutdown_event.is_set():
self._cc_check_command()
- def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo, tsig_key,
- check_soa = True):
+ def xfrin_start(self, zone_name, rrclass, db_file, master_addrinfo,
+ tsig_key, check_soa=True):
if "pydnspp" not in sys.modules:
return (1, "xfrin failed, can't load dns message python library: 'pydnspp'")