datasrc_clients_mgr.py 6.1 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135
  1. # Copyright (C) 2013 Internet Systems Consortium, Inc. ("ISC")
  2. #
  3. # Permission to use, copy, modify, and distribute this software for any
  4. # purpose with or without fee is hereby granted, provided that the above
  5. # copyright notice and this permission notice appear in all copies.
  6. #
  7. # THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SYSTEMS CONSORTIUM
  8. # DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
  9. # IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
  10. # INTERNET SYSTEMS CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
  11. # INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
  12. # FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
  13. # NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
  14. # WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
  15. import isc.dns
  16. import isc.datasrc
  17. import threading
  18. import json
  19. class ConfigError(Exception):
  20. """Exception class raised for data source configuration errors."""
  21. pass
  22. class DataSrcClientsMgr:
  23. """A container of data source client lists.
  24. This class represents a set of isc.datasrc.ConfigurableClientList
  25. objects (currently per RR class), and provides APIs to configure
  26. the lists and access to a specific list in a thread safe manner.
  27. It is intended to be used by applications that refer to the global
  28. 'data_sources' module. The reconfigure() method can be called from
  29. a configuration callback for the module of the application. The
  30. get_client_list() method is a simple search method to get the configured
  31. ConfigurableClientList object for a specified RR class (if any),
  32. while still allowing a separate thread to reconfigure the entire lists.
  33. """
  34. def __init__(self, use_cache=False):
  35. """Constructor.
  36. In the initial implementation, user applications of this class are
  37. generally expected to NOT use in-memory cache; use_cache would be
  38. set to True only for tests. In future, some applications such as
  39. outbound zone transfer may want to set it to True.
  40. Parameter:
  41. use_cache (bool): If set to True, enable in-memory cache on
  42. (re)configuration.
  43. """
  44. self.__use_cache = use_cache
  45. # Map from RRClass to ConfigurableClientList. Resetting this map
  46. # is protected by __map_lock. Note that this lock doesn't protect
  47. # "updates" of the map content (currently it's not a problem, but
  48. # if and when we support more operations such as reloading
  49. # particular zones in in-memory cache, remember that there will have
  50. # to be an additional layer of protection).
  51. self.__clients_map = {}
  52. self.__map_lock = threading.Lock()
  53. def get_client_list(self, rrclass):
  54. """Return the configured ConfigurableClientList for the RR class.
  55. If no client list is configured for the specified RR class, it
  56. returns None.
  57. This method should not raise an exception as long as the parameter
  58. is of valid type.
  59. This method can be safely called from a thread even if a different
  60. thread is calling reconfigure(). Also, it's safe for the caller
  61. to use the returned list even if reconfigure() is called while or
  62. after the call to this thread.
  63. Note that this class does not protect further access to the returned
  64. list from multiple threads; it's the caller's responsbility to make
  65. such access thread safe. In general, the find() method on the list
  66. and the use of ZoneFinder created by a DataSourceClient in the list
  67. cannot be done by multiple threads without explicit synchronization.
  68. On the other hand, multiple threads can create and use ZoneUpdater
  69. or ZoneIterator on a DataSourceClient in parallel.
  70. Parameter:
  71. rrclass (isc.dns.RRClass): the RR class of the ConfigurableClientList
  72. to be returned.
  73. """
  74. with self.__map_lock:
  75. client_list = self.__clients_map.get(rrclass)
  76. return client_list
  77. def reconfigure(self, config):
  78. """(Re)configure the set of client lists.
  79. This method takes a new set of data source configuration, builds
  80. a new set of ConfigurableClientList objects corresponding to the
  81. configuration, and replaces the internal set with the newly built
  82. one. Its parameter is expected to be the "new configuration"
  83. parameter of a configuration update callback for the global
  84. "data_sources" module. It should match the configuration data
  85. of the module spec (see the datasrc.spec file).
  86. Any error in reconfiguration is converted to a ConfigError
  87. exception and is raised from the method. This method guarantees
  88. strong exception safety: unless building a new set for the new
  89. configuration is fully completed, the old set is intact.
  90. This method can be called from a thread while some other thread
  91. is calling get_client_list() and using the result (see
  92. the description of get_client_list()). In general, however,
  93. only one thread can call this method at one time; while data
  94. integrity will still be preserved, the ordering of the change
  95. will not be guaranteed if multiple threads call this method
  96. at the same time.
  97. Parameter:
  98. config (dict): configuration data for the data_sources module.
  99. """
  100. try:
  101. new_map = {}
  102. for rrclass_cfg, class_cfg in config.get('classes').items():
  103. rrclass = isc.dns.RRClass(rrclass_cfg)
  104. new_client_list = isc.datasrc.ConfigurableClientList(rrclass)
  105. new_client_list.configure(json.dumps(class_cfg),
  106. self.__use_cache)
  107. new_map[rrclass] = new_client_list
  108. with self.__map_lock:
  109. self.__clients_map = new_map
  110. except Exception as ex:
  111. # Catch all types of exceptions as a whole: there won't be much
  112. # granularity for exceptions raised from the C++ module anyway.
  113. raise ConfigError(ex)