lease_file_loader.h 9.6 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright (C) 2015 Internet Systems Consortium, Inc. ("ISC")
  2. //
  3. // Permission to use, copy, modify, and/or 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 ISC DISCLAIMS ALL WARRANTIES WITH
  8. // REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY
  9. // AND FITNESS. IN NO EVENT SHALL ISC BE LIABLE FOR ANY SPECIAL, DIRECT,
  10. // INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM
  11. // LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
  12. // OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR
  13. // PERFORMANCE OF THIS SOFTWARE.
  14. #ifndef LEASE_FILE_LOADER_H
  15. #define LEASE_FILE_LOADER_H
  16. #include <dhcpsrv/dhcpsrv_log.h>
  17. #include <dhcpsrv/memfile_lease_storage.h>
  18. #include <util/versioned_csv_file.h>
  19. #include <boost/shared_ptr.hpp>
  20. namespace isc {
  21. namespace dhcp {
  22. /// @brief Utility class to manage bulk of leases in the lease files.
  23. ///
  24. /// This class exposes methods which allow for bulk loading leases from
  25. /// the lease file and dumping the leases held in memory into the
  26. /// lease file. There are two major use cases for this class:
  27. /// - load leases by the DHCP server when the server starts up or
  28. /// reloads configuration,
  29. /// - an application performing a lease file cleanup rewrites the whole
  30. /// lease file to remove the redundant lease entries.
  31. ///
  32. /// In the former case, this class is used by the @c MemFile_LeaseMgr.
  33. /// In the latter case, this class is used by the standalone application
  34. /// which reads the whole lease file into memory (storage) and then
  35. /// dumps the leases held in the storage to another file.
  36. ///
  37. /// The methods in this class are templated so as they can be used both
  38. /// with the @c Lease4Storage and @c Lease6Storage to process the DHCPv4
  39. /// and DHCPv6 leases respectively.
  40. ///
  41. class LeaseFileLoader {
  42. public:
  43. /// @brief Load leases from the lease file into the specified storage.
  44. ///
  45. /// This method iterates over the entries in the lease file in the
  46. /// CSV format, creates @c Lease4 or @c Lease6 objects and inserts
  47. /// them into the storage to which reference is specified as an
  48. /// argument. If there are multiple entries for the particular lease
  49. /// in the lease file the entries further in the lease file override
  50. /// the previous entries.
  51. ///
  52. /// If the method finds the entry with the valid lifetime of 0 it
  53. /// means that the particular lease was released and the method
  54. /// removes an existing lease from the container.
  55. ///
  56. /// @param lease_file A reference to the @c CSVLeaseFile4 or
  57. /// @c CSVLeaseFile6 object representing the lease file. The file
  58. /// doesn't need to be open because the method re-opens the file.
  59. /// @param storage A reference to the container to which leases
  60. /// should be inserted.
  61. /// @param max_errors Maximum number of corrupted leases in the
  62. /// lease file. The method will skip corrupted leases but after
  63. /// exceeding the specified number of errors it will throw an
  64. /// exception.
  65. /// @param close_file_on_exit A boolean flag which indicates if
  66. /// the file should be closed after it has been successfully parsed.
  67. /// One case when the file is not opened is when the server starts
  68. /// up, reads the leases in the file and then leaves the file open
  69. /// for writing future lease updates.
  70. /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
  71. /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
  72. /// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
  73. ///
  74. /// @throw isc::util::CSVFileError when the maximum number of errors
  75. /// has been exceeded.
  76. template<typename LeaseObjectType, typename LeaseFileType,
  77. typename StorageType>
  78. static void load(LeaseFileType& lease_file, StorageType& storage,
  79. const uint32_t max_errors = 0xFFFFFFFF,
  80. const bool close_file_on_exit = true) {
  81. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LEASE_FILE_LOAD)
  82. .arg(lease_file.getFilename());
  83. // Reopen the file, as we don't know whether the file is open
  84. // and we also don't know its current state.
  85. lease_file.close();
  86. lease_file.open();
  87. boost::shared_ptr<LeaseObjectType> lease;
  88. // Track the number of corrupted leases.
  89. uint32_t errcnt = 0;
  90. while (true) {
  91. // Unable to parse the lease.
  92. if (!lease_file.next(lease)) {
  93. // A value of 0xFFFFFFFF indicates that we don't return
  94. // until the whole file is parsed, even if errors occur.
  95. // Otherwise, check if we have exceeded the maximum number
  96. // of errors and throw an exception if we have.
  97. if (++errcnt > max_errors) {
  98. // If we break parsing the CSV file because of too many
  99. // errors, it doesn't make sense to keep the file open.
  100. // This is because the caller wouldn't know where we
  101. // stopped parsing and where the internal file pointer
  102. // is. So, there are probably no cases when the caller
  103. // would continue to use the open file.
  104. lease_file.close();
  105. isc_throw(util::CSVFileError, "exceeded maximum number of"
  106. " failures " << max_errors << " to read a lease"
  107. " from the lease file "
  108. << lease_file.getFilename());
  109. }
  110. // Skip the corrupted lease.
  111. continue;
  112. }
  113. // Lease was found and we successfully parsed it.
  114. if (lease) {
  115. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL_DATA,
  116. DHCPSRV_MEMFILE_LEASE_LOAD)
  117. .arg(lease->toText());
  118. // Check if this lease exists.
  119. typename StorageType::iterator lease_it =
  120. storage.find(lease->addr_);
  121. // The lease doesn't exist yet. Insert the lease if
  122. // it has a positive valid lifetime.
  123. if (lease_it == storage.end()) {
  124. if (lease->valid_lft_ > 0) {
  125. storage.insert(lease);
  126. }
  127. } else {
  128. // The lease exists. If the new entry has a valid
  129. // lifetime of 0 it is an indication to remove the
  130. // existing entry. Otherwise, we update the lease.
  131. if (lease->valid_lft_ == 0) {
  132. storage.erase(lease_it);
  133. } else {
  134. **lease_it = *lease;
  135. }
  136. }
  137. } else {
  138. // Being here means that we hit the end of file.
  139. break;
  140. }
  141. }
  142. if (lease_file.needsConversion()) {
  143. LOG_WARN(dhcpsrv_logger,
  144. (lease_file.getInputSchemaState()
  145. == util::VersionedCSVFile::NEEDS_UPGRADE
  146. ? DHCPSRV_MEMFILE_NEEDS_UPGRADING
  147. : DHCPSRV_MEMFILE_NEEDS_DOWNGRADING))
  148. .arg(lease_file.getFilename())
  149. .arg(lease_file.getSchemaVersion());
  150. }
  151. if (close_file_on_exit) {
  152. lease_file.close();
  153. }
  154. }
  155. /// @brief Write leases from the storage into a lease file
  156. ///
  157. /// This method iterates over the @c Lease4 or @c Lease6 object in the
  158. /// storage specified in the arguments and writes them to the file
  159. /// specified in the arguments.
  160. ///
  161. /// This method writes all entries in the storage to the file, it does
  162. /// not perform any checks for expiration or duplication.
  163. ///
  164. /// The order in which the entries will be written to the file depends
  165. /// on the first index in the multi-index container. Currently that
  166. /// is the v4 or v6 IP address and they are written from lowest to highest.
  167. ///
  168. /// Before writing the method will close the file if it is open
  169. /// and reopen it for writing. After completion it will close
  170. /// the file.
  171. ///
  172. /// @param lease_file A reference to the @c CSVLeaseFile4 or
  173. /// @c CSVLeaseFile6 object representing the lease file. The file
  174. /// doesn't need to be open because the method re-opens the file.
  175. /// @param storage A reference to the container from which leases
  176. /// should be written.
  177. ///
  178. /// @tparam LeaseObjectType A @c Lease4 or @c Lease6.
  179. /// @tparam LeaseFileType A @c CSVLeaseFile4 or @c CSVLeaseFile6.
  180. /// @tparam StorageType A @c Lease4Storage or @c Lease6Storage.
  181. template<typename LeaseObjectType, typename LeaseFileType,
  182. typename StorageType>
  183. static void write(LeaseFileType& lease_file, const StorageType& storage) {
  184. // Reopen the file, as we don't know whether the file is open
  185. // and we also don't know its current state.
  186. lease_file.close();
  187. lease_file.open();
  188. // Iterate over the storage area writing out the leases
  189. for (typename StorageType::const_iterator lease = storage.begin();
  190. lease != storage.end();
  191. ++lease) {
  192. try {
  193. lease_file.append(**lease);
  194. } catch (const isc::Exception&) {
  195. // Close the file
  196. lease_file.close();
  197. throw;
  198. }
  199. }
  200. // Close the file
  201. lease_file.close();
  202. }
  203. };
  204. } // namesapce dhcp
  205. } // namespace isc
  206. #endif // LEASE_FILE_LOADER_H