diff --git a/objectstore_middletier/CMakeLists.txt b/objectstore_middletier/CMakeLists.txt index ebb62593ce2a9c55c8711d98906c252f2a1bbcc5..a84ae67f3e215379f6b4dc29b1fcc0c697d918f2 100644 --- a/objectstore_middletier/CMakeLists.txt +++ b/objectstore_middletier/CMakeLists.txt @@ -5,5 +5,15 @@ include_directories(${CMAKE_CURRENT_SOURCE_DIR}/../middletier) include_directories(${CMAKE_BINARY_DIR}) add_library (CTAObjectStoreMiddleTier - ObjectStoreMiddleTier.cpp + ObjectStoreMiddleTierAdmin.cpp ) + +set(MiddleTierUnitTests + MiddleTierAdminAbstractTest.cpp + MiddleTierTest.cpp +) + +add_executable(ObjectStoreMiddleTierUnitTests unit_tests.cpp ${MiddleTierUnitTests}) +target_link_libraries(ObjectStoreMiddleTierUnitTests + CTAObjectStoreMiddleTier + protobuf rados CTAObjectStore gtest gmock ctamiddletier) diff --git a/objectstore_middletier/MiddleTierAdminAbstractTest.cpp b/objectstore_middletier/MiddleTierAdminAbstractTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..9ff2a596226c4a82f597ccfbc2d5f9749a50af60 --- /dev/null +++ b/objectstore_middletier/MiddleTierAdminAbstractTest.cpp @@ -0,0 +1,715 @@ +/* + * 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/>. + */ + +#include "MiddleTierAdminAbstractTest.hpp" +#include "cta/SqliteMiddleTierAdmin.hpp" +#include "cta/SqliteMiddleTierUser.hpp" + +#include <gtest/gtest.h> +namespace unitTests { + +TEST_P(MiddleTierAdminAbstractTest, createStorageClass_new) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint16_t nbCopies = 2; + const std::string comment = "Comment"; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, name, nbCopies, + comment)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, + createStorageClass_already_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint16_t nbCopies = 2; + const std::string comment = "Comment"; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, name, nbCopies, comment)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } + + ASSERT_THROW(m_middleTier.admin->createStorageClass(requester, name, nbCopies, comment), + std::exception); +} + +TEST_P(MiddleTierAdminAbstractTest, + createStorageClass_lexicographical_order) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, "d", 1, "Comment d")); + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, "b", 1, "Comment b")); + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, "a", 1, "Comment a")); + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, "c", 1, "Comment c")); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(4, storageClasses.size()); + + ASSERT_EQ(std::string("a"), storageClasses.front().getName()); + storageClasses.pop_front(); + ASSERT_EQ(std::string("b"), storageClasses.front().getName()); + storageClasses.pop_front(); + ASSERT_EQ(std::string("c"), storageClasses.front().getName()); + storageClasses.pop_front(); + ASSERT_EQ(std::string("d"), storageClasses.front().getName()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, deleteStorageClass_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint16_t nbCopies = 2; + const std::string comment = "Comment"; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, name, nbCopies, comment)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + + ASSERT_NO_THROW(m_middleTier.admin->deleteStorageClass(requester, name)); + } + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, + deleteStorageClass_in_use_by_directory) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const uint16_t nbCopies = 2; + const std::string comment = "Comment"; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, nbCopies, + comment)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(storageClassName, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } + + ASSERT_NO_THROW(m_middleTier.user->setDirStorageClass(requester, "/", + storageClassName)); + + ASSERT_THROW(m_middleTier.admin->deleteStorageClass(requester, storageClassName), + std::exception); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(storageClassName, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } + + ASSERT_NO_THROW(m_middleTier.user->clearDirStorageClass(requester, "/")); + + ASSERT_NO_THROW(m_middleTier.admin->deleteStorageClass(requester, storageClassName)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, deleteStorageClass_in_use_by_route) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const uint16_t nbCopies = 2; + const std::string comment = "Comment"; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, nbCopies, + comment)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(storageClassName, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + { + std::list<TapePool> tapePools; + ASSERT_NO_THROW(tapePools = m_middleTier.admin->getTapePools(requester)); + ASSERT_EQ(1, tapePools.size()); + + TapePool tapePool; + ASSERT_NO_THROW(tapePool = tapePools.front()); + ASSERT_EQ(tapePoolName, tapePool.getName()); + } + + const uint16_t copyNb = 1; + ASSERT_NO_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } + + ASSERT_THROW(m_middleTier.admin->deleteStorageClass(requester, storageClassName), + std::exception); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(storageClassName, storageClass.getName()); + ASSERT_EQ(nbCopies, storageClass.getNbCopies()); + } + + ASSERT_NO_THROW(m_middleTier.admin->deleteArchivalRoute(requester, storageClassName, + copyNb)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + ASSERT_NO_THROW(m_middleTier.admin->deleteStorageClass(requester, storageClassName)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, deleteStorageClass_non_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + ASSERT_THROW(m_middleTier.admin->deleteStorageClass(requester, name), std::exception); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = m_middleTier.admin->getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, deleteTapePool_in_use) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const std::string comment = "Comment"; + { + const uint16_t nbCopies = 2; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, + nbCopies, comment)); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + const uint16_t copyNb = 1; + ASSERT_NO_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } + + ASSERT_THROW(m_middleTier.admin->deleteTapePool(requester, tapePoolName), std::exception); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, createArchivalRoute_new) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const std::string comment = "Comment"; + { + const uint16_t nbCopies = 2; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, + nbCopies, comment)); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + const uint16_t copyNb = 1; + ASSERT_NO_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, + createArchivalRoute_already_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const std::string comment = "Comment"; + { + const uint16_t nbCopies = 2; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, + nbCopies, comment)); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + const uint16_t copyNb = 1; + ASSERT_NO_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } + + ASSERT_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment), std::exception); +} + +TEST_P(MiddleTierAdminAbstractTest, deleteArchivalRoute_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const std::string comment = "Comment"; + { + const uint16_t nbCopies = 2; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, + nbCopies, comment)); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + const uint16_t copyNb = 1; + ASSERT_NO_THROW(m_middleTier.admin->createArchivalRoute(requester, storageClassName, + copyNb, tapePoolName, comment)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_EQ(1, archivalRoutes.size()); + + ArchivalRoute archivalRoute; + ASSERT_NO_THROW(archivalRoute = archivalRoutes.front()); + ASSERT_EQ(storageClassName, archivalRoute.getStorageClassName()); + ASSERT_EQ(copyNb, archivalRoute.getCopyNb()); + ASSERT_EQ(tapePoolName, archivalRoute.getTapePoolName()); + } + + ASSERT_NO_THROW(m_middleTier.admin->deleteArchivalRoute(requester, storageClassName, + copyNb)); + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, deleteArchivalRoute_non_existing) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<ArchivalRoute> archivalRoutes; + ASSERT_NO_THROW(archivalRoutes = m_middleTier.admin->getArchivalRoutes(requester)); + ASSERT_TRUE(archivalRoutes.empty()); + } + + const std::string storageClassName = "TestStorageClass"; + const std::string comment = "Comment"; + { + const uint16_t nbCopies = 2; + ASSERT_NO_THROW(m_middleTier.admin->createStorageClass(requester, storageClassName, + nbCopies, comment)); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + + const uint16_t copyNb = 1; + ASSERT_THROW(m_middleTier.admin->deleteArchivalRoute(requester, tapePoolName, copyNb), + std::exception); +} + +TEST_P(MiddleTierAdminAbstractTest, createTape_new) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<LogicalLibrary> libraries; + ASSERT_NO_THROW(libraries = m_middleTier.admin->getLogicalLibraries(requester)); + ASSERT_TRUE(libraries.empty()); + } + + { + std::list<TapePool> pools; + ASSERT_NO_THROW(pools = m_middleTier.admin->getTapePools(requester)); + ASSERT_TRUE(pools.empty()); + } + + { + std::list<Tape> tapes; + ASSERT_NO_THROW(tapes = m_middleTier.admin->getTapes(requester)); + ASSERT_TRUE(tapes.empty()); + } + + const std::string libraryName = "TestLogicalLibrary"; + const std::string libraryComment = "Library comment"; + ASSERT_NO_THROW(m_middleTier.admin->createLogicalLibrary(requester, libraryName, + libraryComment)); + { + std::list<LogicalLibrary> libraries; + ASSERT_NO_THROW(libraries = m_middleTier.admin->getLogicalLibraries(requester)); + ASSERT_EQ(1, libraries.size()); + + LogicalLibrary logicalLibrary; + ASSERT_NO_THROW(logicalLibrary = libraries.front()); + ASSERT_EQ(libraryName, logicalLibrary.getName()); + ASSERT_EQ(libraryComment, logicalLibrary.getComment()); + } + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + const std::string comment = "Tape pool omment"; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + { + std::list<TapePool> tapePools; + ASSERT_NO_THROW(tapePools = m_middleTier.admin->getTapePools(requester)); + ASSERT_EQ(1, tapePools.size()); + + TapePool tapePool; + ASSERT_NO_THROW(tapePool = tapePools.front()); + ASSERT_EQ(tapePoolName, tapePool.getName()); + ASSERT_EQ(comment, tapePool.getComment()); + } + + const std::string vid = "TestVid"; + const uint64_t capacityInBytes = 12345678; + const std::string tapeComment = "Tape comment"; + ASSERT_NO_THROW(m_middleTier.admin->createTape(requester, vid, libraryName, tapePoolName, + capacityInBytes, tapeComment)); + { + std::list<Tape> tapes; + ASSERT_NO_THROW(tapes = m_middleTier.admin->getTapes(requester)); + ASSERT_EQ(1, tapes.size()); + + Tape tape; + ASSERT_NO_THROW(tape = tapes.front()); + ASSERT_EQ(vid, tape.getVid()); + ASSERT_EQ(libraryName, tape.getLogicalLibraryName()); + ASSERT_EQ(tapePoolName, tape.getTapePoolName()); + ASSERT_EQ(capacityInBytes, tape.getCapacityInBytes()); + ASSERT_EQ(0, tape.getDataOnTapeInBytes()); + ASSERT_EQ(tapeComment, tape.getComment()); + } +} + +TEST_P(MiddleTierAdminAbstractTest, + createTape_new_non_existing_library) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<LogicalLibrary> libraries; + ASSERT_NO_THROW(libraries = m_middleTier.admin->getLogicalLibraries(requester)); + ASSERT_TRUE(libraries.empty()); + } + + { + std::list<TapePool> pools; + ASSERT_NO_THROW(pools = m_middleTier.admin->getTapePools(requester)); + ASSERT_TRUE(pools.empty()); + } + + { + std::list<Tape> tapes; + ASSERT_NO_THROW(tapes = m_middleTier.admin->getTapes(requester)); + ASSERT_TRUE(tapes.empty()); + } + + const std::string libraryName = "TestLogicalLibrary"; + + const std::string tapePoolName = "TestTapePool"; + const uint16_t nbPartialTapes = 1; + const std::string comment = "Tape pool omment"; + ASSERT_NO_THROW(m_middleTier.admin->createTapePool(requester, tapePoolName, + nbPartialTapes, comment)); + { + std::list<TapePool> tapePools; + ASSERT_NO_THROW(tapePools = m_middleTier.admin->getTapePools(requester)); + ASSERT_EQ(1, tapePools.size()); + + TapePool tapePool; + ASSERT_NO_THROW(tapePool = tapePools.front()); + ASSERT_EQ(tapePoolName, tapePool.getName()); + ASSERT_EQ(comment, tapePool.getComment()); + } + + const std::string vid = "TestVid"; + const uint64_t capacityInBytes = 12345678; + const std::string tapeComment = "Tape comment"; + ASSERT_THROW(m_middleTier.admin->createTape(requester, vid, libraryName, tapePoolName, + capacityInBytes, tapeComment), std::exception); +} + +TEST_P(MiddleTierAdminAbstractTest, createTape_new_non_existing_pool) { + using namespace cta; + + const SecurityIdentity requester; + + { + std::list<LogicalLibrary> libraries; + ASSERT_NO_THROW(libraries = m_middleTier.admin->getLogicalLibraries(requester)); + ASSERT_TRUE(libraries.empty()); + } + + { + std::list<TapePool> pools; + ASSERT_NO_THROW(pools = m_middleTier.admin->getTapePools(requester)); + ASSERT_TRUE(pools.empty()); + } + + { + std::list<Tape> tapes; + ASSERT_NO_THROW(tapes = m_middleTier.admin->getTapes(requester)); + ASSERT_TRUE(tapes.empty()); + } + + const std::string libraryName = "TestLogicalLibrary"; + const std::string libraryComment = "Library comment"; + ASSERT_NO_THROW(m_middleTier.admin->createLogicalLibrary(requester, libraryName, + libraryComment)); + { + std::list<LogicalLibrary> libraries; + ASSERT_NO_THROW(libraries = m_middleTier.admin->getLogicalLibraries(requester)); + ASSERT_EQ(1, libraries.size()); + + LogicalLibrary logicalLibrary; + ASSERT_NO_THROW(logicalLibrary = libraries.front()); + ASSERT_EQ(libraryName, logicalLibrary.getName()); + ASSERT_EQ(libraryComment, logicalLibrary.getComment()); + } + + const std::string tapePoolName = "TestTapePool"; + + const std::string vid = "TestVid"; + const uint64_t capacityInBytes = 12345678; + const std::string tapeComment = "Tape comment"; + ASSERT_THROW(m_middleTier.admin->createTape(requester, vid, libraryName, tapePoolName, + capacityInBytes, tapeComment), std::exception); +} + +} // namespace unitTests diff --git a/objectstore_middletier/MiddleTierAdminAbstractTest.hpp b/objectstore_middletier/MiddleTierAdminAbstractTest.hpp new file mode 100644 index 0000000000000000000000000000000000000000..3097dba548244ab1a21634087944164523383b67 --- /dev/null +++ b/objectstore_middletier/MiddleTierAdminAbstractTest.hpp @@ -0,0 +1,28 @@ +#pragma once + +#include <gtest/gtest.h> +#include "cta/MiddleTierAdmin.hpp" +#include "cta/MiddleTierUser.hpp" + +namespace unitTests { + + class MiddleTierFull { + public: + cta::MiddleTierAdmin * admin; + cta::MiddleTierUser * user; + MiddleTierFull(cta::MiddleTierAdmin * a, cta::MiddleTierUser * u): + admin(a), user(u) {} + MiddleTierFull(): admin(NULL), user(NULL) {} + }; + +class MiddleTierAdminAbstractTest: public ::testing::TestWithParam<MiddleTierFull> { +protected: + MiddleTierAdminAbstractTest() {} + virtual void SetUp() { + m_middleTier = GetParam(); + } + MiddleTierFull m_middleTier; +}; + +} + diff --git a/objectstore_middletier/MiddleTierTest.cpp b/objectstore_middletier/MiddleTierTest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0970ca342f4c2cda72f07937350e6f8a13220b85 --- /dev/null +++ b/objectstore_middletier/MiddleTierTest.cpp @@ -0,0 +1,55 @@ +/* + * 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/>. + */ + +#include "MiddleTierAdminAbstractTest.hpp" +#include "ObjectStoreMiddleTierAdmin.hpp" +#include "objectstore/BackendRados.hpp" +#include "objectstore/BackendVFS.hpp" +#include "middletier/cta/SqliteMiddleTierAdmin.hpp" + + +#define TEST_RADOS 0 +#define TEST_VFS 0 +#define TEST_SQL 1 + +namespace unitTests { + +#if TEST_RADOS +cta::objectstore::BackendRados osRados("tapetest", "tapetest"); +cta::OStoreMiddleTierAdmin mtaRados(osRados); +unitTests::MiddleTierFull middleTierRados; +middleTierRados.admin = &mtaRados; +middleTierRados.user = NULL; + +INSTANTIATE_TEST_CASE_P(MiddleTierRados, MiddleTierAdminAbstractTest , ::testing::Values(middleTierRados)); +#endif + +#if TEST_VFS +cta::objectstore::BackendVFS osVFS; + +#endif + +#if TEST_SQL +cta::SqliteDatabase sqlite; +cta::Vfs vfs; +cta::SqliteMiddleTierAdmin mtaSQL(vfs, sqlite); +MiddleTierFull mtfSQL(&mtaSQL, NULL); +INSTANTIATE_TEST_CASE_P(MiddleTierSQL, MiddleTierAdminAbstractTest, ::testing::Values(mtfSQL)); +#endif + +} \ No newline at end of file diff --git a/objectstore_middletier/ObjectStoreMiddleTierAdmin.cpp b/objectstore_middletier/ObjectStoreMiddleTierAdmin.cpp new file mode 100644 index 0000000000000000000000000000000000000000..47eb1317f6bfe4848536faad409227decc2e3d5a --- /dev/null +++ b/objectstore_middletier/ObjectStoreMiddleTierAdmin.cpp @@ -0,0 +1,65 @@ +/* + * 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/>. + */ + +#include "cta/Exception.hpp" +#include "ObjectStoreMiddleTierAdmin.hpp" +#include "objectstore/Backend.hpp" +#include "objectstore/RootEntry.hpp" +#include "objectstore/AdminUsersList.hpp" + +namespace cta { + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +OStoreMiddleTierAdmin::OStoreMiddleTierAdmin(objectstore::Backend& backend): + m_backend(backend) { + // check that we can at least access the root entry + objectstore::RootEntry re(m_backend); + objectstore::ScopedSharedLock reLock(re); + re.fetch(); +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +OStoreMiddleTierAdmin::~OStoreMiddleTierAdmin() throw() { +} + +//------------------------------------------------------------------------------ +// createAdminUser +//------------------------------------------------------------------------------ +void OStoreMiddleTierAdmin::createAdminUser( + const SecurityIdentity &requester, + const UserIdentity &user, + const std::string &comment) { + // Authz is not handled in this layer. We hence store the new admin user + // without checks. + objectstore::RootEntry re(m_backend); + objectstore::ScopedSharedLock reLock(re); + re.fetch(); + objectstore::AdminUsersList aul(re.getAdminUsersList(), m_backend); + reLock.release(); + objectstore::ScopedExclusiveLock aulLock(aul); + aul.fetch(); + AdminUser au(user, requester.user, comment); + aul.add(au); + aul.commit(); +} + +} diff --git a/objectstore_middletier/ObjectStoreMiddleTierAdmin.hpp b/objectstore_middletier/ObjectStoreMiddleTierAdmin.hpp new file mode 100644 index 0000000000000000000000000000000000000000..80f4a7a11078f360c74b0bafc23603944407bc2e --- /dev/null +++ b/objectstore_middletier/ObjectStoreMiddleTierAdmin.hpp @@ -0,0 +1,311 @@ +/* + * 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 "cta/MiddleTierAdmin.hpp" + +namespace cta { + +namespace objectstore { + class Backend; +} + +/** + * The administration API of the the middle-tier. + * ObjectStore based implementation + */ +class OStoreMiddleTierAdmin: public MiddleTierAdmin { +public: + /** + * Contructor + */ + OStoreMiddleTierAdmin(objectstore::Backend & backend); + + /** + * Destructor + */ + virtual ~OStoreMiddleTierAdmin() throw(); + + /** + * Creates the specified administrator. + * + * @param requester The identity of the user requesting the creation of the + * administrator. + * @param user The identity of the administrator. + * @param comment The comment describing the sministrator. + */ + virtual void createAdminUser( + const SecurityIdentity &requester, + const UserIdentity &user, + const std::string &comment); + + /** + * Deletes the specified administrator. + * + * @param requester The identity of the user requesting the deletion of the + * administrator. + * @param user The identity of the administrator. + */ + virtual void deleteAdminUser( + const SecurityIdentity &requester, + const UserIdentity &user); + + /** + * Returns the current list of administrators in lexicographical order. + * + * @param requester The identity of the user requesting the list. + * @return The current list of administrators in lexicographical order. + */ + virtual std::list<AdminUser> getAdminUsers(const SecurityIdentity &requester) + const; + + /** + * Creates the specified administration host. + * + * @param requester The identity of the user requesting the creation of the + * administration host. + * @param hostName The network name of the administration host. + * @param comment The comment describing the administration host. + */ + virtual void createAdminHost( + const SecurityIdentity &requester, + const std::string &hostName, + const std::string &comment); + + /** + * Deletes the specified administration host. + * + * @param requester The identity of the user requesting the deletion of the + * administration host. + * @param hostName The network name of the administration host. + * @param comment The comment describing the administration host. + */ + virtual void deleteAdminHost( + const SecurityIdentity &requester, + const std::string &hostName); + + /** + * Returns the current list of administration hosts in lexicographical order. + * + * @param requester The identity of the user requesting the list. + * @return The current list of administration hosts in lexicographical order. + */ + virtual std::list<AdminHost> getAdminHosts(const SecurityIdentity &requester) + const; + + /** + * Creates the specified storage class. + * + * @param requester The identity of the user requesting the creation of the + * storage class. + * @param name The name of the storage class. + * @param nbCopies The number of copies a file associated with this storage + * class should have on tape. + * @param comment The comment describing the storage class. + */ + virtual void createStorageClass( + const SecurityIdentity &requester, + const std::string &name, + const uint8_t nbCopies, + const std::string &comment); + + /** + * Deletes the specified storage class. + * + * @param requester The identity of the user requesting the deletion of the + * storage class. + * @param name The name of the storage class. + */ + virtual void deleteStorageClass( + const SecurityIdentity &requester, + const std::string &name); + + /** + * Gets the current list of storage classes in lexicographical order. + * + * @param requester The identity of the user requesting the list. + * @return The current list of storage classes in lexicographical order. + */ + virtual std::list<StorageClass> getStorageClasses( + const SecurityIdentity &requester) const; + + /** + * Creates a tape pool with the specifed name. + * + * @param requester The identity of the user requesting the creation of the + * tape pool. + * @param name The name of the tape pool. + * @param nbDrives The maximum number of drives that can be concurrently + * assigned to this pool independent of whether they are archiving or + * retrieving files. + * @param nbPartialTapes The maximum number of tapes that can be partially + * full at any moment in time. + * @param comment The comment describing the tape pool. + */ + virtual void createTapePool( + const SecurityIdentity &requester, + const std::string &name, + const uint16_t nbDrives, + const uint32_t nbPartialTapes, + const std::string &comment); + + /** + * Delete the tape pool with the specifed name. + * + * @param requester The identity of the user requesting the deletion of the + * tape pool. + * @param name The name of the tape pool. + */ + virtual void deleteTapePool( + const SecurityIdentity &requester, + const std::string &name); + + /** + * Gets the current list of tape pools in lexicographical order. + * + * @param requester The identity of the user requesting the list. + * @return The current list of tape pools in lexicographical order. + */ + virtual std::list<TapePool> getTapePools( + const SecurityIdentity &requester) const; + + /** + * Creates the specified archive route. + * + * @param requester The identity of the user requesting the creation of the + * archive route. + * @param storageClassName The name of the storage class that identifies the + * source disk files. + * @param copyNb The tape copy number. + * @param tapePoolName The name of the destination tape pool. + * @param comment The comment describing the archive route. + */ + virtual void createArchivalRoute( + const SecurityIdentity &requester, + const std::string &storageClassName, + const uint8_t copyNb, + const std::string &tapePoolName, + const std::string &comment); + + /** + * Deletes the specified archive route. + * + * @param requester The identity of the user requesting the deletion of the + * archive route. + * @param storageClassName The name of the storage class that identifies the + * source disk files. + * @param copyNb The tape copy number. + */ + virtual void deleteArchivalRoute( + const SecurityIdentity &requester, + const std::string &storageClassName, + const uint8_t copyNb); + + /** + * Gets the current list of archive routes. + * + * @param requester The identity of the user requesting the list. + */ + virtual std::list<ArchivalRoute> getArchivalRoutes( + const SecurityIdentity &requester) const; + + /** + * Creates a logical library with the specified name. + * + * @param requester The identity of the user requesting the creation of the + * logical library. + * @param name The name of the logical library. + * @param comment The comment describing the logical library. + */ + virtual void createLogicalLibrary( + const SecurityIdentity &requester, + const std::string &name, + const std::string &comment); + + /** + * Deletes the logical library with the specified name. + * + * @param requester The identity of the user requesting the deletion of the + * logical library. + * @param name The name of the logical library. + */ + virtual void deleteLogicalLibrary( + const SecurityIdentity &requester, + const std::string &name); + + /** + * Returns the current list of libraries in lexicographical order. + * + * @param requester The identity of the user requesting the list. + * @return The current list of libraries in lexicographical order. + */ + virtual std::list<LogicalLibrary> getLogicalLibraries( + const SecurityIdentity &requester) const; + + /** + * Creates a tape. + * + * @param requester The identity of the user requesting the creation of the + * tape. + * @param vid The volume identifier of the tape. + * @param logicalLibrary The name of the logical library to which the tape + * belongs. + * @param tapePoolName The name of the tape pool to which the tape belongs. + * @param capacityInBytes The capacity of the tape. + * @param comment The comment describing the logical library. + */ + virtual void createTape( + const SecurityIdentity &requester, + const std::string &vid, + const std::string &logicalLibraryName, + const std::string &tapePoolName, + const uint64_t capacityInBytes, + const std::string &comment); + + /** + * Deletes the tape with the specified volume identifier. + * + * @param requester The identity of the user requesting the deletion of the + * tape. + * @param vid The volume identifier of the tape. + */ + virtual void deleteTape( + const SecurityIdentity &requester, + const std::string &vid); + + /** + * Returns the current list of tapes in the lexicographical order of their + * volume identifiers. + * + * @param requester The identity of the user requesting the list. + * @return The current list of tapes in the lexicographical order of their + * volume identifiers. + */ + virtual std::list<Tape> getTapes( + const SecurityIdentity &requester) const; + +private: + /** + * Reference to the backend used for storing objects + */ + objectstore::Backend & m_backend; +}; + +} + diff --git a/objectstore_middletier/unit_tests.cpp b/objectstore_middletier/unit_tests.cpp new file mode 100644 index 0000000000000000000000000000000000000000..602c1b51ec664c2e41a52303d28dc91e1a06f0c2 --- /dev/null +++ b/objectstore_middletier/unit_tests.cpp @@ -0,0 +1,37 @@ +/* + * 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/>. + */ + +#include <gtest/gtest.h> +#include <gmock/gmock.h> + +int main(int argc, char** argv) { + // The following line must be executed to initialize Google Mock + // (and Google Test) before running the tests. + ::testing::InitGoogleMock(&argc, argv); + int ret = RUN_ALL_TESTS(); + + // Close standard in, out and error so that valgrind can be used with the + // following command-line to track open file-descriptors: + // + // valgrind --track-fds=yes + close(0); + close(1); + close(2); + + return ret; +}