|
@@ -0,0 +1,209 @@
|
|
|
|
+#!@PYTHON@
|
|
|
|
+
|
|
|
|
+# 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.
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+import sys; sys.path.append ('@@PYTHONPATH@@')
|
|
|
|
+import isc
|
|
|
|
+import bind10_config
|
|
|
|
+from isc.dns import *
|
|
|
|
+from isc.config.ccsession import *
|
|
|
|
+from isc.cc import SessionError, SessionTimeout
|
|
|
|
+import isc.util.process
|
|
|
|
+
|
|
|
|
+from isc.log_messages.ddns_messages import *
|
|
|
|
+
|
|
|
|
+from optparse import OptionParser, OptionValueError
|
|
|
|
+import os
|
|
|
|
+import signal
|
|
|
|
+
|
|
|
|
+isc.log.init("b10-ddns")
|
|
|
|
+logger = isc.log.Logger("ddns")
|
|
|
|
+
|
|
|
|
+DATA_PATH = bind10_config.DATA_PATH
|
|
|
|
+if "B10_FROM_SOURCE" in os.environ:
|
|
|
|
+ DATA_PATH = os.environ['B10_FROM_SOURCE'] + "/src/bin/ddns"
|
|
|
|
+SPECFILE_LOCATION = DATA_PATH + "/ddns.spec"
|
|
|
|
+
|
|
|
|
+
|
|
|
|
+isc.util.process.rename()
|
|
|
|
+
|
|
|
|
+class DDNSConfigError(Exception):
|
|
|
|
+ '''An exception indicating an error in updating ddns configuration.
|
|
|
|
+
|
|
|
|
+ This exception is raised when the ddns process encounters an error in
|
|
|
|
+ handling configuration updates. Not all syntax error can be caught
|
|
|
|
+ at the module-CC layer, so ddns needs to (explicitly or implicitly)
|
|
|
|
+ validate the given configuration data itself. When it finds an error
|
|
|
|
+ it raises this exception (either directly or by converting an exception
|
|
|
|
+ from other modules) as a unified error in configuration.
|
|
|
|
+ '''
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+class DDNSSessionError(Exception):
|
|
|
|
+ '''An exception raised for some unexpected events during a ddns session.
|
|
|
|
+ '''
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+class DDNSSession:
|
|
|
|
+ '''Class to handle one DDNS update'''
|
|
|
|
+
|
|
|
|
+ def __init__(self):
|
|
|
|
+ '''Initialize a DDNS Session'''
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+class DDNSServer:
|
|
|
|
+ def __init__(self, cc_session=None):
|
|
|
|
+ '''
|
|
|
|
+ Initialize the DDNS Server.
|
|
|
|
+ This sets up a ModuleCCSession for the BIND 10 system.
|
|
|
|
+ Parameters:
|
|
|
|
+ cc_session: If None (default), a new ModuleCCSession will be set up.
|
|
|
|
+ If specified, the given session will be used. This is
|
|
|
|
+ mainly used for testing.
|
|
|
|
+ '''
|
|
|
|
+ if cc_session is not None:
|
|
|
|
+ self._cc = cc_session
|
|
|
|
+ else:
|
|
|
|
+ self._cc = isc.config.ModuleCCSession(SPECFILE_LOCATION,
|
|
|
|
+ self.config_handler,
|
|
|
|
+ self.command_handler)
|
|
|
|
+
|
|
|
|
+ self._config_data = self._cc.get_full_config()
|
|
|
|
+ self._cc.start()
|
|
|
|
+ self._shutdown = False
|
|
|
|
+
|
|
|
|
+ def config_handler(self, new_config):
|
|
|
|
+ '''Update config data.'''
|
|
|
|
+ answer = create_answer(0)
|
|
|
|
+ return answer
|
|
|
|
+
|
|
|
|
+ def command_handler(self, cmd, args):
|
|
|
|
+ '''
|
|
|
|
+ Handle a CC session command, as sent from bindctl or other
|
|
|
|
+ BIND 10 modules.
|
|
|
|
+ '''
|
|
|
|
+ if cmd == "shutdown":
|
|
|
|
+ logger.info(DDNS_RECEIVED_SHUTDOWN_COMMAND)
|
|
|
|
+ self.trigger_shutdown()
|
|
|
|
+ answer = create_answer(0)
|
|
|
|
+ else:
|
|
|
|
+ answer = create_answer(1, "Unknown command: " + str(cmd))
|
|
|
|
+ return answer
|
|
|
|
+
|
|
|
|
+ def trigger_shutdown(self):
|
|
|
|
+ '''Initiate a shutdown sequence.
|
|
|
|
+
|
|
|
|
+ This method is expected to be called in various ways including
|
|
|
|
+ in the middle of a signal handler, and is designed to be as simple
|
|
|
|
+ as possible to minimize side effects. Actual shutdown will take
|
|
|
|
+ place in a normal control flow.
|
|
|
|
+
|
|
|
|
+ '''
|
|
|
|
+ logger.info(DDNS_SHUTDOWN)
|
|
|
|
+ self._shutdown = True
|
|
|
|
+
|
|
|
|
+ def shutdown_cleanup(self):
|
|
|
|
+ '''
|
|
|
|
+ Perform any cleanup that is necessary when shutting down the server.
|
|
|
|
+ Do NOT call this to initialize shutdown, use trigger_shutdown().
|
|
|
|
+
|
|
|
|
+ Currently, it does nothing, but cleanup routines are expected.
|
|
|
|
+ '''
|
|
|
|
+ pass
|
|
|
|
+
|
|
|
|
+ def run(self):
|
|
|
|
+ '''
|
|
|
|
+ Get and process all commands sent from cfgmgr or other modules.
|
|
|
|
+ This loops waiting for events until self.shutdown() has been called.
|
|
|
|
+ '''
|
|
|
|
+ logger.info(DDNS_RUNNING)
|
|
|
|
+ while not self._shutdown:
|
|
|
|
+ # We do not catch any exceptions here right now, but this would
|
|
|
|
+ # be a good place to catch any exceptions that b10-ddns can
|
|
|
|
+ # recover from. We currently have no exception hierarchy to
|
|
|
|
+ # make such a distinction easily, but once we do, this would
|
|
|
|
+ # be the place to catch.
|
|
|
|
+ self._cc.check_command(False)
|
|
|
|
+ self.shutdown_cleanup()
|
|
|
|
+ logger.info(DDNS_STOPPED)
|
|
|
|
+
|
|
|
|
+def create_signal_handler(ddns_server):
|
|
|
|
+ '''
|
|
|
|
+ This creates a signal_handler for use in set_signal_handler, which
|
|
|
|
+ shuts down the given DDNSServer (or any object that has a shutdown()
|
|
|
|
+ method)
|
|
|
|
+ '''
|
|
|
|
+ def signal_handler(signal, frame):
|
|
|
|
+ '''
|
|
|
|
+ Handler for process signals. Since only signals to shut down are sent
|
|
|
|
+ here, the actual signal is not checked and the server is simply shut
|
|
|
|
+ down.
|
|
|
|
+ '''
|
|
|
|
+ ddns_server.trigger_shutdown()
|
|
|
|
+ return signal_handler
|
|
|
|
+
|
|
|
|
+def set_signal_handler(signal_handler):
|
|
|
|
+ '''
|
|
|
|
+ Sets the signal handler(s).
|
|
|
|
+ '''
|
|
|
|
+ signal.signal(signal.SIGTERM, signal_handler)
|
|
|
|
+ signal.signal(signal.SIGINT, signal_handler)
|
|
|
|
+
|
|
|
|
+def set_cmd_options(parser):
|
|
|
|
+ '''
|
|
|
|
+ Helper function to set command-line options
|
|
|
|
+ '''
|
|
|
|
+ parser.add_option("-v", "--verbose", dest="verbose", action="store_true",
|
|
|
|
+ help="display more about what is going on")
|
|
|
|
+
|
|
|
|
+def main(ddns_server=None):
|
|
|
|
+ '''
|
|
|
|
+ The main function.
|
|
|
|
+ Parameters:
|
|
|
|
+ ddns_server: If None (default), a DDNSServer object is initialized.
|
|
|
|
+ If specified, the given DDNSServer will be used. This is
|
|
|
|
+ mainly used for testing.
|
|
|
|
+ cc_session: If None (default), a new ModuleCCSession will be set up.
|
|
|
|
+ If specified, the given session will be used. This is
|
|
|
|
+ mainly used for testing.
|
|
|
|
+ '''
|
|
|
|
+ try:
|
|
|
|
+ parser = OptionParser()
|
|
|
|
+ set_cmd_options(parser)
|
|
|
|
+ (options, args) = parser.parse_args()
|
|
|
|
+ if options.verbose:
|
|
|
|
+ print("[b10-ddns] Warning: -v verbose option is ignored at this point.")
|
|
|
|
+
|
|
|
|
+ if ddns_server is None:
|
|
|
|
+ ddns_server = DDNSServer()
|
|
|
|
+ set_signal_handler(create_signal_handler(ddns_server))
|
|
|
|
+ ddns_server.run()
|
|
|
|
+ except KeyboardInterrupt:
|
|
|
|
+ logger.info(DDNS_STOPPED_BY_KEYBOARD)
|
|
|
|
+ except SessionError as e:
|
|
|
|
+ logger.error(DDNS_CC_SESSION_ERROR, str(e))
|
|
|
|
+ except ModuleCCSessionError as e:
|
|
|
|
+ logger.error(DDNS_MODULECC_SESSION_ERROR, str(e))
|
|
|
|
+ except DDNSConfigError as e:
|
|
|
|
+ logger.error(DDNS_CONFIG_ERROR, str(e))
|
|
|
|
+ except SessionTimeout as e:
|
|
|
|
+ logger.error(DDNS_CC_SESSION_TIMEOUT_ERROR)
|
|
|
|
+ except Exception as e:
|
|
|
|
+ logger.error(DDNS_UNCAUGHT_EXCEPTION, type(e).__name__, str(e))
|
|
|
|
+
|
|
|
|
+if '__main__' == __name__:
|
|
|
|
+ main()
|