From 5a82646b184960428257c35f8680ae61dcd76afb Mon Sep 17 00:00:00 2001 From: Steven Murray <steven.murray@cern.ch> Date: Mon, 13 Jun 2016 11:35:33 +0200 Subject: [PATCH] Added delete functionality to the Catalogue --- catalogue/CMakeLists.txt | 6 +- catalogue/Catalogue.hpp | 47 +- catalogue/DbStmt.hpp | 8 + catalogue/InMemoryCatalogueTest.cpp | 999 ++++++++++++++++-- catalogue/OcciStmt.cpp | 7 + catalogue/OcciStmt.hpp | 8 + catalogue/OracleCatalogue.cpp | 7 + catalogue/OracleCatalogue.hpp | 10 + catalogue/RdbmsCatalogue.cpp | 450 ++++++-- catalogue/RdbmsCatalogue.hpp | 81 +- catalogue/RequesterAndGroupMountPolicies.hpp | 47 + catalogue/SqliteCatalogue.cpp | 107 +- catalogue/SqliteCatalogue.hpp | 10 + catalogue/SqliteConn.cpp | 17 +- catalogue/SqliteConn.hpp | 6 + catalogue/SqliteStmt.cpp | 17 +- catalogue/SqliteStmt.hpp | 23 +- catalogue/sqlite_catalogue_schema_header.sql | 1 - catalogue/sqlite_catalogue_schema_trailer.sql | 2 + scheduler/SchedulerTest.cpp | 4 +- xroot_plugins/XrdCtaFile.cpp | 4 +- 21 files changed, 1644 insertions(+), 217 deletions(-) create mode 100644 catalogue/RequesterAndGroupMountPolicies.hpp create mode 100644 catalogue/sqlite_catalogue_schema_trailer.sql diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt index 7a33b8136b..4b1a8caacb 100644 --- a/catalogue/CMakeLists.txt +++ b/catalogue/CMakeLists.txt @@ -61,9 +61,9 @@ target_link_libraries (ctacatalogue ${SQLITE_LIBRARIES}) add_custom_command(OUTPUT sqlite_catalogue_schema.sql oracle_catalogue_schema.sql - COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql > sqlite_catalogue_schema.sql + COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql > sqlite_catalogue_schema.sql COMMAND cat ${CMAKE_CURRENT_SOURCE_DIR}/catalogue_common_schema.sql > oracle_catalogue_schema.sql - DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql catalogue_common_schema.sql) + DEPENDS ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_header.sql catalogue_common_schema.sql ${CMAKE_CURRENT_SOURCE_DIR}/sqlite_catalogue_schema_trailer.sql) install (FILES ${CMAKE_CURRENT_BINARY_DIR}/sqlite_catalogue_schema.sql DESTINATION usr/share/cta-${CTA_VERSION}/sql @@ -74,7 +74,7 @@ install (FILES ${CMAKE_CURRENT_BINARY_DIR}/oracle_catalogue_schema.sql PERMISSIONS OWNER_READ OWNER_WRITE GROUP_READ WORLD_READ) add_custom_command(OUTPUT in_memory_catalogue_schema.cpp - COMMAND sed 's/^/\ \ \ \ \"/' sqlite_catalogue_schema.sql | sed 's/$$/\"/' > in_memory_catalogue_schema.cpp + COMMAND sed 's/^/\ \ \"/' sqlite_catalogue_schema.sql | sed 's/$$/\"/' > in_memory_catalogue_schema.cpp DEPENDS sqlite_catalogue_schema.sql) add_custom_command(OUTPUT InMemoryCatalogueSchema.cpp diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp index f2d354f9ba..e99fdc86fb 100644 --- a/catalogue/Catalogue.hpp +++ b/catalogue/Catalogue.hpp @@ -138,11 +138,11 @@ public: virtual void createRequester( const common::dataStructures::SecurityIdentity &cliIdentity, - const common::dataStructures::UserIdentity &user, + const std::string &requesterName, const std::string &mountPolicy, const std::string &comment) = 0; - virtual void deleteRequester(const common::dataStructures::UserIdentity &user) = 0; + virtual void deleteRequester(const std::string &requesterName) = 0; virtual std::list<common::dataStructures::Requester> getRequesters() const = 0; virtual void modifyRequesterMountPolicy(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy) = 0; virtual void modifyRequesterComment(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &comment) = 0; @@ -157,6 +157,35 @@ public: const uint64_t maxDrivesAllowed, const std::string &comment) = 0; + /** + * Assigns the specified mount policy to the specified requester. + * + * Please note that requester mount-policies overrule requester-group + * mount-policies. + * + * @param cliIdentity The user of the command-line tool. + * @param mountPolicyName The name of the mount policy. + * @param requesterName The name of the requester. + * @param comment Comment. + */ + virtual void assignMountPolicyToRequester( + const common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &mountPolicyName, + const std::string &requesterName, + const std::string &comment) = 0; + + /** + * Assigns the specified mount policy to the specified requester group. + * + * Please note that requester mount-policies overrule requester-group + * mount-policies. + * + * @param mountPolicyName The name of the mount policy. + * @param requesterGrouprName The name of the requester group. + */ + virtual void assignMountPolicyToRequesterGroup(const std::string &mountPolicyName, + const std::string &requesterGroupName) = 0; + virtual void deleteMountPolicy(const std::string &name) = 0; virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const = 0; virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &name, const uint64_t archivePriority) = 0; @@ -222,6 +251,16 @@ public: */ virtual void fileWrittenToTape(const TapeFileWritten &event) = 0; + /** + * Deletes the specified archive file and its associated tape copies from the + * catalogue. + * + * @param archiveFileId The unique identifier of the archive file. + * @return The metadata of the deleted archive file including the metadata of + * the associated and also deleted tape copies. + */ + virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId) = 0; + /** * Prepares for a file retrieval by returning the information required to * queue the associated retrieve request(s). @@ -243,10 +282,10 @@ public: /** * Returns the mount policy for the specified end user. * - * @param user The name of the end user. + * @param username The name of the end user. * @return The mount policy. */ - virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const = 0; + virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const std::string &username) const = 0; virtual bool isAdmin(const common::dataStructures::SecurityIdentity &cliIdentity) const = 0; diff --git a/catalogue/DbStmt.hpp b/catalogue/DbStmt.hpp index 1ab5b09af0..039cbe17fe 100644 --- a/catalogue/DbStmt.hpp +++ b/catalogue/DbStmt.hpp @@ -78,6 +78,14 @@ public: */ virtual void executeNonQuery() = 0; + /** + * Returns the number of rows affected by the last execution of this + * statement. + * + * @return The number of affected rows. + */ + virtual uint64_t getNbAffectedRows() const = 0; + }; // class DbStmt } // namespace catalogue diff --git a/catalogue/InMemoryCatalogueTest.cpp b/catalogue/InMemoryCatalogueTest.cpp index 353f67a033..c34ca7ede7 100644 --- a/catalogue/InMemoryCatalogueTest.cpp +++ b/catalogue/InMemoryCatalogueTest.cpp @@ -47,10 +47,73 @@ public: protected: virtual void SetUp() { + using namespace cta; using namespace cta::catalogue; const DbLogin catalogueLogin(DbLogin::DBTYPE_IN_MEMORY, "", "", ""); m_catalogue.reset(dynamic_cast<RdbmsCatalogue*>(CatalogueFactory::create(catalogueLogin))); + + { + const std::list<common::dataStructures::AdminUser> adminUsers = m_catalogue->getAdminUsers(); + for(auto &adminUser: adminUsers) { + m_catalogue->deleteAdminUser(adminUser.name); + } + } + { + const std::list<common::dataStructures::AdminHost> adminHosts = m_catalogue->getAdminHosts(); + for(auto &adminHost: adminHosts) { + m_catalogue->deleteAdminHost(adminHost.name); + } + } + { + const std::list<common::dataStructures::ArchiveRoute> archiveRoutes = m_catalogue->getArchiveRoutes(); + for(auto &archiveRoute: archiveRoutes) { + m_catalogue->deleteArchiveRoute(archiveRoute.storageClassName, archiveRoute.copyNb); + } + } + { + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + for(auto &requester: requesters) { + m_catalogue->deleteRequester(requester.name); + } + } + { + const std::list<common::dataStructures::ArchiveFile> archiveFiles = + m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", ""); + for(auto &archiveFile: archiveFiles) { + m_catalogue->deleteArchiveFile(archiveFile.archiveFileID); + } + } + { + const std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes("", "", "", "", "", "", "", ""); + for(auto &tape: tapes) { + m_catalogue->deleteTape(tape.vid); + } + } + { + const std::list<common::dataStructures::StorageClass> storageClasses = m_catalogue->getStorageClasses(); + for(auto &storageClass: storageClasses) { + m_catalogue->deleteStorageClass(storageClass.name); + } + } + { + const std::list<common::dataStructures::TapePool> tapePools = m_catalogue->getTapePools(); + for(auto &tapePool: tapePools) { + m_catalogue->deleteTapePool(tapePool.name); + } + } + { + const std::list<common::dataStructures::LogicalLibrary> logicalLibraries = m_catalogue->getLogicalLibraries(); + for(auto &logicalLibrary: logicalLibraries) { + m_catalogue->deleteLogicalLibrary(logicalLibrary.name); + } + } + { + const std::list<common::dataStructures::MountPolicy> mountPolicies = m_catalogue->getMountPolicies(); + for(auto &mountPolicy: mountPolicies) { + m_catalogue->deleteMountPolicy(mountPolicy.name); + } + } } virtual void TearDown() { @@ -210,6 +273,8 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser) { TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser_same_twice) { using namespace cta; + ASSERT_TRUE(m_catalogue->getAdminUsers().empty()); + m_catalogue->createBootstrapAdminAndHostNoAuth( m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment); @@ -236,6 +301,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminUser_same_twice) { "comment 2"), catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminUser) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getAdminUsers().empty()); + + m_catalogue->createBootstrapAdminAndHostNoAuth( + m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment); + + { + std::list<common::dataStructures::AdminUser> admins; + admins = m_catalogue->getAdminUsers(); + ASSERT_EQ(1, admins.size()); + + const common::dataStructures::AdminUser admin = admins.front(); + ASSERT_EQ(m_bootstrapComment, admin.comment); + + const common::dataStructures::EntryLog creationLog = admin.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + admin.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + + m_catalogue->deleteAdminUser(m_bootstrapAdminSI.username); + + ASSERT_TRUE(m_catalogue->getAdminUsers().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminUser_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getAdminUsers().empty()); + ASSERT_THROW(m_catalogue->deleteAdminUser("non_existant_sdmin_user"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminHost) { using namespace cta; @@ -345,6 +447,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createAdminHost_same_twice) { anotherAdminHost, "comment 2"), catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminHost) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getAdminHosts().empty()); + + m_catalogue->createBootstrapAdminAndHostNoAuth( + m_cliSI, m_bootstrapAdminSI.username, m_bootstrapAdminSI.host, m_bootstrapComment); + + { + std::list<common::dataStructures::AdminUser> admins; + admins = m_catalogue->getAdminUsers(); + ASSERT_EQ(1, admins.size()); + + const common::dataStructures::AdminUser admin = admins.front(); + ASSERT_EQ(m_bootstrapComment, admin.comment); + + const common::dataStructures::EntryLog creationLog = admin.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + admin.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + + m_catalogue->deleteAdminHost(m_bootstrapAdminSI.host); + + ASSERT_TRUE(m_catalogue->getAdminHosts().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteAdminHost_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getAdminHosts().empty()); + ASSERT_THROW(m_catalogue->deleteAdminHost("non_exstant_admin_host"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, isAdmin_false) { using namespace cta; @@ -401,6 +540,46 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createStorageClass_same_twice) { storageClassName, nbCopies, comment), catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteStorageClass) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getStorageClasses().empty()); + + const std::string storageClassName = "storage_class"; + const uint64_t nbCopies = 2; + const std::string comment = "create storage class"; + m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, comment); + + const std::list<common::dataStructures::StorageClass> storageClasses = + m_catalogue->getStorageClasses(); + + ASSERT_EQ(1, storageClasses.size()); + + const common::dataStructures::StorageClass storageClass = + storageClasses.front(); + ASSERT_EQ(storageClassName, storageClass.name); + ASSERT_EQ(nbCopies, storageClass.nbCopies); + ASSERT_EQ(comment, storageClass.comment); + + const common::dataStructures::EntryLog creationLog = storageClass.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + storageClass.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + m_catalogue->deleteStorageClass(storageClass.name); + ASSERT_TRUE(m_catalogue->getStorageClasses().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteStorageClass_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getStorageClasses().empty()); + ASSERT_THROW(m_catalogue->deleteStorageClass("non_existant_storage_class"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, createTapePool) { using namespace cta; @@ -446,6 +625,48 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createTapePool_same_twice) { catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTapePool) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + + const std::string tapePoolName = "tape_pool"; + const uint64_t nbPartialTapes = 2; + const bool is_encrypted = true; + const std::string comment = "create tape pool"; + m_catalogue->createTapePool(m_cliSI, tapePoolName, nbPartialTapes, is_encrypted, + comment); + + const std::list<common::dataStructures::TapePool> pools = + m_catalogue->getTapePools(); + + ASSERT_EQ(1, pools.size()); + + const common::dataStructures::TapePool pool = pools.front(); + ASSERT_EQ(tapePoolName, pool.name); + ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes); + ASSERT_EQ(is_encrypted, pool.encryption); + ASSERT_EQ(comment, pool.comment); + + const common::dataStructures::EntryLog creationLog = pool.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + pool.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + m_catalogue->deleteTapePool(pool.name); + ASSERT_TRUE(m_catalogue->getTapePools().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTapePool_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + ASSERT_THROW(m_catalogue->deleteTapePool("non_existant_tape_pool"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRoute) { using namespace cta; @@ -514,6 +735,63 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRouteTapePool_same_twic exception::Exception); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveRoute) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty()); + + const std::string storageClassName = "storage_class"; + const uint64_t nbCopies = 2; + m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, "create storage class"); + + const std::string tapePoolName = "tape_pool"; + const uint64_t nbPartialTapes = 2; + const bool is_encrypted = true; + m_catalogue->createTapePool(m_cliSI, tapePoolName, nbPartialTapes, is_encrypted, "create tape pool"); + + const uint64_t copyNb = 1; + const std::string comment = "create archive route"; + m_catalogue->createArchiveRoute(m_cliSI, storageClassName, copyNb, tapePoolName, + comment); + + const std::list<common::dataStructures::ArchiveRoute> routes = + m_catalogue->getArchiveRoutes(); + + ASSERT_EQ(1, routes.size()); + + const common::dataStructures::ArchiveRoute route = routes.front(); + ASSERT_EQ(storageClassName, route.storageClassName); + ASSERT_EQ(copyNb, route.copyNb); + ASSERT_EQ(tapePoolName, route.tapePoolName); + ASSERT_EQ(comment, route.comment); + + const common::dataStructures::EntryLog creationLog = route.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + route.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + common::dataStructures::TapeCopyToPoolMap copyToPoolMap = + m_catalogue->getTapeCopyToPoolMap(storageClassName); + ASSERT_EQ(1, copyToPoolMap.size()); + std::pair<uint64_t, std::string> maplet = *(copyToPoolMap.begin()); + ASSERT_EQ(copyNb, maplet.first); + ASSERT_EQ(tapePoolName, maplet.second); + + m_catalogue->deleteArchiveRoute(storageClassName, copyNb); + + ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveRoute_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getArchiveRoutes().empty()); + ASSERT_THROW(m_catalogue->deleteArchiveRoute("non_existant_storage_class", 1234), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, createArchiveRoute_deleteStorageClass) { using namespace cta; @@ -598,6 +876,43 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createLogicalLibrary_same_twice) { ASSERT_THROW(m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, comment), catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteLogicalLibrary) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty()); + + const std::string logicalLibraryName = "logical_library"; + const std::string comment = "create logical library"; + m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, comment); + + const std::list<common::dataStructures::LogicalLibrary> libs = + m_catalogue->getLogicalLibraries(); + + ASSERT_EQ(1, libs.size()); + + const common::dataStructures::LogicalLibrary lib = libs.front(); + ASSERT_EQ(logicalLibraryName, lib.name); + ASSERT_EQ(comment, lib.comment); + + const common::dataStructures::EntryLog creationLog = lib.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + lib.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + m_catalogue->deleteLogicalLibrary(logicalLibraryName); + ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteLogicalLibrary_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getLogicalLibraries().empty()); + ASSERT_THROW(m_catalogue->deleteLogicalLibrary("non_existant_logical_library"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, createTape) { using namespace cta; @@ -665,6 +980,61 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createTape_same_twice) { comment), catalogue::UserError); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTape) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty()); + + const std::string vid = "vid"; + const std::string logicalLibraryName = "logical_library_name"; + const std::string tapePoolName = "tape_pool_name"; + const std::string encryptionKey = "encryption_key"; + const uint64_t capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000; + const bool disabledValue = true; + const bool fullValue = false; + const std::string comment = "create tape"; + + m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, + "create logical library"); + m_catalogue->createTapePool(m_cliSI, tapePoolName, 2, true, "create tape pool"); + m_catalogue->createTape(m_cliSI, vid, logicalLibraryName, tapePoolName, + encryptionKey, capacityInBytes, disabledValue, fullValue, + comment); + + const std::list<common::dataStructures::Tape> tapes = + m_catalogue->getTapes("", "", "", "", "", "", "", ""); + + ASSERT_EQ(1, tapes.size()); + + const common::dataStructures::Tape tape = tapes.front(); + ASSERT_EQ(vid, tape.vid); + ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName); + ASSERT_EQ(tapePoolName, tape.tapePoolName); + ASSERT_EQ(encryptionKey, tape.encryptionKey); + ASSERT_EQ(capacityInBytes, tape.capacityInBytes); + ASSERT_TRUE(disabledValue == tape.disabled); + ASSERT_TRUE(fullValue == tape.full); + ASSERT_EQ(comment, tape.comment); + + const common::dataStructures::EntryLog creationLog = tape.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + tape.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + m_catalogue->deleteTape(tape.vid); + ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteTape_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapes("", "", "", "", "", "", "", "").empty()); + ASSERT_THROW(m_catalogue->deleteTape("non_exsitant_tape"), catalogue::UserError); +} + TEST_F(cta_catalogue_InMemoryCatalogueTest, getTapesForWriting) { using namespace cta; @@ -703,13 +1073,13 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy) { ASSERT_TRUE(m_catalogue->getMountPolicies().empty()); - const std::string name = "mount_group"; + const std::string name = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 2; const uint64_t retrievePriority = 3; const uint64_t minRetrieveRequestAge = 4; const uint64_t maxDrivesAllowed = 5; - const std::string &comment = "create mount group"; + const std::string &comment = "create mount policy"; m_catalogue->createMountPolicy( m_cliSI, @@ -721,31 +1091,31 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy) { maxDrivesAllowed, comment); - const std::list<common::dataStructures::MountPolicy> groups = + const std::list<common::dataStructures::MountPolicy> mountPolicies = m_catalogue->getMountPolicies(); - ASSERT_EQ(1, groups.size()); + ASSERT_EQ(1, mountPolicies.size()); - const common::dataStructures::MountPolicy group = groups.front(); + const common::dataStructures::MountPolicy mountPolicy = mountPolicies.front(); - ASSERT_EQ(name, group.name); + ASSERT_EQ(name, mountPolicy.name); - ASSERT_EQ(archivePriority, group.archivePriority); - ASSERT_EQ(minArchiveRequestAge, group.archiveMinRequestAge); + ASSERT_EQ(archivePriority, mountPolicy.archivePriority); + ASSERT_EQ(minArchiveRequestAge, mountPolicy.archiveMinRequestAge); - ASSERT_EQ(retrievePriority, group.retrievePriority); - ASSERT_EQ(minRetrieveRequestAge, group.retrieveMinRequestAge); + ASSERT_EQ(retrievePriority, mountPolicy.retrievePriority); + ASSERT_EQ(minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge); - ASSERT_EQ(maxDrivesAllowed, group.maxDrivesAllowed); + ASSERT_EQ(maxDrivesAllowed, mountPolicy.maxDrivesAllowed); - ASSERT_EQ(comment, group.comment); + ASSERT_EQ(comment, mountPolicy.comment); - const common::dataStructures::EntryLog creationLog = group.creationLog; + const common::dataStructures::EntryLog creationLog = mountPolicy.creationLog; ASSERT_EQ(m_cliSI.username, creationLog.username); ASSERT_EQ(m_cliSI.host, creationLog.host); const common::dataStructures::EntryLog lastModificationLog = - group.lastModificationLog; + mountPolicy.lastModificationLog; ASSERT_EQ(creationLog, lastModificationLog); } @@ -754,13 +1124,13 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy_same_twice) { ASSERT_TRUE(m_catalogue->getMountPolicies().empty()); - const std::string name = "mount_group"; + const std::string name = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 4; const uint64_t retrievePriority = 5; const uint64_t minRetrieveRequestAge = 8; const uint64_t maxDrivesAllowed = 9; - const std::string &comment = "create mount group"; + const std::string &comment = "create mount policy"; m_catalogue->createMountPolicy( m_cliSI, @@ -772,23 +1142,218 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createMountPolicy_same_twice) { maxDrivesAllowed, comment); - ASSERT_THROW(m_catalogue->createMountPolicy( - m_cliSI, - name, - archivePriority, - minArchiveRequestAge, - retrievePriority, - minRetrieveRequestAge, - maxDrivesAllowed, - comment), catalogue::UserError); + ASSERT_THROW(m_catalogue->createMountPolicy( + m_cliSI, + name, + archivePriority, + minArchiveRequestAge, + retrievePriority, + minRetrieveRequestAge, + maxDrivesAllowed, + comment), catalogue::UserError); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteMountPolicy) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getMountPolicies().empty()); + + const std::string name = "mount_policy"; + const uint64_t archivePriority = 1; + const uint64_t minArchiveRequestAge = 2; + const uint64_t retrievePriority = 3; + const uint64_t minRetrieveRequestAge = 4; + const uint64_t maxDrivesAllowed = 5; + const std::string &comment = "create mount policy"; + + m_catalogue->createMountPolicy( + m_cliSI, + name, + archivePriority, + minArchiveRequestAge, + retrievePriority, + minRetrieveRequestAge, + maxDrivesAllowed, + comment); + + const std::list<common::dataStructures::MountPolicy> mountPolicies = + m_catalogue->getMountPolicies(); + + ASSERT_EQ(1, mountPolicies.size()); + + const common::dataStructures::MountPolicy mountPolicy = mountPolicies.front(); + + ASSERT_EQ(name, mountPolicy.name); + + ASSERT_EQ(archivePriority, mountPolicy.archivePriority); + ASSERT_EQ(minArchiveRequestAge, mountPolicy.archiveMinRequestAge); + + ASSERT_EQ(retrievePriority, mountPolicy.retrievePriority); + ASSERT_EQ(minRetrieveRequestAge, mountPolicy.retrieveMinRequestAge); + + ASSERT_EQ(maxDrivesAllowed, mountPolicy.maxDrivesAllowed); + + ASSERT_EQ(comment, mountPolicy.comment); + + const common::dataStructures::EntryLog creationLog = mountPolicy.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + mountPolicy.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + + m_catalogue->deleteMountPolicy(name); + + ASSERT_TRUE(m_catalogue->getMountPolicies().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteMountPolicy_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getMountPolicies().empty()); + ASSERT_THROW(m_catalogue->deleteMountPolicy("non_existant_mount_policy"), catalogue::UserError); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, createRequester) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getRequesters().empty()); + + const std::string mountPolicyName = "mount_policy"; + const uint64_t archivePriority = 1; + const uint64_t minArchiveRequestAge = 4; + const uint64_t retrievePriority = 5; + const uint64_t minRetrieveRequestAge = 8; + const uint64_t maxDrivesAllowed = 9; + + m_catalogue->createMountPolicy( + m_cliSI, + mountPolicyName, + archivePriority, + minArchiveRequestAge, + retrievePriority, + minRetrieveRequestAge, + maxDrivesAllowed, + "create mount group"); + + const std::string comment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment); + + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + ASSERT_EQ(1, requesters.size()); + + const common::dataStructures::Requester requester = requesters.front(); + + ASSERT_EQ(requesterName, requester.name); + ASSERT_EQ(mountPolicyName, requester.mountPolicy); + ASSERT_EQ(comment, requester.comment); + ASSERT_EQ(m_cliSI.username, requester.creationLog.username); + ASSERT_EQ(m_cliSI.host, requester.creationLog.host); + ASSERT_EQ(requester.creationLog, requester.lastModificationLog); + + const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName); + + ASSERT_EQ(archivePriority, policy.archivePriority); + ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge); + ASSERT_EQ(maxDrivesAllowed, policy.maxDrivesAllowed); + ASSERT_EQ(retrievePriority, policy.retrievePriority); + ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, createRequester_same_twice) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getRequesters().empty()); + + const std::string mountPolicyName = "mount_policy"; + const uint64_t archivePriority = 1; + const uint64_t minArchiveRequestAge = 4; + const uint64_t retrievePriority = 5; + const uint64_t minRetrieveRequestAge = 8; + const uint64_t maxDrivesAllowed = 9; + + m_catalogue->createMountPolicy( + m_cliSI, + mountPolicyName, + archivePriority, + minArchiveRequestAge, + retrievePriority, + minRetrieveRequestAge, + maxDrivesAllowed, + "create mount group"); + + const std::string comment = "create requester"; + const std::string requesterName = "requester_name"; + const std::string mountPolicy = "mount_policy"; + m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment); + ASSERT_THROW(m_catalogue->createRequester(m_cliSI, requesterName, mountPolicy, comment), catalogue::UserError); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteRequester) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getRequesters().empty()); + + const std::string mountPolicyName = "mount_policy"; + const uint64_t archivePriority = 1; + const uint64_t minArchiveRequestAge = 4; + const uint64_t retrievePriority = 5; + const uint64_t minRetrieveRequestAge = 8; + const uint64_t maxDrivesAllowed = 9; + + m_catalogue->createMountPolicy( + m_cliSI, + mountPolicyName, + archivePriority, + minArchiveRequestAge, + retrievePriority, + minRetrieveRequestAge, + maxDrivesAllowed, + "create mount group"); + + const std::string comment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, comment); + + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + ASSERT_EQ(1, requesters.size()); + + const common::dataStructures::Requester requester = requesters.front(); + + ASSERT_EQ(requesterName, requester.name); + ASSERT_EQ(mountPolicyName, requester.mountPolicy); + ASSERT_EQ(comment, requester.comment); + ASSERT_EQ(m_cliSI.username, requester.creationLog.username); + ASSERT_EQ(m_cliSI.host, requester.creationLog.host); + ASSERT_EQ(requester.creationLog, requester.lastModificationLog); + + const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName); + + ASSERT_EQ(archivePriority, policy.archivePriority); + ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge); + ASSERT_EQ(maxDrivesAllowed, policy.maxDrivesAllowed); + ASSERT_EQ(retrievePriority, policy.retrievePriority); + ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge); + + m_catalogue->deleteRequester(requesterName); + ASSERT_TRUE(m_catalogue->getRequesters().empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteRequester_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getRequesters().empty()); + ASSERT_THROW(m_catalogue->deleteRequester("non_existant_requester"), catalogue::UserError); } -TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) { +TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester) { using namespace cta; ASSERT_TRUE(m_catalogue->getRequesters().empty()); - const std::string mountPolicyName = "mount_group"; + const std::string mountPolicyName = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 4; const uint64_t retrievePriority = 5; @@ -805,28 +1370,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) { maxDrivesAllowed, "create mount group"); - const std::string comment = "create user"; - const std::string userName = "user_name"; - const std::string group = "group"; - cta::common::dataStructures::UserIdentity userIdentity; - userIdentity.name=userName; - userIdentity.group=group; - m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, comment); + const std::string comment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment); - const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters(); - ASSERT_EQ(1, users.size()); + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + ASSERT_EQ(1, requesters.size()); - const common::dataStructures::Requester user = users.front(); + const common::dataStructures::Requester requester = requesters.front(); - ASSERT_EQ(userName, user.name); - ASSERT_EQ(mountPolicyName, user.mountPolicy); - ASSERT_EQ(comment, user.comment); - ASSERT_EQ(m_cliSI.username, user.creationLog.username); - ASSERT_EQ(m_cliSI.host, user.creationLog.host); - ASSERT_EQ(user.creationLog, user.lastModificationLog); + ASSERT_EQ(requesterName, requester.name); + ASSERT_EQ(mountPolicyName, requester.mountPolicy); + ASSERT_EQ(comment, requester.comment); + ASSERT_EQ(m_cliSI.username, requester.creationLog.username); + ASSERT_EQ(m_cliSI.host, requester.creationLog.host); + ASSERT_EQ(requester.creationLog, requester.lastModificationLog); - const common::dataStructures::MountPolicy policy = - m_catalogue->getMountPolicyForAUser(userIdentity); + const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName); ASSERT_EQ(archivePriority, policy.archivePriority); ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge); @@ -835,12 +1395,12 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser) { ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge); } -TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser_same_twice) { +TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester_same_twice) { using namespace cta; ASSERT_TRUE(m_catalogue->getRequesters().empty()); - const std::string mountPolicyName = "mount_group"; + const std::string mountPolicyName = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 4; const uint64_t retrievePriority = 5; @@ -856,17 +1416,24 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, createUser_same_twice) { minRetrieveRequestAge, maxDrivesAllowed, "create mount group"); - - const std::string comment = "create user"; - const std::string name = "name"; - const std::string mountPolicy = "mount_group"; - const std::string group = "group"; - cta::common::dataStructures::UserIdentity userIdentity; - userIdentity.name=name; - userIdentity.group=group; - m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, comment); - ASSERT_THROW(m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicy, comment), - exception::Exception); + + const std::string comment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment); + ASSERT_THROW(m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment), + catalogue::UserError); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, assignMountPolicyToRequester_non_existant_mount_policy) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getRequesters().empty()); + + const std::string comment = "create requester"; + const std::string mountPolicyName = "non_existant_mount_policy"; + const std::string requesterName = "requsterName"; + ASSERT_THROW(m_catalogue->assignMountPolicyToRequester(m_cliSI, mountPolicyName, requesterName, comment), + catalogue::UserError); } TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) { @@ -874,7 +1441,7 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) { ASSERT_TRUE(m_catalogue->getRequesters().empty()); - const std::string mountPolicyName = "mount_group"; + const std::string mountPolicyName = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 2; const uint64_t retrievePriority = 3; @@ -891,28 +1458,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) { maxDrivesAllowed, "create mount group"); - const std::string userComment = "create user"; - const std::string userName = "user_name"; - const std::string group = "group"; - cta::common::dataStructures::UserIdentity userIdentity; - userIdentity.name=userName; - userIdentity.group=group; - m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, userComment); + const std::string userComment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, userComment); - const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters(); - ASSERT_EQ(1, users.size()); + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + ASSERT_EQ(1, requesters.size()); - const common::dataStructures::Requester user = users.front(); + const common::dataStructures::Requester requester = requesters.front(); - ASSERT_EQ(userName, user.name); - ASSERT_EQ(mountPolicyName, user.mountPolicy); - ASSERT_EQ(userComment, user.comment); - ASSERT_EQ(m_cliSI.username, user.creationLog.username); - ASSERT_EQ(m_cliSI.host, user.creationLog.host); - ASSERT_EQ(user.creationLog, user.lastModificationLog); + ASSERT_EQ(requesterName, requester.name); + ASSERT_EQ(mountPolicyName, requester.mountPolicy); + ASSERT_EQ(userComment, requester.comment); + ASSERT_EQ(m_cliSI.username, requester.creationLog.username); + ASSERT_EQ(m_cliSI.host, requester.creationLog.host); + ASSERT_EQ(requester.creationLog, requester.lastModificationLog); - const common::dataStructures::MountPolicy policy = - m_catalogue->getMountPolicyForAUser(userIdentity); + const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName); ASSERT_EQ(archivePriority, policy.archivePriority); ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge); @@ -964,11 +1526,20 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareForNewFile) { ASSERT_EQ(copyNb, maplet.first); ASSERT_EQ(tapePoolName, maplet.second); - for(uint64_t i = 1; i<=10; i++) { + common::dataStructures::UserIdentity userIdentity; + userIdentity.name = requesterName; + userIdentity.group = "group"; + uint64_t expectedArchiveFileId = 0; + for(uint64_t i = 0; i<10; i++) { const common::dataStructures::ArchiveFileQueueCriteria queueCriteria = m_catalogue->prepareForNewFile(storageClassName, userIdentity); - ASSERT_EQ(i, queueCriteria.fileId); + if(0 == i) { + expectedArchiveFileId = queueCriteria.fileId; + } else { + expectedArchiveFileId++; + } + ASSERT_EQ(expectedArchiveFileId, queueCriteria.fileId); ASSERT_EQ(1, queueCriteria.copyToPoolMap.size()); ASSERT_EQ(copyNb, queueCriteria.copyToPoolMap.begin()->first); @@ -1148,7 +1719,7 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) { ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize); } - const std::string mountPolicyName = "mount_group"; + const std::string mountPolicyName = "mount_policy"; const uint64_t archivePriority = 1; const uint64_t minArchiveRequestAge = 2; const uint64_t retrievePriority = 3; @@ -1165,27 +1736,23 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) { maxDrivesAllowed, "create mount group"); - const std::string userComment = "create user"; - const std::string userName = "user_name"; - const std::string group = "group"; - cta::common::dataStructures::UserIdentity userIdentity; - userIdentity.name=userName; - userIdentity.group=group; - m_catalogue->createRequester(m_cliSI, userIdentity, mountPolicyName, userComment); + const std::string userComment = "create requester"; + const std::string requesterName = "requester_name"; + m_catalogue->createRequester(m_cliSI, requesterName, mountPolicyName, userComment); - const std::list<common::dataStructures::Requester> users = m_catalogue->getRequesters(); - ASSERT_EQ(1, users.size()); + const std::list<common::dataStructures::Requester> requesters = m_catalogue->getRequesters(); + ASSERT_EQ(1, requesters.size()); - const common::dataStructures::Requester user = users.front(); + const common::dataStructures::Requester requester = requesters.front(); - ASSERT_EQ(userName, user.name); - ASSERT_EQ(mountPolicyName, user.mountPolicy); - ASSERT_EQ(userComment, user.comment); - ASSERT_EQ(m_cliSI.username, user.creationLog.username); - ASSERT_EQ(m_cliSI.host, user.creationLog.host); - ASSERT_EQ(user.creationLog, user.lastModificationLog); + ASSERT_EQ(requesterName, requester.name); + ASSERT_EQ(mountPolicyName, requester.mountPolicy); + ASSERT_EQ(userComment, requester.comment); + ASSERT_EQ(m_cliSI.username, requester.creationLog.username); + ASSERT_EQ(m_cliSI.host, requester.creationLog.host); + ASSERT_EQ(requester.creationLog, requester.lastModificationLog); - const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(userIdentity); + const common::dataStructures::MountPolicy policy = m_catalogue->getMountPolicyForAUser(requesterName); ASSERT_EQ(archivePriority, policy.archivePriority); ASSERT_EQ(minArchiveRequestAge, policy.archiveMinRequestAge); @@ -1193,6 +1760,9 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, prepareToRetrieveFile) { ASSERT_EQ(retrievePriority, policy.retrievePriority); ASSERT_EQ(minRetrieveRequestAge, policy.retrieveMinRequestAge); + common::dataStructures::UserIdentity userIdentity; + userIdentity.name = requesterName; + userIdentity.group = "group"; const common::dataStructures::RetrieveFileQueueCriteria queueCriteria = m_catalogue->prepareToRetrieveFile(archiveFileId, userIdentity); @@ -1632,4 +2202,239 @@ TEST_F(cta_catalogue_InMemoryCatalogueTest, fileWrittenToTape_2_tape_files_corru ASSERT_THROW(m_catalogue->fileWrittenToTape(file2Written), exception::Exception); } +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveFile) { + using namespace cta; + + const std::string vid1 = "VID123"; + const std::string vid2 = "VID456"; + const std::string logicalLibraryName = "logical_library_name"; + const std::string tapePoolName = "tape_pool_name"; + const std::string encryptionKey = "encryption_key"; + const uint64_t capacityInBytes = (uint64_t)10 * 1000 * 1000 * 1000 * 1000; + const bool disabledValue = true; + const bool fullValue = false; + const std::string comment = "create tape"; + + m_catalogue->createLogicalLibrary(m_cliSI, logicalLibraryName, + "create logical library"); + m_catalogue->createTapePool(m_cliSI, tapePoolName, 2, true, "create tape pool"); + m_catalogue->createTape(m_cliSI, vid1, logicalLibraryName, tapePoolName, + encryptionKey, capacityInBytes, disabledValue, fullValue, + comment); + m_catalogue->createTape(m_cliSI, vid2, logicalLibraryName, tapePoolName, + encryptionKey, capacityInBytes, disabledValue, fullValue, + comment); + + const std::list<common::dataStructures::Tape> tapes = + m_catalogue->getTapes("", "", "", "", "", "", "", ""); + + ASSERT_EQ(2, tapes.size()); + + const std::map<std::string, common::dataStructures::Tape> vidToTape = tapeListToMap(tapes); + { + auto it = vidToTape.find(vid1); + const common::dataStructures::Tape &tape = it->second; + ASSERT_EQ(vid1, tape.vid); + ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName); + ASSERT_EQ(tapePoolName, tape.tapePoolName); + ASSERT_EQ(encryptionKey, tape.encryptionKey); + ASSERT_EQ(capacityInBytes, tape.capacityInBytes); + ASSERT_TRUE(disabledValue == tape.disabled); + ASSERT_TRUE(fullValue == tape.full); + ASSERT_EQ(comment, tape.comment); + + const common::dataStructures::EntryLog creationLog = tape.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + tape.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + { + auto it = vidToTape.find(vid2); + const common::dataStructures::Tape &tape = it->second; + ASSERT_EQ(vid2, tape.vid); + ASSERT_EQ(logicalLibraryName, tape.logicalLibraryName); + ASSERT_EQ(tapePoolName, tape.tapePoolName); + ASSERT_EQ(encryptionKey, tape.encryptionKey); + ASSERT_EQ(capacityInBytes, tape.capacityInBytes); + ASSERT_TRUE(disabledValue == tape.disabled); + ASSERT_TRUE(fullValue == tape.full); + ASSERT_EQ(comment, tape.comment); + + const common::dataStructures::EntryLog creationLog = tape.creationLog; + ASSERT_EQ(m_cliSI.username, creationLog.username); + ASSERT_EQ(m_cliSI.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = + tape.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + + const uint64_t archiveFileId = 1234; + + ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty()); + ASSERT_THROW(m_catalogue->getArchiveFileById(archiveFileId), exception::Exception); + + const std::string storageClassName = "storage_class"; + const uint64_t nbCopies = 2; + m_catalogue->createStorageClass(m_cliSI, storageClassName, nbCopies, "create storage class"); + + const uint64_t archiveFileSize = 1; + + catalogue::TapeFileWritten file1Written; + file1Written.archiveFileId = archiveFileId; + file1Written.diskInstance = "PUBLIC"; + file1Written.diskFileId = "5678"; + file1Written.diskFilePath = "/public_dir/public_file"; + file1Written.diskFileUser = "public_disk_user"; + file1Written.diskFileGroup = "public_disk_group"; + file1Written.diskFileRecoveryBlob = "opaque_disk_file_recovery_contents"; + file1Written.size = archiveFileSize; + file1Written.storageClassName = storageClassName; + file1Written.vid = vid1; + file1Written.fSeq = 1; + file1Written.blockId = 4321; + file1Written.compressedSize = 1; + file1Written.copyNb = 1; + m_catalogue->fileWrittenToTape(file1Written); + + { + std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes(file1Written.vid, "", "", "", "", "", "", ""); + ASSERT_EQ(1, tapes.size()); + const common::dataStructures::Tape &tape = tapes.front(); + ASSERT_EQ(1, tape.lastFSeq); + } + + { + const common::dataStructures::ArchiveFile archiveFile = m_catalogue->getArchiveFileById(archiveFileId); + + ASSERT_EQ(file1Written.archiveFileId, archiveFile.archiveFileID); + ASSERT_EQ(file1Written.diskFileId, archiveFile.diskFileID); + ASSERT_EQ(file1Written.size, archiveFile.fileSize); + ASSERT_EQ(file1Written.storageClassName, archiveFile.storageClass); + + ASSERT_EQ(file1Written.diskInstance, archiveFile.diskInstance); + ASSERT_EQ(file1Written.diskFilePath, archiveFile.drData.drPath); + ASSERT_EQ(file1Written.diskFileUser, archiveFile.drData.drOwner); + ASSERT_EQ(file1Written.diskFileGroup, archiveFile.drData.drGroup); + ASSERT_EQ(file1Written.diskFileRecoveryBlob, archiveFile.drData.drBlob); + + ASSERT_EQ(1, archiveFile.tapeFiles.size()); + auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1); + ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end()); + const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second; + ASSERT_EQ(file1Written.vid, tapeFile1.vid); + ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq); + ASSERT_EQ(file1Written.blockId, tapeFile1.blockId); + ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize); + ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb); + } + + catalogue::TapeFileWritten file2Written; + file2Written.archiveFileId = file1Written.archiveFileId; + file2Written.diskInstance = file1Written.diskInstance; + file2Written.diskFileId = file1Written.diskFileId; + file2Written.diskFilePath = file1Written.diskFilePath; + file2Written.diskFileUser = file1Written.diskFileUser; + file2Written.diskFileGroup = file1Written.diskFileGroup; + file2Written.diskFileRecoveryBlob = file1Written.diskFileRecoveryBlob; + file2Written.size = archiveFileSize; + file2Written.storageClassName = storageClassName; + file2Written.vid = vid2; + file2Written.fSeq = 1; + file2Written.blockId = 4331; + file2Written.compressedSize = 1; + file2Written.copyNb = 2; + m_catalogue->fileWrittenToTape(file2Written); + + { + ASSERT_EQ(2, m_catalogue->getTapes("", "", "", "", "", "", "", "").size()); + std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes(file2Written.vid, "", "", "", "", "", "", ""); + ASSERT_EQ(1, tapes.size()); + const common::dataStructures::Tape &tape = tapes.front(); + ASSERT_EQ(1, tape.lastFSeq); + } + + { + const common::dataStructures::ArchiveFile archiveFile = m_catalogue->getArchiveFileById(archiveFileId); + + ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID); + ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileID); + ASSERT_EQ(file2Written.size, archiveFile.fileSize); + ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass); + + ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance); + ASSERT_EQ(file2Written.diskFilePath, archiveFile.drData.drPath); + ASSERT_EQ(file2Written.diskFileUser, archiveFile.drData.drOwner); + ASSERT_EQ(file2Written.diskFileGroup, archiveFile.drData.drGroup); + ASSERT_EQ(file2Written.diskFileRecoveryBlob, archiveFile.drData.drBlob); + + ASSERT_EQ(2, archiveFile.tapeFiles.size()); + + auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1); + ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end()); + const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second; + ASSERT_EQ(file1Written.vid, tapeFile1.vid); + ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq); + ASSERT_EQ(file1Written.blockId, tapeFile1.blockId); + ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize); + ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb); + + auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2); + ASSERT_FALSE(copyNbToTapeFile2Itor == archiveFile.tapeFiles.end()); + const common::dataStructures::TapeFile &tapeFile2 = copyNbToTapeFile2Itor->second; + ASSERT_EQ(file2Written.vid, tapeFile2.vid); + ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq); + ASSERT_EQ(file2Written.blockId, tapeFile2.blockId); + ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize); + ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb); + } + + { + const common::dataStructures::ArchiveFile archiveFile = m_catalogue->deleteArchiveFile(archiveFileId); + + ASSERT_EQ(file2Written.archiveFileId, archiveFile.archiveFileID); + ASSERT_EQ(file2Written.diskFileId, archiveFile.diskFileID); + ASSERT_EQ(file2Written.size, archiveFile.fileSize); + ASSERT_EQ(file2Written.storageClassName, archiveFile.storageClass); + + ASSERT_EQ(file2Written.diskInstance, archiveFile.diskInstance); + ASSERT_EQ(file2Written.diskFilePath, archiveFile.drData.drPath); + ASSERT_EQ(file2Written.diskFileUser, archiveFile.drData.drOwner); + ASSERT_EQ(file2Written.diskFileGroup, archiveFile.drData.drGroup); + ASSERT_EQ(file2Written.diskFileRecoveryBlob, archiveFile.drData.drBlob); + + ASSERT_EQ(2, archiveFile.tapeFiles.size()); + + auto copyNbToTapeFile1Itor = archiveFile.tapeFiles.find(1); + ASSERT_FALSE(copyNbToTapeFile1Itor == archiveFile.tapeFiles.end()); + const common::dataStructures::TapeFile &tapeFile1 = copyNbToTapeFile1Itor->second; + ASSERT_EQ(file1Written.vid, tapeFile1.vid); + ASSERT_EQ(file1Written.fSeq, tapeFile1.fSeq); + ASSERT_EQ(file1Written.blockId, tapeFile1.blockId); + ASSERT_EQ(file1Written.compressedSize, tapeFile1.compressedSize); + ASSERT_EQ(file1Written.copyNb, tapeFile1.copyNb); + + auto copyNbToTapeFile2Itor = archiveFile.tapeFiles.find(2); + ASSERT_FALSE(copyNbToTapeFile2Itor == archiveFile.tapeFiles.end()); + const common::dataStructures::TapeFile &tapeFile2 = copyNbToTapeFile2Itor->second; + ASSERT_EQ(file2Written.vid, tapeFile2.vid); + ASSERT_EQ(file2Written.fSeq, tapeFile2.fSeq); + ASSERT_EQ(file2Written.blockId, tapeFile2.blockId); + ASSERT_EQ(file2Written.compressedSize, tapeFile2.compressedSize); + ASSERT_EQ(file2Written.copyNb, tapeFile2.copyNb); + } + + ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty()); +} + +TEST_F(cta_catalogue_InMemoryCatalogueTest, deleteArchiveFile_non_existant) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getArchiveFiles("", "", "", "", "", "", "", "", "").empty()); + ASSERT_THROW(m_catalogue->deleteArchiveFile(12345678), catalogue::UserError); +} + } // namespace unitTests diff --git a/catalogue/OcciStmt.cpp b/catalogue/OcciStmt.cpp index 432d1df19d..1f90f3fa49 100644 --- a/catalogue/OcciStmt.cpp +++ b/catalogue/OcciStmt.cpp @@ -143,6 +143,13 @@ void OcciStmt::executeNonQuery() { } } +//------------------------------------------------------------------------------ +// getNbAffectedRows +//------------------------------------------------------------------------------ +uint64_t OcciStmt::getNbAffectedRows() const { + throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); +} + //------------------------------------------------------------------------------ // get //------------------------------------------------------------------------------ diff --git a/catalogue/OcciStmt.hpp b/catalogue/OcciStmt.hpp index d355b67494..fcf76840bd 100644 --- a/catalogue/OcciStmt.hpp +++ b/catalogue/OcciStmt.hpp @@ -107,6 +107,14 @@ public: */ virtual void executeNonQuery(); + /** + * Returns the number of rows affected by the last execution of this + * statement. + * + * @return The number of affected rows. + */ + virtual uint64_t getNbAffectedRows() const; + /** * Returns the underlying OCCI result set. * diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp index d068a1f7fe..ebd8e198f0 100644 --- a/catalogue/OracleCatalogue.cpp +++ b/catalogue/OracleCatalogue.cpp @@ -40,6 +40,13 @@ OracleCatalogue::OracleCatalogue( OracleCatalogue::~OracleCatalogue() { } +//------------------------------------------------------------------------------ +// deleteArchiveFile +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFile OracleCatalogue::deleteArchiveFile(const uint64_t archiveFileId) { + throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); +} + //------------------------------------------------------------------------------ // getNextArchiveFileId //------------------------------------------------------------------------------ diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp index 0710ad4cd4..a785a4cbe7 100644 --- a/catalogue/OracleCatalogue.hpp +++ b/catalogue/OracleCatalogue.hpp @@ -58,6 +58,16 @@ public: */ virtual ~OracleCatalogue(); + /** + * Deletes the specified archive file and its associated tape copies from the + * catalogue. + * + * @param archiveFileId The unique identifier of the archive file. + * @return The metadata of the deleted archive file including the metadata of + * the associated and also deleted tape copies. + */ + virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId); + /** * Returns a unique archive ID that can be used by a new archive file within * the catalogue. diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index 32dee6abac..2dfab1c6ad 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -147,7 +147,20 @@ bool RdbmsCatalogue::adminUserExists(const std::string adminUsername) const { // deleteAdminUser //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteAdminUser(const std::string &username) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":ADMIN_USER_NAME", username); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -220,7 +233,7 @@ void RdbmsCatalogue::createAdminHost( const std::string &hostName, const std::string &comment) { try { - if (adminHostExists(hostName)) { + if(adminHostExists(hostName)) { throw UserError(std::string("Cannot create admin host " + hostName + " because an admin host with the same name already exists")); } @@ -297,7 +310,20 @@ bool RdbmsCatalogue::adminHostExists(const std::string adminHost) const { // deleteAdminHost //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteAdminHost(const std::string &hostName) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM ADMIN_HOST WHERE ADMIN_HOST_NAME = :ADMIN_HOST_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":ADMIN_HOST_NAME", hostName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete admin-host ") + hostName + " because it does not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -319,7 +345,7 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con "LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME," "LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME," "LAST_UPDATE_TIME AS LAST_UPDATE_TIME " - "FROM " + "FROM " "ADMIN_HOST"; std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); std::unique_ptr<DbRset> rset(stmt->executeQuery()); @@ -329,9 +355,6 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con host.name = rset->columnText("ADMIN_HOST_NAME"); host.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -339,9 +362,6 @@ std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() con host.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -375,7 +395,7 @@ void RdbmsCatalogue::createStorageClass( const uint64_t nbCopies, const std::string &comment) { try { - if (storageClassExists(name)) { + if(storageClassExists(name)) { throw UserError(std::string("Cannot create storage class ") + name + " because a storage class with the same name already exists"); } @@ -460,12 +480,17 @@ void RdbmsCatalogue::deleteStorageClass(const std::string &name) { "DELETE FROM " "STORAGE_CLASS " "WHERE " - "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME;"; std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); stmt->bindString(":STORAGE_CLASS_NAME", name); stmt->executeNonQuery(); + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete storage-class ") + name + " because it does not exist"); + } + } catch(UserError &) { + throw; } catch(exception::Exception &ex) { throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); } @@ -503,9 +528,6 @@ std::list<common::dataStructures::StorageClass> storageClass.nbCopies = rset->columnUint64("NB_COPIES"); storageClass.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -513,9 +535,6 @@ std::list<common::dataStructures::StorageClass> storageClass.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -639,7 +658,20 @@ bool RdbmsCatalogue::tapePoolExists(const std::string &tapePoolName) const { // deleteTapePool //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteTapePool(const std::string &name) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":TAPE_POOL_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -677,9 +709,6 @@ std::list<common::dataStructures::TapePool> pool.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -687,9 +716,6 @@ std::list<common::dataStructures::TapePool> pool.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -793,7 +819,29 @@ void RdbmsCatalogue::createArchiveRoute( // deleteArchiveRoute //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteArchiveRoute(const std::string &storageClassName, const uint64_t copyNb) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = + "DELETE FROM " + "ARCHIVE_ROUTE " + "WHERE " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND " + "COPY_NB = :COPY_NB;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + UserError ue; + ue.getMessage() << "Cannot delete archive route for storage-class " << storageClassName + " and copy number " << + copyNb << " because it does not exist"; + throw ue; + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -831,9 +879,6 @@ std::list<common::dataStructures::ArchiveRoute> route.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -841,9 +886,6 @@ std::list<common::dataStructures::ArchiveRoute> route.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -882,7 +924,7 @@ void RdbmsCatalogue::createLogicalLibrary( const std::string &name, const std::string &comment) { try { - if (logicalLibraryExists(name)) { + if(logicalLibraryExists(name)) { throw UserError(std::string("Cannot create logical library ") + name + " because a logical library with the same name already exists"); } @@ -959,7 +1001,20 @@ bool RdbmsCatalogue::logicalLibraryExists(const std::string &logicalLibraryName) // deleteLogicalLibrary //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteLogicalLibrary(const std::string &name) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM LOGICAL_LIBRARY WHERE LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":LOGICAL_LIBRARY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete logical-library ") + name + " because it does not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -993,9 +1048,6 @@ std::list<common::dataStructures::LogicalLibrary> lib.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -1003,9 +1055,6 @@ std::list<common::dataStructures::LogicalLibrary> lib.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -1043,7 +1092,7 @@ void RdbmsCatalogue::createTape( const bool fullValue, const std::string &comment) { try { - if (tapeExists(vid)) { + if(tapeExists(vid)) { throw UserError(std::string("Cannot create tape ") + vid + " because a tape with the same volume identifier already exists"); } @@ -1174,7 +1223,20 @@ bool RdbmsCatalogue::tapeExists(const std::string &vid) const { // deleteTape //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteTape(const std::string &vid) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM TAPE WHERE VID = :VID;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete tape ") + vid + " because it does not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -1306,9 +1368,6 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes( tape.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -1316,9 +1375,6 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes( tape.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -1431,10 +1487,14 @@ void RdbmsCatalogue::modifyTapeComment(const common::dataStructures::SecurityIde //------------------------------------------------------------------------------ void RdbmsCatalogue::createRequester( const common::dataStructures::SecurityIdentity &cliIdentity, - const common::dataStructures::UserIdentity &user, + const std::string &requesterName, const std::string &mountPolicy, const std::string &comment) { try { + if(requesterExists(requesterName)) { + throw UserError(std::string("Cannot create requester ") + requesterName + + " because a requester with the same username already exists"); + } const uint64_t now = time(NULL); const char *const sql = "INSERT INTO REQUESTER(" @@ -1465,7 +1525,7 @@ void RdbmsCatalogue::createRequester( ":LAST_UPDATE_TIME)"; std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); - stmt->bindString(":REQUESTER_NAME", user.name); + stmt->bindString(":REQUESTER_NAME", requesterName); stmt->bindString(":MOUNT_POLICY_NAME", mountPolicy); stmt->bindString(":USER_COMMENT", comment); @@ -1479,6 +1539,8 @@ void RdbmsCatalogue::createRequester( stmt->bindUint64(":LAST_UPDATE_TIME", now); stmt->executeNonQuery(); + } catch(UserError &) { + throw; } catch(exception::Exception &ex) { throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); } @@ -1487,8 +1549,21 @@ void RdbmsCatalogue::createRequester( //------------------------------------------------------------------------------ // deleteRequester //------------------------------------------------------------------------------ -void RdbmsCatalogue::deleteRequester(const common::dataStructures::UserIdentity &user) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); +void RdbmsCatalogue::deleteRequester(const std::string &requesterName) { + try { + const char *const sql = "DELETE FROM REQUESTER WHERE REQUESTER_NAME = :REQUESTER_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete requester ") + requesterName + " because they do not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -1525,9 +1600,6 @@ std::list<common::dataStructures::Requester> user.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); - common::dataStructures::EntryLog creationLog; creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); @@ -1535,9 +1607,6 @@ std::list<common::dataStructures::Requester> user.creationLog = creationLog; - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - common::dataStructures::EntryLog updateLog; updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); @@ -1581,7 +1650,7 @@ void RdbmsCatalogue::createMountPolicy( const uint64_t maxDrivesAllowed, const std::string &comment) { try { - if (mountPolicyExists(name)) { + if(mountPolicyExists(name)) { throw UserError(std::string("Cannot create mount policy ") + name + " because a mount policy with the same name already exists"); } @@ -1657,6 +1726,83 @@ void RdbmsCatalogue::createMountPolicy( } } +//------------------------------------------------------------------------------ +// assignMountPolicyToRequester +//------------------------------------------------------------------------------ +void RdbmsCatalogue::assignMountPolicyToRequester( + const common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &mountPolicyName, + const std::string &requesterName, + const std::string &comment) { + try { + std::unique_ptr<common::dataStructures::MountPolicy> requesterMountPolicy(getRequesterMountPolicy(requesterName)); + if(NULL != requesterMountPolicy.get()) { + throw UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester " + requesterName + + " because the requester is already assigned mount-policy " + requesterMountPolicy->name); + } + if(!mountPolicyExists(mountPolicyName)) { + throw UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester " + requesterName + + " because mount-policy " + mountPolicyName + " does not exist"); + } + const uint64_t now = time(NULL); + const char *const sql = + "INSERT INTO REQUESTER(" + "REQUESTER_NAME," + "MOUNT_POLICY_NAME," + + "USER_COMMENT," + + "CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME," + + "LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME)" + "VALUES(" + ":REQUESTER_NAME," + ":MOUNT_POLICY_NAME," + + ":USER_COMMENT," + + ":CREATION_LOG_USER_NAME," + ":CREATION_LOG_HOST_NAME," + ":CREATION_LOG_TIME," + + ":LAST_UPDATE_USER_NAME," + ":LAST_UPDATE_HOST_NAME," + ":LAST_UPDATE_TIME)"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicyName); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", cliIdentity.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", cliIdentity.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", cliIdentity.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", cliIdentity.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// assignMountPolicyToRequesterGroup +//------------------------------------------------------------------------------ +void RdbmsCatalogue::assignMountPolicyToRequesterGroup(const std::string &mountPolicyName, + const std::string &requesterGroupName) { + throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); +} + //------------------------------------------------------------------------------ // mountPolicyExists //------------------------------------------------------------------------------ @@ -1678,11 +1824,136 @@ bool RdbmsCatalogue::mountPolicyExists(const std::string &mountPolicyName) const } } +//------------------------------------------------------------------------------ +// requesterExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::requesterExists(const std::string &requesterName) const { + try { + const char *const sql = + "SELECT " + "REQUESTER_NAME AS REQUESTER_NAME " + "FROM " + "REQUESTER " + "WHERE " + "REQUESTER_NAME = :REQUESTER_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":REQUESTER_NAME", requesterName); + std::unique_ptr<DbRset> rset(stmt->executeQuery()); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getRequesterMountPolicy +//------------------------------------------------------------------------------ +common::dataStructures::MountPolicy *RdbmsCatalogue::getRequesterMountPolicy(const std::string &requesterName) const { + try { + const char *const sql = + "SELECT " + "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME," + + "MOUNT_POLICY.ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY," + "MOUNT_POLICY.ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE," + + "MOUNT_POLICY.RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY," + "MOUNT_POLICY.RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE," + + "MOUNT_POLICY.MAX_DRIVES_ALLOWED AS MAX_DRIVES_ALLOWED," + + "MOUNT_POLICY.USER_COMMENT AS USER_COMMENT," + + "MOUNT_POLICY.CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "MOUNT_POLICY.CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "MOUNT_POLICY.CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "MOUNT_POLICY.LAST_UPDATE_USER_NAME AS LAST_UPDATE_USER_NAME," + "MOUNT_POLICY.LAST_UPDATE_HOST_NAME AS LAST_UPDATE_HOST_NAME," + "MOUNT_POLICY.LAST_UPDATE_TIME AS LAST_UPDATE_TIME " + "FROM " + "MOUNT_POLICY " + "INNER JOIN " + "REQUESTER " + "ON " + "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER.MOUNT_POLICY_NAME " + "WHERE " + "REQUESTER.REQUESTER_NAME = :REQUESTER_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":REQUESTER_NAME", requesterName); + std::unique_ptr<DbRset> rset(stmt->executeQuery()); + if(rset->next()) { + std::unique_ptr<common::dataStructures::MountPolicy> policy(new common::dataStructures::MountPolicy); + + policy->name = rset->columnText("MOUNT_POLICY_NAME"); + + policy->archivePriority = rset->columnUint64("ARCHIVE_PRIORITY"); + policy->archiveMinRequestAge = rset->columnUint64("ARCHIVE_MIN_REQUEST_AGE"); + + policy->retrievePriority = rset->columnUint64("RETRIEVE_PRIORITY"); + policy->retrieveMinRequestAge = rset->columnUint64("RETRIEVE_MIN_REQUEST_AGE"); + + policy->maxDrivesAllowed = rset->columnUint64("MAX_DRIVES_ALLOWED"); + + policy->comment = rset->columnText("USER_COMMENT"); + + policy->creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); + policy->creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); + policy->creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + + common::dataStructures::EntryLog updateLog; + policy->lastModificationLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); + policy->lastModificationLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); + policy->lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + return policy.release(); + } else { + return NULL; + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// requesterGroupExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::requesterGroupExists(const std::string &requesterGroupName) const { + try { + const char *const sql = + "SELECT " + "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME " + "FROM " + "REQUESTER_GROUP " + "WHERE " + "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + std::unique_ptr<DbRset> rset(stmt->executeQuery()); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + //------------------------------------------------------------------------------ // deleteMountPolicy //------------------------------------------------------------------------------ void RdbmsCatalogue::deleteMountPolicy(const std::string &name) { - throw exception::Exception(std::string(__FUNCTION__) + " not implemented"); + try { + const char *const sql = "DELETE FROM MOUNT_POLICY WHERE MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw UserError(std::string("Cannot delete mount policy ") + name + " because it does not exist"); + } + } catch(UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } } //------------------------------------------------------------------------------ @@ -1723,38 +1994,22 @@ std::list<common::dataStructures::MountPolicy> policy.name = rset->columnText("MOUNT_POLICY_NAME"); policy.archivePriority = rset->columnUint64("ARCHIVE_PRIORITY"); - policy.archiveMinRequestAge = - rset->columnUint64("ARCHIVE_MIN_REQUEST_AGE"); + policy.archiveMinRequestAge = rset->columnUint64("ARCHIVE_MIN_REQUEST_AGE"); - policy.retrievePriority = - rset->columnUint64("RETRIEVE_PRIORITY"); - policy.retrieveMinRequestAge = - rset->columnUint64("RETRIEVE_MIN_REQUEST_AGE"); + policy.retrievePriority = rset->columnUint64("RETRIEVE_PRIORITY"); + policy.retrieveMinRequestAge = rset->columnUint64("RETRIEVE_MIN_REQUEST_AGE"); - policy.maxDrivesAllowed = - rset->columnUint64("MAX_DRIVES_ALLOWED"); + policy.maxDrivesAllowed = rset->columnUint64("MAX_DRIVES_ALLOWED"); policy.comment = rset->columnText("USER_COMMENT"); - common::dataStructures::UserIdentity creatorUI; - creatorUI.name = rset->columnText("CREATION_LOG_USER_NAME"); + policy.creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); + policy.creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); + policy.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); - common::dataStructures::EntryLog creationLog; - creationLog.username = rset->columnText("CREATION_LOG_USER_NAME"); - creationLog.host = rset->columnText("CREATION_LOG_HOST_NAME"); - creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); - - policy.creationLog = creationLog; - - common::dataStructures::UserIdentity updaterUI; - updaterUI.name = rset->columnText("LAST_UPDATE_USER_NAME"); - - common::dataStructures::EntryLog updateLog; - updateLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); - updateLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); - updateLog.time = rset->columnUint64("LAST_UPDATE_TIME"); - - policy.lastModificationLog = updateLog; + policy.lastModificationLog.username = rset->columnText("LAST_UPDATE_USER_NAME"); + policy.lastModificationLog.host = rset->columnText("LAST_UPDATE_HOST_NAME"); + policy.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); policies.push_back(policy); } @@ -2063,7 +2318,7 @@ common::dataStructures::ArchiveFile RdbmsCatalogue::getArchiveFileById(const uin std::unique_ptr<common::dataStructures::ArchiveFile> file(getArchiveFile(id)); // Throw an exception if the archive file does not exist - if (NULL == file.get()) { + if(NULL == file.get()) { exception::Exception ex; ex.getMessage() << "No such archive file with ID " << id; throw (ex); @@ -2107,7 +2362,7 @@ common::dataStructures::ArchiveFileQueueCriteria throw ex; } - common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user); + common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user.name); // Now that we have both the archive routes and the mount policy it's safe to // consume an archive file identifier @@ -2175,8 +2430,7 @@ uint64_t RdbmsCatalogue::getExpectedNbArchiveRoutes(const std::string &storageCl //------------------------------------------------------------------------------ // getArchiveMountPolicy //------------------------------------------------------------------------------ -common::dataStructures::MountPolicy RdbmsCatalogue:: - getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const { +common::dataStructures::MountPolicy RdbmsCatalogue::getMountPolicyForAUser(const std::string &username) const { const char *const sql = "SELECT " "MOUNT_POLICY.MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME," @@ -2204,7 +2458,7 @@ common::dataStructures::MountPolicy RdbmsCatalogue:: "WHERE " "REQUESTER.REQUESTER_NAME = :REQUESTER_NAME"; std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); - stmt->bindString(":REQUESTER_NAME", user.name); + stmt->bindString(":REQUESTER_NAME", username); std::unique_ptr<DbRset> rset(stmt->executeQuery()); if(rset->next()) { common::dataStructures::MountPolicy policy; @@ -2238,8 +2492,7 @@ common::dataStructures::MountPolicy RdbmsCatalogue:: return policy; } else { exception::Exception ex; - ex.getMessage() << "Failed to find a mount policy for user " << - user; + ex.getMessage() << "Failed to find a mount policy for user " << username; throw ex; } } @@ -2379,7 +2632,7 @@ common::dataStructures::RetrieveFileQueueCriteria RdbmsCatalogue::prepareToRetri const uint64_t archiveFileId, const common::dataStructures::UserIdentity &user) { const std::map<uint64_t, common::dataStructures::TapeFile> tapeFiles = getTapeFiles(archiveFileId); - const common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user); + const common::dataStructures::MountPolicy mountPolicy = getMountPolicyForAUser(user.name); return common::dataStructures::RetrieveFileQueueCriteria(tapeFiles, mountPolicy); } @@ -2424,6 +2677,15 @@ std::map<uint64_t, common::dataStructures::TapeFile> RdbmsCatalogue::getTapeFile } } +//------------------------------------------------------------------------------ +// getMountPolicies +//------------------------------------------------------------------------------ +RequesterAndGroupMountPolicies RdbmsCatalogue::getMountPolicies(const std::string &requesterName, + const std::string &requesterGroupName) { + RequesterAndGroupMountPolicies policies; + return policies; +} + //------------------------------------------------------------------------------ // isAdmin //------------------------------------------------------------------------------ diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index 3b669e096a..b206837e50 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -20,6 +20,7 @@ #include "catalogue/Catalogue.hpp" #include "catalogue/DbConn.hpp" +#include "catalogue/RequesterAndGroupMountPolicies.hpp" #include <memory> #include <mutex> @@ -118,8 +119,13 @@ public: virtual void setTapeLbp(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &vid, const bool lbpValue); // internal function (noCLI) virtual void modifyTapeComment(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &vid, const std::string &comment); - virtual void createRequester(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy, const std::string &comment); - virtual void deleteRequester(const common::dataStructures::UserIdentity &user); + virtual void createRequester( + const common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &requesterName, + const std::string &mountPolicy, + const std::string &comment); + + virtual void deleteRequester(const std::string &requesterName); virtual std::list<common::dataStructures::Requester> getRequesters() const; virtual void modifyRequesterMountPolicy(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &mountPolicy); virtual void modifyRequesterComment(const common::dataStructures::SecurityIdentity &cliIdentity, const common::dataStructures::UserIdentity &user, const std::string &comment); @@ -134,6 +140,35 @@ public: const uint64_t maxDrivesAllowed, const std::string &comment); + /** + * Assigns the specified mount policy to the specified requester. + * + * Please note that requester mount-policies overrule requester-group + * mount-policies. + * + * @param cliIdentity The user of the command-line tool. + * @param mountPolicyName The name of the mount policy. + * @param requesterName The name of the requester. + * @param comment Comment. + */ + virtual void assignMountPolicyToRequester( + const common::dataStructures::SecurityIdentity &cliIdentity, + const std::string &mountPolicyName, + const std::string &requesterName, + const std::string &comment); + + /** + * Assigns the specified mount policy to the specified requester group. + * + * Please note that requester mount-policies overrule requester-group + * mount-policies. + * + * @param mountPolicyName The name of the mount policy. + * @param requesterGrouprName The name of the requester group. + */ + virtual void assignMountPolicyToRequesterGroup(const std::string &mountPolicyName, + const std::string &requesterGroupName); + virtual void deleteMountPolicy(const std::string &name); virtual std::list<common::dataStructures::MountPolicy> getMountPolicies() const; virtual void modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &cliIdentity, const std::string &name, const uint64_t archivePriority); @@ -219,15 +254,15 @@ public: /** * Returns the mount policy for the specified end user. * - * @param user The name of the end user. - * @return The archive mount policy. + * @param username The name of the end user. + * @return The mount policy. */ - virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const common::dataStructures::UserIdentity &user) const; + virtual common::dataStructures::MountPolicy getMountPolicyForAUser(const std::string &username) const; /** * Returns true if the specified user has admin privileges. * - * @param cliIdentity The user. + * @param cliIdentity The user of the command-line tool. * @return True if the specified user has admin privileges.:w */ virtual bool isAdmin(const common::dataStructures::SecurityIdentity &cliIdentity) const; @@ -298,6 +333,30 @@ protected: */ bool mountPolicyExists(const std::string &mountPolicyName) const; + /** + * Returns true if the specified requester exists. + * + * @param requesterName The username of the requester. + * @return True if the requester exists. + */ + bool requesterExists(const std::string &requesterName) const; + + /** + * Returns the specified requester mount-policy or NULL if one does not exist. + * + * @param requesterName The name of the requester. + * @return The mount policy. + */ + common::dataStructures::MountPolicy *getRequesterMountPolicy(const std::string &requesterName) const; + + /** + * Returns true if the specified requester group exists. + * + * @param requesterGroupName The name of the requester group. + * @return True if the requester group exists. + */ + bool requesterGroupExists(const std::string &requesterGroupName) const; + /** * An RdbmsCatalogue specific method that inserts the specified row into the * ArchiveFile table. @@ -409,6 +468,16 @@ protected: */ std::map<uint64_t, common::dataStructures::TapeFile>getTapeFiles(const uint64_t archiveFileId) const; + /** + * Returns the mount policies for the specified requester and requester group. + * + * @param requesterName The name of the requester. + * @param requesterGroupName The name of the requester group. + * @return The mount policies. + */ + RequesterAndGroupMountPolicies getMountPolicies(const std::string &requesterName, + const std::string &requesterGroupName); + /** * Returns a unique archive ID that can be used by a new archive file within * the catalogue. diff --git a/catalogue/RequesterAndGroupMountPolicies.hpp b/catalogue/RequesterAndGroupMountPolicies.hpp new file mode 100644 index 0000000000..f5aff6aeac --- /dev/null +++ b/catalogue/RequesterAndGroupMountPolicies.hpp @@ -0,0 +1,47 @@ +/* + * The CERN Tape Archive (CTA) project + * Copyright (C) 2015 CERN + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#pragma once + +#include "common/dataStructures/MountPolicy.hpp" + +#include <list> + +namespace cta { +namespace catalogue { + +/** + * Structure containing a list of requester mount-polices and a list of + * requester-group mount-policies. + */ +struct RequesterAndGroupMountPolicies { + + /** + * List of requester mount-policies. + */ + std::list<common::dataStructures::MountPolicy> requesterMountPolicies; + + /** + * List of requester-group mount-policies. + */ + std::list<common::dataStructures::MountPolicy> requesterGroupMountPolicies; + +}; // struct RequesterAndGroupMountPolicies + +} // namespace catalogue +} // namespace cta diff --git a/catalogue/SqliteCatalogue.cpp b/catalogue/SqliteCatalogue.cpp index 3f16202984..eb7a799e4d 100644 --- a/catalogue/SqliteCatalogue.cpp +++ b/catalogue/SqliteCatalogue.cpp @@ -16,9 +16,11 @@ * along with this program. If not, see <http://www.gnu.org/licenses/>. */ -#include "catalogue/SqliteCatalogue.hpp" +#include "catalogue/AutoRollback.hpp" #include "catalogue/RdbmsCatalogueSchema.hpp" +#include "catalogue/SqliteCatalogue.hpp" #include "catalogue/SqliteConn.hpp" +#include "catalogue/UserError.hpp" #include "common/exception/Exception.hpp" namespace cta { @@ -44,12 +46,115 @@ SqliteCatalogue::SqliteCatalogue() { SqliteCatalogue::~SqliteCatalogue() { } +//------------------------------------------------------------------------------ +// deleteArchiveFile +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFile SqliteCatalogue::deleteArchiveFile(const uint64_t archiveFileId) { + try { + std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile; + + m_conn->executeNonQuery("BEGIN EXCLUSIVE;"); + const char *selectSql = + "SELECT " + "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID," + "ARCHIVE_FILE.DISK_INSTANCE AS DISK_INSTANCE," + "ARCHIVE_FILE.DISK_FILE_ID AS DISK_FILE_ID," + "ARCHIVE_FILE.DISK_FILE_PATH AS DISK_FILE_PATH," + "ARCHIVE_FILE.DISK_FILE_USER AS DISK_FILE_USER," + "ARCHIVE_FILE.DISK_FILE_GROUP AS DISK_FILE_GROUP," + "ARCHIVE_FILE.DISK_FILE_RECOVERY_BLOB AS DISK_FILE_RECOVERY_BLOB," + "ARCHIVE_FILE.FILE_SIZE AS FILE_SIZE," + "ARCHIVE_FILE.CHECKSUM_TYPE AS CHECKSUM_TYPE," + "ARCHIVE_FILE.CHECKSUM_VALUE AS CHECKSUM_VALUE," + "ARCHIVE_FILE.STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME," + "ARCHIVE_FILE.CREATION_TIME AS ARCHIVE_FILE_CREATION_TIME," + "ARCHIVE_FILE.RECONCILIATION_TIME AS RECONCILIATION_TIME," + "TAPE_FILE.VID AS VID," + "TAPE_FILE.FSEQ AS FSEQ," + "TAPE_FILE.BLOCK_ID AS BLOCK_ID," + "TAPE_FILE.COMPRESSED_SIZE AS COMPRESSED_SIZE," + "TAPE_FILE.COPY_NB AS COPY_NB," + "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME " + "FROM " + "ARCHIVE_FILE " + "LEFT OUTER JOIN TAPE_FILE ON " + "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID " + "WHERE " + "ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID"; + std::unique_ptr<DbStmt> selectStmt(m_conn->createStmt(selectSql)); + selectStmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + std::unique_ptr<DbRset> selectRset(selectStmt->executeQuery()); + while(selectRset->next()) { + if(NULL == archiveFile.get()) { + archiveFile.reset(new common::dataStructures::ArchiveFile); + + archiveFile->archiveFileID = selectRset->columnUint64("ARCHIVE_FILE_ID"); + archiveFile->diskInstance = selectRset->columnText("DISK_INSTANCE"); + archiveFile->diskFileID = selectRset->columnText("DISK_FILE_ID"); + archiveFile->drData.drPath = selectRset->columnText("DISK_FILE_PATH"); + archiveFile->drData.drOwner = selectRset->columnText("DISK_FILE_USER"); + archiveFile->drData.drGroup = selectRset->columnText("DISK_FILE_GROUP"); + archiveFile->drData.drBlob = selectRset->columnText("DISK_FILE_RECOVERY_BLOB"); + archiveFile->fileSize = selectRset->columnUint64("FILE_SIZE"); + archiveFile->checksumType = selectRset->columnText("CHECKSUM_TYPE"); + archiveFile->checksumValue = selectRset->columnText("CHECKSUM_VALUE"); + archiveFile->storageClass = selectRset->columnText("STORAGE_CLASS_NAME"); + archiveFile->creationTime = selectRset->columnUint64("ARCHIVE_FILE_CREATION_TIME"); + archiveFile->reconciliationTime = selectRset->columnUint64("RECONCILIATION_TIME"); + } + + // If there is a tape file + if(!selectRset->columnIsNull("VID")) { + // Add the tape file to the archive file's in-memory structure + common::dataStructures::TapeFile tapeFile; + tapeFile.vid = selectRset->columnText("VID"); + tapeFile.fSeq = selectRset->columnUint64("FSEQ"); + tapeFile.blockId = selectRset->columnUint64("BLOCK_ID"); + tapeFile.compressedSize = selectRset->columnUint64("COMPRESSED_SIZE"); + tapeFile.copyNb = selectRset->columnUint64("COPY_NB"); + tapeFile.creationTime = selectRset->columnUint64("TAPE_FILE_CREATION_TIME"); + archiveFile->tapeFiles[selectRset->columnUint64("COPY_NB")] = tapeFile; + } + } + + if(NULL == archiveFile.get()) { + UserError ue; + ue.getMessage() << "Failed to delete archive file with ID " << archiveFileId << " because it does not exist"; + throw ue; + } + + { + const char *const sql = "DELETE FROM TAPE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + stmt->executeNonQuery(); + } + + { + const char *const sql = "DELETE FROM ARCHIVE_FILE WHERE ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID;"; + std::unique_ptr<DbStmt> stmt(m_conn->createStmt(sql)); + stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + stmt->executeNonQuery(); + } + + m_conn->commit(); + + return *archiveFile; + } catch(UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + //------------------------------------------------------------------------------ // getNextArchiveFileId //------------------------------------------------------------------------------ uint64_t SqliteCatalogue::getNextArchiveFileId() { try { m_conn->executeNonQuery("BEGIN EXCLUSIVE;"); + AutoRollback autoRollback(m_conn.get()); + m_conn->executeNonQuery("UPDATE ARCHIVE_FILE_ID SET ID = ID + 1;"); uint64_t archiveFileId = 0; { diff --git a/catalogue/SqliteCatalogue.hpp b/catalogue/SqliteCatalogue.hpp index 526b08ec14..be79f94387 100644 --- a/catalogue/SqliteCatalogue.hpp +++ b/catalogue/SqliteCatalogue.hpp @@ -63,6 +63,16 @@ public: */ virtual ~SqliteCatalogue(); + /** + * Deletes the specified archive file and its associated tape copies from the + * catalogue. + * + * @param archiveFileId The unique identifier of the archive file. + * @return The metadata of the deleted archive file including the metadata of + * the associated and also deleted tape copies. + */ + virtual common::dataStructures::ArchiveFile deleteArchiveFile(const uint64_t archiveFileId); + protected: /** diff --git a/catalogue/SqliteConn.cpp b/catalogue/SqliteConn.cpp index 1114ffb837..013dee73f6 100644 --- a/catalogue/SqliteConn.cpp +++ b/catalogue/SqliteConn.cpp @@ -33,11 +33,24 @@ namespace catalogue { SqliteConn::SqliteConn(const std::string &filename) { try { m_conn = NULL; - if (sqlite3_open_v2(filename.c_str(), &m_conn, SQLITE_OPEN_READWRITE | SQLITE_OPEN_CREATE | SQLITE_OPEN_URI, NULL)) { + if(sqlite3_open_v2(filename.c_str(), &m_conn, SQLITE_OPEN_READWRITE|SQLITE_OPEN_CREATE|SQLITE_OPEN_URI, NULL)) { std::string msg = sqlite3_errmsg(m_conn); sqlite3_close(m_conn); throw exception::Exception(msg); } + { + char *errMsg = NULL; + if(SQLITE_OK != sqlite3_exec(m_conn, "PRAGMA foreign_keys = ON;", NULL, NULL, &errMsg)) { + exception::Exception ex; + ex.getMessage() << "Failed to to set PRAGMA foreign_keys = ON"; + if(NULL != errMsg) { + ex.getMessage() << ": " << errMsg; + sqlite3_free(errMsg); + } + sqlite3_close(m_conn); + throw ex; + } + } } catch(exception::Exception &ex) { throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); } @@ -78,7 +91,7 @@ DbStmt *SqliteConn::createStmt(const std::string &sql) { throw exception::Exception(msg); } - return new SqliteStmt(sql, stmt); + return new SqliteStmt(*this, sql, stmt); } catch(exception::Exception &ex) { throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + sql + ": " + ex.getMessage().str()); diff --git a/catalogue/SqliteConn.hpp b/catalogue/SqliteConn.hpp index 6ca4b1f1cf..931dfb1287 100644 --- a/catalogue/SqliteConn.hpp +++ b/catalogue/SqliteConn.hpp @@ -38,6 +38,12 @@ class SqliteStmt; class SqliteConn: public DbConn { public: + /** + * The SqliteStmt class can lock the m_mutex member of the SqliteConn class + * and it can read the pointer to the SQLite connection. + */ + friend SqliteStmt; + /** * Constructor. * diff --git a/catalogue/SqliteStmt.cpp b/catalogue/SqliteStmt.cpp index 7cb608e963..e1f466d09a 100644 --- a/catalogue/SqliteStmt.cpp +++ b/catalogue/SqliteStmt.cpp @@ -32,10 +32,12 @@ namespace catalogue { //------------------------------------------------------------------------------ // constructor //------------------------------------------------------------------------------ -SqliteStmt::SqliteStmt(const std::string &sql, sqlite3_stmt *const stmt): +SqliteStmt::SqliteStmt(SqliteConn &conn, const std::string &sql, sqlite3_stmt *const stmt): + m_conn(conn), m_sql(sql), m_paramNameToIdx(sql), - m_stmt(stmt) { + m_stmt(stmt), + m_nbAffectedRows(0) { if (NULL == stmt) { throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + sql + ": stmt is NULL"); @@ -116,6 +118,8 @@ DbRset *SqliteStmt::executeQuery() { // executeNonQuery //------------------------------------------------------------------------------ void SqliteStmt::executeNonQuery() { + std::lock_guard<std::mutex> connLock(m_conn.m_mutex); + const int stepRc = sqlite3_step(m_stmt); // Throw an exception if the call to sqlite3_step() failed @@ -124,6 +128,8 @@ void SqliteStmt::executeNonQuery() { Sqlite::rcToStr(stepRc)); } + m_nbAffectedRows = sqlite3_changes(m_conn.m_conn); + // Throw an exception if the SQL statement returned a result set if(SQLITE_ROW == stepRc) { throw exception::Exception(std::string(__FUNCTION__) + " failed for SQL statement " + getSql() + @@ -131,5 +137,12 @@ void SqliteStmt::executeNonQuery() { } } +//------------------------------------------------------------------------------ +// getNbAffectedRows +//------------------------------------------------------------------------------ +uint64_t SqliteStmt::getNbAffectedRows() const { + return m_nbAffectedRows; +} + } // namespace catalogue } // namespace cta diff --git a/catalogue/SqliteStmt.hpp b/catalogue/SqliteStmt.hpp index 3b3ba0cd49..2ce22d5149 100644 --- a/catalogue/SqliteStmt.hpp +++ b/catalogue/SqliteStmt.hpp @@ -41,11 +41,12 @@ public: /** * Constructor. - * + * + * @param conn The database connection. * @param sql The SQL statement. * @param stmt The prepared statement. */ - SqliteStmt(const std::string &sql, sqlite3_stmt *const stmt); + SqliteStmt(SqliteConn &conn, const std::string &sql, sqlite3_stmt *const stmt); /** * Destructor. @@ -103,6 +104,14 @@ public: */ virtual void executeNonQuery(); + /** + * Returns the number of rows affected by the last execution of this + * statement. + * + * @return The number of affected rows. + */ + virtual uint64_t getNbAffectedRows() const; + private: /** @@ -110,6 +119,11 @@ private: */ std::mutex m_mutex; + /** + * The SQL connection. + */ + SqliteConn &m_conn; + /** * The SQL statement. */ @@ -125,6 +139,11 @@ private: */ sqlite3_stmt *m_stmt; + /** + * The number of rows affected by the last execution of this statement. + */ + uint64_t m_nbAffectedRows; + }; // class SqlLiteStmt } // namespace catalogue diff --git a/catalogue/sqlite_catalogue_schema_header.sql b/catalogue/sqlite_catalogue_schema_header.sql index bbb9f67ad2..4bced1513c 100644 --- a/catalogue/sqlite_catalogue_schema_header.sql +++ b/catalogue/sqlite_catalogue_schema_header.sql @@ -1,4 +1,3 @@ -PRAGMA foreign_keys = ON; CREATE TABLE ARCHIVE_FILE_ID( ID INTEGER, CONSTRAINT ARCHIVE_FILE_ID_PK PRIMARY KEY(ID) diff --git a/catalogue/sqlite_catalogue_schema_trailer.sql b/catalogue/sqlite_catalogue_schema_trailer.sql new file mode 100644 index 0000000000..0be4ee2b60 --- /dev/null +++ b/catalogue/sqlite_catalogue_schema_trailer.sql @@ -0,0 +1,2 @@ +.quit + diff --git a/scheduler/SchedulerTest.cpp b/scheduler/SchedulerTest.cpp index 903a4d7fe5..45468a4738 100644 --- a/scheduler/SchedulerTest.cpp +++ b/scheduler/SchedulerTest.cpp @@ -169,9 +169,7 @@ TEST_P(SchedulerTest, archive_to_new_file) { const std::string userName = "user_name"; const std::string userGroup = "group"; cta::common::dataStructures::UserIdentity userIdentity; - userIdentity.name=userName; - userIdentity.group=userGroup; - catalogue.createRequester(s_adminOnAdminHost, userIdentity, mountPolicyName, userComment); + catalogue.createRequester(s_adminOnAdminHost, userName, mountPolicyName, userComment); std::list<common::dataStructures::Requester> users; users = catalogue.getRequesters(); diff --git a/xroot_plugins/XrdCtaFile.cpp b/xroot_plugins/XrdCtaFile.cpp index 03792bdc52..6f0227bc9f 100644 --- a/xroot_plugins/XrdCtaFile.cpp +++ b/xroot_plugins/XrdCtaFile.cpp @@ -1125,7 +1125,7 @@ int XrdCtaFile::xCom_user(const std::vector<std::string> &tokens, const cta::com m_rc = cta::common::dataStructures::FrontendReturnCode::userErrorNoRetry; return SFS_OK; } - m_catalogue->createRequester(cliIdentity, userIdentity, mountpolicy, comment); + m_catalogue->createRequester(cliIdentity, userIdentity.name, mountpolicy, comment); } else if("ch" == tokens[2]) { //ch std::string mountpolicy = getOptionValue(tokens, "-u", "--mountpolicy", false); @@ -1143,7 +1143,7 @@ int XrdCtaFile::xCom_user(const std::vector<std::string> &tokens, const cta::com } } else { //rm - m_catalogue->deleteRequester(userIdentity); + m_catalogue->deleteRequester(userIdentity.name); } } else if("ls" == tokens[2]) { //ls -- GitLab