memfile_lease_mgr.cc 30 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876
  1. // Copyright (C) 2012-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. #include <config.h>
  15. #include <dhcpsrv/cfgmgr.h>
  16. #include <dhcpsrv/dhcpsrv_log.h>
  17. #include <dhcpsrv/lease_file_loader.h>
  18. #include <dhcpsrv/memfile_lease_mgr.h>
  19. #include <exceptions/exceptions.h>
  20. #include <util/pid_file.h>
  21. #include <util/process_spawn.h>
  22. #include <util/signal_set.h>
  23. #include <cstdio>
  24. #include <cstring>
  25. #include <errno.h>
  26. #include <iostream>
  27. #include <sstream>
  28. namespace {
  29. /// @brief Maximum number of errors to read the leases from the lease file.
  30. const uint32_t MAX_LEASE_ERRORS = 100;
  31. /// @brief A name of the environmental variable specifying the kea-lfc
  32. /// program location.
  33. ///
  34. /// This variable can be set by tests to point to the location of the
  35. /// kea-lfc program within a build directory. If this variable is not
  36. /// set, the backend will use the location of the kea-lfc in the
  37. /// Kea installation directory.
  38. const char* KEA_LFC_EXECUTABLE_ENV_NAME = "KEA_LFC_EXECUTABLE";
  39. } // end of anonymous namespace
  40. using namespace isc::util;
  41. namespace isc {
  42. namespace dhcp {
  43. /// @brief Represents a configuration for Lease File Cleanup.
  44. ///
  45. /// This class is solely used by the @c Memfile_LeaseMgr as a configuration
  46. /// information storage for %Lease File Cleanup. Internally, it creates
  47. /// the interval timer and assigns a callback function (pointer to which is
  48. /// passed in the constructor), which will be called at the specified
  49. /// intervals to perform the cleanup. It is also responsible for creating
  50. /// and maintaing the object which is used to spawn the new process which
  51. /// executes the @c kea-lfc program.
  52. ///
  53. /// This functionality is enclosed in a separate class so as the implementation
  54. /// details are not exposed in the @c Memfile_LeaseMgr header file and
  55. /// to maintain a single place with the LFC configuration, instead of multiple
  56. /// members and functions scattered in the @c Memfile_LeaseMgr class.
  57. class LFCSetup {
  58. public:
  59. /// @brief Constructor.
  60. ///
  61. /// Assigns a pointer to the function triggered to perform the cleanup.
  62. /// This pointer should point to the appropriate method of the
  63. /// @c Memfile_LeaseMgr class.
  64. ///
  65. /// @param callback A pointer to the callback function.
  66. /// @param io_service An io service used to create the interval timer.
  67. LFCSetup(asiolink::IntervalTimer::Callback callback,
  68. asiolink::IOService& io_service);
  69. /// @brief Sets the new configuration for the %Lease File Cleanup.
  70. ///
  71. /// @param lfc_interval An interval in seconds at which the cleanup should
  72. /// be performed.
  73. /// @param lease_file4 A pointer to the DHCPv4 lease file to be cleaned up
  74. /// or NULL. If this is NULL, the @c lease_file6 must be non-null.
  75. /// @param lease_file6 A pointer to the DHCPv6 lease file to be cleaned up
  76. /// or NULL. If this is NULL, the @c lease_file4 must be non-null.
  77. void setup(const uint32_t lfc_interval,
  78. const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
  79. const boost::shared_ptr<CSVLeaseFile6>& lease_file6);
  80. /// @brief Spawns a new process.
  81. void execute();
  82. /// @brief Returns interval at which the cleanup is performed.
  83. ///
  84. /// @return Interval in milliseconds.
  85. long getInterval() const;
  86. /// @brief Checks if the lease file cleanup is in progress.
  87. ///
  88. /// @return true if the lease file cleanup is being executed.
  89. bool isRunning() const;
  90. /// @brief Returns exit code of the last completed cleanup.
  91. int getExitStatus() const;
  92. private:
  93. /// @brief Interval timer for LFC.
  94. asiolink::IntervalTimer timer_;
  95. /// @brief A pointer to the @c ProcessSpawn object used to execute
  96. /// the LFC.
  97. boost::scoped_ptr<util::ProcessSpawn> process_;
  98. /// @brief A pointer to the callback function executed by the timer.
  99. asiolink::IntervalTimer::Callback callback_;
  100. /// @brief A PID of the last executed LFC process.
  101. pid_t pid_;
  102. };
  103. LFCSetup::LFCSetup(asiolink::IntervalTimer::Callback callback,
  104. asiolink::IOService& io_service)
  105. : timer_(io_service), process_(), callback_(callback), pid_(0) {
  106. }
  107. void
  108. LFCSetup::setup(const uint32_t lfc_interval,
  109. const boost::shared_ptr<CSVLeaseFile4>& lease_file4,
  110. const boost::shared_ptr<CSVLeaseFile6>& lease_file6) {
  111. // If LFC is enabled, we have to setup the interval timer and prepare for
  112. // executing the kea-lfc process.
  113. if (lfc_interval > 0) {
  114. std::string executable;
  115. char* c_executable = getenv(KEA_LFC_EXECUTABLE_ENV_NAME);
  116. if (c_executable == NULL) {
  117. executable = KEA_LFC_EXECUTABLE;
  118. } else {
  119. executable = c_executable;
  120. }
  121. // Set the timer to call callback function periodically.
  122. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SETUP).arg(lfc_interval);
  123. // Multiple the lfc_interval value by 1000 as this value specifies
  124. // a timeout in seconds, whereas the setup() method expects the
  125. // timeout in milliseconds.
  126. timer_.setup(callback_, lfc_interval * 1000);
  127. // Start preparing the command line for kea-lfc.
  128. // Gather the base file name.
  129. std::string lease_file = lease_file4 ? lease_file4->getFilename() :
  130. lease_file6->getFilename();
  131. // Create the other names by appending suffixes to the base name.
  132. util::ProcessArgs args;
  133. // Universe: v4 or v6.
  134. args.push_back(lease_file4 ? "-4" : "-6");
  135. // Previous file.
  136. args.push_back("-x");
  137. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  138. Memfile_LeaseMgr::FILE_PREVIOUS));
  139. // Input file.
  140. args.push_back("-i");
  141. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  142. Memfile_LeaseMgr::FILE_INPUT));
  143. // Output file.
  144. args.push_back("-o");
  145. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  146. Memfile_LeaseMgr::FILE_OUTPUT));
  147. // Finish file.
  148. args.push_back("-f");
  149. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  150. Memfile_LeaseMgr::FILE_FINISH));
  151. // PID file.
  152. args.push_back("-p");
  153. args.push_back(Memfile_LeaseMgr::appendSuffix(lease_file,
  154. Memfile_LeaseMgr::FILE_PID));
  155. // The configuration file is currently unused.
  156. args.push_back("-c");
  157. args.push_back("ignored-path");
  158. // Create the process (do not start it yet).
  159. process_.reset(new util::ProcessSpawn(executable, args));
  160. }
  161. }
  162. long
  163. LFCSetup::getInterval() const {
  164. return (timer_.getInterval());
  165. }
  166. void
  167. LFCSetup::execute() {
  168. try {
  169. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_EXECUTE)
  170. .arg(process_->getCommandLine());
  171. pid_ = process_->spawn();
  172. } catch (const ProcessSpawnError& ex) {
  173. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_SPAWN_FAIL);
  174. }
  175. }
  176. bool
  177. LFCSetup::isRunning() const {
  178. return (process_ && process_->isRunning(pid_));
  179. }
  180. int
  181. LFCSetup::getExitStatus() const {
  182. if (!process_) {
  183. isc_throw(InvalidOperation, "unable to obtain LFC process exit code: "
  184. " the process is NULL");
  185. }
  186. return (process_->getExitStatus(pid_));
  187. }
  188. Memfile_LeaseMgr::Memfile_LeaseMgr(const ParameterMap& parameters)
  189. : DataSource(parameters),
  190. lfc_setup_(new LFCSetup(boost::bind(&Memfile_LeaseMgr::lfcCallback, this),
  191. *getIOService()))
  192. {
  193. // Check the universe and use v4 file or v6 file.
  194. std::string universe = getParameter("universe");
  195. if (universe == "4") {
  196. std::string file4 = initLeaseFilePath(V4);
  197. if (!file4.empty()) {
  198. loadLeasesFromFiles<Lease4, CSVLeaseFile4>(file4, lease_file4_,
  199. storage4_);
  200. }
  201. } else {
  202. std::string file6 = initLeaseFilePath(V6);
  203. if (!file6.empty()) {
  204. loadLeasesFromFiles<Lease6, CSVLeaseFile6>(file6, lease_file6_,
  205. storage6_);
  206. }
  207. }
  208. // If lease persistence have been disabled for both v4 and v6,
  209. // issue a warning. It is ok not to write leases to disk when
  210. // doing testing, but it should not be done in normal server
  211. // operation.
  212. if (!persistLeases(V4) && !persistLeases(V6)) {
  213. LOG_WARN(dhcpsrv_logger, DHCPSRV_MEMFILE_NO_STORAGE);
  214. } else {
  215. lfcSetup();
  216. }
  217. }
  218. Memfile_LeaseMgr::~Memfile_LeaseMgr() {
  219. if (lease_file4_) {
  220. lease_file4_->close();
  221. lease_file4_.reset();
  222. }
  223. if (lease_file6_) {
  224. lease_file6_->close();
  225. lease_file6_.reset();
  226. }
  227. }
  228. std::string
  229. Memfile_LeaseMgr::getDBVersion() {
  230. std::stringstream tmp;
  231. tmp << "Memfile backend " << MAJOR_VERSION;
  232. tmp << "." << MINOR_VERSION;
  233. return (tmp.str());
  234. }
  235. bool
  236. Memfile_LeaseMgr::addLease(const Lease4Ptr& lease) {
  237. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  238. DHCPSRV_MEMFILE_ADD_ADDR4).arg(lease->addr_.toText());
  239. if (getLease4(lease->addr_)) {
  240. // there is a lease with specified address already
  241. return (false);
  242. }
  243. // Try to write a lease to disk first. If this fails, the lease will
  244. // not be inserted to the memory and the disk and in-memory data will
  245. // remain consistent.
  246. if (persistLeases(V4)) {
  247. lease_file4_->append(*lease);
  248. }
  249. storage4_.insert(lease);
  250. return (true);
  251. }
  252. bool
  253. Memfile_LeaseMgr::addLease(const Lease6Ptr& lease) {
  254. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  255. DHCPSRV_MEMFILE_ADD_ADDR6).arg(lease->addr_.toText());
  256. if (getLease6(lease->type_, lease->addr_)) {
  257. // there is a lease with specified address already
  258. return (false);
  259. }
  260. // Try to write a lease to disk first. If this fails, the lease will
  261. // not be inserted to the memory and the disk and in-memory data will
  262. // remain consistent.
  263. if (persistLeases(V6)) {
  264. lease_file6_->append(*lease);
  265. }
  266. storage6_.insert(lease);
  267. return (true);
  268. }
  269. Lease4Ptr
  270. Memfile_LeaseMgr::getLease4(const isc::asiolink::IOAddress& addr) const {
  271. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  272. DHCPSRV_MEMFILE_GET_ADDR4).arg(addr.toText());
  273. typedef Lease4Storage::nth_index<0>::type SearchIndex;
  274. const SearchIndex& idx = storage4_.get<0>();
  275. Lease4Storage::iterator l = idx.find(addr);
  276. if (l == storage4_.end()) {
  277. return (Lease4Ptr());
  278. } else {
  279. return (Lease4Ptr(new Lease4(**l)));
  280. }
  281. }
  282. Lease4Collection
  283. Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr) const {
  284. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  285. DHCPSRV_MEMFILE_GET_HWADDR).arg(hwaddr.toText());
  286. typedef Lease4Storage::nth_index<0>::type SearchIndex;
  287. Lease4Collection collection;
  288. const SearchIndex& idx = storage4_.get<0>();
  289. for(SearchIndex::const_iterator lease = idx.begin();
  290. lease != idx.end(); ++lease) {
  291. // Every Lease4 has a hardware address, so we can compare it
  292. if ( (*(*lease)->hwaddr_) == hwaddr) {
  293. collection.push_back((*lease));
  294. }
  295. }
  296. return (collection);
  297. }
  298. Lease4Ptr
  299. Memfile_LeaseMgr::getLease4(const HWAddr& hwaddr, SubnetID subnet_id) const {
  300. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  301. DHCPSRV_MEMFILE_GET_SUBID_HWADDR).arg(subnet_id)
  302. .arg(hwaddr.toText());
  303. // We are going to use index #1 of the multi index container.
  304. // We define SearchIndex locally in this function because
  305. // currently only this function uses this index.
  306. typedef Lease4Storage::nth_index<1>::type SearchIndex;
  307. // Get the index.
  308. const SearchIndex& idx = storage4_.get<1>();
  309. // Try to find the lease using HWAddr and subnet id.
  310. SearchIndex::const_iterator lease =
  311. idx.find(boost::make_tuple(hwaddr.hwaddr_, subnet_id));
  312. // Lease was not found. Return empty pointer to the caller.
  313. if (lease == idx.end()) {
  314. return (Lease4Ptr());
  315. }
  316. // Lease was found. Return it to the caller.
  317. return (Lease4Ptr(new Lease4(**lease)));
  318. }
  319. Lease4Collection
  320. Memfile_LeaseMgr::getLease4(const ClientId& client_id) const {
  321. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  322. DHCPSRV_MEMFILE_GET_CLIENTID).arg(client_id.toText());
  323. typedef Lease4Storage::nth_index<0>::type SearchIndex;
  324. Lease4Collection collection;
  325. const SearchIndex& idx = storage4_.get<0>();
  326. for(SearchIndex::const_iterator lease = idx.begin();
  327. lease != idx.end(); ++ lease) {
  328. // client-id is not mandatory in DHCPv4. There can be a lease that does
  329. // not have a client-id. Dereferencing null pointer would be a bad thing
  330. if((*lease)->client_id_ && *(*lease)->client_id_ == client_id) {
  331. collection.push_back((*lease));
  332. }
  333. }
  334. return (collection);
  335. }
  336. Lease4Ptr
  337. Memfile_LeaseMgr::getLease4(const ClientId& client_id,
  338. const HWAddr& hwaddr,
  339. SubnetID subnet_id) const {
  340. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  341. DHCPSRV_MEMFILE_GET_CLIENTID_HWADDR_SUBID).arg(client_id.toText())
  342. .arg(hwaddr.toText())
  343. .arg(subnet_id);
  344. // We are going to use index #3 of the multi index container.
  345. // We define SearchIndex locally in this function because
  346. // currently only this function uses this index.
  347. typedef Lease4Storage::nth_index<3>::type SearchIndex;
  348. // Get the index.
  349. const SearchIndex& idx = storage4_.get<3>();
  350. // Try to get the lease using client id, hardware address and subnet id.
  351. SearchIndex::const_iterator lease =
  352. idx.find(boost::make_tuple(client_id.getClientId(), hwaddr.hwaddr_,
  353. subnet_id));
  354. if (lease == idx.end()) {
  355. // Lease was not found. Return empty pointer to the caller.
  356. return (Lease4Ptr());
  357. }
  358. // Lease was found. Return it to the caller.
  359. return (*lease);
  360. }
  361. Lease4Ptr
  362. Memfile_LeaseMgr::getLease4(const ClientId& client_id,
  363. SubnetID subnet_id) const {
  364. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  365. DHCPSRV_MEMFILE_GET_SUBID_CLIENTID).arg(subnet_id)
  366. .arg(client_id.toText());
  367. // We are going to use index #2 of the multi index container.
  368. // We define SearchIndex locally in this function because
  369. // currently only this function uses this index.
  370. typedef Lease4Storage::nth_index<2>::type SearchIndex;
  371. // Get the index.
  372. const SearchIndex& idx = storage4_.get<2>();
  373. // Try to get the lease using client id and subnet id.
  374. SearchIndex::const_iterator lease =
  375. idx.find(boost::make_tuple(client_id.getClientId(), subnet_id));
  376. // Lease was not found. Return empty pointer to the caller.
  377. if (lease == idx.end()) {
  378. return (Lease4Ptr());
  379. }
  380. // Lease was found. Return it to the caller.
  381. return (Lease4Ptr(new Lease4(**lease)));
  382. }
  383. Lease6Ptr
  384. Memfile_LeaseMgr::getLease6(Lease::Type type,
  385. const isc::asiolink::IOAddress& addr) const {
  386. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  387. DHCPSRV_MEMFILE_GET_ADDR6)
  388. .arg(addr.toText())
  389. .arg(Lease::typeToText(type));
  390. Lease6Storage::iterator l = storage6_.find(addr);
  391. if (l == storage6_.end() || !(*l) || ((*l)->type_ != type)) {
  392. return (Lease6Ptr());
  393. } else {
  394. return (Lease6Ptr(new Lease6(**l)));
  395. }
  396. }
  397. Lease6Collection
  398. Memfile_LeaseMgr::getLeases6(Lease::Type type,
  399. const DUID& duid, uint32_t iaid) const {
  400. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  401. DHCPSRV_MEMFILE_GET_IAID_DUID)
  402. .arg(iaid)
  403. .arg(duid.toText())
  404. .arg(Lease::typeToText(type));
  405. // We are going to use index #1 of the multi index container.
  406. typedef Lease6Storage::nth_index<1>::type SearchIndex;
  407. // Get the index.
  408. const SearchIndex& idx = storage6_.get<1>();
  409. // Try to get the lease using the DUID, IAID and lease type.
  410. std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
  411. idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
  412. Lease6Collection collection;
  413. for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
  414. collection.push_back(Lease6Ptr(new Lease6(**lease)));
  415. }
  416. return (collection);
  417. }
  418. Lease6Collection
  419. Memfile_LeaseMgr::getLeases6(Lease::Type type,
  420. const DUID& duid, uint32_t iaid,
  421. SubnetID subnet_id) const {
  422. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  423. DHCPSRV_MEMFILE_GET_IAID_SUBID_DUID)
  424. .arg(iaid)
  425. .arg(subnet_id)
  426. .arg(duid.toText())
  427. .arg(Lease::typeToText(type));
  428. // We are going to use index #1 of the multi index container.
  429. typedef Lease6Storage::nth_index<1>::type SearchIndex;
  430. // Get the index.
  431. const SearchIndex& idx = storage6_.get<1>();
  432. // Try to get the lease using the DUID, IAID and lease type.
  433. std::pair<SearchIndex::iterator, SearchIndex::iterator> l =
  434. idx.equal_range(boost::make_tuple(duid.getDuid(), iaid, type));
  435. Lease6Collection collection;
  436. for(SearchIndex::iterator lease = l.first; lease != l.second; ++lease) {
  437. // Filter out the leases which subnet id doesn't match.
  438. if((*lease)->subnet_id_ == subnet_id) {
  439. collection.push_back(Lease6Ptr(new Lease6(**lease)));
  440. }
  441. }
  442. return (collection);
  443. }
  444. void
  445. Memfile_LeaseMgr::updateLease4(const Lease4Ptr& lease) {
  446. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  447. DHCPSRV_MEMFILE_UPDATE_ADDR4).arg(lease->addr_.toText());
  448. Lease4Storage::iterator lease_it = storage4_.find(lease->addr_);
  449. if (lease_it == storage4_.end()) {
  450. isc_throw(NoSuchLease, "failed to update the lease with address "
  451. << lease->addr_ << " - no such lease");
  452. }
  453. // Try to write a lease to disk first. If this fails, the lease will
  454. // not be inserted to the memory and the disk and in-memory data will
  455. // remain consistent.
  456. if (persistLeases(V4)) {
  457. lease_file4_->append(*lease);
  458. }
  459. **lease_it = *lease;
  460. }
  461. void
  462. Memfile_LeaseMgr::updateLease6(const Lease6Ptr& lease) {
  463. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  464. DHCPSRV_MEMFILE_UPDATE_ADDR6).arg(lease->addr_.toText());
  465. Lease6Storage::iterator lease_it = storage6_.find(lease->addr_);
  466. if (lease_it == storage6_.end()) {
  467. isc_throw(NoSuchLease, "failed to update the lease with address "
  468. << lease->addr_ << " - no such lease");
  469. }
  470. // Try to write a lease to disk first. If this fails, the lease will
  471. // not be inserted to the memory and the disk and in-memory data will
  472. // remain consistent.
  473. if (persistLeases(V6)) {
  474. lease_file6_->append(*lease);
  475. }
  476. **lease_it = *lease;
  477. }
  478. bool
  479. Memfile_LeaseMgr::deleteLease(const isc::asiolink::IOAddress& addr) {
  480. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  481. DHCPSRV_MEMFILE_DELETE_ADDR).arg(addr.toText());
  482. if (addr.isV4()) {
  483. // v4 lease
  484. Lease4Storage::iterator l = storage4_.find(addr);
  485. if (l == storage4_.end()) {
  486. // No such lease
  487. return (false);
  488. } else {
  489. if (persistLeases(V4)) {
  490. // Copy the lease. The valid lifetime needs to be modified and
  491. // we don't modify the original lease.
  492. Lease4 lease_copy = **l;
  493. // Setting valid lifetime to 0 means that lease is being
  494. // removed.
  495. lease_copy.valid_lft_ = 0;
  496. lease_file4_->append(lease_copy);
  497. }
  498. storage4_.erase(l);
  499. return (true);
  500. }
  501. } else {
  502. // v6 lease
  503. Lease6Storage::iterator l = storage6_.find(addr);
  504. if (l == storage6_.end()) {
  505. // No such lease
  506. return (false);
  507. } else {
  508. if (persistLeases(V6)) {
  509. // Copy the lease. The lifetimes need to be modified and we
  510. // don't modify the original lease.
  511. Lease6 lease_copy = **l;
  512. // Setting lifetimes to 0 means that lease is being removed.
  513. lease_copy.valid_lft_ = 0;
  514. lease_copy.preferred_lft_ = 0;
  515. lease_file6_->append(lease_copy);
  516. }
  517. storage6_.erase(l);
  518. return (true);
  519. }
  520. }
  521. }
  522. std::string
  523. Memfile_LeaseMgr::getDescription() const {
  524. return (std::string("This is a dummy memfile backend implementation.\n"
  525. "It does not offer any useful lease management and its only\n"
  526. "purpose is to test abstract lease manager API."));
  527. }
  528. void
  529. Memfile_LeaseMgr::commit() {
  530. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL, DHCPSRV_MEMFILE_COMMIT);
  531. }
  532. void
  533. Memfile_LeaseMgr::rollback() {
  534. LOG_DEBUG(dhcpsrv_logger, DHCPSRV_DBG_TRACE_DETAIL,
  535. DHCPSRV_MEMFILE_ROLLBACK);
  536. }
  537. std::string
  538. Memfile_LeaseMgr::appendSuffix(const std::string& file_name,
  539. const LFCFileType& file_type) {
  540. std::string name(file_name);
  541. switch (file_type) {
  542. case FILE_INPUT:
  543. name += ".1";
  544. break;
  545. case FILE_PREVIOUS:
  546. name += ".2";
  547. break;
  548. case FILE_OUTPUT:
  549. name += ".output";
  550. break;
  551. case FILE_FINISH:
  552. name += ".completed";
  553. break;
  554. case FILE_PID:
  555. name += ".pid";
  556. break;
  557. default:
  558. // Do not append any suffix for the FILE_CURRENT.
  559. ;
  560. }
  561. return (name);
  562. }
  563. uint32_t
  564. Memfile_LeaseMgr::getIOServiceExecInterval() const {
  565. return (static_cast<uint32_t>(lfc_setup_->getInterval() / 1000));
  566. }
  567. std::string
  568. Memfile_LeaseMgr::getDefaultLeaseFilePath(Universe u) const {
  569. std::ostringstream s;
  570. s << CfgMgr::instance().getDataDir() << "/kea-leases";
  571. s << (u == V4 ? "4" : "6");
  572. s << ".csv";
  573. return (s.str());
  574. }
  575. std::string
  576. Memfile_LeaseMgr::getLeaseFilePath(Universe u) const {
  577. if (u == V4) {
  578. return (lease_file4_ ? lease_file4_->getFilename() : "");
  579. }
  580. return (lease_file6_ ? lease_file6_->getFilename() : "");
  581. }
  582. bool
  583. Memfile_LeaseMgr::persistLeases(Universe u) const {
  584. // Currently, if the lease file IO is not created, it means that writes to
  585. // disk have been explicitly disabled by the administrator. At some point,
  586. // there may be a dedicated ON/OFF flag implemented to control this.
  587. if (u == V4 && lease_file4_) {
  588. return (true);
  589. }
  590. return (u == V6 && lease_file6_);
  591. }
  592. std::string
  593. Memfile_LeaseMgr::initLeaseFilePath(Universe u) {
  594. std::string persist_val;
  595. try {
  596. persist_val = getParameter("persist");
  597. } catch (const Exception& ex) {
  598. // If parameter persist hasn't been specified, we use a default value
  599. // 'yes'.
  600. persist_val = "true";
  601. }
  602. // If persist_val is 'false' we will not store leases to disk, so let's
  603. // return empty file name.
  604. if (persist_val == "false") {
  605. return ("");
  606. } else if (persist_val != "true") {
  607. isc_throw(isc::BadValue, "invalid value 'persist="
  608. << persist_val << "'");
  609. }
  610. std::string lease_file;
  611. try {
  612. lease_file = getParameter("name");
  613. } catch (const Exception& ex) {
  614. lease_file = getDefaultLeaseFilePath(u);
  615. }
  616. return (lease_file);
  617. }
  618. template<typename LeaseObjectType, typename LeaseFileType, typename StorageType>
  619. void Memfile_LeaseMgr::loadLeasesFromFiles(const std::string& filename,
  620. boost::shared_ptr<LeaseFileType>& lease_file,
  621. StorageType& storage) {
  622. // Check if the instance of the LFC is running right now. If it is
  623. // running, we refuse to load leases as the LFC may be writing to the
  624. // lease files right now. When the user retries server configuration
  625. // it should go through.
  626. /// @todo Consider applying a timeout for an LFC and retry when this
  627. /// timeout elapses.
  628. PIDFile pid_file(appendSuffix(filename, FILE_PID));
  629. if (pid_file.check()) {
  630. isc_throw(DbOpenError, "unable to load leases from files while the "
  631. "lease file cleanup is in progress");
  632. }
  633. storage.clear();
  634. // Load the leasefile.completed, if exists.
  635. lease_file.reset(new LeaseFileType(std::string(filename + ".completed")));
  636. if (lease_file->exists()) {
  637. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  638. MAX_LEASE_ERRORS);
  639. } else {
  640. // If the leasefile.completed doesn't exist, let's load the leases
  641. // from leasefile.2 and leasefile.1, if they exist.
  642. lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_PREVIOUS)));
  643. if (lease_file->exists()) {
  644. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  645. MAX_LEASE_ERRORS);
  646. }
  647. lease_file.reset(new LeaseFileType(appendSuffix(filename, FILE_INPUT)));
  648. if (lease_file->exists()) {
  649. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  650. MAX_LEASE_ERRORS);
  651. }
  652. }
  653. // Always load leases from the primary lease file. If the lease file
  654. // doesn't exist it will be created by the LeaseFileLoader. Note
  655. // that the false value passed as the last parameter to load
  656. // function causes the function to leave the file open after
  657. // it is parsed. This file will be used by the backend to record
  658. // future lease updates.
  659. lease_file.reset(new LeaseFileType(filename));
  660. LeaseFileLoader::load<LeaseObjectType>(*lease_file, storage,
  661. MAX_LEASE_ERRORS, false);
  662. }
  663. bool
  664. Memfile_LeaseMgr::isLFCRunning() const {
  665. return (lfc_setup_->isRunning());
  666. }
  667. int
  668. Memfile_LeaseMgr::getLFCExitStatus() const {
  669. return (lfc_setup_->getExitStatus());
  670. }
  671. void
  672. Memfile_LeaseMgr::lfcCallback() {
  673. LOG_INFO(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_START);
  674. // Check if we're in the v4 or v6 space and use the appropriate file.
  675. if (lease_file4_) {
  676. lfcExecute(lease_file4_);
  677. } else if (lease_file6_) {
  678. lfcExecute(lease_file6_);
  679. }
  680. }
  681. void
  682. Memfile_LeaseMgr::lfcSetup() {
  683. std::string lfc_interval_str = "0";
  684. try {
  685. lfc_interval_str = getParameter("lfc-interval");
  686. } catch (const std::exception& ex) {
  687. // Ignore and default to 0.
  688. }
  689. uint32_t lfc_interval = 0;
  690. try {
  691. lfc_interval = boost::lexical_cast<uint32_t>(lfc_interval_str);
  692. } catch (boost::bad_lexical_cast& ex) {
  693. isc_throw(isc::BadValue, "invalid value of the lfc-interval "
  694. << lfc_interval_str << " specified");
  695. }
  696. if (lfc_interval > 0) {
  697. lfc_setup_->setup(lfc_interval, lease_file4_, lease_file6_);
  698. }
  699. }
  700. template<typename LeaseFileType>
  701. void Memfile_LeaseMgr::lfcExecute(boost::shared_ptr<LeaseFileType>& lease_file) {
  702. bool do_lfc = true;
  703. // Check the status of the LFC instance.
  704. // If the finish file exists or the copy of the lease file exists it
  705. // is an indication that another LFC instance may be in progress or
  706. // may be stalled. In that case we don't want to rotate the current
  707. // lease file to avoid overriding the contents of the existing file.
  708. CSVFile lease_file_finish(appendSuffix(lease_file->getFilename(), FILE_FINISH));
  709. CSVFile lease_file_copy(appendSuffix(lease_file->getFilename(), FILE_INPUT));
  710. if (!lease_file_finish.exists() && !lease_file_copy.exists()) {
  711. // Close the current file so as we can move it to the copy file.
  712. lease_file->close();
  713. // Move the current file to the copy file. Remember the result
  714. // because we don't want to run LFC if the rename failed.
  715. do_lfc = (rename(lease_file->getFilename().c_str(),
  716. lease_file_copy.getFilename().c_str()) == 0);
  717. if (!do_lfc) {
  718. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_RENAME_FAIL)
  719. .arg(lease_file->getFilename())
  720. .arg(lease_file_copy.getFilename())
  721. .arg(strerror(errno));
  722. }
  723. // Regardless if we successfully moved the current file or not,
  724. // we need to re-open the current file for the server to write
  725. // new lease updates. If the file has been successfully moved,
  726. // this will result in creation of the new file. Otherwise,
  727. // an existing file will be opened.
  728. try {
  729. lease_file->open(true);
  730. } catch (const CSVFileError& ex) {
  731. // If we're unable to open the lease file this is a serious
  732. // error because the server will not be able to persist
  733. // leases.
  734. /// @todo We need to better address this error. It should
  735. /// trigger an alarm (once we have a monitoring system in
  736. /// place) so as an administrator can correct it. In
  737. /// practice it should be very rare that this happens and
  738. /// is most likely related to a human error, e.g. changing
  739. /// file permissions.
  740. LOG_ERROR(dhcpsrv_logger, DHCPSRV_MEMFILE_LFC_LEASE_FILE_REOPEN_FAIL)
  741. .arg(lease_file->getFilename())
  742. .arg(ex.what());
  743. // Reset the pointer to the file so as the backend doesn't
  744. // try to write leases to disk.
  745. lease_file.reset();
  746. do_lfc = false;
  747. }
  748. }
  749. // Once the files have been rotated, or untouched if another LFC had
  750. // not finished, a new process is started.
  751. if (do_lfc) {
  752. lfc_setup_->execute();
  753. }
  754. }
  755. } // end of namespace isc::dhcp
  756. } // end of namespace isc