vaulter.cc

Go to the documentation of this file.
00001 #include "config.h"
00002 
00003 #include <iostream>
00004 #include <vector>
00005 #include <map>
00006 #include <string>
00007 
00008 #include "asserts.h"
00009 #include "error.h"
00010 #include "rmath.h"
00011 #include "fs.h"
00012 #include "rconfig.h"
00013 #include "logger.h"
00014 #include "timer.h"
00015 #include "estat.h"
00016 #include "reporter.h"
00017 #include "strfmt.h"
00018 
00019 #include "vaulter.h"
00020 
00021 /** C'tor */
00022 vault_manager::vault_manager()
00023 {
00024         if (this != &vaulter)
00025                 throw(INTERNAL_ERROR(0,"Attempt to allocate multiple vault managers"));
00026         clear();
00027 }
00028 
00029 /** Clear the vault manager */
00030 void vault_manager::clear(void)
00031 {
00032         m_selected_vault.erase();
00033         m_deleted_archives.clear();
00034         m_da_err = false;
00035         m_initialized = false;
00036 }
00037 
00038 /** Initialize the vault manager */
00039 void vault_manager::init(void)
00040 {
00041         clear();
00042         m_initialized = true;
00043 }
00044 
00045 /** Return the initialized status of the vault manager */
00046 const bool vault_manager::initialized(void) const
00047 {
00048         return(m_initialized);
00049 }
00050 
00051 /** Select a vault
00052 
00053         If an archive of the same timestamp for the given timestamp resolution
00054         already exists on any vault, then that vault is selected.  Otherwise select
00055         a vault according to vault-selection-behavior.
00056 
00057  */
00058 void vault_manager::select(void)
00059 {
00060         std::string es;
00061         configuration_manager::vaults_type::const_iterator vi;
00062 
00063         if (!initialized())
00064                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00065 
00066         TRY_nomem(es = "Could not select a vault");
00067         try {
00068                 // If an archive exists in any vault by the same timestamp as conf.stamp(),
00069                 // then use that vault regardless of the selection behavior.
00070                 for (
00071                         vi = config.vaults().begin(); 
00072                         vi != config.vaults().end(); 
00073                         vi++
00074                         )
00075                 {
00076                         subdirectory subdir;
00077                         subdirectory::iterator sdi;
00078         
00079                         subdir.path(*vi);
00080                         for (sdi = subdir.begin(); sdi != subdir.end(); sdi++) {
00081                                 if (*sdi == config.timestamp().str()) {
00082                                         TRY_nomem(m_selected_vault = *vi);
00083                                         return;
00084                                 }
00085                         }
00086                 }
00087         
00088                 // If an archive by the same timestamp does not already exist, then select
00089                 // a vault.
00090                 if (config.vault_selection_behavior() 
00091                         == configuration_manager::selection_round_robin)
00092                 {
00093                         std::pair<std::string,std::string> youngest;
00094         
00095                         for (
00096                                 vi = config.vaults().begin();
00097                                 vi != config.vaults().end();
00098                                 vi++
00099                                 )
00100                         {
00101                                 subdirectory subdir;
00102                                 subdirectory::iterator sdi;
00103         
00104                                 subdir.path(*vi);
00105                                 for (sdi = subdir.begin(); sdi != subdir.end(); sdi++) {
00106                                         if ((youngest.first < *sdi) || (youngest.first.size() == 0)) {
00107                                                 TRY_nomem(youngest.first = *sdi);
00108                                                 TRY_nomem(youngest.second = *vi);
00109                                         }
00110                                 }
00111                         }
00112                         
00113                         TRY_nomem(m_selected_vault = "");
00114                         if (youngest.second.size() == 0) {
00115                                 TRY_nomem(m_selected_vault = config.vaults()[0]);
00116                         }
00117                         else {
00118                                 for (
00119                                         vi = config.vaults().begin(); 
00120                                         vi != config.vaults().end(); 
00121                                         vi++
00122                                         )
00123                                 {
00124                                         if (*vi == youngest.second) {
00125                                                 if ((vi+1) == config.vaults().end()) {
00126                                                         TRY_nomem(m_selected_vault = config.vaults()[0]);
00127                                                 }
00128                                                 else {
00129                                                         TRY_nomem(m_selected_vault = *(vi+1));
00130                                                 }
00131                                         }
00132                                 }
00133                         }
00134                 }
00135                 else {
00136                         std::pair<std::string,filesystem::size_type> most_space;
00137                         filesystem fsys;
00138         
00139                         TRY_nomem(most_space.first = config.vaults()[0]);
00140                         fsys.path(config.vaults()[0]);
00141                         most_space.second = fsys.free_blocks();
00142                         for (
00143                                 vi = (config.vaults().begin()+1); 
00144                                 vi != config.vaults().end();
00145                                 vi++
00146                                 )
00147                         {
00148                                 fsys.path(*vi);
00149                                 if (most_space.second < fsys.free_blocks()) {
00150                                         TRY_nomem(most_space.first = *vi);
00151                                         most_space.second = fsys.free_blocks();
00152                                 }
00153                         }
00154                         TRY_nomem(m_selected_vault = most_space.first);
00155                 }
00156         }
00157         catch(error e) {
00158                 e.push_back(es);
00159                 throw(e);
00160         }
00161         catch(...) {
00162                 error e(0);
00163                 if (errno == ENOMEM) {
00164                         e = err_nomem;
00165                         e.push_back(es);
00166                         throw(e);
00167                 }
00168                 e = err_unknown;
00169                 e.push_back(es);
00170                 throw(e);
00171         }
00172 
00173         if (m_selected_vault.size() == 0)
00174                 throw(ERROR(0,es));
00175 }
00176 
00177 /** Return a list of archive directories in the selected vault */
00178 const subdirectory vault_manager::get_archive_list(void)
00179 {
00180         subdirectory subdir;
00181 
00182         if (!initialized())
00183                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00184 
00185         if (!selected())
00186                 throw(INTERNAL_ERROR(0,"No vault selected"));
00187 
00188         subdir.path(vault());
00189 
00190         return(subdir);
00191 }
00192 
00193 /** Return the path to the selected vault */
00194 const std::string vault_manager::vault(void) const
00195 {
00196         if (!initialized())
00197                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00198 
00199         if (!selected())
00200                 throw(INTERNAL_ERROR(0,"No vault selected"));
00201 
00202         return(m_selected_vault);
00203 }
00204 
00205 /** Return whether or not a vault has been selected yet */
00206 const bool vault_manager::selected(void) const
00207 {
00208         bool value = true;
00209 
00210         if (!initialized())
00211                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00212 
00213         if (m_selected_vault.size() == 0)
00214                 value = false;
00215         
00216         return(value);
00217 }
00218 
00219 /** Return the percent of used blocks and used inodes in the selected vault */
00220 void vault_manager::usage(uint16 &a_blocks, uint16 &a_inodes) const
00221 {
00222         filesystem fsys;
00223         safe_num<uint64> blocks, inodes;
00224 
00225         if (!initialized())
00226                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00227 
00228         if (!selected())
00229                 throw(INTERNAL_ERROR(0,"No vault selected"));
00230 
00231         fsys.path(vault());
00232 
00233         if (fsys.total_blocks() == 0)
00234                 blocks = 0;
00235         else {
00236                 try {
00237                         blocks = fsys.free_blocks();
00238                         blocks *= 100;
00239                         blocks /= fsys.total_blocks();
00240                 }
00241                 catch(error e) {
00242                         logger.write("*** ERROR: Overflow error detected in vault_manager::usage() while calculating percent blocks used\n");
00243                         blocks = 0;
00244                 }
00245                 catch(...) {
00246                         logger.write("*** ERROR: Unknown error detected in vault_manager::usage() while calculating percent blocks used\n");
00247                         blocks = 0;
00248                 }
00249         }
00250 
00251         if (fsys.free_inodes() == 0)
00252                 inodes = 0;
00253         else {
00254                 try {
00255                         inodes = fsys.free_inodes();
00256                         inodes *= 100;
00257                         inodes /= fsys.total_inodes();
00258                 }
00259                 catch(error e) {
00260                         logger.write("*** ERROR: Overflow error detected in vault_manager::usage() while calculating percent inodes used\n");
00261                         blocks = 0;
00262                 }
00263                 catch(...) {
00264                         logger.write("*** ERROR: Unknown error detected in vault_manager::usage() while calculating percent inodes used\n");
00265                         blocks = 0;
00266                 }
00267         }
00268 
00269         ASSERT(blocks <= max_limit(blocks));
00270         ASSERT(inodes <= max_limit(inodes));
00271 
00272         a_blocks = blocks.value();
00273         a_inodes = inodes.value();
00274 }
00275 
00276 /** Test to see if a vault has exceeded it's overflow threshold */
00277 const bool vault_manager::overflow(bool a_report)
00278 {
00279         uint16 free_blocks;
00280         uint16 free_inodes;
00281         bool value = false;
00282         std::string lstr;
00283 
00284         if (!initialized())
00285                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00286 
00287         if (!selected())
00288                 throw(INTERNAL_ERROR(0,"No vault selected"));
00289 
00290         usage(free_blocks, free_inodes);
00291         if (free_blocks < config.vault_overflow_blocks())
00292                 value = true;
00293         if (free_inodes < config.vault_overflow_inodes())
00294                 value = true;
00295         
00296         if (value && a_report) {
00297                 TRY_nomem(lstr = "Vault overflow detected: ");
00298                 TRY_nomem(lstr += vault());
00299                 TRY_nomem(lstr += "\n");
00300                 logger.write(lstr);
00301 
00302                 TRY_nomem(lstr = "     Threshold: ");
00303                 TRY_nomem(lstr += 
00304                         percent_string(config.vault_overflow_blocks(),
00305                                 static_cast<uint16>(100)));
00306                 TRY_nomem(lstr += " free blocks, ");
00307                 TRY_nomem(lstr += 
00308                         percent_string(config.vault_overflow_inodes(),
00309                                 static_cast<uint16>(100)));
00310                 TRY_nomem(lstr += " free inodes");
00311                 TRY_nomem(lstr += "\n");
00312                 logger.write(lstr);
00313 
00314                 TRY_nomem(lstr = "Vault capacity: ");
00315                 TRY_nomem(lstr += percent_string(free_blocks,static_cast<uint16>(100)));
00316                 TRY_nomem(lstr += " free blocks, ");
00317                 TRY_nomem(lstr += percent_string(free_inodes,static_cast<uint16>(100)));
00318                 TRY_nomem(lstr += " free inodes");
00319                 TRY_nomem(lstr += "\n");
00320                 logger.write(lstr);
00321 
00322                 reporter.vault().add_report(
00323                         vault_stats_report(estring("Overflow Detected:"),filesystem(vault()))
00324                         );
00325         }
00326         
00327         return(value);
00328 }
00329 
00330 /** Find the oldest archive in the vault and delete it */
00331 void vault_manager::delete_oldest_archive(void)
00332 {
00333         std::string es;
00334         std::string lstr;
00335         subdirectory archive_list; 
00336         std::string oldest;
00337         std::string dir;
00338         estring estr;
00339         timer t;
00340         uint16 free_blocks, free_inodes;
00341 
00342         if (!initialized())
00343                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00344         
00345         if (!selected())
00346                 throw(INTERNAL_ERROR(0,"No vault selected"));
00347 
00348         archive_list = get_archive_list();
00349         if (
00350                 (archive_list.size() == 0)
00351                 || (
00352                         (archive_list.size() == 1)
00353                         && (archive_list[0] == config.timestamp().str())
00354                         )
00355                 )
00356         {
00357                 TRY_nomem(es = "Vault has insufficient space: \"");
00358                 TRY_nomem(es += vault());
00359                 TRY_nomem(es += "\"");
00360                 exit_manager.assign(exitstat::vault_full);
00361                 throw(ERROR(0,es));
00362         }
00363 
00364         TRY_nomem(oldest = archive_list[0]);
00365         if (oldest == config.timestamp().str()) {
00366                 TRY_nomem(oldest = archive_list[1]);
00367         }
00368 
00369         TRY_nomem(lstr = "Deleting oldest archive: \"");
00370         TRY_nomem(lstr += oldest);
00371         TRY_nomem(lstr += "\"\n");
00372         logger.write(lstr);
00373 
00374         TRY_nomem(dir = vault());
00375         TRY_nomem(dir += "/");
00376         TRY_nomem(dir += oldest);
00377         t.start();
00378         try {
00379                 m_deleted_archives.push_back(oldest);
00380         }
00381         catch(...) {
00382                 m_da_err = true;
00383         }
00384         rm_recursive(dir);
00385         t.stop();
00386 
00387         TRY_nomem(lstr = "Deletion complete, duration: ");
00388         TRY_nomem(lstr += t.duration());
00389         TRY_nomem(lstr += "\n");
00390         logger.write(lstr);
00391 
00392         usage(free_blocks, free_inodes);
00393         TRY_nomem(lstr = "Vault capacity: ");
00394         TRY_nomem(lstr += percent_string(free_blocks,static_cast<uint16>(100)));
00395         TRY_nomem(lstr += " free blocks, ");
00396         TRY_nomem(lstr += percent_string(free_inodes,static_cast<uint16>(100)));
00397         TRY_nomem(lstr += " free inodes");
00398         TRY_nomem(lstr += "\n");
00399         logger.write(lstr);
00400 
00401         estr = "Deleted ";
00402         estr += oldest;
00403         estr += ":";
00404         reporter.vault().add_report(vault_stats_report(estr,filesystem(vault())));
00405 }
00406 
00407 /** Prepare the selected vault
00408 
00409         Check the selected vault for overflow.  If the overflow threshold has been
00410         exceeded and the vault-overflow-behavior setting is not quit, then delete
00411         the oldest archive in the vault.  Depending on vault-overflow-behavior,
00412         possibly repeat this action until either there is space free or there are no
00413         archives left in the vault.
00414 
00415 */
00416 void vault_manager::prepare(bool a_assume_overflow)
00417 {
00418         std::string es;
00419         std::string lstr;
00420 
00421         /*
00422         estring debug_estr;
00423         
00424         debug_estr = "[DEBUG]: vault_manager::prepare() - a_assume_overflow = ";
00425         debug_estr += estring(a_assume_overflow);
00426         debug_estr += "\n";
00427         logger.write(debug_estr);
00428         */
00429 
00430         if (!initialized())
00431                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00432 
00433         if (!selected())
00434                 throw(INTERNAL_ERROR(0,"No vault selected"));
00435         
00436         if (!overflow() && !a_assume_overflow)
00437                 return;
00438         
00439         // logger.write("Vault has insufficient space\n");
00440         // logger.write("[DEBUG]: vault_manager::prepare() - Cleaning vault...\n");
00441 
00442         if (config.vault_overflow_behavior() 
00443                 == configuration_manager::overflow_quit)
00444         {
00445                 TRY_nomem(es = "Vault has insufficient space: \"");
00446                 TRY_nomem(es += vault());
00447                 TRY_nomem(es += "\"");
00448                 throw(ERROR(0,es));
00449         }
00450 
00451         if (config.vault_overflow_behavior() 
00452                         == configuration_manager::overflow_delete_oldest)
00453         {
00454                 if (m_deleted_archives.size() == 0) {
00455                         TRY(delete_oldest_archive(),"Failure preparing vault");
00456                         a_assume_overflow = false;
00457                 }
00458                 else {
00459                         logger.write("Vault has insufficient space\n");
00460                         throw(ERROR(0,"Vault has insufficient space"));
00461                 }
00462         }
00463 
00464         if (!overflow() && !a_assume_overflow)
00465                 return;
00466 
00467         if (config.vault_overflow_behavior() 
00468                 == configuration_manager::overflow_delete_until_free)
00469         {
00470                 while (overflow() || a_assume_overflow) {
00471                         // logger.write("Vault has insufficient space\n");
00472                         TRY(delete_oldest_archive(),"Failure preparing vault");
00473                         a_assume_overflow = false;
00474                 }
00475         }
00476         else {
00477                 // logger.write("Vault has insufficient space\n");
00478                 exit_manager.assign(exitstat::vault_full);
00479                 // throw(ERROR(0,"Vault has insufficient space"));
00480         }
00481 }
00482 
00483 /** Return a list of deleted archives */
00484 const std::vector<std::string>& vault_manager::deleted_archives(void) const
00485 {
00486         if (!initialized())
00487                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00488 
00489         return(m_deleted_archives);
00490 }
00491 
00492 /** Return whether or not there was an error deleting archives */
00493 const bool vault_manager::err_deleted_archives(void) const
00494 {
00495         if (!initialized())
00496                 throw(INTERNAL_ERROR(0,"Vault manager not initialized"));
00497 
00498         return(m_da_err);
00499 }
00500 
00501 /** The global vault manager */
00502 vault_manager vaulter;
00503 

Generated on Mon Jul 12 12:02:56 2004 for rvm by doxygen 1.3.6