lease_file_loader.h 9.2 KB

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