Commit 25789340 authored by JINMEI Tatuya's avatar JINMEI Tatuya
Browse files

[2013] added data surce configuration.

expecting we'll soon replace it fundamentally, this only provides minimal
feature in a separate function so we can just remove that function when
we are ready.
parent 2b243925
......@@ -21,11 +21,13 @@ import isc
import bind10_config
from isc.dns import *
import isc.ddns.session
from isc.ddns.zone_config import ZoneConfig
from isc.config.ccsession import *
from isc.cc import SessionError, SessionTimeout
import isc.util.process
import isc.util.cio.socketsession
import isc.server_common.tsig_keyring
from isc.datasrc import DataSourceClient
import select
import errno
......@@ -41,26 +43,37 @@ isc.log.init("b10-ddns")
logger = isc.log.Logger("ddns")
TRACE_BASIC = logger.DBGLVL_TRACE_BASIC
# Well known path settings. We need to define
# SPECFILE_LOCATION: ddns configuration spec file
# SOCKET_FILE: Unix domain socket file to communicate with b10-auth
# AUTH_SPECFILE_LOCATION: b10-auth configuration spec file (tentatively
# necessarily for sqlite3-only-and-older-datasrc-API stuff). This should be
# gone once we migrate to the new API and start using generalized config.
#
# If B10_FROM_SOURCE is set in the environment, we use data files
# from a directory relative to that, otherwise we use the ones
# installed on the system
if "B10_FROM_SOURCE" in os.environ:
SPECFILE_LOCATION = os.environ["B10_FROM_SOURCE"] + os.sep + \
"src" + os.sep + "bin" + os.sep + "ddns" + os.sep + "ddns.spec"
SPECFILE_PATH = os.environ["B10_FROM_SOURCE"] + "/src/bin/ddns"
else:
PREFIX = "@prefix@"
DATAROOTDIR = "@datarootdir@"
SPECFILE_LOCATION = "@datadir@" + os.sep + "@PACKAGE@" + os.sep + "ddns.spec"
SPECFILE_LOCATION = SPECFILE_LOCATION.replace("${datarootdir}", DATAROOTDIR)\
.replace("${prefix}", PREFIX)
SPECFILE_PATH = "@datadir@/@PACKAGE@".replace("${datarootdir}", DATAROOTDIR)
SPECFILE_PATH = SPECFILE_PATH.replace("${prefix}", PREFIX)
SOCKET_FILE = bind10_config.DATA_PATH + '/ddns_socket'
if "B10_FROM_BUILD" in os.environ:
AUTH_SPECFILE_PATH = os.environ["B10_FROM_BUILD"] + "/src/bin/auth"
if "B10_FROM_SOURCE_LOCALSTATEDIR" in os.environ:
SOCKET_FILE = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"] + \
"/ddns_socket"
SOCKET_FILE_PATH = os.environ["B10_FROM_SOURCE_LOCALSTATEDIR"]
else:
SOCKET_FILE = os.environ["B10_FROM_BUILD"] + "/ddns_socket"
SOCKET_FILE_PATH = os.environ["B10_FROM_BUILD"]
else:
SOCKET_FILE_PATH = bind10_config.DATA_PATH
AUTH_SPECFILE_PATH = SPECFILE_PATH
SPECFILE_LOCATION = SPECFILE_PATH + "/ddns.spec"
SOCKET_FILE = SOCKET_FILE_PATH + '/ddns_socket'
AUTH_SPECFILE_LOCATION = AUTH_SPECFILE_PATH + '/auth.spec'
isc.util.process.rename()
......@@ -95,6 +108,43 @@ def clear_socket():
if os.path.exists(SOCKET_FILE):
os.remove(SOCKET_FILE)
def get_datasrc_client(cc_session):
'''Return data source client for update requests.
This is supposed to have a very short lifetime and should soon be replaced
with generic data source configuration framework. Based on that
observation we simply hardcode everything except the SQLite3 database file,
which will be retrieved from the auth server configuration (this behavior
will also be deprecated). When something goes wrong with it this function
still returns a dummy client so that the caller doesn't have to bother
to handle the error (which would also have to be replaced anyway).
The caller will subsequently call its find_zone method via an update
session object, which will result in an exception, and then result in
a SERVFAIL response.
Once we are ready for introducing the general framework, the whole
function will simply be removed.
'''
try:
HARDCODED_DATASRC_CLASS = RRClass.IN()
file, is_default = cc_session.get_remote_config_value("Auth",
"database_file")
# See xfrout.py:get_db_file() for this trick:
if is_default and "B10_FROM_BUILD" in os.environ:
file = os.environ["B10_FROM_BUILD"] + "/bind10_zones.sqlite3"
datasrc_config = '{ "database_file": "' + file + '"}'
return HARDCODED_DATASRC_CLASS, DataSourceClient('sqlite3',
datasrc_config)
except isc.datasrc.Error as ex:
class DummyDataSourceClient:
def __init__(self, ex):
self.__ex = ex
def find_zone(self, zone_name):
raise isc.datasrc.Error(self.__ex)
return HARDCODED_DATASRC_CLASS, DummyDataSourceClient(ex)
class DDNSServer:
def __init__(self, cc_session=None):
'''
......@@ -114,7 +164,10 @@ class DDNSServer:
self._config_data = self._cc.get_full_config()
self._cc.start()
self._cc.add_remote_config(AUTH_SPECFILE_LOCATION)
isc.server_common.tsig_keyring.init_keyring(self._cc)
self._shutdown = False
# List of the session receivers where we get the requests
self._socksession_receivers = {}
......@@ -261,9 +314,13 @@ class DDNSServer:
# TODO: Don't propagate most of the exceptions (like datasrc errors),
# just drop the packet.
# Let an update session object handle the request.
# Let an update session object handle the request. Note: things around
# ZoneConfig will soon be substantially revised. For now we don't
# bother to generalize it.
datasrc_class, datasrc_client = get_datasrc_client(self._cc)
zone_cfg = ZoneConfig([], datasrc_class, datasrc_client, {})
update_session = self._UpdateSessionClass(self.__request_msg,
remote_addr, None)
remote_addr, zone_cfg)
result, zname, zclass = update_session.handle()
# If the request should be dropped, we're done; otherwise, send the
......
......@@ -25,5 +25,6 @@ endif
$(LIBRARY_PATH_PLACEHOLDER) \
PYTHONPATH=$(COMMON_PYTHON_PATH):$(abs_top_builddir)/src/bin/ddns:$(abs_top_builddir)/src/lib/dns/python/.libs:$(abs_top_builddir)/src/lib/util/io/.libs \
TESTDATASRCDIR=$(abs_srcdir)/testdata/ \
TESTDATA_PATH=$(abs_top_srcdir)/src/lib/testutils/testdata \
$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
done
......@@ -15,19 +15,21 @@
'''Tests for the DDNS module'''
import unittest
import isc
from isc.ddns.session import *
from isc.dns import *
import isc.util.cio.socketsession
from isc.datasrc import DataSourceClient
import ddns
import isc.config
import select
import errno
import isc.util.cio.socketsession
import os
import select
import shutil
import socket
import os.path
from isc.dns import *
import unittest
# Some common test parameters
TESTDATA_PATH = os.environ['TESTDATA_PATH'] + os.sep
READ_ZONE_DB_FILE = TESTDATA_PATH + "rwtest.sqlite3" # original, to be copied
TEST_ZONE_NAME = Name('example.org')
UPDATE_RRTYPE = RRType.SOA()
TEST_QID = 5353 # arbitrary chosen
......@@ -132,13 +134,15 @@ class FakeKeyringModule:
return TEST_TSIG_KEYRING
class MyCCSession(isc.config.ConfigData):
'''Fake session with minimal interface compliance'''
'''Fake session with minimal interface compliance.'''
def __init__(self):
module_spec = isc.config.module_spec_from_file(
ddns.SPECFILE_LOCATION)
isc.config.ConfigData.__init__(self, module_spec)
self._started = False
self._stopped = False
# Used as the return value of get_remote_config_value. Customizable.
self.auth_db_file = READ_ZONE_DB_FILE
def start(self):
'''Called by DDNSServer initialization, but not used in tests'''
......@@ -154,6 +158,13 @@ class MyCCSession(isc.config.ConfigData):
"""
return FakeSocket(1)
def add_remote_config(self, spec_file_name):
pass
def get_remote_config_value(self, module_name, item):
if module_name == "Auth" and item == "database_file":
return self.auth_db_file, False
class MyDDNSServer():
'''Fake DDNS server used to test the main() function'''
def __init__(self):
......@@ -616,6 +627,37 @@ class TestMain(unittest.TestCase):
self.assertRaises(BaseException, ddns.main, self._server)
self.assertTrue(self._server.exception_raised)
class TestConfig(unittest.TestCase):
def setUp(self):
self.__ccsession = MyCCSession()
def test_file_path(self):
# Check some common paths
self.assertEqual(os.environ["B10_FROM_BUILD"] + "/ddns_socket",
ddns.SOCKET_FILE)
self.assertEqual(os.environ["B10_FROM_SOURCE"] +
"/src/bin/ddns/ddns.spec", ddns.SPECFILE_LOCATION)
self.assertEqual(os.environ["B10_FROM_BUILD"] +
"/src/bin/auth/auth.spec",
ddns.AUTH_SPECFILE_LOCATION)
def test_get_datasrc_client(self):
# The test sqlite DB should contain the example.org zone.
rrclass, datasrc_client = ddns.get_datasrc_client(self.__ccsession)
self.assertEqual(RRClass.IN(), rrclass)
self.assertEqual(DataSourceClient.SUCCESS,
datasrc_client.find_zone(Name('example.org'))[0])
def test_get_datasrc_client_fail(self):
# DB file is in a non existent directory, and creatng the client
# will fail. get_datasrc_client will return a dummy client, which
# will subsequently make find_zone() fail.
self.__ccsession.auth_db_file = './notexistentdir/somedb.sqlite3'
rrclass, datasrc_client = ddns.get_datasrc_client(self.__ccsession)
self.assertEqual(RRClass.IN(), rrclass)
self.assertRaises(isc.datasrc.Error,
datasrc_client.find_zone, Name('example.org'))
if __name__== "__main__":
isc.log.resetUnitTestRootLogger()
unittest.main()
Markdown is supported
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment