Browse Source

Merge branch 'master' into work/loader

Conflicts:
	src/lib/acl/tests/acl_test.cc
Michal 'vorner' Vaner 14 years ago
parent
commit
4c250f85ed

+ 6 - 0
ChangeLog

@@ -1,3 +1,9 @@
+257.	[bug]           y-aharen
+	Fixed a bug an instance of IntervalTimerImpl may be destructed 
+	while deadline_timer is holding the handler. This fix addresses
+	occasional failure of IntervalTimerTest.destructIntervalTimer.
+	(Trac #957, git e59c215e14b5718f62699ec32514453b983ff603)
+
 256.	[bug]		jerry
 256.	[bug]		jerry
 	src/bin/xfrin: update xfrin to check TSIG before other part of
 	src/bin/xfrin: update xfrin to check TSIG before other part of
 	incoming message.
 	incoming message.

+ 2 - 1
configure.ac

@@ -885,8 +885,8 @@ AC_OUTPUT([doc/version.ent
            src/lib/config/tests/data_def_unittests_config.h
            src/lib/config/tests/data_def_unittests_config.h
            src/lib/python/isc/config/tests/config_test
            src/lib/python/isc/config/tests/config_test
            src/lib/python/isc/cc/tests/cc_test
            src/lib/python/isc/cc/tests/cc_test
-           src/lib/python/isc/log/tests/log_test
            src/lib/python/isc/notify/tests/notify_out_test
            src/lib/python/isc/notify/tests/notify_out_test
+           src/lib/python/isc/log/tests/log_console.py
            src/lib/dns/gen-rdatacode.py
            src/lib/dns/gen-rdatacode.py
            src/lib/python/bind10_config.py
            src/lib/python/bind10_config.py
            src/lib/dns/tests/testdata/gen-wiredata.py
            src/lib/dns/tests/testdata/gen-wiredata.py
@@ -928,6 +928,7 @@ AC_OUTPUT([doc/version.ent
            chmod +x src/lib/log/tests/destination_test.sh
            chmod +x src/lib/log/tests/destination_test.sh
            chmod +x src/lib/log/tests/severity_test.sh
            chmod +x src/lib/log/tests/severity_test.sh
            chmod +x src/lib/util/python/mkpywrapper.py
            chmod +x src/lib/util/python/mkpywrapper.py
+           chmod +x src/lib/python/isc/log/tests/log_console.py
            chmod +x tests/system/conf.sh
            chmod +x tests/system/conf.sh
           ])
           ])
 AC_OUTPUT
 AC_OUTPUT

+ 3 - 3
src/bin/xfrout/tests/xfrout_test.py.in

@@ -116,8 +116,8 @@ class TestXfroutSession(unittest.TestCase):
 
 
     def setUp(self):
     def setUp(self):
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
         self.sock = MySocket(socket.AF_INET,socket.SOCK_STREAM)
-        self.log = isc.log.NSLogger('xfrout', '',  severity = 'critical', log_to_console = False )
-        self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), self.log, TSIGKeyRing())
+        #self.log = isc.log.NSLogger('xfrout', '',  severity = 'critical', log_to_console = False )
+        self.xfrsess = MyXfroutSession(self.sock, None, Dbserver(), TSIGKeyRing())
         self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
         self.mdata = bytes(b'\xd6=\x00\x00\x00\x01\x00\x00\x00\x00\x00\x00\x07example\x03com\x00\x00\xfc\x00\x01')
         self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
         self.soa_record = (4, 3, 'example.com.', 'com.example.', 3600, 'SOA', None, 'master.example.com. admin.example.com. 1234 3600 1800 2419200 7200')
 
 
@@ -520,7 +520,7 @@ class MyUnixSockServer(UnixSockServer):
         self._shutdown_event = threading.Event()
         self._shutdown_event = threading.Event()
         self._max_transfers_out = 10
         self._max_transfers_out = 10
         self._cc = MyCCSession()
         self._cc = MyCCSession()
-        self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
+        #self._log = isc.log.NSLogger('xfrout', '', severity = 'critical', log_to_console = False )
 
 
 class TestUnixSockServer(unittest.TestCase):
 class TestUnixSockServer(unittest.TestCase):
     def setUp(self):
     def setUp(self):

+ 42 - 39
src/bin/xfrout/xfrout.py.in

@@ -26,7 +26,7 @@ from isc.datasrc import sqlite3_ds
 from socketserver import *
 from socketserver import *
 import os
 import os
 from isc.config.ccsession import *
 from isc.config.ccsession import *
-from isc.log.log import *
+#from isc.log.log import *
 from isc.cc import SessionError, SessionTimeout
 from isc.cc import SessionError, SessionTimeout
 from isc.notify import notify_out
 from isc.notify import notify_out
 import isc.util.process
 import isc.util.process
@@ -88,13 +88,13 @@ def get_rrset_len(rrset):
 
 
 
 
 class XfroutSession():
 class XfroutSession():
-    def __init__(self, sock_fd, request_data, server, log, tsig_key_ring):
+    def __init__(self, sock_fd, request_data, server, tsig_key_ring):
         # The initializer for the superclass may call functions
         # The initializer for the superclass may call functions
         # that need _log to be set, so we set it first
         # that need _log to be set, so we set it first
         self._sock_fd = sock_fd
         self._sock_fd = sock_fd
         self._request_data = request_data
         self._request_data = request_data
         self._server = server
         self._server = server
-        self._log = log
+        #self._log = log
         self._tsig_key_ring = tsig_key_ring
         self._tsig_key_ring = tsig_key_ring
         self._tsig_ctx = None
         self._tsig_ctx = None
         self._tsig_len = 0
         self._tsig_len = 0
@@ -110,7 +110,8 @@ class XfroutSession():
             self.dns_xfrout_start(self._sock_fd, self._request_data)
             self.dns_xfrout_start(self._sock_fd, self._request_data)
             #TODO, avoid catching all exceptions
             #TODO, avoid catching all exceptions
         except Exception as e:
         except Exception as e:
-            self._log.log_message("error", str(e))
+            #self._log.log_message("error", str(e))
+            pass
 
 
         os.close(self._sock_fd)
         os.close(self._sock_fd)
 
 
@@ -137,7 +138,7 @@ class XfroutSession():
             rcode = self._check_request_tsig(msg, mdata)
             rcode = self._check_request_tsig(msg, mdata)
 
 
         except Exception as err:
         except Exception as err:
-            self._log.log_message("error", str(err))
+            #self._log.log_message("error", str(err))
             return Rcode.FORMERR(), None
             return Rcode.FORMERR(), None
 
 
         return rcode, msg
         return rcode, msg
@@ -244,16 +245,17 @@ class XfroutSession():
         zone_name = self._get_query_zone_name(msg)
         zone_name = self._get_query_zone_name(msg)
         rcode_ = self._check_xfrout_available(zone_name)
         rcode_ = self._check_xfrout_available(zone_name)
         if rcode_ != Rcode.NOERROR():
         if rcode_ != Rcode.NOERROR():
-            self._log.log_message("info", "transfer of '%s/IN' failed: %s",
-                                  zone_name, rcode_.to_text())
+            #self._log.log_message("info", "transfer of '%s/IN' failed: %s",
+            #                      zone_name, rcode_.to_text())
             return self. _reply_query_with_error_rcode(msg, sock_fd, rcode_)
             return self. _reply_query_with_error_rcode(msg, sock_fd, rcode_)
 
 
         try:
         try:
-            self._log.log_message("info", "transfer of '%s/IN': AXFR started" % zone_name)
+            #self._log.log_message("info", "transfer of '%s/IN': AXFR started" % zone_name)
             self._reply_xfrout_query(msg, sock_fd, zone_name)
             self._reply_xfrout_query(msg, sock_fd, zone_name)
-            self._log.log_message("info", "transfer of '%s/IN': AXFR end" % zone_name)
+            #self._log.log_message("info", "transfer of '%s/IN': AXFR end" % zone_name)
         except Exception as err:
         except Exception as err:
-            self._log.log_message("error", str(err))
+            #self._log.log_message("error", str(err))
+            pass
 
 
         self._server.decrease_transfers_counter()
         self._server.decrease_transfers_counter()
         return
         return
@@ -317,7 +319,7 @@ class XfroutSession():
 
 
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
         for rr_data in sqlite3_ds.get_zone_datas(zone_name, self._server.get_db_file()):
             if  self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
             if  self._server._shutdown_event.is_set(): # Check if xfrout is shutdown
-                self._log.log_message("info", "xfrout process is being shutdown")
+                #self._log.log_message("info", "xfrout process is being shutdown")
                 return
                 return
             # TODO: RRType.SOA() ?
             # TODO: RRType.SOA() ?
             if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
             if RRType(rr_data[5]) == RRType("SOA"): #ignore soa record
@@ -357,7 +359,7 @@ class XfroutSession():
 class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
 class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
     '''The unix domain socket server which accept xfr query sent from auth server.'''
     '''The unix domain socket server which accept xfr query sent from auth server.'''
 
 
-    def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc, log):
+    def __init__(self, sock_file, handle_class, shutdown_event, config_data, cc):
         self._remove_unused_sock_file(sock_file)
         self._remove_unused_sock_file(sock_file)
         self._sock_file = sock_file
         self._sock_file = sock_file
         socketserver_mixin.NoPollMixIn.__init__(self)
         socketserver_mixin.NoPollMixIn.__init__(self)
@@ -366,7 +368,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
         self._transfers_counter = 0
         self._transfers_counter = 0
         self._shutdown_event = shutdown_event
         self._shutdown_event = shutdown_event
         self._write_sock, self._read_sock = socket.socketpair()
         self._write_sock, self._read_sock = socket.socketpair()
-        self._log = log
+        #self._log = log
         self.update_config_data(config_data)
         self.update_config_data(config_data)
         self._cc = cc
         self._cc = cc
 
 
@@ -394,7 +396,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
         try:
         try:
             request, client_address = self.get_request()
             request, client_address = self.get_request()
         except socket.error:
         except socket.error:
-            self._log.log_message("error", "Failed to fetch request")
+            #self._log.log_message("error", "Failed to fetch request")
             return
             return
 
 
         # Check self._shutdown_event to ensure the real shutdown comes.
         # Check self._shutdown_event to ensure the real shutdown comes.
@@ -408,7 +410,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
                     (rlist, wlist, xlist) = ([], [], [])
                     (rlist, wlist, xlist) = ([], [], [])
                     continue
                     continue
                 else:
                 else:
-                    self._log.log_message("error", "Error with select(): %s" %e)
+                    #self._log.log_message("error", "Error with select(): %s" %e)
                     break
                     break
 
 
             # self.server._shutdown_event will be set by now, if it is not a false
             # self.server._shutdown_event will be set by now, if it is not a false
@@ -419,8 +421,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
             try:
             try:
                 self.process_request(request)
                 self.process_request(request)
             except:
             except:
-                self._log.log_message("error", "Exception happened during processing of %s"
-                                      % str(client_address))
+                #self._log.log_message("error", "Exception happened during processing of %s"
+                #                      % str(client_address))
                 break
                 break
 
 
     def _handle_request_noblock(self):
     def _handle_request_noblock(self):
@@ -438,8 +440,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
             # This may happen when one xfrout process try to connect to
             # This may happen when one xfrout process try to connect to
             # xfrout unix socket server, to check whether there is another
             # xfrout unix socket server, to check whether there is another
             # xfrout running.
             # xfrout running.
-            if sock_fd == FD_COMM_ERROR:
-                self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
+            #if sock_fd == FD_COMM_ERROR:
+                #self._log.log_message("error", "Failed to receive the file descriptor for XFR connection")
             return
             return
 
 
         # receive request msg
         # receive request msg
@@ -456,7 +458,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
 
 
     def finish_request(self, sock_fd, request_data):
     def finish_request(self, sock_fd, request_data):
         '''Finish one request by instantiating RequestHandlerClass.'''
         '''Finish one request by instantiating RequestHandlerClass.'''
-        self.RequestHandlerClass(sock_fd, request_data, self, self._log, self.tsig_key_ring)
+        self.RequestHandlerClass(sock_fd, request_data, self, self.tsig_key_ring)
 
 
     def _remove_unused_sock_file(self, sock_file):
     def _remove_unused_sock_file(self, sock_file):
         '''Try to remove the socket file. If the file is being used
         '''Try to remove the socket file. If the file is being used
@@ -464,8 +466,8 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
         If it's not a socket file or nobody is listening
         If it's not a socket file or nobody is listening
         , it will be removed. If it can't be removed, exit from python. '''
         , it will be removed. If it can't be removed, exit from python. '''
         if self._sock_file_in_use(sock_file):
         if self._sock_file_in_use(sock_file):
-            self._log.log_message("error", "Fail to start xfrout process, unix socket file '%s'"
-                                 " is being used by another xfrout process\n" % sock_file)
+            #self._log.log_message("error", "Fail to start xfrout process, unix socket file '%s'"
+            #                     " is being used by another xfrout process\n" % sock_file)
             sys.exit(0)
             sys.exit(0)
         else:
         else:
             if not os.path.exists(sock_file):
             if not os.path.exists(sock_file):
@@ -474,7 +476,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
             try:
             try:
                 os.unlink(sock_file)
                 os.unlink(sock_file)
             except OSError as err:
             except OSError as err:
-                self._log.log_message("error", "[b10-xfrout] Fail to remove file %s: %s\n" % (sock_file, err))
+                #self._log.log_message("error", "[b10-xfrout] Fail to remove file %s: %s\n" % (sock_file, err))
                 sys.exit(0)
                 sys.exit(0)
 
 
     def _sock_file_in_use(self, sock_file):
     def _sock_file_in_use(self, sock_file):
@@ -495,17 +497,18 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
         try:
         try:
             os.unlink(self._sock_file)
             os.unlink(self._sock_file)
         except Exception as e:
         except Exception as e:
-            self._log.log_message('error', str(e))
+            #self._log.log_message('error', str(e))
+            pass
 
 
     def update_config_data(self, new_config):
     def update_config_data(self, new_config):
         '''Apply the new config setting of xfrout module. '''
         '''Apply the new config setting of xfrout module. '''
-        self._log.log_message('info', 'update config data start.')
+        #self._log.log_message('info', 'update config data start.')
         self._lock.acquire()
         self._lock.acquire()
         self._max_transfers_out = new_config.get('transfers_out')
         self._max_transfers_out = new_config.get('transfers_out')
         self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
         self.set_tsig_key_ring(new_config.get('tsig_key_ring'))
-        self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
+        #self._log.log_message('info', 'max transfer out : %d', self._max_transfers_out)
         self._lock.release()
         self._lock.release()
-        self._log.log_message('info', 'update config data complete.')
+        #self._log.log_message('info', 'update config data complete.')
 
 
     def set_tsig_key_ring(self, key_list):
     def set_tsig_key_ring(self, key_list):
         """Set the tsig_key_ring , given a TSIG key string list representation. """
         """Set the tsig_key_ring , given a TSIG key string list representation. """
@@ -521,7 +524,7 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
                 self.tsig_key_ring.add(TSIGKey(key_item))
                 self.tsig_key_ring.add(TSIGKey(key_item))
             except InvalidParameter as ipe:
             except InvalidParameter as ipe:
                 errmsg = "bad TSIG key string: " + str(key_item)
                 errmsg = "bad TSIG key string: " + str(key_item)
-                self._log.log_message('error', '%s' % errmsg)
+                #self._log.log_message('error', '%s' % errmsg)
 
 
     def get_db_file(self):
     def get_db_file(self):
         file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
         file, is_default = self._cc.get_remote_config_value("Auth", "database_file")
@@ -553,16 +556,16 @@ class UnixSockServer(socketserver_mixin.NoPollMixIn, ThreadingUnixStreamServer):
 class XfroutServer:
 class XfroutServer:
     def __init__(self):
     def __init__(self):
         self._unix_socket_server = None
         self._unix_socket_server = None
-        self._log = None
+        #self._log = None
         self._listen_sock_file = UNIX_SOCKET_FILE
         self._listen_sock_file = UNIX_SOCKET_FILE
         self._shutdown_event = threading.Event()
         self._shutdown_event = threading.Event()
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
         self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION, self.config_handler, self.command_handler)
         self._config_data = self._cc.get_full_config()
         self._config_data = self._cc.get_full_config()
         self._cc.start()
         self._cc.start()
         self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
         self._cc.add_remote_config(AUTH_SPECFILE_LOCATION);
-        self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
-                                self._config_data.get('log_severity'), self._config_data.get('log_versions'),
-                                self._config_data.get('log_max_bytes'), True)
+        #self._log = isc.log.NSLogger(self._config_data.get('log_name'), self._config_data.get('log_file'),
+        #                        self._config_data.get('log_severity'), self._config_data.get('log_versions'),
+        #                        self._config_data.get('log_max_bytes'), True)
         self._start_xfr_query_listener()
         self._start_xfr_query_listener()
         self._start_notifier()
         self._start_notifier()
 
 
@@ -570,13 +573,13 @@ class XfroutServer:
         '''Start a new thread to accept xfr query. '''
         '''Start a new thread to accept xfr query. '''
         self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession,
         self._unix_socket_server = UnixSockServer(self._listen_sock_file, XfroutSession,
                                                   self._shutdown_event, self._config_data,
                                                   self._shutdown_event, self._config_data,
-                                                  self._cc, self._log);
+                                                  self._cc)
         listener = threading.Thread(target=self._unix_socket_server.serve_forever)
         listener = threading.Thread(target=self._unix_socket_server.serve_forever)
         listener.start()
         listener.start()
 
 
     def _start_notifier(self):
     def _start_notifier(self):
         datasrc = self._unix_socket_server.get_db_file()
         datasrc = self._unix_socket_server.get_db_file()
-        self._notifier = notify_out.NotifyOut(datasrc, self._log)
+        self._notifier = notify_out.NotifyOut(datasrc)
         self._notifier.dispatcher()
         self._notifier.dispatcher()
 
 
     def send_notify(self, zone_name, zone_class):
     def send_notify(self, zone_name, zone_class):
@@ -591,8 +594,8 @@ class XfroutServer:
                 continue
                 continue
             self._config_data[key] = new_config[key]
             self._config_data[key] = new_config[key]
 
 
-        if self._log:
-            self._log.update_config(new_config)
+        #if self._log:
+        #    self._log.update_config(new_config)
 
 
         if self._unix_socket_server:
         if self._unix_socket_server:
             self._unix_socket_server.update_config_data(self._config_data)
             self._unix_socket_server.update_config_data(self._config_data)
@@ -621,7 +624,7 @@ class XfroutServer:
 
 
     def command_handler(self, cmd, args):
     def command_handler(self, cmd, args):
         if cmd == "shutdown":
         if cmd == "shutdown":
-            self._log.log_message("info", "Received shutdown command.")
+            #self._log.log_message("info", "Received shutdown command.")
             self.shutdown()
             self.shutdown()
             answer = create_answer(0)
             answer = create_answer(0)
 
 
@@ -629,8 +632,8 @@ class XfroutServer:
             zone_name = args.get('zone_name')
             zone_name = args.get('zone_name')
             zone_class = args.get('zone_class')
             zone_class = args.get('zone_class')
             if zone_name and zone_class:
             if zone_name and zone_class:
-                self._log.log_message("info", "zone '%s/%s': receive notify others command" \
-                                       % (zone_name, zone_class))
+                #self._log.log_message("info", "zone '%s/%s': receive notify others command" \
+                #                       % (zone_name, zone_class))
                 self.send_notify(zone_name, zone_class)
                 self.send_notify(zone_name, zone_class)
                 answer = create_answer(0)
                 answer = create_answer(0)
             else:
             else:

+ 44 - 38
src/lib/acl/acl.h

@@ -19,6 +19,7 @@
 #include <vector>
 #include <vector>
 
 
 #include <boost/shared_ptr.hpp>
 #include <boost/shared_ptr.hpp>
+#include <boost/noncopyable.hpp>
 
 
 namespace isc {
 namespace isc {
 namespace acl {
 namespace acl {
@@ -28,9 +29,10 @@ namespace acl {
  *
  *
  * This is the default for the ACL class. It is possible to specify any other
  * This is the default for the ACL class. It is possible to specify any other
  * data type, as the ACL class does nothing about them, but these look
  * data type, as the ACL class does nothing about them, but these look
- * reasonable, so they are provided for convenience.
+ * reasonable, so they are provided for convenience. It is not specified what
+ * exactly these mean and it's up to whoever uses them.
  */
  */
-enum Action {
+enum BasicAction {
     ACCEPT,
     ACCEPT,
     REJECT,
     REJECT,
     DROP
     DROP
@@ -44,39 +46,32 @@ enum Action {
  * whenever the action matches. They are tested in the order and first
  * whenever the action matches. They are tested in the order and first
  * match counts.
  * match counts.
  *
  *
- * \note There are protected members. In fact, you should consider them
+ * This is non-copyable. It seems that there's no need to copy them (even
+ * when it would be technically possible), so we forbid it just to prevent
+ * copying it by accident. If there really is legitimate use, this restriction
+ * can be removed.
+ *
+ * The class is template. It is possible to specify on which context the checks
+ * match and which actions it returns. The actions must be copyable
+ * for this to work and it is expected to be something small, usually an enum
+ * (but other objects are also possible).
+ *
+ * \note There are protected functions. In fact, you should consider them
  *     private, they are protected so tests can get inside. This class
  *     private, they are protected so tests can get inside. This class
  *     is not expected to be subclassed in real applications.
  *     is not expected to be subclassed in real applications.
  */
  */
-template<typename Context, typename Action = isc::acl::Action> class Acl {
-private:
-    /**
-     * \brief Copy constructor.
-     *
-     * It is private on purpose, this class is not supposed to be copied.
-     * It is technically possible though, so if the need arises, it can be
-     * added (or, more correctly, this privade one removed so default one
-     * is created).
-     */
-    Acl(const Acl<Context, Action>& other);
-    /**
-     * \brief Assignment operator.
-     *
-     * It is private on purpose, this class is not supposed to be copied.
-     * It is technically possible though, so if the need arises, it can be
-     * added (or, more correctly, this privade one removed so default one
-     * is created).
-     */
-    Acl& operator =(const Acl<Context, Action>& other);
+template<typename Context, typename Action = BasicAction> class ACL :
+    public boost::noncopyable {
 public:
 public:
     /**
     /**
      * \brief Constructor.
      * \brief Constructor.
      *
      *
-     * \param policy It is the action that is returned when the checked things
-     *     "falls off" the end of the list (when no rule matched).
+     * \param default_action It is the action that is returned when the checked
+     *     things "falls off" the end of the list (when no rule matched).
      */
      */
-    Acl(Action policy) : policy_(policy)
-    { }
+    ACL(const Action& default_action) : default_action_(default_action)
+    {}
+
     /**
     /**
      * \brief Pointer to the check.
      * \brief Pointer to the check.
      *
      *
@@ -84,26 +79,29 @@ public:
      * However, we might need to copy the entries (when we concatenate ACLs
      * However, we might need to copy the entries (when we concatenate ACLs
      * together in future).
      * together in future).
      */
      */
-    typedef boost::shared_ptr<Check<Context> > CheckPtr;
+    typedef boost::shared_ptr<const Check<Context> > ConstCheckPtr;
+
     /**
     /**
      * \brief The actual main function that decides.
      * \brief The actual main function that decides.
      *
      *
      * This is the function that takes the entries one by one, checks
      * This is the function that takes the entries one by one, checks
      * the context against conditions and if it matches, returns the
      * the context against conditions and if it matches, returns the
-     * action that belongs to the first matched entry or policy action
+     * action that belongs to the first matched entry or default action
      * if nothing matches.
      * if nothing matches.
      * \param context The thing that should be checked. It is directly
      * \param context The thing that should be checked. It is directly
      *     passed to the checks.
      *     passed to the checks.
      */
      */
-    Action execute(const Context& context) const {
-        for (typename Entries::const_iterator i(entries_.begin());
-             i != entries_.end(); ++ i) {
+    const Action& execute(const Context& context) const {
+        const typename Entries::const_iterator end(entries_.end());
+        for (typename Entries::const_iterator i(entries_.begin()); i != end;
+             ++i) {
             if (i->first->matches(context)) {
             if (i->first->matches(context)) {
                 return (i->second);
                 return (i->second);
             }
             }
         }
         }
-        return (policy_);
+        return (default_action_);
     }
     }
+
     /**
     /**
      * \brief Add new entry at the end of the list.
      * \brief Add new entry at the end of the list.
      *
      *
@@ -114,18 +112,26 @@ public:
      * \param check The check to test if the thing matches.
      * \param check The check to test if the thing matches.
      * \param action The action to return when the thing matches this check.
      * \param action The action to return when the thing matches this check.
      */
      */
-    void append(CheckPtr check, const Action& action) {
+    void append(ConstCheckPtr check, const Action& action) {
         entries_.push_back(Entry(check, action));
         entries_.push_back(Entry(check, action));
     }
     }
 private:
 private:
     // Just type abbreviations.
     // Just type abbreviations.
-    typedef std::pair<CheckPtr, Action> Entry;
+    typedef std::pair<ConstCheckPtr, Action> Entry;
     typedef std::vector<Entry> Entries;
     typedef std::vector<Entry> Entries;
-protected:
-    /// \brief The policy.
-    Action policy_;
+    /// \brief The default action, when nothing mathes.
+    const Action default_action_;
     /// \brief The entries we have.
     /// \brief The entries we have.
     Entries entries_;
     Entries entries_;
+protected:
+    /**
+     * \brief Get the default action.
+     *
+     * This is for testing purposes only.
+     */
+    const Action& getDefaultAction() const {
+        return (default_action_);
+    }
 };
 };
 
 
 }
 }

+ 1 - 1
src/lib/acl/loader.cc

@@ -19,7 +19,7 @@ using namespace std;
 namespace isc {
 namespace isc {
 namespace acl {
 namespace acl {
 
 
-Action defaultActionLoader(data::ConstElementPtr actionEl) {
+BasicAction defaultActionLoader(data::ConstElementPtr actionEl) {
     try {
     try {
         const string action(actionEl->stringValue());
         const string action(actionEl->stringValue());
         if (action == "ACCEPT") {
         if (action == "ACCEPT") {

+ 7 - 7
src/lib/acl/loader.h

@@ -78,13 +78,13 @@ public:
  *     and contain one of "ACCEPT", "REJECT" or "DENY".
  *     and contain one of "ACCEPT", "REJECT" or "DENY".
  * \note We could define different names or add aliases if needed.
  * \note We could define different names or add aliases if needed.
  */
  */
-Action defaultActionLoader(data::ConstElementPtr action);
+BasicAction defaultActionLoader(data::ConstElementPtr action);
 
 
 /**
 /**
  * \brief Loader of ACLs.
  * \brief Loader of ACLs.
  *
  *
  * The goal of this class is to convert JSON description of an ACL to object
  * The goal of this class is to convert JSON description of an ACL to object
- * of the Acl class (including the checks inside it).
+ * of the ACL class (including the checks inside it).
  *
  *
  * The class can be used to load the checks only. This is supposed to be used
  * The class can be used to load the checks only. This is supposed to be used
  * by compound checks to create the subexpressions.
  * by compound checks to create the subexpressions.
@@ -92,7 +92,7 @@ Action defaultActionLoader(data::ConstElementPtr action);
  * To allow any kind of checks to exist in the application, creators are
  * To allow any kind of checks to exist in the application, creators are
  * registered for the names of the checks.
  * registered for the names of the checks.
  */
  */
-template<typename Context, typename Action = isc::acl::Action> class Loader {
+template<typename Context, typename Action = BasicAction> class Loader {
 public:
 public:
     /**
     /**
      * \brief Constructor.
      * \brief Constructor.
@@ -100,7 +100,7 @@ public:
      * \param default_action The default action for created ACLs.
      * \param default_action The default action for created ACLs.
      * \param actionLoader is the loader which will be used to convert actions
      * \param actionLoader is the loader which will be used to convert actions
      *     from their JSON representation. The default value is suitable for
      *     from their JSON representation. The default value is suitable for
-     *     the isc::acl::Action enum. If you did not specify the second
+     *     the BasicAction enum. If you did not specify the second
      *     template argument, you don't need to specify this loader.
      *     template argument, you don't need to specify this loader.
      */
      */
     Loader(const Action& defaultAction,
     Loader(const Action& defaultAction,
@@ -236,7 +236,7 @@ public:
      *
      *
      * \param description The JSON list of ACL.
      * \param description The JSON list of ACL.
      */
      */
-    boost::shared_ptr<Acl<Context, Action> > load(const data::ConstElementPtr&
+    boost::shared_ptr<ACL<Context, Action> > load(const data::ConstElementPtr&
                                                   description)
                                                   description)
     {
     {
         // We first check it's a list, so we can use the list reference
         // We first check it's a list, so we can use the list reference
@@ -247,8 +247,8 @@ public:
         }
         }
         // First create an empty ACL
         // First create an empty ACL
         const List &list(description->listValue());
         const List &list(description->listValue());
-        boost::shared_ptr<Acl<Context, Action> > result(
-            new Acl<Context, Action>(default_action_));
+        boost::shared_ptr<ACL<Context, Action> > result(
+            new ACL<Context, Action>(default_action_));
         // Run trough the list of elements
         // Run trough the list of elements
         for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
         for (List::const_iterator i(list.begin()); i != list.end(); ++i) {
             Map map;
             Map map;

+ 1 - 0
src/lib/acl/tests/Makefile.am

@@ -1,4 +1,5 @@
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
 AM_CPPFLAGS = -I$(top_builddir)/src/lib -I$(top_srcdir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
 
 
 TESTS =
 TESTS =
 if HAVE_GTEST
 if HAVE_GTEST

+ 26 - 26
src/lib/acl/tests/acl_test.cc

@@ -18,53 +18,53 @@ namespace {
 
 
 // Test version of the Acl class. It adds few methods to examine the protected
 // Test version of the Acl class. It adds few methods to examine the protected
 // data, but does not change the implementation.
 // data, but does not change the implementation.
-class TestAcl : public Acl<Log*> {
+class TestACL : public ACL<Log> {
 public:
 public:
-    TestAcl() :
-        Acl(DROP)
-    { }
-    // Check the stored policy there
-    void checkPolicy(Action ac) {
-        EXPECT_EQ(policy_, ac);
+    TestACL() :
+        ACL<Log>(DROP)
+    {}
+    // Check the stored default action there
+    void checkDefaultAction(BasicAction ac) {
+        EXPECT_EQ(getDefaultAction(), ac);
     }
     }
 };
 };
 
 
 // The test fixture. Contains some members so they don't need to be manually
 // The test fixture. Contains some members so they don't need to be manually
 // created each time and some convenience functions.
 // created each time and some convenience functions.
-class AclTest : public ::testing::Test {
+class ACLTest : public ::testing::Test {
 public:
 public:
-    AclTest() :
-        nextCheck_(0)
-    { }
-    TestAcl acl_;
+    ACLTest() :
+        next_check_(0)
+    {}
+    TestACL acl_;
     Log log_;
     Log log_;
-    size_t nextCheck_;
-    shared_ptr<Check<Log*> > getCheck(bool accepts) {
-        return (shared_ptr<Check<Log*> >(new ConstCheck(accepts,
-                                                        nextCheck_ ++)));
+    size_t next_check_;
+    shared_ptr<Check<Log> > getCheck(bool accepts) {
+        return (shared_ptr<Check<Log> >(new ConstCheck(accepts,
+                                                       next_check_++)));
     }
     }
 };
 };
 
 
 /*
 /*
- * This tests the policy (default return value) and that nothing is run
- * if nothing is inserted (it's hard to imagine otherwise though).
+ * This tests the default action and that nothing is run if nothing is
+ * inserted (it's hard to imagine otherwise though).
  *
  *
  * We use the default ACL unchanged from the test class.
  * We use the default ACL unchanged from the test class.
  */
  */
-TEST_F(AclTest, emptyPolicy) {
-    acl_.checkPolicy(DROP);
-    EXPECT_EQ(DROP, acl_.execute(&log_));
+TEST_F(ACLTest, emptyRule) {
+    acl_.checkDefaultAction(DROP);
+    EXPECT_EQ(DROP, acl_.execute(log_));
     // No test was run
     // No test was run
     log_.checkFirst(0);
     log_.checkFirst(0);
 }
 }
 
 
 /*
 /*
- * This tests the policy in case no check matches.
+ * This tests the default action in case no check matches.
  */
  */
-TEST_F(AclTest, policy) {
+TEST_F(ACLTest, noMatch) {
     acl_.append(getCheck(false), ACCEPT);
     acl_.append(getCheck(false), ACCEPT);
     acl_.append(getCheck(false), REJECT);
     acl_.append(getCheck(false), REJECT);
-    EXPECT_EQ(DROP, acl_.execute(&log_));
+    EXPECT_EQ(DROP, acl_.execute(log_));
     // The first two checks were actually run (and didn't match)
     // The first two checks were actually run (and didn't match)
     log_.checkFirst(2);
     log_.checkFirst(2);
 }
 }
@@ -73,11 +73,11 @@ TEST_F(AclTest, policy) {
  * Checks that it takes the first matching check and returns the
  * Checks that it takes the first matching check and returns the
  * value. Also checks that the others aren't run at all.
  * value. Also checks that the others aren't run at all.
  */
  */
-TEST_F(AclTest, check) {
+TEST_F(ACLTest, firstMatch) {
     acl_.append(getCheck(false), ACCEPT);
     acl_.append(getCheck(false), ACCEPT);
     acl_.append(getCheck(true), REJECT);
     acl_.append(getCheck(true), REJECT);
     acl_.append(getCheck(true), ACCEPT);
     acl_.append(getCheck(true), ACCEPT);
-    EXPECT_EQ(REJECT, acl_.execute(&log_));
+    EXPECT_EQ(REJECT, acl_.execute(log_));
     log_.checkFirst(2);
     log_.checkFirst(2);
 }
 }
 
 

+ 21 - 21
src/lib/acl/tests/loader_test.cc

@@ -63,20 +63,19 @@ TEST(LoaderHelpers, DefaultActionLoader) {
 
 
 // A check that doesn't check anything but remembers it's own name
 // A check that doesn't check anything but remembers it's own name
 // and data
 // and data
-class NamedCheck : public Check<Log*> {
+class NamedCheck : public Check<Log> {
 public:
 public:
     NamedCheck(const string& name, ConstElementPtr data) :
     NamedCheck(const string& name, ConstElementPtr data) :
         name_(name),
         name_(name),
         data_(data)
         data_(data)
     {}
     {}
-    typedef Log* LogPtr;
-    virtual bool matches(const LogPtr&) const { return (true); }
+    virtual bool matches(const Log&) const { return (true); }
     const string name_;
     const string name_;
     const ConstElementPtr data_;
     const ConstElementPtr data_;
 };
 };
 
 
 // The creator of NamedCheck
 // The creator of NamedCheck
-class NamedCreator : public Loader<Log*>::CheckCreator {
+class NamedCreator : public Loader<Log>::CheckCreator {
 public:
 public:
     NamedCreator(const string& name, bool abbreviatedList = true) :
     NamedCreator(const string& name, bool abbreviatedList = true) :
         abbreviated_list_(abbreviatedList)
         abbreviated_list_(abbreviatedList)
@@ -90,7 +89,7 @@ public:
     vector<string> names() const {
     vector<string> names() const {
         return (names_);
         return (names_);
     }
     }
-    shared_ptr<Check<Log*> > create(const string& name,
+    shared_ptr<Check<Log> > create(const string& name,
                                            ConstElementPtr data)
                                            ConstElementPtr data)
     {
     {
         bool found(false);
         bool found(false);
@@ -103,7 +102,7 @@ public:
         }
         }
         EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
         EXPECT_TRUE(found) << "Name " << name << " passed to creator which "
             "doesn't handle it.";
             "doesn't handle it.";
-        return (shared_ptr<Check<Log*> >(new NamedCheck(name, data)));
+        return (shared_ptr<Check<Log> >(new NamedCheck(name, data)));
     }
     }
     bool allowListAbbreviation() const {
     bool allowListAbbreviation() const {
         return (abbreviated_list_);
         return (abbreviated_list_);
@@ -117,41 +116,40 @@ private:
 class TestCreatorError {};
 class TestCreatorError {};
 
 
 // This will throw every time it should create something
 // This will throw every time it should create something
-class ThrowCreator : public Loader<Log*>::CheckCreator {
+class ThrowCreator : public Loader<Log>::CheckCreator {
 public:
 public:
     vector<string> names() const {
     vector<string> names() const {
         vector<string> result;
         vector<string> result;
         result.push_back("throw");
         result.push_back("throw");
         return (result);
         return (result);
     }
     }
-    shared_ptr<Check<Log*> > create(const string&, ConstElementPtr) {
+    shared_ptr<Check<Log> > create(const string&, ConstElementPtr) {
         throw TestCreatorError();
         throw TestCreatorError();
     }
     }
 };
 };
 
 
 // This throws whenever the match is called on it
 // This throws whenever the match is called on it
-class ThrowCheck : public Check<Log*> {
+class ThrowCheck : public Check<Log> {
 public:
 public:
-    typedef Log* LogPtr;
-    virtual bool matches(const LogPtr&) const {
+    virtual bool matches(const Log&) const {
         throw TestCreatorError();
         throw TestCreatorError();
     }
     }
 };
 };
 
 
 // And creator for it
 // And creator for it
-class ThrowCheckCreator : public Loader<Log*>::CheckCreator {
+class ThrowCheckCreator : public Loader<Log>::CheckCreator {
 public:
 public:
     vector<string> names() const {
     vector<string> names() const {
         vector<string> result;
         vector<string> result;
         result.push_back("throwcheck");
         result.push_back("throwcheck");
         return (result);
         return (result);
     }
     }
-    shared_ptr<Check<Log*> > create(const string&, ConstElementPtr) {
-        return (shared_ptr<Check<Log*> >(new ThrowCheck()));
+    shared_ptr<Check<Log> > create(const string&, ConstElementPtr) {
+        return (shared_ptr<Check<Log> >(new ThrowCheck()));
     }
     }
 };
 };
 
 
-class LogCreator : public Loader<Log*>::CheckCreator {
+class LogCreator : public Loader<Log>::CheckCreator {
 public:
 public:
     vector<string> names() const {
     vector<string> names() const {
         vector<string> result;
         vector<string> result;
@@ -163,7 +161,7 @@ public:
      * logging cell used, the second is result of the check. No error checking
      * logging cell used, the second is result of the check. No error checking
      * is done, if there's bug in the test, it will throw TypeError for us.
      * is done, if there's bug in the test, it will throw TypeError for us.
      */
      */
-    shared_ptr<Check<Log*> > create(const string&,
+    shared_ptr<Check<Log> > create(const string&,
                                     ConstElementPtr definition)
                                     ConstElementPtr definition)
     {
     {
         vector<ConstElementPtr> list(definition->listValue());
         vector<ConstElementPtr> list(definition->listValue());
@@ -180,7 +178,7 @@ public:
     LoaderTest() :
     LoaderTest() :
         loader_(REJECT)
         loader_(REJECT)
     {}
     {}
-    Loader<Log*> loader_;
+    Loader<Log> loader_;
     Log log_;
     Log log_;
     // Some convenience functions to set up
     // Some convenience functions to set up
 
 
@@ -199,7 +197,7 @@ public:
     // Load a check and convert it to named check to examine it
     // Load a check and convert it to named check to examine it
     shared_ptr<NamedCheck> loadCheck(const string& definition) {
     shared_ptr<NamedCheck> loadCheck(const string& definition) {
         SCOPED_TRACE("Loading check " + definition);
         SCOPED_TRACE("Loading check " + definition);
-        shared_ptr<Check<Log*> > loaded;
+        shared_ptr<Check<Log> > loaded;
         EXPECT_NO_THROW(loaded = loader_.loadCheck(el(definition)));
         EXPECT_NO_THROW(loaded = loader_.loadCheck(el(definition)));
         shared_ptr<NamedCheck> result(dynamic_pointer_cast<NamedCheck>(
         shared_ptr<NamedCheck> result(dynamic_pointer_cast<NamedCheck>(
             loaded));
             loaded));
@@ -237,12 +235,14 @@ public:
     // log items it marked
     // log items it marked
     //
     //
     // Works with preset names throw and logcheck
     // Works with preset names throw and logcheck
-    void aclRun(const string& JSON, Action expectedResult, size_t logged) {
+    void aclRun(const string& JSON, BasicAction expectedResult,
+                size_t logged)
+    {
         SCOPED_TRACE("Running ACL for " + JSON);
         SCOPED_TRACE("Running ACL for " + JSON);
         aclSetup();
         aclSetup();
-        shared_ptr<Acl<Log*> > acl;
+        shared_ptr<ACL<Log> > acl;
         EXPECT_NO_THROW(acl = loader_.load(el(JSON)));
         EXPECT_NO_THROW(acl = loader_.load(el(JSON)));
-        EXPECT_EQ(expectedResult, acl->execute(&log_));
+        EXPECT_EQ(expectedResult, acl->execute(log_));
         log_.checkFirst(logged);
         log_.checkFirst(logged);
     }
     }
     // Check it throws an error when creating the ACL
     // Check it throws an error when creating the ACL

+ 9 - 5
src/lib/acl/tests/logcheck.h

@@ -32,7 +32,7 @@ const size_t LOG_SIZE = 10;
 // This will remember which checks did run already.
 // This will remember which checks did run already.
 struct Log {
 struct Log {
     // The actual log cells, if i-th check did run
     // The actual log cells, if i-th check did run
-    bool run[LOG_SIZE];
+    mutable bool run[LOG_SIZE];
     Log() {
     Log() {
         // Nothing run yet
         // Nothing run yet
         for (size_t i(0); i < LOG_SIZE; ++ i) {
         for (size_t i(0); i < LOG_SIZE; ++ i) {
@@ -61,7 +61,7 @@ struct Log {
 
 
 // This returns true or false every time, no matter what is passed to it.
 // This returns true or false every time, no matter what is passed to it.
 // But it logs that it did run.
 // But it logs that it did run.
-class ConstCheck : public Check<Log*> {
+class ConstCheck : public Check<Log> {
 public:
 public:
     ConstCheck(bool accepts, size_t logNum) :
     ConstCheck(bool accepts, size_t logNum) :
         logNum_(logNum),
         logNum_(logNum),
@@ -69,9 +69,13 @@ public:
     {
     {
         assert(logNum < LOG_SIZE); // If this fails, the LOG_SIZE is too small
         assert(logNum < LOG_SIZE); // If this fails, the LOG_SIZE is too small
     }
     }
-    typedef Log* LPtr;
-    virtual bool matches(const LPtr& log) const {
-        log->run[logNum_] = true;
+    virtual bool matches(const Log& log) const {
+        /*
+         * This is abuse of the context. It is designed to carry the
+         * information to check, not to modify it. However, this is the
+         * easiest way to do the test, so we go against the design.
+         */
+        log.run[logNum_] = true;
         return (accepts_);
         return (accepts_);
     }
     }
 private:
 private:

+ 39 - 24
src/lib/asiolink/interval_timer.cc

@@ -14,11 +14,9 @@
 
 
 #include <config.h>
 #include <config.h>
 
 
-#include <unistd.h>             // for some IPC/network system calls
-#include <sys/socket.h>
-#include <netinet/in.h>
-
 #include <boost/bind.hpp>
 #include <boost/bind.hpp>
+#include <boost/enable_shared_from_this.hpp>
+#include <boost/shared_ptr.hpp>
 
 
 #include <exceptions/exceptions.h>
 #include <exceptions/exceptions.h>
 
 
@@ -29,7 +27,16 @@
 namespace isc {
 namespace isc {
 namespace asiolink {
 namespace asiolink {
 
 
-class IntervalTimerImpl {
+/// This class holds a call back function of asynchronous operations.
+/// To ensure the object is alive while an asynchronous operation refers
+/// to it, we use shared_ptr and enable_shared_from_this.
+/// The object will be destructed in case IntervalTimer has been destructed
+/// and no asynchronous operation refers to it.
+/// Please follow the link to get an example:
+/// http://think-async.com/asio/asio-1.4.8/doc/asio/tutorial/tutdaytime3.html#asio.tutorial.tutdaytime3.the_tcp_connection_class
+class IntervalTimerImpl :
+    public boost::enable_shared_from_this<IntervalTimerImpl>
+{
 private:
 private:
     // prohibit copy
     // prohibit copy
     IntervalTimerImpl(const IntervalTimerImpl& source);
     IntervalTimerImpl(const IntervalTimerImpl& source);
@@ -53,14 +60,18 @@ private:
     long interval_;
     long interval_;
     // asio timer
     // asio timer
     asio::deadline_timer timer_;
     asio::deadline_timer timer_;
+    // interval_ will be set to this value in destructor in order to detect
+    // use-after-free type of bugs.
+    static const long INVALIDATED_INTERVAL = -1;
 };
 };
 
 
 IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
 IntervalTimerImpl::IntervalTimerImpl(IOService& io_service) :
     interval_(0), timer_(io_service.get_io_service())
     interval_(0), timer_(io_service.get_io_service())
 {}
 {}
 
 
-IntervalTimerImpl::~IntervalTimerImpl()
-{}
+IntervalTimerImpl::~IntervalTimerImpl() {
+    interval_ = INVALIDATED_INTERVAL;
+}
 
 
 void
 void
 IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
 IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
@@ -81,42 +92,46 @@ IntervalTimerImpl::setup(const IntervalTimer::Callback& cbfunc,
     // At this point the timer is not running yet and will not expire.
     // At this point the timer is not running yet and will not expire.
     // After calling IOService::run(), the timer will expire.
     // After calling IOService::run(), the timer will expire.
     update();
     update();
-    return;
 }
 }
 
 
 void
 void
 IntervalTimerImpl::update() {
 IntervalTimerImpl::update() {
-    if (interval_ == 0) {
-        // timer has been canceled.  Do nothing.
-        return;
-    }
     try {
     try {
         // Update expire time to (current time + interval_).
         // Update expire time to (current time + interval_).
         timer_.expires_from_now(boost::posix_time::millisec(interval_));
         timer_.expires_from_now(boost::posix_time::millisec(interval_));
+        // Reset timer.
+        // Pass a function bound with a shared_ptr to this.
+        timer_.async_wait(boost::bind(&IntervalTimerImpl::callback,
+                                      shared_from_this(),
+                                      asio::placeholders::error));
     } catch (const asio::system_error& e) {
     } catch (const asio::system_error& e) {
-        isc_throw(isc::Unexpected, "Failed to update timer");
+        isc_throw(isc::Unexpected, "Failed to update timer: " << e.what());
+    } catch (const boost::bad_weak_ptr&) {
+        // Can't happen. It means a severe internal bug.
+        assert(0);
     }
     }
-    // Reset timer.
-    timer_.async_wait(boost::bind(&IntervalTimerImpl::callback, this, _1));
 }
 }
 
 
 void
 void
-IntervalTimerImpl::callback(const asio::error_code& cancelled) {
-    // Do not call cbfunc_ in case the timer was cancelled.
-    // The timer will be canelled in the destructor of asio::deadline_timer.
-    if (!cancelled) {
-        cbfunc_();
+IntervalTimerImpl::callback(const asio::error_code& ec) {
+    assert(interval_ != INVALIDATED_INTERVAL);
+    if (interval_ == 0 || ec) {
+        // timer has been canceled. Do nothing.
+    } else {
         // Set next expire time.
         // Set next expire time.
         update();
         update();
+        // Invoke the call back function.
+        cbfunc_();
     }
     }
 }
 }
 
 
-IntervalTimer::IntervalTimer(IOService& io_service) {
-    impl_ = new IntervalTimerImpl(io_service);
-}
+IntervalTimer::IntervalTimer(IOService& io_service) :
+    impl_(new IntervalTimerImpl(io_service))
+{}
 
 
 IntervalTimer::~IntervalTimer() {
 IntervalTimer::~IntervalTimer() {
-    delete impl_;
+    // Cancel the timer to make sure cbfunc_() will not be called any more.
+    cancel();
 }
 }
 
 
 void
 void

+ 4 - 6
src/lib/asiolink/interval_timer.h

@@ -16,6 +16,7 @@
 #define __ASIOLINK_INTERVAL_TIMER_H 1
 #define __ASIOLINK_INTERVAL_TIMER_H 1
 
 
 #include <boost/function.hpp>
 #include <boost/function.hpp>
+#include <boost/shared_ptr.hpp>
 
 
 #include <asiolink/io_service.h>
 #include <asiolink/io_service.h>
 
 
@@ -42,9 +43,6 @@ class IntervalTimerImpl;
 /// The call back function will not be called if the instance of this class is
 /// The call back function will not be called if the instance of this class is
 /// destroyed before the timer is expired.
 /// destroyed before the timer is expired.
 ///
 ///
-/// Note: Destruction of an instance of this class while call back is pending
-/// causes throwing an exception from \c IOService.
-///
 /// Sample code:
 /// Sample code:
 /// \code
 /// \code
 ///  void function_to_call_back() {
 ///  void function_to_call_back() {
@@ -100,12 +98,12 @@ public:
     /// \param interval Interval in milliseconds (greater than 0)
     /// \param interval Interval in milliseconds (greater than 0)
     ///
     ///
     /// Note: IntervalTimer will not pass \c asio::error_code to
     /// Note: IntervalTimer will not pass \c asio::error_code to
-    /// call back function. In case the timer is cancelled, the function
+    /// call back function. In case the timer is canceled, the function
     /// will not be called.
     /// will not be called.
     ///
     ///
     /// \throw isc::InvalidParameter cbfunc is empty
     /// \throw isc::InvalidParameter cbfunc is empty
     /// \throw isc::BadValue interval is less than or equal to 0
     /// \throw isc::BadValue interval is less than or equal to 0
-    /// \throw isc::Unexpected ASIO library error
+    /// \throw isc::Unexpected internal runtime error
     void setup(const Callback& cbfunc, const long interval);
     void setup(const Callback& cbfunc, const long interval);
 
 
     /// Cancel the timer.
     /// Cancel the timer.
@@ -127,7 +125,7 @@ public:
     long getInterval() const;
     long getInterval() const;
 
 
 private:
 private:
-    IntervalTimerImpl* impl_;
+    boost::shared_ptr<IntervalTimerImpl> impl_;
 };
 };
 
 
 } // namespace asiolink
 } // namespace asiolink

+ 0 - 4
src/lib/datasrc/messagedef.mes

@@ -424,10 +424,6 @@ and type in the database.
 Debug information. The SQLite data source is trying to identify, which zone
 Debug information. The SQLite data source is trying to identify, which zone
 should hold this domain.
 should hold this domain.
 
 
-% SQLITE_ENCLOSURE_BAD_CLASS class mismatch looking for a zone ('%1' and '%2')
-The SQLite data source can handle only one class at a time and it was asked
-to identify which zone is holding data of a different class.
-
 % SQLITE_ENCLOSURE_NOTFOUND no zone contains it
 % SQLITE_ENCLOSURE_NOTFOUND no zone contains it
 Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
 Debug information. The last SQLITE_ENCLOSURE query was unsuccessful, there's
 no such zone in our data.
 no such zone in our data.

+ 0 - 2
src/lib/datasrc/sqlite3_datasrc.cc

@@ -351,8 +351,6 @@ Sqlite3DataSrc::findClosestEnclosure(DataSrcMatch& match) const {
     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE).
     LOG_DEBUG(logger, DBG_TRACE_DATA, DATASRC_SQLITE_ENCLOSURE).
         arg(match.getName());
         arg(match.getName());
     if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
     if (match.getClass() != getClass() && match.getClass() != RRClass::ANY()) {
-        LOG_ERROR(logger, DATASRC_SQLITE_ENCLOSURE_BAD_CLASS).arg(getClass()).
-            arg(match.getClass());
         return;
         return;
     }
     }
 
 

+ 2 - 2
src/lib/log/compiler/message.cc

@@ -266,12 +266,12 @@ writePythonFile(const string& file, MessageDictionary& dictionary) {
         "# File created from " << message_file.fullName() << " on " <<
         "# File created from " << message_file.fullName() << " on " <<
             currentTime() << "\n" <<
             currentTime() << "\n" <<
         "\n" <<
         "\n" <<
-        "import isc.log.message\n" <<
+        "import isc.log\n" <<
         "\n";
         "\n";
 
 
     vector<string> idents(sortedIdentifiers(dictionary));
     vector<string> idents(sortedIdentifiers(dictionary));
     BOOST_FOREACH(const string& ident, idents) {
     BOOST_FOREACH(const string& ident, idents) {
-        pyfile << ident << " = isc.log.message.create(\"" <<
+        pyfile << ident << " = isc.log.create_message(\"" <<
             ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
             ident << "\", \"" << quoteString(dictionary.getText(ident)) <<
             "\")\n";
             "\")\n";
     }
     }

+ 0 - 1
src/lib/python/isc/__init__.py

@@ -2,4 +2,3 @@ import isc.datasrc
 import isc.cc
 import isc.cc
 import isc.config
 import isc.config
 #import isc.dns
 #import isc.dns
-import isc.log

+ 14 - 4
src/lib/python/isc/log/Makefile.am

@@ -1,8 +1,18 @@
 SUBDIRS = . tests
 SUBDIRS = . tests
 
 
-python_PYTHON = __init__.py log.py
+AM_CPPFLAGS = -I$(top_srcdir)/src/lib -I$(top_builddir)/src/lib
+AM_CPPFLAGS += $(BOOST_INCLUDES)
+AM_CXXFLAGS = $(B10_CXXFLAGS)
 
 
-pythondir = $(pyexecdir)/isc/log
+pythondir = $(pyexecdir)/isc
+python_LTLIBRARIES = log.la
+log_la_SOURCES = log.cc
 
 
-pytest:
-	$(SHELL) tests/log_test
+log_la_CPPFLAGS = $(AM_CPPFLAGS) $(PYTHON_INCLUDES)
+log_la_LDFLAGS = $(PYTHON_LDFLAGS)
+log_la_LDFLAGS += -module
+log_la_LIBADD = $(top_builddir)/src/lib/log/liblog.la
+log_la_LIBADD += $(PYTHON_LIB)
+
+# This is not installed, it helps locate the module during tests
+EXTRA_DIST = __init__.py

+ 29 - 1
src/lib/python/isc/log/__init__.py

@@ -1 +1,29 @@
-from isc.log.log import *
+# Copyright (C) 2011  Internet Systems Consortium.
+#
+# Permission to use, copy, modify, and distribute this software for any
+# purpose with or without fee is hereby granted, provided that the above
+# copyright notice and this permission notice appear in all copies.
+#
+# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
+# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
+# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
+# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
+# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
+# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
+# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
+# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+
+# This file is not installed. The log.so is installed into the right place.
+# It is only to find it in the .libs directory when we run as a test or
+# from the build directory.
+# But as nobody gives us the builddir explicitly (and we can't use generation
+# from .in file, as it would put us into the builddir and we wouldn't be found)
+# we guess from current directory. Any idea for something better? This should
+# be enough for the tests, but would it work for B10_FROM_SOURCE as well?
+# Should we look there? Or define something in bind10_config?
+
+import os
+cwd = os.getcwd()
+pos = cwd.rfind('/src/')
+import sys; sys.path.insert(0, cwd[:pos] + '/src/lib/python/isc/log/.libs')
+from log import *

+ 629 - 0
src/lib/python/isc/log/log.cc

@@ -0,0 +1,629 @@
+// Copyright (C) 2011  Internet Systems Consortium, Inc. ("ISC")
+//
+// Permission to use, copy, modify, and/or distribute this software for any
+// purpose with or without fee is hereby granted, provided that the above
+// copyright notice and this permission notice appear in all copies.
+//
+// THE SOFTWARE IS PROVIDED "AS IS" AND ISC DISCLAIMS ALL WARRANTIES WITH
+// REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
+// AND FITNESS.  IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
+// INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
+// LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
+// OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
+// PERFORMANCE OF THIS SOFTWARE.
+
+#define PY_SSIZE_T_CLEAN
+#include <Python.h>
+#include <structmember.h>
+
+#include <config.h>
+
+#include <log/message_dictionary.h>
+#include <log/logger_manager.h>
+#include <log/logger.h>
+
+#include <string>
+#include <boost/bind.hpp>
+
+using namespace isc::log;
+using std::string;
+using boost::bind;
+
+namespace {
+
+// This is for testing only. The real module will have it always set as
+// NULL and will use the global dictionary.
+MessageDictionary* testDictionary = NULL;
+
+PyObject*
+setTestDictionary(PyObject*, PyObject* args) {
+    PyObject* enableO;
+    // The API doesn't seem to provide conversion to bool,
+    // so we do it little bit manually
+    if (!PyArg_ParseTuple(args, "O", &enableO)) {
+        return (NULL);
+    }
+    int enableI(PyObject_IsTrue(enableO));
+    if (enableI == -1) {
+        return (NULL);
+    }
+    bool enable(enableI != 0);
+
+    try {
+        delete testDictionary;
+        testDictionary = NULL;
+        if (enable) {
+            testDictionary = new MessageDictionary;
+        }
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+    Py_RETURN_NONE;
+}
+
+PyObject*
+createMessage(PyObject*, PyObject* args) {
+    const char* mid;
+    const char* text;
+    // We parse the strings
+    if (!PyArg_ParseTuple(args, "ss", &mid, &text)) {
+        return (NULL);
+    }
+    PyObject* origMid;
+    // And extract the original representation of the message
+    // ID, so we can return it instead of creating another instance.
+    // This call shouldn't fail if the previous suceeded.
+    if (!PyArg_ParseTuple(args, "Os", &origMid, &text)) {
+        return (NULL);
+    }
+
+    try {
+        MessageDictionary* dict = testDictionary ? testDictionary :
+            &MessageDictionary::globalDictionary();
+
+        // We ignore the result, they will be in some kind of dupe list
+        // if there's a problem
+        dict->add(mid, text);
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+
+    // Return the ID
+    Py_INCREF(origMid);
+    return (origMid);
+}
+
+PyObject*
+getMessage(PyObject*, PyObject* args) {
+    const char* mid;
+    if (!PyArg_ParseTuple(args, "s", &mid)) {
+        return (NULL);
+    }
+
+    try {
+        MessageDictionary* dict = testDictionary ? testDictionary :
+            &MessageDictionary::globalDictionary();
+
+        const std::string& result(dict->getText(mid));
+        if (result.empty()) {
+            Py_RETURN_NONE;
+        } else {
+            return (Py_BuildValue("s", result.c_str()));
+        }
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+reset(PyObject*, PyObject*) {
+    LoggerManager::reset();
+    Py_RETURN_NONE;
+}
+
+PyObject*
+init(PyObject*, PyObject* args) {
+    const char* root;
+    const char* file(NULL);
+    const char* severity("INFO");
+    int dbglevel(0);
+    if (!PyArg_ParseTuple(args, "s|siz", &root, &severity, &dbglevel, &file)) {
+        return (NULL);
+    }
+
+    try {
+        LoggerManager::init(root, getSeverity(severity), dbglevel, file);
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+    Py_RETURN_NONE;
+}
+
+PyMethodDef methods[] = {
+    {"set_test_dictionary", setTestDictionary, METH_VARARGS,
+        "Set or unset testing mode for message dictionary. In testing, "
+        "the create_message and get_message functions work on different "
+        "than the logger-global dictionary, not polluting it."},
+    {"create_message", createMessage, METH_VARARGS,
+        "Creates a new message in the dictionary. You shouldn't need to "
+        "call this directly, it should be called by the generated message "
+        "file. Returns the identifier to be used in logging. The text "
+        "shouldn't be empty."},
+    {"get_message", getMessage, METH_VARARGS,
+        "Get a message. This function is for testing purposes and you don't "
+        "need to call it. It returns None if the message does not exist."},
+    {"reset", reset, METH_NOARGS,
+        "Reset all logging. For testing purposes only, do not use."},
+    {"init", init, METH_VARARGS,
+        "Run-time initialization. You need to call this before you do any "
+        "logging, to configure the root logger name. You may also provide "
+        "logging severity (one of 'DEBUG', 'INFO', 'WARN', 'ERROR' or "
+        "'FATAL'), a debug level (integer in the range 0-99) and a file name "
+        "of a dictionary with message text translations."},
+    {NULL, NULL, 0, NULL}
+};
+
+class LoggerWrapper : public PyObject {
+// Everything is public here, as it is accessible only inside this .cc file.
+public:
+    Logger *logger_;
+};
+
+extern PyTypeObject logger_type;
+
+int
+Logger_init(LoggerWrapper* self, PyObject* args) {
+    const char* name;
+    if (!PyArg_ParseTuple(args, "s", &name)) {
+        return (-1);
+    }
+    try {
+        self->logger_ = new Logger(name);
+        return (0);
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (-1);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (-1);
+    }
+}
+
+void
+Logger_destroy(LoggerWrapper* const self) {
+    delete self->logger_;
+    self->logger_ = NULL;
+    Py_TYPE(self)->tp_free(self);
+}
+
+// The isc::log doesn't contain function to convert this way
+const char*
+severityToText(const Severity& severity) {
+    switch (severity) {
+        case DEFAULT:
+            return ("DEFAULT");
+        case DEBUG:
+            return ("DEBUG");
+        case INFO:
+            return ("INFO");
+        case WARN:
+            return ("WARN");
+        case ERROR:
+            return ("ERROR");
+        case FATAL:
+            return ("FATAL");
+        default:
+            return (NULL);
+    }
+}
+
+PyObject*
+Logger_getEffectiveSeverity(LoggerWrapper* self, PyObject*) {
+    try {
+        return (Py_BuildValue("s",
+                              severityToText(
+                                  self->logger_->getEffectiveSeverity())));
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+Logger_getEffectiveDebugLevel(LoggerWrapper* self, PyObject*) {
+    try {
+        return (Py_BuildValue("i", self->logger_->getEffectiveDebugLevel()));
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+Logger_setSeverity(LoggerWrapper* self, PyObject* args) {
+    const char* severity;
+    int dbgLevel = 0;
+    if (!PyArg_ParseTuple(args, "z|i", &severity, &dbgLevel)) {
+        return (NULL);
+    }
+    try {
+        self->logger_->setSeverity((severity == NULL) ? DEFAULT :
+                                   getSeverity(severity), dbgLevel);
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+    Py_RETURN_NONE;
+}
+
+template<class FPtr> // Who should remember the pointer-to-method syntax
+PyObject*
+Logger_isLevelEnabled(LoggerWrapper* self, FPtr function) {
+    try {
+        if ((self->logger_->*function)()) {
+            Py_RETURN_TRUE;
+        } else {
+            Py_RETURN_FALSE;
+        }
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+PyObject*
+Logger_isInfoEnabled(LoggerWrapper* self, PyObject*) {
+    return (Logger_isLevelEnabled(self, &Logger::isInfoEnabled));
+}
+
+PyObject*
+Logger_isWarnEnabled(LoggerWrapper* self, PyObject*) {
+    return (Logger_isLevelEnabled(self, &Logger::isWarnEnabled));
+}
+
+PyObject*
+Logger_isErrorEnabled(LoggerWrapper* self, PyObject*) {
+    return (Logger_isLevelEnabled(self, &Logger::isErrorEnabled));
+}
+
+PyObject*
+Logger_isFatalEnabled(LoggerWrapper* self, PyObject*) {
+    return (Logger_isLevelEnabled(self, &Logger::isFatalEnabled));
+}
+
+PyObject*
+Logger_isDebugEnabled(LoggerWrapper* self, PyObject* args) {
+    int level = MIN_DEBUG_LEVEL;
+    if (!PyArg_ParseTuple(args, "|i", &level)) {
+        return (NULL);
+    }
+
+    try {
+        if (self->logger_->isDebugEnabled(level)) {
+            Py_RETURN_TRUE;
+        } else {
+            Py_RETURN_FALSE;
+        }
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+// To propagate python exceptions trough our code
+class InternalError {};
+
+string
+objectToStr(PyObject* object, bool convert) {
+    PyObject* cleanup(NULL);
+    if (convert) {
+        object = cleanup = PyObject_Str(object);
+        if (object == NULL) {
+            throw InternalError();
+        }
+    }
+    const char* value;
+    PyObject* tuple(Py_BuildValue("(O)", object));
+    if (tuple == NULL) {
+        if (cleanup != NULL) {
+            Py_DECREF(cleanup);
+        }
+        throw InternalError();
+    }
+
+    if (!PyArg_ParseTuple(tuple, "s", &value)) {
+        Py_DECREF(tuple);
+        if (cleanup != NULL) {
+            Py_DECREF(cleanup);
+        }
+        throw InternalError();
+    }
+    string result(value);
+    Py_DECREF(tuple);
+    if (cleanup != NULL) {
+        Py_DECREF(cleanup);
+    }
+    return (result);
+}
+
+// Generic function to output the logging message. Called by the real functions.
+template<class Function>
+PyObject*
+Logger_performOutput(Function function, PyObject* args, bool dbgLevel) {
+    try {
+        Py_ssize_t number(PyObject_Length(args));
+        if (number < 0) {
+            return (NULL);
+        }
+
+        // Which argument is the first to format?
+        size_t start(1);
+        if (dbgLevel) {
+            start ++;
+        }
+
+        if (number < start) {
+            return (PyErr_Format(PyExc_TypeError, "Too few arguments to "
+                                 "logging call, at least %zu needed and %zd "
+                                 "given", start, number));
+        }
+
+        // Extract the fixed arguments
+        PyObject *midO(PySequence_GetItem(args, start - 1));
+        if (midO == NULL) {
+            return (NULL);
+        }
+        string mid(objectToStr(midO, false));
+        long dbg(0);
+        if (dbgLevel) {
+            PyObject *dbgO(PySequence_GetItem(args, 0));
+            if (dbgO == NULL) {
+                return (NULL);
+            }
+            dbg = PyLong_AsLong(dbgO);
+            if (PyErr_Occurred()) {
+                return (NULL);
+            }
+        }
+
+        // We create the logging message right now. If we fail to convert a
+        // parameter to string, at least the part that we already did will
+        // be output
+        Logger::Formatter formatter(function(dbg, mid.c_str()));
+
+        // Now process the rest of parameters, convert each to string and put
+        // into the formatter. It will print itself in the end.
+        for (size_t i(start); i < number; ++ i) {
+            PyObject* param(PySequence_GetItem(args, i));
+            if (param == NULL) {
+                return (NULL);
+            }
+            formatter = formatter.arg(objectToStr(param, true));
+        }
+        Py_RETURN_NONE;
+    }
+    catch (const InternalError&) {
+        return (NULL);
+    }
+    catch (const std::exception& e) {
+        PyErr_SetString(PyExc_RuntimeError, e.what());
+        return (NULL);
+    }
+    catch (...) {
+        PyErr_SetString(PyExc_RuntimeError, "Unknown C++ exception");
+        return (NULL);
+    }
+}
+
+// Now map the functions into the performOutput. I wish C++ could do
+// functional programming.
+PyObject*
+Logger_debug(LoggerWrapper* self, PyObject* args) {
+    return (Logger_performOutput(bind(&Logger::debug, self->logger_, _1, _2),
+                                 args, true));
+}
+
+PyObject*
+Logger_info(LoggerWrapper* self, PyObject* args) {
+    return (Logger_performOutput(bind(&Logger::info, self->logger_, _2),
+                                 args, false));
+}
+
+PyObject*
+Logger_warn(LoggerWrapper* self, PyObject* args) {
+    return (Logger_performOutput(bind(&Logger::warn, self->logger_, _2),
+                                 args, false));
+}
+
+PyObject*
+Logger_error(LoggerWrapper* self, PyObject* args) {
+    return (Logger_performOutput(bind(&Logger::error, self->logger_, _2),
+                                 args, false));
+}
+
+PyObject*
+Logger_fatal(LoggerWrapper* self, PyObject* args) {
+    return (Logger_performOutput(bind(&Logger::fatal, self->logger_, _2),
+                                 args, false));
+}
+
+PyMethodDef loggerMethods[] = {
+    { "get_effective_severity",
+        reinterpret_cast<PyCFunction>(Logger_getEffectiveSeverity),
+        METH_NOARGS, "Returns the effective logging severity as string" },
+    { "get_effective_debug_level",
+        reinterpret_cast<PyCFunction>(Logger_getEffectiveDebugLevel),
+        METH_NOARGS, "Returns the current debug level." },
+    { "set_severity",
+        reinterpret_cast<PyCFunction>(Logger_setSeverity), METH_VARARGS,
+        "Sets the severity of a logger. The parameters are severity as a "
+        "string and, optionally, a debug level (integer in range 0-99). "
+        "The severity may be NULL, in which case an inherited value is taken."
+    },
+    { "is_debug_enabled", reinterpret_cast<PyCFunction>(Logger_isDebugEnabled),
+        METH_VARARGS, "Returns if the logger would log debug message now. "
+            "You can provide a desired debug level." },
+    { "is_info_enabled", reinterpret_cast<PyCFunction>(Logger_isInfoEnabled),
+        METH_NOARGS, "Returns if the logger would log info message now." },
+    { "is_warn_enabled", reinterpret_cast<PyCFunction>(Logger_isWarnEnabled),
+        METH_NOARGS, "Returns if the logger would log warn message now." },
+    { "is_error_enabled", reinterpret_cast<PyCFunction>(Logger_isErrorEnabled),
+        METH_NOARGS, "Returns if the logger would log error message now." },
+    { "is_fatal_enabled", reinterpret_cast<PyCFunction>(Logger_isFatalEnabled),
+        METH_NOARGS, "Returns if the logger would log fatal message now." },
+    { "debug", reinterpret_cast<PyCFunction>(Logger_debug), METH_VARARGS,
+        "Logs a debug-severity message. It takes the debug level, message ID "
+        "and any number of stringifiable arguments to the message." },
+    { "info", reinterpret_cast<PyCFunction>(Logger_info), METH_VARARGS,
+        "Logs a info-severity message. It taskes the message ID and any "
+        "number of stringifiable arguments to the message." },
+    { "warn", reinterpret_cast<PyCFunction>(Logger_warn), METH_VARARGS,
+        "Logs a warn-severity message. It taskes the message ID and any "
+        "number of stringifiable arguments to the message." },
+    { "error", reinterpret_cast<PyCFunction>(Logger_error), METH_VARARGS,
+        "Logs a error-severity message. It taskes the message ID and any "
+        "number of stringifiable arguments to the message." },
+    { "fatal", reinterpret_cast<PyCFunction>(Logger_fatal), METH_VARARGS,
+        "Logs a fatal-severity message. It taskes the message ID and any "
+        "number of stringifiable arguments to the message." },
+    { NULL, NULL, 0, NULL }
+};
+
+PyTypeObject logger_type = {
+    PyVarObject_HEAD_INIT(NULL, 0)
+    "isc.log.Logger",
+    sizeof(LoggerWrapper),                 // tp_basicsize
+    0,                                  // tp_itemsize
+    reinterpret_cast<destructor>(Logger_destroy),       // tp_dealloc
+    NULL,                               // tp_print
+    NULL,                               // tp_getattr
+    NULL,                               // tp_setattr
+    NULL,                               // tp_reserved
+    NULL,                               // tp_repr
+    NULL,                               // tp_as_number
+    NULL,                               // tp_as_sequence
+    NULL,                               // tp_as_mapping
+    NULL,                               // tp_hash 
+    NULL,                               // tp_call
+    NULL,                               // tp_str
+    NULL,                               // tp_getattro
+    NULL,                               // tp_setattro
+    NULL,                               // tp_as_buffer
+    Py_TPFLAGS_DEFAULT,                 // tp_flags
+    "Wrapper around the C++ isc::log::Logger class."
+    "It is not complete, but everything important should be here.",
+    NULL,                               // tp_traverse
+    NULL,                               // tp_clear
+    NULL,                               // tp_richcompare
+    0,                                  // tp_weaklistoffset
+    NULL,                               // tp_iter
+    NULL,                               // tp_iternext
+    loggerMethods,                      // tp_methods
+    NULL,                               // tp_members
+    NULL,                               // tp_getset
+    NULL,                               // tp_base
+    NULL,                               // tp_dict
+    NULL,                               // tp_descr_get
+    NULL,                               // tp_descr_set
+    0,                                  // tp_dictoffset
+    reinterpret_cast<initproc>(Logger_init),            // tp_init
+    NULL,                               // tp_alloc
+    PyType_GenericNew,                  // tp_new
+    NULL,                               // tp_free
+    NULL,                               // tp_is_gc
+    NULL,                               // tp_bases
+    NULL,                               // tp_mro
+    NULL,                               // tp_cache
+    NULL,                               // tp_subclasses
+    NULL,                               // tp_weaklist
+    NULL,                               // tp_del
+    0                                   // tp_version_tag
+};
+
+PyModuleDef iscLog = {
+    { PyObject_HEAD_INIT(NULL) NULL, 0, NULL},
+    "log",
+    "Python bindings for the classes in the isc::log namespace.\n\n"
+    "These bindings are close match to the C++ API, but they are not complete "
+    "(some parts are not needed) and some are done in more python-like ways.",
+    -1,
+    methods,
+    NULL,
+    NULL,
+    NULL,
+    NULL
+};
+
+} // end anonymous namespace
+
+PyMODINIT_FUNC
+PyInit_log(void) {
+    PyObject* mod = PyModule_Create(&iscLog);
+    if (mod == NULL) {
+        return (NULL);
+    }
+
+    if (PyType_Ready(&logger_type) < 0) {
+        return (NULL);
+    }
+
+    if (PyModule_AddObject(mod, "Logger",
+                           static_cast<PyObject*>(static_cast<void*>(
+                               &logger_type))) < 0) {
+        return (NULL);
+    }
+    Py_INCREF(&logger_type);
+
+    return (mod);
+}

+ 0 - 280
src/lib/python/isc/log/log.py

@@ -1,280 +0,0 @@
-# Copyright (C) 2010  Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-"""This module is to convert python logging module over
-to log4python.
-Copyright (C) 2010  Internet Systems Consortium.
-To use, simply 'import isc.log.log' and log away!
-"""
-import os
-import sys
-import syslog
-import logging
-import logging.handlers
-
-"""LEVELS: logging levels mapping
-"""
-LEVELS = {'debug' : logging.DEBUG,
-       	  'info' : logging.INFO,
-          'warning' : logging.WARNING,
-          'error' : logging.ERROR,
-          'critical' : logging.CRITICAL}
-
-FORMATTER = logging.Formatter("%(name)s: %(levelname)s: %(message)s")
-TIME_FORMATTER = logging.Formatter("%(asctime)s.%(msecs)03d %(name)s: %(levelname)s: %(message)s",
-                                   "%d-%b-%Y %H:%M:%S")
-
-def log_err(err_type, err_msg):
-    sys.stderr.write(err_type + ": " + "%s.\n" % str(err_msg)[str(err_msg).find(']')+1:])
-
-
-class NSFileLogHandler(logging.handlers.RotatingFileHandler):
-    """RotatingFileHandler: replace RotatingFileHandler with a custom handler"""
-
-    def __init__(self, filename, mode='a', maxBytes=0, backupCount=0, encoding=None, delay=0):
-        abs_file_name = self._get_abs_file_path(filename)
-        """Create log directory beforehand, because the underlying logging framework won't 
-        create non-exsiting log directory on writing logs.
-        """
-        if not (os.path.exists(os.path.dirname(abs_file_name))):
-            os.makedirs(os.path.dirname(abs_file_name))
-        super(NSFileLogHandler, self).__init__(abs_file_name, mode, maxBytes,
-                                                backupCount, encoding, delay)
-
-    def handleError(self, record):
-        """Overwrite handleError to provide more user-friendly error messages"""
-        if logging.raiseExceptions:
-            ei = sys.exc_info()
-            if (ei[1]):
-                sys.stderr.write("[b10-logging] : " + str(ei[1]))
-
-    def _get_abs_file_path(self, file_name):
-        """ Get absolute file path"""
-        # For a bare filename, log_dir will be set the current directory.
-        if not os.path.dirname(file_name):
-            abs_file_dir = os.getcwd()
-        else:
-            abs_file_dir = os.path.abspath(os.path.dirname(file_name))
-        abs_file_name = os.path.join(abs_file_dir, os.path.basename(file_name))
-        return abs_file_name
-
-    def shouldRollover(self, record):
-        """Rewrite RotatingFileHandler.shouldRollover. 
-       
-        If the log file is deleted at runtime, a new file will be created.
-        """
-        dfn = self.baseFilename
-        if (self.stream) and (not os.path.exists(dfn)): #Does log file exist?
-            self.stream = None
-            """ Log directory may be deleted while bind10 running or updated with a
-             non-existing directory. Need to create log directory beforehand, because
-             the underlying logging framework won't create non-exsiting log directory
-             on writing logs.
-             """
-            if not (os.path.exists(os.path.dirname(dfn))): #Does log subdirectory exist?
-                os.makedirs(os.path.dirname(dfn))
-            self.stream = self._open()
-        return super(NSFileLogHandler, self).shouldRollover(record)
-    
-    def update_config(self, file_name, backup_count, max_bytes):
-        """Update RotatingFileHandler configuration.
-        Changes will be picked up in the next call to shouldRollover().
-
-        input:
-            log file name
-            max backup count
-            predetermined log file size
-        """
-        abs_file_name = self._get_abs_file_path(file_name)
-        self.baseFilename = abs_file_name 
-        self.maxBytes = max_bytes
-        self.backupCount = backup_count
-
-
-class NSSysLogHandler(logging.Handler):    
-    """Replace SysLogHandler with a custom handler 
-
-    A handler class which sends formatted logging records to a syslog
-    server.
-    """
-    def __init__(self, ident, logopt=0, facility=syslog.LOG_USER):        
-        """Initialize a handler.
-    
-        If facility is not specified, LOG_USER is used.
-        """
-        super(NSSysLogHandler, self).__init__()
-        self._ident = ident        
-        self._logopt = logopt        
-        self._facility = facility        
-        self._mappings = {            
-                logging.DEBUG: syslog.LOG_DEBUG,            
-                logging.INFO: syslog.LOG_INFO,            
-                logging.WARNING: syslog.LOG_WARNING,            
-                logging.ERROR: syslog.LOG_ERR,            
-                logging.CRITICAL: syslog.LOG_CRIT,            
-                }   
-        
-    def _encodeLevel(self, level):        
-        """Encoding the priority."""
-        return self._mappings.get(level, syslog.LOG_INFO)    
-   
-    def emit(self, record):   
-        """Emit a record.
-     
-        The record is formatted, and then sent to the syslog server. If
-        exception information is present, it is NOT sent to the server.
-        """
-        syslog.openlog(self._ident, self._logopt, self._facility)        
-        msg = self.format(record)        
-        prio = self._encodeLevel(record.levelno)        
-        syslog.syslog(prio, msg)        
-        syslog.closelog()
-
-
-class NSLogger(logging.getLoggerClass()):
-    """Override logging.logger behaviour."""
-    def __init__(self, log_name, log_file, severity='debug', versions=0,
-                 max_bytes=0, log_to_console=True):
-        """Initializes the logger with some specific parameters
-
-        If log_to_console is True, stream handler will be used;
-        else syslog handler will be used.
-
-        To disable file handler, set log_file = ''.
-        """
-        self._log_name = log_name
-        self._log_file = log_file
-        self._severity = severity
-        self._versions = versions
-        self._max_bytes = max_bytes
-
-        super(NSLogger, self).__init__(self._log_name)
-
-        # Set up a specific logger with our desired output level
-        logLevel = LEVELS.get(self._severity, logging.NOTSET)
-        self.setLevel(logLevel)
-
-        self._file_handler = None
-        self._stream_handler = None
-        self._syslog_handler = None
-
-        self._add_rotate_handler(self._log_file, self._versions, self._max_bytes)
-        if log_to_console:
-            self._add_stream_handler()
-        else:
-            self._add_syslog_handler()
-
-    def _add_rotate_handler(self, log_file, backup_count, max_bytes):
-        """Add a rotate file handler.
-   
-        input:
-            log_file : the location of log file. Handler will not be created 
-                       if log_file=''
-            max_bytes : limit log growth
-            backup_count : max backup count
-        """
-        if (log_file != 0  and log_file != ''):
-            try:
-                self._file_handler = NSFileLogHandler(filename = log_file,
-                                          maxBytes = max_bytes, backupCount = backup_count)
-            except (IOError, OSError) as e:
-                self._file_handler = None
-                log_err("[b10-logging] Add file handler fail", str(e))
-                return
-            self._file_handler.setFormatter(TIME_FORMATTER)
-            self.addHandler(self._file_handler)
-
-    def _add_stream_handler(self):
-        """Add a stream handler.
-    
-        sys.stderr will be used for logging output.
-        """
-        self._stream_handler = logging.StreamHandler()
-        self._stream_handler.setFormatter(TIME_FORMATTER)
-        self.addHandler(self._stream_handler)
-
-    def _add_syslog_handler(self, nsfacility=syslog.LOG_USER):
-        """Add a syslog handler.
-   
-        If facility is not specified, LOG_USER is used.
-        The default severity level is INFO.
-        """
-        self._syslog_handler = NSSysLogHandler('BIND10', facility = nsfacility)
-        self._syslog_handler.setFormatter(FORMATTER)
-        #set syslog handler severity level INFO
-        self._syslog_handler.setLevel(logging.INFO)
-        self.addHandler(self._syslog_handler)
-
-    def _update_rotate_handler(self, log_file, backup_count, max_bytes):
-        """If the rotate file handler has been added to the logger, update its 
-        configuration, or add it to the logger.
-        """
-        if (self._file_handler in self.handlers):
-            if (log_file != 0 and log_file != ''):
-                self._file_handler.update_config(log_file, backup_count, max_bytes)
-            else:
-                """If log file is empty, the handler will be removed."""
-                self._file_handler.flush()
-                self._file_handler.close()
-                self.removeHandler(self._file_handler)
-        else:
-            self._add_rotate_handler(log_file, backup_count, max_bytes)
-
-    def _get_config(self, config_data):
-         """Get config data from module configuration"""
-         
-         log_file_str = config_data.get('log_file')
-         if (log_file_str):
-            self._log_file = log_file_str
-         
-         severity_str = config_data.get('log_severity')
-         if (severity_str):
-             self._severity = severity_str
-
-         versions_str = config_data.get('log_versions')
-         if (versions_str):
-             self._versions = int(versions_str)
-
-         max_bytes_str = config_data.get('log_max_bytes')
-         if (max_bytes_str):
-             self._max_bytes = int(max_bytes_str)
-
-    def update_config(self, config_data):
-        """Update logger's configuration.
-
-        We can update logger's log level and its rotate file handler's configuration.
-        """
-        self._get_config(config_data)
-
-        logLevel = LEVELS.get(self._severity, logging.NOTSET)
-        if (logLevel != self.getEffectiveLevel()):
-            self.setLevel(logLevel)
-        self._update_rotate_handler(self._log_file, self._versions, self._max_bytes)
-
-    def log_message(self, level, msg, *args, **kwargs):
-        """Log 'msg % args' with the integer severity 'level'.
-     
-        To pass exception information, use the keyword argument exc_info with
-        a true value, e.g.
-  
-        logger.log_message('info', "We have a %s", "mysterious problem").
-        """
-        logLevel = LEVELS.get(level, logging.NOTSET)
-        try:
-            self.log(logLevel, msg, *args, **kwargs)
-        except (TypeError, KeyError) as e:
-            sys.stderr.write("[b10-logging] Log message fail %s\n" % (str(e)))
-
-

+ 14 - 3
src/lib/python/isc/log/tests/Makefile.am

@@ -1,16 +1,27 @@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYCOVERAGE_RUN = @PYCOVERAGE_RUN@
 PYTESTS = log_test.py
 PYTESTS = log_test.py
-EXTRA_DIST = $(PYTESTS)
+EXTRA_DIST = $(PYTESTS) log_console.py.in console.out check_output.sh
+
+# If necessary (rare cases), explicitly specify paths to dynamic libraries
+# required by loadable python modules.
+LIBRARY_PATH_PLACEHOLDER =
+if SET_ENV_LIBRARY_PATH
+LIBRARY_PATH_PLACEHOLDER += $(ENV_LIBRARY_PATH)=$(abs_top_builddir)/src/lib/log/.libs:$(abs_top_builddir)/src/lib/util/.libs:$(abs_top_builddir)/src/lib/exceptions/.libs:$$$(ENV_LIBRARY_PATH)
+endif
 
 
 # test using command-line arguments, so use check-local target instead of TESTS
 # test using command-line arguments, so use check-local target instead of TESTS
 check-local:
 check-local:
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+	$(abs_srcdir)/check_output.sh $(abs_builddir)/log_console.py $(abs_srcdir)/console.out
 if ENABLE_PYTHON_COVERAGE
 if ENABLE_PYTHON_COVERAGE
-	touch $(abs_top_srcdir)/.coverage 
+	touch $(abs_top_srcdir)/.coverage
 	rm -f .coverage
 	rm -f .coverage
 	${LN_S} $(abs_top_srcdir)/.coverage .coverage
 	${LN_S} $(abs_top_srcdir)/.coverage .coverage
 endif
 endif
 	for pytest in $(PYTESTS) ; do \
 	for pytest in $(PYTESTS) ; do \
 	echo Running test: $$pytest ; \
 	echo Running test: $$pytest ; \
-	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log \
+	$(LIBRARY_PATH_PLACEHOLDER) \
+	env PYTHONPATH=$(abs_top_srcdir)/src/lib/python:$(abs_top_builddir)/src/lib/python:$(abs_top_builddir)/src/lib/python/isc/log:$(abs_top_builddir)/src/lib/log/python/.libs \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	$(PYCOVERAGE_RUN) $(abs_srcdir)/$$pytest || exit ; \
 	done
 	done

+ 3 - 0
src/lib/python/isc/log/tests/check_output.sh

@@ -0,0 +1,3 @@
+#!/bin/sh
+
+"$1" 2>&1 | cut -d\  -f3- | diff - "$2" 1>&2

+ 4 - 0
src/lib/python/isc/log/tests/console.out

@@ -0,0 +1,4 @@
+INFO  [test.output] MSG_ID, Message with list [1, 2, 3, 4]
+WARN  [test.output] DIFFERENT, Different message
+FATAL [test.output] MSG_ID, Message with 2 1
+DEBUG [test.output] MSG_ID, Message with 3 2

+ 15 - 0
src/lib/python/isc/log/tests/log_console.py.in

@@ -0,0 +1,15 @@
+#!@PYTHON@
+
+import isc.log
+# This would come from a dictionary in real life
+MSG_ID = isc.log.create_message("MSG_ID", "Message with %2 %1")
+DIFFERENT = isc.log.create_message("DIFFERENT", "Different message")
+isc.log.init("test")
+logger = isc.log.Logger("output")
+
+logger.debug(20, MSG_ID, "test", "no output")
+logger.info(MSG_ID, [1, 2, 3, 4], "list")
+logger.warn(DIFFERENT)
+logger.fatal(MSG_ID, 1, 2)
+logger.set_severity("DEBUG", 99)
+logger.debug(1, MSG_ID, 2, 3)

+ 0 - 26
src/lib/python/isc/log/tests/log_test.in

@@ -1,26 +0,0 @@
-#! /bin/sh
-
-# Copyright (C) 2010  Internet Systems Consortium.
-#
-# Permission to use, copy, modify, and distribute this software for any
-# purpose with or without fee is hereby granted, provided that the above
-# copyright notice and this permission notice appear in all copies.
-#
-# THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
-# DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
-# IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
-# INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
-# INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
-# FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
-# NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
-# WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
-
-PYTHON_EXEC=${PYTHON_EXEC:-@PYTHON@}
-export PYTHON_EXEC
-
-TEST_PATH=@abs_top_srcdir@/src/lib/python/isc/log/tests
-PYTHONPATH=@abs_top_srcdir@/src/lib/python:@abs_top_builddir@/src/lib/python
-export PYTHONPATH
-
-cd ${TEST_PATH}
-exec ${PYTHON_EXEC} -O log_test.py $*

+ 108 - 212
src/lib/python/isc/log/tests/log_test.py

@@ -1,4 +1,4 @@
-# Copyright (C) 2010  Internet Systems Consortium.
+# Copyright (C) 2011  Internet Systems Consortium.
 #
 #
 # Permission to use, copy, modify, and distribute this software for any
 # Permission to use, copy, modify, and distribute this software for any
 # purpose with or without fee is hereby granted, provided that the above
 # purpose with or without fee is hereby granted, provided that the above
@@ -13,225 +13,121 @@
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
 
 
-#
-# Tests for the python logging module
-#
-
-from isc.log.log import *
+# This tests it can be loaded, nothing more yet
+import isc.log
 import unittest
 import unittest
-import os
-import sys
-import tempfile
-
-
-class TestRotateFileHandler(unittest.TestCase):
 
 
+class LogDict(unittest.TestCase):
     def setUp(self):
     def setUp(self):
-        self.FILE_LOG1 = tempfile.NamedTemporaryFile(mode='w',
-                                                 prefix="b10",
-                                                 delete=True)
-        self.FILE_LOG2 = tempfile.NamedTemporaryFile(mode='w',
-                                                 prefix="b10",
-                                                 delete=True)
-        self.FILE_LOG3 = tempfile.NamedTemporaryFile(mode='w',
-                                                 prefix="b10",
-                                                 delete=True)
-        self.handler = NSFileLogHandler(filename = self.FILE_LOG1.name, 
-                                        maxBytes = 1024, 
-                                        backupCount = 5)
-
-    def test_shouldRollover(self):
-        if(os.path.exists(self.FILE_LOG1.name)):
-            os.remove(self.FILE_LOG1.name)
-        record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None)
-        self.handler.shouldRollover(record)
-        self.assertTrue(os.path.exists(self.FILE_LOG1.name))
-
-    def test_get_absolute_file_path(self):
-        abs_file_name = self.handler._get_abs_file_path(self.FILE_LOG1.name)
-        self.assertEqual(abs_file_name, self.FILE_LOG1.name)
-        # test bare filename
-        file_name1 = "bind10.py"
-        abs_file_name = self.handler._get_abs_file_path(file_name1)
-        self.assertEqual(abs_file_name, os.path.join(os.getcwd(), file_name1))
-        # test relative path 
-        file_name2 = "./bind10.py"
-        abs_file_name = self.handler._get_abs_file_path(file_name2)
-        self.assertEqual(abs_file_name, os.path.join(os.getcwd(), os.path.basename(file_name2)))
-         
-    def test_update_config(self):
-        self.handler.update_config(self.FILE_LOG2.name, 3, 512)
-        self.assertEqual(self.handler.baseFilename, self.FILE_LOG2.name)
-        self.assertEqual(self.handler.maxBytes, 512)
-        self.assertEqual(self.handler.backupCount, 3)
-
-        # check the existence of new log file.
-        # emit() will call shouldRollover() to update the log file
-        if(os.path.exists(self.FILE_LOG2.name)):
-            os.remove(self.FILE_LOG2.name)
-        record = logging.LogRecord(None, None, "", 0, "rotate file handler", (), None, None)
-        self.handler.emit(record)
-        self.assertTrue(os.path.exists(self.FILE_LOG2.name))
-
-    def test_handle_Error(self):
-        if(os.path.exists(self.FILE_LOG3.name)):
-            os.remove(self.FILE_LOG3.name)
-        # redirect error message to file
-        savederr = sys.stderr
-        errfd = open(self.FILE_LOG3.name, 'w+')
-        sys.stderr = errfd
-        record = logging.LogRecord(None, None, "", 0, "record message", (), None, None)
-        try:
-            raise ValueError("ValueError")
-        except ValueError:
-            self.handler.handleError(record)
-
-        self.assertEqual("[b10-logging] : ValueError", errfd.read())
-        sys.stderr = savederr
-        errfd.close()
-
+        # We work on a test dictionary now.
+        isc.log.set_test_dictionary(True)
     def tearDown(self):
     def tearDown(self):
-        self.handler.flush()
-        self.handler.close()
-        self.FILE_LOG1.close()
-        self.FILE_LOG2.close()
-        self.FILE_LOG3.close()
-
-class TestSysLogHandler(unittest.TestCase):
-    def setUp(self):
-        self.handler = NSSysLogHandler("BIND10")
+        # Return to the global dictionary
+        isc.log.set_test_dictionary(False)
 
 
-    def test_encodeLevel(self):
-        sysLevel = self.handler._encodeLevel(logging.ERROR)
-        self.assertEqual(sysLevel, syslog.LOG_ERR)
+    def test_load_msgs(self):
+        # Try loading a message and see it's there, but nothing more
+        self.assertEqual(isc.log.create_message("ID", "Text"), "ID")
+        self.assertEqual(isc.log.get_message("ID"), "Text")
+        self.assertEqual(isc.log.get_message("no-ID"), None)
 
 
-    def test_emit(self):
-        syslog_message = "bind10 syslog testing"
-        record = logging.LogRecord(None, None, "", 0, syslog_message, (), None, None)
-        self.handler.emit(record)
+class Manager(unittest.TestCase):
+    def tearDown(self):
+        isc.log.reset()
+
+    def test_init_debug(self):
+        # We try calling it now only, as we don't have any other functions
+        # to check the outcome by it. Once we add the logger class, we may
+        # check more.
+        isc.log.init("root", "DEBUG", 50, None)
+
+    def test_init_defaults(self):
+        # We try calling it now only, as we don't have any other functions
+        # to check the outcome by it. Once we add the logger class, we may
+        # check more.
+        isc.log.init("root")
+
+    def test_init_notfound(self):
+        # This should not throw, because the C++ one doesn't. Should we really
+        # ignore errors like missing file?
+        isc.log.init("root", "INFO", 0, "/no/such/file");
+
+class Logger(unittest.TestCase):
+    def tearDown(self):
+        isc.log.reset()
 
 
-class TestLogging(unittest.TestCase):
-    
     def setUp(self):
     def setUp(self):
-        self.FILE_STREAM_LOG1 = tempfile.NamedTemporaryFile(mode='w',
-                                                      prefix="b10",
-                                                      delete=True)
-        self.FILE_STREAM_LOG2 = tempfile.NamedTemporaryFile(mode='w',
-                                                      prefix="b10",
-                                                      delete=True)
-        self.FILE_STREAM_LOG3 = tempfile.NamedTemporaryFile(mode='w',
-                                                      prefix="b10",
-                                                      delete=True)
-        self.file_stream_logger = NSLogger('File_Stream_Logger',
-                                           self.FILE_STREAM_LOG1.name,
-                                           'debug', 5, 1024, True)
-        self.syslog_logger = NSLogger('SysLogger', '', 'info', 5, 1024, False)
-        self.stderr_bak = sys.stderr
-        sys.stderr = open(os.devnull, 'w')
-    
-    def test_logging_init(self):
-        self.assertNotEqual(self.file_stream_logger._file_handler, None)
-        self.assertNotEqual(self.file_stream_logger._stream_handler, None)
-        self.assertEqual(self.file_stream_logger._syslog_handler, None)
-
-        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-        self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
-        self.assertNotIn(self.file_stream_logger._syslog_handler, self.file_stream_logger.handlers)
-        logLevel = LEVELS.get('debug', logging.NOTSET)
-        self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel)
-
-        self.assertEqual(self.syslog_logger._file_handler, None)
-        self.assertEqual(self.syslog_logger._stream_handler, None)
-        self.assertNotEqual(self.syslog_logger._syslog_handler, None)
-        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-        self.assertNotIn(self.syslog_logger._stream_handler, self.syslog_logger.handlers)
-        self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
-
-        logLevel = LEVELS.get('info', logging.NOTSET)
-        self.assertEqual(self.syslog_logger.getEffectiveLevel(), logLevel)
-
-    def test_add_rotate_handler(self):
-        if(self.syslog_logger._file_handler in self.syslog_logger.handlers):
-            self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
-        
-        self.syslog_logger._add_rotate_handler('', 5, 1024)
-        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
-        self.syslog_logger._add_rotate_handler(self.FILE_STREAM_LOG1.name, 5, 1024)
-        self.assertIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
-        # test IOError exception
-        self.syslog_logger.removeHandler(self.syslog_logger._file_handler)
-        log_file = self.FILE_STREAM_LOG1.name + '/logfile'
-        self.syslog_logger._add_rotate_handler(log_file, 5, 1024)
-        self.assertNotIn(self.syslog_logger._file_handler, self.syslog_logger.handlers)
-
-    def test_add_stream_handler(self):
-        if(self.file_stream_logger._stream_handler in self.file_stream_logger.handlers):
-            self.file_stream_logger.removeHandler(self.file_stream_logger._stream_handler)
-
-        self.file_stream_logger._add_stream_handler()
-        self.assertIn(self.file_stream_logger._stream_handler, self.file_stream_logger.handlers)
-
-    def test_add_syslog_handler(self):
-        if(self.syslog_logger._syslog_handler in self.syslog_logger.handlers):
-            self.syslog_logger.removeHandler(self.syslog_logger._syslog_handler)
-
-        self.syslog_logger._add_syslog_handler()
-        self.assertIn(self.syslog_logger._syslog_handler, self.syslog_logger.handlers)
-
-    def test_update_rotate_handler(self):
-        self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG2.name, 4, 1024)
-        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
-        self.file_stream_logger._update_rotate_handler('', 5, 1024)
-        self.assertNotIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
-        self.file_stream_logger._update_rotate_handler(self.FILE_STREAM_LOG1.name, 4, 1024)
-        self.assertIn(self.file_stream_logger._file_handler, self.file_stream_logger.handlers)
-
-    def test_get_config(self):
-        config_data = {'log_file' : self.FILE_STREAM_LOG1.name,
-                       'log_severity' : 'critical',
-                       'log_versions' : 4,
-                       'log_max_bytes' : 1024}
-        self.file_stream_logger._get_config(config_data)
-        self.assertEqual(self.file_stream_logger._log_file, self.FILE_STREAM_LOG1.name)
-        self.assertEqual(self.file_stream_logger._severity, 'critical')
-        self.assertEqual(self.file_stream_logger._versions, 4)
-        self.assertEqual(self.file_stream_logger._max_bytes, 1024)
-
-
-    def test_update_config(self):
-        update_config = {'log_file' : self.FILE_STREAM_LOG1.name,
-                         'log_severity' : 'error',
-                         'log_versions' : 4,
-                         'log_max_bytes' : 1024}
-        self.file_stream_logger.update_config(update_config)
-        logLevel = LEVELS.get('error', logging.NOTSET)
-        self.assertEqual(self.file_stream_logger.getEffectiveLevel(), logLevel)
-
-    def test_log_message(self):
-        update_config = {'log_file' : self.FILE_STREAM_LOG3.name,
-                         'log_severity' : 'critical',
-                         'log_versions' : 4,
-                         'log_max_bytes' : 1024}
-        self.file_stream_logger.update_config(update_config)
-        self.file_stream_logger.log_message('debug', 'debug message')
-        self.file_stream_logger.log_message('warning', 'warning message')
-        self.file_stream_logger.log_message('error', 'error message')
-        #test non-exist log level
-        self.assertRaises(None, self.file_stream_logger.log_message('not-exist', 'not exist message'))
-        #test log_message KeyError exception
-        self.assertRaises(None, self.file_stream_logger.log_message('critical', 'critical message', extra=['message', 'asctime']))
-        self.assertTrue(os.path.exists(self.FILE_STREAM_LOG3.name))
-    
-    def tearDown(self):
-        self.FILE_STREAM_LOG1.close()
-        self.FILE_STREAM_LOG2.close()
-        self.FILE_STREAM_LOG3.close()
-        sys.stderr.flush();
-        sys.stderr = self.stderr_bak
+        isc.log.init("root", "DEBUG", 50)
+        self.sevs = ['INFO', 'WARN', 'ERROR', 'FATAL']
+
+    # Checks defaults of the logger
+    def defaults(self, logger):
+        self.assertEqual(logger.get_effective_severity(), "DEBUG")
+        self.assertEqual(logger.get_effective_debug_level(), 50)
+
+    def test_default_severity(self):
+        logger = isc.log.Logger("child")
+        self.defaults(logger)
+
+    # Try changing the severities little bit
+    def test_severity(self):
+        logger = isc.log.Logger("child")
+        logger.set_severity('DEBUG', 25)
+        self.assertEqual(logger.get_effective_severity(), "DEBUG")
+        self.assertEqual(logger.get_effective_debug_level(), 25)
+        for sev in self.sevs:
+            logger.set_severity(sev)
+            self.assertEqual(logger.get_effective_severity(), sev)
+            self.assertEqual(logger.get_effective_debug_level(), 0)
+        # Return to default
+        logger.set_severity(None)
+        self.defaults(logger)
+
+    def test_enabled(self):
+        logger = isc.log.Logger("child")
+        self.sevs.insert(0, 'DEBUG')
+        methods = {
+            'DEBUG': logger.is_debug_enabled,
+            'INFO': logger.is_info_enabled,
+            'WARN': logger.is_warn_enabled,
+            'ERROR': logger.is_error_enabled,
+            'FATAL': logger.is_fatal_enabled
+        }
+        for sev in self.sevs:
+            logger.set_severity(sev)
+            enabled = False
+            for tested in self.sevs:
+                if tested == sev:
+                    enabled = True
+                self.assertEqual(methods[tested](), enabled)
+        logger.set_severity('DEBUG', 50)
+        self.assertTrue(logger.is_debug_enabled())
+        self.assertTrue(logger.is_debug_enabled(0))
+        self.assertTrue(logger.is_debug_enabled(50))
+        self.assertFalse(logger.is_debug_enabled(99))
+
+    def test_invalid_params(self):
+        """
+           Tests invalid arguments for logging functions. The output is tested
+           in check_output.sh.
+        """
+        logger = isc.log.Logger("child")
+        methods = [
+            logger.info,
+            logger.warn,
+            logger.error,
+            logger.fatal
+        ]
+        for meth in methods:
+            # Not enough arguments
+            self.assertRaises(TypeError, meth)
+            # Bad type
+            self.assertRaises(TypeError, meth, 1)
+        # Too few arguments
+        self.assertRaises(TypeError, logger.debug, 42)
+        self.assertRaises(TypeError, logger.debug)
+        # Bad type
+        self.assertRaises(TypeError, logger.debug, "42", "hello")
 
 
 if __name__ == '__main__':
 if __name__ == '__main__':
     unittest.main()
     unittest.main()