diff --git a/catalogue/CMakeLists.txt b/catalogue/CMakeLists.txt index b46717c56fa40107568986b89929423588cee423..dffdef2ef4d3f2b3527988b6c9634a094fa0f836 100644 --- a/catalogue/CMakeLists.txt +++ b/catalogue/CMakeLists.txt @@ -58,6 +58,7 @@ set (CATALOGUE_LIB_SRC_FILES UserSpecifiedAnEmptyStringLogicalLibraryName.cpp UserSpecifiedAnEmptyStringMediaType.cpp UserSpecifiedAnEmptyStringStorageClassName.cpp + UserSpecifiedAnEmptyStringSupply.cpp UserSpecifiedAnEmptyStringTapePoolName.cpp UserSpecifiedAnEmptyStringUsername.cpp UserSpecifiedAnEmptyStringVendor.cpp diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp index 19184a28376c170036944fefc1410d612985614b..1294bdf63cd0664799e8323a640e16d59e8231a8 100644 --- a/catalogue/Catalogue.hpp +++ b/catalogue/Catalogue.hpp @@ -246,6 +246,7 @@ public: virtual void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbPartialTapes) = 0; virtual void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) = 0; virtual void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool encryptionValue) = 0; + virtual void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &supply) = 0; virtual void createArchiveRoute( const common::dataStructures::SecurityIdentity &admin, diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp index 98e56ada30b8b4c9543661f33c77c7aec8f1f4c9..9441b90b4588e76f11c218d98fa782ef821e3908 100644 --- a/catalogue/CatalogueRetryWrapper.hpp +++ b/catalogue/CatalogueRetryWrapper.hpp @@ -165,6 +165,10 @@ public: return retryOnLostConnection(m_log, [&]{return m_catalogue->setTapePoolEncryption(admin, name, encryptionValue);}, m_maxTriesToConnect); } + void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &supply) override { + return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyTapePoolSupply(admin, name, supply);}, m_maxTriesToConnect); + } + void createArchiveRoute(const common::dataStructures::SecurityIdentity &admin, const std::string &diskInstanceName, const std::string &storageClassName, const uint32_t copyNb, const std::string &tapePoolName, const std::string &comment) override { return retryOnLostConnection(m_log, [&]{return m_catalogue->createArchiveRoute(admin, diskInstanceName, storageClassName, copyNb, tapePoolName, comment);}, m_maxTriesToConnect); } diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp index efb9c670d00422e2a2f2b6248e8b36280f0c25c3..c4ee97d352ef80f27f88eb56d36a5b2b72ce7b4f 100644 --- a/catalogue/CatalogueTest.cpp +++ b/catalogue/CatalogueTest.cpp @@ -28,6 +28,7 @@ #include "catalogue/UserSpecifiedAnEmptyStringLogicalLibraryName.hpp" #include "catalogue/UserSpecifiedAnEmptyStringMediaType.hpp" #include "catalogue/UserSpecifiedAnEmptyStringStorageClassName.hpp" +#include "catalogue/UserSpecifiedAnEmptyStringSupply.hpp" #include "catalogue/UserSpecifiedAnEmptyStringTapePoolName.hpp" #include "catalogue/UserSpecifiedAnEmptyStringUsername.hpp" #include "catalogue/UserSpecifiedAnEmptyStringVendor.hpp" @@ -1470,6 +1471,139 @@ TEST_P(cta_catalogue_CatalogueTest, setTapePoolEncryption_nonExistentTapePool) { ASSERT_THROW(m_catalogue->setTapePoolEncryption(m_admin, tapePoolName, isEncrypted), exception::UserError); } +TEST_P(cta_catalogue_CatalogueTest, modifyTapePoolSupply) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + + const std::string tapePoolName = "tape_pool"; + const std::string vo = "vo"; + const uint64_t nbPartialTapes = 2; + const bool isEncrypted = true; + const cta::optional<std::string> supply("value for the supply pool mechanism"); + const std::string comment = "Create tape pool"; + m_catalogue->createTapePool(m_admin, tapePoolName, vo, nbPartialTapes, isEncrypted, supply, comment); + + { + const auto pools = m_catalogue->getTapePools(); + + ASSERT_EQ(1, pools.size()); + + const auto &pool = pools.front(); + ASSERT_EQ(tapePoolName, pool.name); + ASSERT_EQ(vo, pool.vo); + ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes); + ASSERT_EQ(isEncrypted, pool.encryption); + ASSERT_TRUE((bool)supply); + ASSERT_EQ(supply.value(), pool.supply.value()); + ASSERT_EQ(supply, pool.supply); + ASSERT_EQ(0, pool.nbTapes); + ASSERT_EQ(0, pool.capacityBytes); + ASSERT_EQ(0, pool.dataBytes); + ASSERT_EQ(0, pool.nbPhysicalFiles); + ASSERT_EQ(comment, pool.comment); + + const common::dataStructures::EntryLog creationLog = pool.creationLog; + ASSERT_EQ(m_admin.username, creationLog.username); + ASSERT_EQ(m_admin.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + + const std::string modifiedSupply("Modified supply"); + m_catalogue->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply); + + { + const auto pools = m_catalogue->getTapePools(); + + ASSERT_EQ(1, pools.size()); + + const auto &pool = pools.front(); + ASSERT_EQ(tapePoolName, pool.name); + ASSERT_EQ(vo, pool.vo); + ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes); + ASSERT_EQ(isEncrypted, pool.encryption); + ASSERT_TRUE((bool)supply); + ASSERT_EQ(modifiedSupply, pool.supply.value()); + ASSERT_EQ(0, pool.nbTapes); + ASSERT_EQ(0, pool.capacityBytes); + ASSERT_EQ(0, pool.dataBytes); + ASSERT_EQ(0, pool.nbPhysicalFiles); + ASSERT_EQ(comment, pool.comment); + + const common::dataStructures::EntryLog creationLog = pool.creationLog; + ASSERT_EQ(m_admin.username, creationLog.username); + ASSERT_EQ(m_admin.host, creationLog.host); + } +} + +TEST_P(cta_catalogue_CatalogueTest, modifyTapePoolSupply_emptyStringTapePoolName) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + + const std::string tapePoolName = ""; + const std::string modifiedSupply = "Modified supply"; + ASSERT_THROW(m_catalogue->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply), + catalogue::UserSpecifiedAnEmptyStringTapePoolName); +} + +TEST_P(cta_catalogue_CatalogueTest, modifyTapePoolSupply_emptyStringSupply) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + + const std::string tapePoolName = "tape_pool"; + const std::string vo = "vo"; + const uint64_t nbPartialTapes = 2; + const bool isEncrypted = true; + const cta::optional<std::string> supply("value for the supply pool mechanism"); + const std::string comment = "Create tape pool"; + m_catalogue->createTapePool(m_admin, tapePoolName, vo, nbPartialTapes, isEncrypted, supply, comment); + + { + const auto pools = m_catalogue->getTapePools(); + + ASSERT_EQ(1, pools.size()); + + const auto &pool = pools.front(); + ASSERT_EQ(tapePoolName, pool.name); + ASSERT_EQ(vo, pool.vo); + ASSERT_EQ(nbPartialTapes, pool.nbPartialTapes); + ASSERT_EQ(isEncrypted, pool.encryption); + ASSERT_TRUE((bool)supply); + ASSERT_EQ(supply.value(), pool.supply.value()); + ASSERT_EQ(supply, pool.supply); + ASSERT_EQ(0, pool.nbTapes); + ASSERT_EQ(0, pool.capacityBytes); + ASSERT_EQ(0, pool.dataBytes); + ASSERT_EQ(0, pool.nbPhysicalFiles); + ASSERT_EQ(comment, pool.comment); + + const common::dataStructures::EntryLog creationLog = pool.creationLog; + ASSERT_EQ(m_admin.username, creationLog.username); + ASSERT_EQ(m_admin.host, creationLog.host); + + const common::dataStructures::EntryLog lastModificationLog = pool.lastModificationLog; + ASSERT_EQ(creationLog, lastModificationLog); + } + + const std::string modifiedSupply = ""; + ASSERT_THROW(m_catalogue->modifyTapePoolSupply(m_admin, tapePoolName, modifiedSupply), + catalogue::UserSpecifiedAnEmptyStringSupply); +} + +TEST_P(cta_catalogue_CatalogueTest, modifyTapePoolSupply_nonExistentTapePool) { + using namespace cta; + + ASSERT_TRUE(m_catalogue->getTapePools().empty()); + + const std::string tapePoolName = "tape_pool"; + const std::string supply = "value for the supply pool mechanism"; + ASSERT_THROW(m_catalogue->modifyTapePoolSupply(m_admin, tapePoolName, supply), exception::UserError); +} + TEST_P(cta_catalogue_CatalogueTest, createArchiveRoute) { using namespace cta; diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp index 69ab14469c10a676e01a8ac4ef72e4b24051882f..289454feacd4f7d792c43a8aead0632629b2cccd 100644 --- a/catalogue/DummyCatalogue.hpp +++ b/catalogue/DummyCatalogue.hpp @@ -97,6 +97,7 @@ public: void modifyTapePoolComment(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void modifyTapePoolVo(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbPartialTapes) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } + void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const std::string& supply) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& tapePoolName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void noSpaceLeftOnTape(const std::string& vid) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } void ping() override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); } diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp index a2b8ddbecd704d1c538e8a73e12d434788d0f5b5..92bb7b1b8b54ee960f81cf9a32f0e931b48d90ea 100644 --- a/catalogue/RdbmsCatalogue.cpp +++ b/catalogue/RdbmsCatalogue.cpp @@ -29,6 +29,7 @@ #include "catalogue/UserSpecifiedAnEmptyStringLogicalLibraryName.hpp" #include "catalogue/UserSpecifiedAnEmptyStringMediaType.hpp" #include "catalogue/UserSpecifiedAnEmptyStringStorageClassName.hpp" +#include "catalogue/UserSpecifiedAnEmptyStringSupply.hpp" #include "catalogue/UserSpecifiedAnEmptyStringTapePoolName.hpp" #include "catalogue/UserSpecifiedAnEmptyStringUsername.hpp" #include "catalogue/UserSpecifiedAnEmptyStringVendor.hpp" @@ -1131,6 +1132,51 @@ void RdbmsCatalogue::setTapePoolEncryption(const common::dataStructures::Securit } } +//------------------------------------------------------------------------------ +// modifyTapePoolSupply +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const std::string &supply) { + try { + if(name.empty()) { + throw UserSpecifiedAnEmptyStringTapePoolName("Cannot modify tape pool because the tape pool name is an empty" + " string"); + } + + if(supply.empty()) { + throw UserSpecifiedAnEmptyStringSupply("Cannot modify tape pool because the new supply value is an empty" + " string"); + } + + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE_POOL SET " + "SUPPLY = :SUPPLY," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "TAPE_POOL_NAME = :TAPE_POOL_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql); + stmt.bindString(":SUPPLY", supply); + stmt.bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt.bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt.bindUint64(":LAST_UPDATE_TIME", now); + stmt.bindString(":TAPE_POOL_NAME", name); + stmt.executeNonQuery(); + + if(0 == stmt.getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape pool ") + name + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str()); + throw; + } +} + //------------------------------------------------------------------------------ // createArchiveRoute //------------------------------------------------------------------------------ diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp index dc32daccacd26f734b4a9dff9fbccc4ee75a75f3..d9d8d924cfdfe58ec8c710433be8d27f38e6b4a6 100644 --- a/catalogue/RdbmsCatalogue.hpp +++ b/catalogue/RdbmsCatalogue.hpp @@ -240,6 +240,7 @@ public: void modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const uint64_t nbPartialTapes) override; void modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) override; void setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const bool encryptionValue) override; + void modifyTapePoolSupply(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &supply) override; void createArchiveRoute( const common::dataStructures::SecurityIdentity &admin, diff --git a/catalogue/UserSpecifiedAnEmptyStringSupply.cpp b/catalogue/UserSpecifiedAnEmptyStringSupply.cpp new file mode 100644 index 0000000000000000000000000000000000000000..dee220acbdda2094afaba2686ae51321d11264c1 --- /dev/null +++ b/catalogue/UserSpecifiedAnEmptyStringSupply.cpp @@ -0,0 +1,39 @@ +/* + * 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 "catalogue/UserSpecifiedAnEmptyStringSupply.hpp" + +namespace cta { +namespace catalogue { + + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +UserSpecifiedAnEmptyStringSupply::UserSpecifiedAnEmptyStringSupply(const std::string &context, + const bool embedBacktrace): cta::exception::UserError(context, embedBacktrace) { +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +UserSpecifiedAnEmptyStringSupply::~UserSpecifiedAnEmptyStringSupply() { +} + +} // namespace catalogue +} // namespace cta diff --git a/catalogue/UserSpecifiedAnEmptyStringSupply.hpp b/catalogue/UserSpecifiedAnEmptyStringSupply.hpp new file mode 100644 index 0000000000000000000000000000000000000000..12803e2c96497cf8af9e850831a3f0aefe8f1796 --- /dev/null +++ b/catalogue/UserSpecifiedAnEmptyStringSupply.hpp @@ -0,0 +1,48 @@ +/* + * 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/exception/UserError.hpp" + +namespace cta { +namespace catalogue { + +/** + * User specified an empty string for a supply value when this is not permitted. + */ +class UserSpecifiedAnEmptyStringSupply: public exception::UserError { +public: + /** + * Constructor. + * + * @param context optional context string added to the message + * at initialisation time. + * @param embedBacktrace whether to embed a backtrace of where the + * exception was throw in the message + */ + UserSpecifiedAnEmptyStringSupply(const std::string &context = "", const bool embedBacktrace = true); + + /** + * Destructor. + */ + ~UserSpecifiedAnEmptyStringSupply() override; +}; // class UserSpecifiedAnEmptyStringSupply + +} // namespace catalogue +} // namespace cta