diff --git a/catalogue/RdbmsArchiveFileItor.cpp b/catalogue/RdbmsArchiveFileItor.cpp new file mode 100644 index 0000000000000000000000000000000000000000..72f2f23b51fb417f3785009c012dddf0af87d51c --- /dev/null +++ b/catalogue/RdbmsArchiveFileItor.cpp @@ -0,0 +1,4632 @@ +/* + * 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/ArchiveFileRow.hpp" +#include "catalogue/SqliteCatalogueSchema.hpp" +#include "catalogue/RdbmsCatalogue.hpp" +#include "common/dataStructures/TapeFile.hpp" +#include "common/exception/Exception.hpp" +#include "common/exception/UserError.hpp" +#include "common/make_unique.hpp" +#include "common/threading/MutexLocker.hpp" +#include "common/utils/utils.hpp" +#include "rdbms/AutoRollback.hpp" + +#include <ctype.h> +#include <memory> +#include <time.h> + +namespace cta { +namespace catalogue { + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +RdbmsCatalogue::RdbmsCatalogue(std::unique_ptr<rdbms::ConnFactory> connFactory, const uint64_t nbConns): + m_connFactory(std::move(connFactory)), + m_connPool(*m_connFactory, nbConns) { +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +RdbmsCatalogue::~RdbmsCatalogue() { +} + +//------------------------------------------------------------------------------ +// createAdminUser +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createAdminUser( + const common::dataStructures::SecurityIdentity &admin, + const std::string &username, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + if(adminUserExists(conn, username)) { + throw exception::UserError(std::string("Cannot create admin user " + username + + " because an admin user with the same name already exists")); + } + const uint64_t now = time(nullptr); + const char *const sql = + "INSERT INTO ADMIN_USER(" + "ADMIN_USER_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(" + ":ADMIN_USER_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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":ADMIN_USER_NAME", username); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// adminUserExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::adminUserExists(rdbms::PooledConn &conn, const std::string adminUsername) const { + try { + const char *const sql = + "SELECT " + "ADMIN_USER_NAME AS ADMIN_USER_NAME " + "FROM " + "ADMIN_USER " + "WHERE " + "ADMIN_USER_NAME = :ADMIN_USER_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":ADMIN_USER_NAME", adminUsername); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteAdminUser +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteAdminUser(const std::string &username) { + try { + const char *const sql = "DELETE FROM ADMIN_USER WHERE ADMIN_USER_NAME = :ADMIN_USER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":ADMIN_USER_NAME", username); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete admin-user ") + username + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getAdminUsers +//------------------------------------------------------------------------------ +std::list<common::dataStructures::AdminUser> RdbmsCatalogue::getAdminUsers() const { + try { + std::list<common::dataStructures::AdminUser> admins; + const char *const sql = + "SELECT " + "ADMIN_USER_NAME AS ADMIN_USER_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "ADMIN_USER " + "ORDER BY " + "ADMIN_USER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::AdminUser admin; + + admin.name = rset->columnString("ADMIN_USER_NAME"); + admin.comment = rset->columnString("USER_COMMENT"); + admin.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + admin.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + admin.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + admin.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + admin.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + admin.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + admins.push_back(admin); + } + + return admins; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyAdminUserComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &username, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE ADMIN_USER SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "ADMIN_USER_NAME = :ADMIN_USER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":ADMIN_USER_NAME", username); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify admin user ") + username + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createAdminHost +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createAdminHost( + const common::dataStructures::SecurityIdentity &admin, + const std::string &hostName, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + if(adminHostExists(conn, hostName)) { + throw exception::UserError(std::string("Cannot create admin host " + hostName + + " because an admin host with the same name already exists")); + } + const uint64_t now = time(nullptr); + const char *const sql = + "INSERT INTO ADMIN_HOST(" + "ADMIN_HOST_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(" + ":ADMIN_HOST_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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":ADMIN_HOST_NAME", hostName); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// adminHostExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::adminHostExists(rdbms::PooledConn &conn, const std::string adminHost) const { + try { + const char *const sql = + "SELECT " + "ADMIN_HOST_NAME AS ADMIN_HOST_NAME " + "FROM " + "ADMIN_HOST " + "WHERE " + "ADMIN_HOST_NAME = :ADMIN_HOST_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":ADMIN_HOST_NAME", adminHost); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteAdminHost +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteAdminHost(const std::string &hostName) { + try { + const char *const sql = "DELETE FROM ADMIN_HOST WHERE ADMIN_HOST_NAME = :ADMIN_HOST_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":ADMIN_HOST_NAME", hostName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete admin-host ") + hostName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getAdminHosts +//------------------------------------------------------------------------------ +std::list<common::dataStructures::AdminHost> RdbmsCatalogue::getAdminHosts() const { + try { + std::list<common::dataStructures::AdminHost> hosts; + const char *const sql = + "SELECT " + "ADMIN_HOST_NAME AS ADMIN_HOST_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "ADMIN_HOST " + "ORDER BY " + "ADMIN_HOST_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::AdminHost host; + + host.name = rset->columnString("ADMIN_HOST_NAME"); + host.comment = rset->columnString("USER_COMMENT"); + host.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + host.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + host.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + host.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + host.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + host.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + hosts.push_back(host); + } + + return hosts; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyAdminHostComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyAdminHostComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &hostName, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE ADMIN_HOST SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "ADMIN_HOST_NAME = :ADMIN_HOST_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":ADMIN_HOST_NAME", hostName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify admin host ") + hostName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createStorageClass +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createStorageClass( + const common::dataStructures::SecurityIdentity &admin, + const common::dataStructures::StorageClass &storageClass) { + try { + auto conn = m_connPool.getConn(); + if(storageClassExists(conn, storageClass.diskInstance, storageClass.name)) { + throw exception::UserError(std::string("Cannot create storage class ") + storageClass.diskInstance + ":" + + storageClass.name + " because it already exists"); + } + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO STORAGE_CLASS(" + "DISK_INSTANCE_NAME," + "STORAGE_CLASS_NAME," + "NB_COPIES," + + "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(" + ":DISK_INSTANCE_NAME," + ":STORAGE_CLASS_NAME," + ":NB_COPIES," + + ":USER_COMMENT," + + ":CREATION_LOG_USER_NAME," + ":CREATION_LOG_HOST_NAME," + ":CREATION_LOG_TIME," + + ":LAST_UPDATE_USER_NAME," + ":LAST_UPDATE_HOST_NAME," + ":LAST_UPDATE_TIME)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":DISK_INSTANCE_NAME", storageClass.diskInstance); + stmt->bindString(":STORAGE_CLASS_NAME", storageClass.name); + stmt->bindUint64(":NB_COPIES", storageClass.nbCopies); + + stmt->bindString(":USER_COMMENT", storageClass.comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// storageClassExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::storageClassExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &storageClassName) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME " + "FROM " + "STORAGE_CLASS " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteStorageClass +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteStorageClass(const std::string &diskInstanceName, const std::string &storageClassName) { + try { + const char *const sql = + "DELETE FROM " + "STORAGE_CLASS " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql,rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + + stmt->executeNonQuery(); + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete storage-class ") + diskInstanceName + ":" + + storageClassName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getStorageClasses +//------------------------------------------------------------------------------ +std::list<common::dataStructures::StorageClass> + RdbmsCatalogue::getStorageClasses() const { + try { + std::list<common::dataStructures::StorageClass> storageClasses; + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME," + "NB_COPIES AS NB_COPIES," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "STORAGE_CLASS " + "ORDER BY " + "DISK_INSTANCE_NAME, STORAGE_CLASS_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::StorageClass storageClass; + + storageClass.diskInstance = rset->columnString("DISK_INSTANCE_NAME"); + storageClass.name = rset->columnString("STORAGE_CLASS_NAME"); + storageClass.nbCopies = rset->columnUint64("NB_COPIES"); + storageClass.comment = rset->columnString("USER_COMMENT"); + storageClass.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + storageClass.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + storageClass.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + storageClass.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + storageClass.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + storageClass.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + storageClasses.push_back(storageClass); + } + + return storageClasses; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyStorageClassNbCopies +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &name, const uint64_t nbCopies) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE STORAGE_CLASS SET " + "NB_COPIES = :NB_COPIES," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":NB_COPIES", nbCopies); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":STORAGE_CLASS_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify storage class ") + instanceName + ":" + name + + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyStorageClassComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyStorageClassComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &name, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE STORAGE_CLASS SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":STORAGE_CLASS_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify storage class ") + instanceName + ":" + name + + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createTapePool +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createTapePool( + const common::dataStructures::SecurityIdentity &admin, + const std::string &name, + const uint64_t nbPartialTapes, + const bool encryptionValue, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + + if(tapePoolExists(conn, name)) { + throw exception::UserError(std::string("Cannot create tape pool ") + name + + " because a tape pool with the same name already exists"); + } + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO TAPE_POOL(" + "TAPE_POOL_NAME," + "NB_PARTIAL_TAPES," + "IS_ENCRYPTED," + + "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(" + ":TAPE_POOL_NAME," + ":NB_PARTIAL_TAPES," + ":IS_ENCRYPTED," + + ":USER_COMMENT," + + ":CREATION_LOG_USER_NAME," + ":CREATION_LOG_HOST_NAME," + ":CREATION_LOG_TIME," + + ":LAST_UPDATE_USER_NAME," + ":LAST_UPDATE_HOST_NAME," + ":LAST_UPDATE_TIME)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":TAPE_POOL_NAME", name); + stmt->bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes); + stmt->bindBool(":IS_ENCRYPTED", encryptionValue); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapePoolExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::tapePoolExists(const std::string &tapePoolName) const { + try { + auto conn = m_connPool.getConn(); + return tapePoolExists(conn, tapePoolName); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapePoolExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::tapePoolExists(rdbms::PooledConn &conn, const std::string &tapePoolName) const { + try { + const char *const sql = + "SELECT " + "TAPE_POOL_NAME AS TAPE_POOL_NAME " + "FROM " + "TAPE_POOL " + "WHERE " + "TAPE_POOL_NAME = :TAPE_POOL_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":TAPE_POOL_NAME", tapePoolName); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// archiveFileExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::archiveFileIdExists(rdbms::PooledConn &conn, const uint64_t archiveFileId) const { + try { + const char *const sql = + "SELECT " + "ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID " + "FROM " + "ARCHIVE_FILE " + "WHERE " + "ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// diskFileIdExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::diskFileIdExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &diskFileId) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "DISK_FILE_ID AS DISK_FILE_ID " + "FROM " + "ARCHIVE_FILE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "DISK_FILE_ID = :DISK_FILE_ID"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":DISK_FILE_ID", diskFileId); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// diskFilePathExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::diskFilePathExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &diskFilePath) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "DISK_FILE_PATH AS DISK_FILE_PATH " + "FROM " + "ARCHIVE_FILE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "DISK_FILE_PATH = :DISK_FILE_PATH"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":DISK_FILE_PATH", diskFilePath); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// diskFileUserExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::diskFileUserExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &diskFileUser) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "DISK_FILE_USER AS DISK_FILE_USER " + "FROM " + "ARCHIVE_FILE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "DISK_FILE_USER = :DISK_FILE_USER"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":DISK_FILE_USER", diskFileUser); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// diskFileGroupExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::diskFileGroupExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &diskFileGroup) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "DISK_FILE_GROUP AS DISK_FILE_GROUP " + "FROM " + "ARCHIVE_FILE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "DISK_FILE_GROUP = :DISK_FILE_GROUP"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":DISK_FILE_GROUP", diskFileGroup); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// archiveRouteExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::archiveRouteExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &storageClassName, const uint64_t copyNb) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME," + "COPY_NB AS COPY_NB " + "FROM " + "ARCHIVE_ROUTE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND " + "COPY_NB = :COPY_NB"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteTapePool +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteTapePool(const std::string &name) { + try { + const char *const sql = "DELETE FROM TAPE_POOL WHERE TAPE_POOL_NAME = :TAPE_POOL_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":TAPE_POOL_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete tape-pool ") + name + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapePools +//------------------------------------------------------------------------------ +std::list<common::dataStructures::TapePool> RdbmsCatalogue::getTapePools() const { + try { + std::list<common::dataStructures::TapePool> pools; + const char *const sql = + "SELECT " + "TAPE_POOL_NAME AS TAPE_POOL_NAME," + "NB_PARTIAL_TAPES AS NB_PARTIAL_TAPES," + "IS_ENCRYPTED AS IS_ENCRYPTED," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "TAPE_POOL " + "ORDER BY " + "TAPE_POOL_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::TapePool pool; + + pool.name = rset->columnString("TAPE_POOL_NAME"); + pool.nbPartialTapes = rset->columnUint64("NB_PARTIAL_TAPES"); + pool.encryption = rset->columnBool("IS_ENCRYPTED"); + pool.comment = rset->columnString("USER_COMMENT"); + pool.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + pool.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + pool.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + pool.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + pool.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + pool.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + pools.push_back(pool); + } + + return pools; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapePoolNbPartialTapes +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapePoolNbPartialTapes(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t nbPartialTapes) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE_POOL SET " + "NB_PARTIAL_TAPES = :NB_PARTIAL_TAPES," + "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, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":NB_PARTIAL_TAPES", nbPartialTapes); + 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) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapePoolComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapePoolComment(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE_POOL SET " + "USER_COMMENT = :USER_COMMENT," + "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, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + 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) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// setTapePoolEncryption +//------------------------------------------------------------------------------ +void RdbmsCatalogue::setTapePoolEncryption(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const bool encryptionValue) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE_POOL SET " + "IS_ENCRYPTED = :IS_ENCRYPTED," + "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, rdbms::Stmt::AutocommitMode::ON); + stmt->bindBool(":IS_ENCRYPTED", encryptionValue); + 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) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createArchiveRoute +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createArchiveRoute( + const common::dataStructures::SecurityIdentity &admin, + const std::string &diskInstanceName, + const std::string &storageClassName, + const uint64_t copyNb, + const std::string &tapePoolName, + const std::string &comment) { + try { + const time_t now = time(nullptr); + auto conn = m_connPool.getConn(); + if(archiveRouteExists(conn, diskInstanceName, storageClassName, copyNb)) { + exception::UserError ue; + ue.getMessage() << "Cannot create archive route " << diskInstanceName << ":" << storageClassName << "," << copyNb + << "->" << tapePoolName << " because it already exists"; + throw ue; + } + if(!storageClassExists(conn, diskInstanceName, storageClassName)) { + exception::UserError ue; + ue.getMessage() << "Cannot create archive route " << diskInstanceName << ":" << storageClassName << "," << copyNb + << "->" << tapePoolName << " because storage class " << diskInstanceName << ":" << storageClassName << + " does not exist"; + throw ue; + } + if(!tapePoolExists(conn, tapePoolName)) { + exception::UserError ue; + ue.getMessage() << "Cannot create archive route " << diskInstanceName << ":" << storageClassName << "," << copyNb + << "->" << tapePoolName << " because tape pool " << tapePoolName + " does not exist"; + throw ue; + } + + const char *const sql = + "INSERT INTO ARCHIVE_ROUTE(" + "DISK_INSTANCE_NAME," + "STORAGE_CLASS_NAME," + "COPY_NB," + "TAPE_POOL_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(" + ":DISK_INSTANCE_NAME," + ":STORAGE_CLASS_NAME," + ":COPY_NB," + ":TAPE_POOL_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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + stmt->bindString(":TAPE_POOL_NAME", tapePoolName); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteArchiveRoute +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteArchiveRoute(const std::string &diskInstanceName, const std::string &storageClassName, + const uint64_t copyNb) { + try { + const char *const sql = + "DELETE FROM " + "ARCHIVE_ROUTE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND " + "COPY_NB = :COPY_NB"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + exception::UserError ue; + ue.getMessage() << "Cannot delete archive route for storage-class " << diskInstanceName + ":" + storageClassName + + " and copy number " << copyNb << " because it does not exist"; + throw ue; + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getArchiveRoutes +//------------------------------------------------------------------------------ +std::list<common::dataStructures::ArchiveRoute> RdbmsCatalogue::getArchiveRoutes() const { + try { + std::list<common::dataStructures::ArchiveRoute> routes; + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "STORAGE_CLASS_NAME AS STORAGE_CLASS_NAME," + "COPY_NB AS COPY_NB," + "TAPE_POOL_NAME AS TAPE_POOL_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "ARCHIVE_ROUTE " + "ORDER BY " + "DISK_INSTANCE_NAME, STORAGE_CLASS_NAME, COPY_NB"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::ArchiveRoute route; + + route.diskInstanceName = rset->columnString("DISK_INSTANCE_NAME"); + route.storageClassName = rset->columnString("STORAGE_CLASS_NAME"); + route.copyNb = rset->columnUint64("COPY_NB"); + route.tapePoolName = rset->columnString("TAPE_POOL_NAME"); + route.comment = rset->columnString("USER_COMMENT"); + route.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + route.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + route.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + route.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + route.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + route.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + routes.push_back(route); + } + + return routes; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyArchiveRouteTapePoolName +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &storageClassName, const uint64_t copyNb, + const std::string &tapePoolName) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE ARCHIVE_ROUTE SET " + "TAPE_POOL_NAME = :TAPE_POOL_NAME," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND " + "COPY_NB = :COPY_NB"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":TAPE_POOL_NAME", tapePoolName); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + exception::UserError ue; + ue.getMessage() << "Cannot modify archive route for storage-class " << instanceName + ":" + storageClassName + + " and copy number " << copyNb << " because it does not exist"; + throw ue; + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyArchiveRouteComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &storageClassName, const uint64_t copyNb, + const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE ARCHIVE_ROUTE SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME AND " + "COPY_NB = :COPY_NB"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + stmt->bindUint64(":COPY_NB", copyNb); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + exception::UserError ue; + ue.getMessage() << "Cannot modify archive route for storage-class " << instanceName + ":" + storageClassName + + " and copy number " << copyNb << " because it does not exist"; + throw ue; + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createLogicalLibrary +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createLogicalLibrary( + const common::dataStructures::SecurityIdentity &admin, + const std::string &name, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + if(logicalLibraryExists(conn, name)) { + throw exception::UserError(std::string("Cannot create logical library ") + name + + " because a logical library with the same name already exists"); + } + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO LOGICAL_LIBRARY(" + "LOGICAL_LIBRARY_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(" + ":LOGICAL_LIBRARY_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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":LOGICAL_LIBRARY_NAME", name); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(std::exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.what()); + } +} + +//------------------------------------------------------------------------------ +// logicalLibraryExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::logicalLibraryExists(rdbms::PooledConn &conn, const std::string &logicalLibraryName) const { + try { + const char *const sql = + "SELECT " + "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME " + "FROM " + "LOGICAL_LIBRARY " + "WHERE " + "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteLogicalLibrary +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteLogicalLibrary(const std::string &name) { + try { + const char *const sql = "DELETE FROM LOGICAL_LIBRARY WHERE LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LOGICAL_LIBRARY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete logical-library ") + name + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getLogicalLibraries +//------------------------------------------------------------------------------ +std::list<common::dataStructures::LogicalLibrary> + RdbmsCatalogue::getLogicalLibraries() const { + try { + std::list<common::dataStructures::LogicalLibrary> libs; + const char *const sql = + "SELECT " + "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "LOGICAL_LIBRARY " + "ORDER BY " + "LOGICAL_LIBRARY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::LogicalLibrary lib; + + lib.name = rset->columnString("LOGICAL_LIBRARY_NAME"); + lib.comment = rset->columnString("USER_COMMENT"); + lib.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + lib.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + lib.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + lib.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + lib.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + lib.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + libs.push_back(lib); + } + + return libs; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyLogicalLibraryComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyLogicalLibraryComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE LOGICAL_LIBRARY SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":LOGICAL_LIBRARY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify logical library ") + name + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createTape +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createTape( + const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, + const std::string &logicalLibraryName, + const std::string &tapePoolName, + const uint64_t capacityInBytes, + const bool disabled, + const bool full, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + if(tapeExists(conn, vid)) { + throw exception::UserError(std::string("Cannot create tape ") + vid + + " because a tape with the same volume identifier already exists"); + } + if(!logicalLibraryExists(conn, logicalLibraryName)) { + throw exception::UserError(std::string("Cannot create tape ") + vid + " because logical library " + + logicalLibraryName + " does not exist"); + } + if(!tapePoolExists(conn, tapePoolName)) { + throw exception::UserError(std::string("Cannot create tape ") + vid + " because tape pool " + + tapePoolName + " does not exist"); + } + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO TAPE(" + "VID," + "LOGICAL_LIBRARY_NAME," + "TAPE_POOL_NAME," + "CAPACITY_IN_BYTES," + "DATA_IN_BYTES," + "LAST_FSEQ," + "IS_DISABLED," + "IS_FULL," + + "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(" + ":VID," + ":LOGICAL_LIBRARY_NAME," + ":TAPE_POOL_NAME," + ":CAPACITY_IN_BYTES," + ":DATA_IN_BYTES," + ":LAST_FSEQ," + ":IS_DISABLED," + ":IS_FULL," + + ":USER_COMMENT," + + ":CREATION_LOG_USER_NAME," + ":CREATION_LOG_HOST_NAME," + ":CREATION_LOG_TIME," + + ":LAST_UPDATE_USER_NAME," + ":LAST_UPDATE_HOST_NAME," + ":LAST_UPDATE_TIME)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":VID", vid); + stmt->bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName); + stmt->bindString(":TAPE_POOL_NAME", tapePoolName); + stmt->bindUint64(":CAPACITY_IN_BYTES", capacityInBytes); + stmt->bindUint64(":DATA_IN_BYTES", 0); + stmt->bindUint64(":LAST_FSEQ", 0); + stmt->bindBool(":IS_DISABLED", disabled); + stmt->bindBool(":IS_FULL", full); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapeExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::tapeExists(const std::string &vid) const { + try { + auto conn = m_connPool.getConn(); + return tapeExists(conn, vid); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapeExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::tapeExists(rdbms::PooledConn &conn, const std::string &vid) const { + try { + const char *const sql = + "SELECT " + "VID AS VID " + "FROM " + "TAPE " + "WHERE " + "VID = :VID"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":VID", vid); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteTape +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteTape(const std::string &vid) { + try { + const char *const sql = "DELETE FROM TAPE WHERE VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapes +//------------------------------------------------------------------------------ +std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(const TapeSearchCriteria &searchCriteria) const { + try { + auto conn = m_connPool.getConn(); + return getTapes(conn, searchCriteria); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapes +//------------------------------------------------------------------------------ +std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::PooledConn &conn, + const TapeSearchCriteria &searchCriteria) const { + try { + std::list<common::dataStructures::Tape> tapes; + std::string sql = + "SELECT " + "VID AS VID," + "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME," + "TAPE_POOL_NAME AS TAPE_POOL_NAME," + "ENCRYPTION_KEY AS ENCRYPTION_KEY," + "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES," + "DATA_IN_BYTES AS DATA_IN_BYTES," + "LAST_FSEQ AS LAST_FSEQ," + "IS_DISABLED AS IS_DISABLED," + "IS_FULL AS IS_FULL," + "LBP_IS_ON AS LBP_IS_ON," + + "LABEL_DRIVE AS LABEL_DRIVE," + "LABEL_TIME AS LABEL_TIME," + + "LAST_READ_DRIVE AS LAST_READ_DRIVE," + "LAST_READ_TIME AS LAST_READ_TIME," + + "LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE," + "LAST_WRITE_TIME AS LAST_WRITE_TIME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "TAPE"; + + if(searchCriteria.vid|| + searchCriteria.logicalLibrary|| + searchCriteria.tapePool|| + searchCriteria.capacityInBytes|| + searchCriteria.disabled|| + searchCriteria.full|| + searchCriteria.lbp) { + sql += " WHERE "; + } + + bool addedAWhereConstraint = false; + + if(searchCriteria.vid) { + sql += " VID = :VID"; + addedAWhereConstraint = true; + } + if(searchCriteria.logicalLibrary) { + if(addedAWhereConstraint) sql += " AND "; + sql += " LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME"; + addedAWhereConstraint = true; + } + if(searchCriteria.tapePool) { + if(addedAWhereConstraint) sql += " AND "; + sql += " TAPE_POOL_NAME = :TAPE_POOL_NAME"; + addedAWhereConstraint = true; + } + if(searchCriteria.capacityInBytes) { + if(addedAWhereConstraint) sql += " AND "; + sql += " CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES"; + addedAWhereConstraint = true; + } + if(searchCriteria.disabled) { + if(addedAWhereConstraint) sql += " AND "; + sql += " IS_DISABLED = :IS_DISABLED"; + addedAWhereConstraint = true; + } + if(searchCriteria.full) { + if(addedAWhereConstraint) sql += " AND "; + sql += " IS_FULL = :IS_FULL"; + addedAWhereConstraint = true; + } + if(searchCriteria.lbp) { + if(addedAWhereConstraint) sql += " AND "; + sql += " LBP_IS_ON = :LBP_IS_ON"; + } + + sql += " ORDER BY VID"; + + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + + if(searchCriteria.vid) stmt->bindString(":VID", searchCriteria.vid.value()); + if(searchCriteria.logicalLibrary) stmt->bindString(":LOGICAL_LIBRARY_NAME", searchCriteria.logicalLibrary.value()); + if(searchCriteria.tapePool) stmt->bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value()); + if(searchCriteria.capacityInBytes) stmt->bindUint64(":CAPACITY_IN_BYTES", searchCriteria.capacityInBytes.value()); + if(searchCriteria.disabled) stmt->bindBool(":IS_DISABLED", searchCriteria.disabled.value()); + if(searchCriteria.full) stmt->bindBool(":IS_FULL", searchCriteria.full.value()); + if(searchCriteria.lbp) stmt->bindBool(":LBP_IS_ON", searchCriteria.lbp.value()); + + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::Tape tape; + + tape.vid = rset->columnString("VID"); + tape.logicalLibraryName = rset->columnString("LOGICAL_LIBRARY_NAME"); + tape.tapePoolName = rset->columnString("TAPE_POOL_NAME"); + tape.encryptionKey = rset->columnOptionalString("ENCRYPTION_KEY"); + tape.capacityInBytes = rset->columnUint64("CAPACITY_IN_BYTES"); + tape.dataOnTapeInBytes = rset->columnUint64("DATA_IN_BYTES"); + tape.lastFSeq = rset->columnUint64("LAST_FSEQ"); + tape.disabled = rset->columnBool("IS_DISABLED"); + tape.full = rset->columnBool("IS_FULL"); + tape.lbp = rset->columnOptionalBool("LBP_IS_ON"); + + tape.labelLog = getTapeLogFromRset(*rset, "LABEL_DRIVE", "LABEL_TIME"); + tape.lastReadLog = getTapeLogFromRset(*rset, "LAST_READ_DRIVE", "LAST_READ_TIME"); + tape.lastWriteLog = getTapeLogFromRset(*rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME"); + + tape.comment = rset->columnString("USER_COMMENT"); + tape.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + tape.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + tape.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + tape.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + tape.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + tape.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + tapes.push_back(tape); + } + + return tapes; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapesByVid +//------------------------------------------------------------------------------ +common::dataStructures::VidToTapeMap RdbmsCatalogue::getTapesByVid(const std::set<std::string> &vids) const { + try { + common::dataStructures::VidToTapeMap vidToTapeMap; + std::string sql = + "SELECT " + "VID AS VID," + "LOGICAL_LIBRARY_NAME AS LOGICAL_LIBRARY_NAME," + "TAPE_POOL_NAME AS TAPE_POOL_NAME," + "ENCRYPTION_KEY AS ENCRYPTION_KEY," + "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES," + "DATA_IN_BYTES AS DATA_IN_BYTES," + "LAST_FSEQ AS LAST_FSEQ," + "IS_DISABLED AS IS_DISABLED," + "IS_FULL AS IS_FULL," + "LBP_IS_ON AS LBP_IS_ON," + + "LABEL_DRIVE AS LABEL_DRIVE," + "LABEL_TIME AS LABEL_TIME," + + "LAST_READ_DRIVE AS LAST_READ_DRIVE," + "LAST_READ_TIME AS LAST_READ_TIME," + + "LAST_WRITE_DRIVE AS LAST_WRITE_DRIVE," + "LAST_WRITE_TIME AS LAST_WRITE_TIME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "TAPE"; + + if(!vids.empty()) { + sql += " WHERE "; + } + + { + for(uint64_t vidNb = 1; vidNb <= vids.size(); vidNb++) { + if(1 < vidNb) { + sql += " OR "; + } + sql += "VID = :VID" + std::to_string(vidNb); + } + } + + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + + { + uint64_t vidNb = 1; + for(auto &vid : vids) { + stmt->bindString(":VID" + std::to_string(vidNb), vid); + vidNb++; + } + } + + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::Tape tape; + + tape.vid = rset->columnString("VID"); + tape.logicalLibraryName = rset->columnString("LOGICAL_LIBRARY_NAME"); + tape.tapePoolName = rset->columnString("TAPE_POOL_NAME"); + tape.encryptionKey = rset->columnOptionalString("ENCRYPTION_KEY"); + tape.capacityInBytes = rset->columnUint64("CAPACITY_IN_BYTES"); + tape.dataOnTapeInBytes = rset->columnUint64("DATA_IN_BYTES"); + tape.lastFSeq = rset->columnUint64("LAST_FSEQ"); + tape.disabled = rset->columnBool("IS_DISABLED"); + tape.full = rset->columnBool("IS_FULL"); + tape.lbp = rset->columnOptionalBool("LBP_IS_ON"); + + tape.labelLog = getTapeLogFromRset(*rset, "LABEL_DRIVE", "LABEL_TIME"); + tape.lastReadLog = getTapeLogFromRset(*rset, "LAST_READ_DRIVE", "LAST_READ_TIME"); + tape.lastWriteLog = getTapeLogFromRset(*rset, "LAST_WRITE_DRIVE", "LAST_WRITE_TIME"); + + tape.comment = rset->columnString("USER_COMMENT"); + tape.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + tape.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + tape.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + tape.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + tape.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + tape.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + vidToTapeMap[tape.vid] = tape; + } + + if(vids.size() != vidToTapeMap.size()) { + throw exception::Exception("Not all tapes were found"); + } + + return vidToTapeMap; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// reclaimTape +//------------------------------------------------------------------------------ +void RdbmsCatalogue::reclaimTape(const common::dataStructures::SecurityIdentity &admin, const std::string &vid) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LAST_FSEQ = 0, " + "IS_FULL = 0," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :UPDATE_VID AND " + "IS_FULL != 0 AND " + "NOT EXISTS (SELECT VID FROM TAPE_FILE WHERE VID = :SELECT_VID)"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":UPDATE_VID", vid); + stmt->bindString(":SELECT_VID", vid); + stmt->executeNonQuery(); + + // If the update failed due to a user error + if(0 == stmt->getNbAffectedRows()) { + // Try to determine the user error + // + // Please note that this is a best effort diagnosis because there is no + // lock on the database to prevent other concurrent updates from taking + // place on the TAPE and TAPE_FILE tables + TapeSearchCriteria searchCriteria; + searchCriteria.vid = vid; + const auto tapes = getTapes(conn, searchCriteria); + + if(tapes.empty()) { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it does not exist"); + } else { + if(!tapes.front().full) { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because it is not FULL"); + } else { + throw exception::UserError(std::string("Cannot reclaim tape ") + vid + " because there is at least one tape" + " file in the catalogue that is on the tape"); + } + } + } + + conn.commit(); + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapeLogFromRset +//------------------------------------------------------------------------------ +optional<common::dataStructures::TapeLog> RdbmsCatalogue::getTapeLogFromRset(const rdbms::Rset &rset, + const std::string &driveColName, const std::string &timeColName) const { + try { + const optional<std::string> drive = rset.columnOptionalString(driveColName); + const optional<uint64_t> time = rset.columnOptionalUint64(timeColName); + + if(!drive && !time) { + return nullopt; + } + + if(drive && !time) { + throw exception::Exception(std::string("Database column ") + driveColName + " contains " + drive.value() + + " but column " + timeColName + " is nullptr"); + } + + if(time && !drive) { + throw exception::Exception(std::string("Database column ") + timeColName + " contains " + + std::to_string(time.value()) + " but column " + driveColName + " is nullptr"); + } + + common::dataStructures::TapeLog tapeLog; + tapeLog.drive = drive.value(); + tapeLog.time = time.value(); + + return tapeLog; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapeLogicalLibraryName +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const std::string &logicalLibraryName) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapeTapePoolName +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const std::string &tapePoolName) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "TAPE_POOL_NAME = :TAPE_POOL_NAME," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":TAPE_POOL_NAME", tapePoolName); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapeCapacityInBytes +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapeCapacityInBytes(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const uint64_t capacityInBytes) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "CAPACITY_IN_BYTES = :CAPACITY_IN_BYTES," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":CAPACITY_IN_BYTES", capacityInBytes); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapeEncryptionKey +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapeEncryptionKey(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const std::string &encryptionKey) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "ENCRYPTION_KEY = :ENCRYPTION_KEY," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":ENCRYPTION_KEY", encryptionKey); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapeMountedForArchive +//------------------------------------------------------------------------------ +void RdbmsCatalogue::tapeMountedForArchive(const std::string &vid, const std::string &drive) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE," + "LAST_WRITE_TIME = :LAST_WRITE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LAST_WRITE_DRIVE", drive); + stmt->bindUint64(":LAST_WRITE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapeMountedForRetrieve +//------------------------------------------------------------------------------ +void RdbmsCatalogue::tapeMountedForRetrieve(const std::string &vid, const std::string &drive) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LAST_READ_DRIVE = :LAST_READ_DRIVE," + "LAST_READ_TIME = :LAST_READ_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LAST_READ_DRIVE", drive); + stmt->bindUint64(":LAST_READ_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// setTapeFull +//------------------------------------------------------------------------------ +void RdbmsCatalogue::setTapeFull(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, + const bool fullValue) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "IS_FULL = :IS_FULL," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindBool(":IS_FULL", fullValue); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// noSpaceLeftOnTape +//------------------------------------------------------------------------------ +void RdbmsCatalogue::noSpaceLeftOnTape(const std::string &vid) { + try { + const char *const sql = + "UPDATE TAPE SET " + "IS_FULL = 1 " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::Exception(std::string("Tape ") + vid + " does not exist"); + } + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// setTapeDisabled +//------------------------------------------------------------------------------ +void RdbmsCatalogue::setTapeDisabled(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const bool disabledValue) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "IS_DISABLED = :IS_DISABLED," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindBool(":IS_DISABLED", disabledValue); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyTapeComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyTapeComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &vid, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyRequesterMountRulePolicy +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyRequesterMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &requesterName, const std::string &mountPolicy) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE REQUESTER_MOUNT_RULE SET " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_NAME = :REQUESTER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicy); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" + + requesterName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyRequesteMountRuleComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyRequesteMountRuleComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &requesterName, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE REQUESTER_MOUNT_RULE SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_NAME = :REQUESTER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify requester mount rule ") + instanceName + ":" + + requesterName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyRequesterGroupMountRulePolicy +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyRequesterGroupMountRulePolicy(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &requesterGroupName, const std::string &mountPolicy) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE REQUESTER_GROUP_MOUNT_RULE SET " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicy); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" + + requesterGroupName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyRequesterGroupMountRuleComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyRequesterGroupMountRuleComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &instanceName, const std::string &requesterGroupName, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE REQUESTER_GROUP_MOUNT_RULE SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":DISK_INSTANCE_NAME", instanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify requester group mount rule ") + instanceName + ":" + + requesterGroupName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createMountPolicy +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createMountPolicy( + const common::dataStructures::SecurityIdentity &admin, + const std::string &name, + const uint64_t archivePriority, + const uint64_t minArchiveRequestAge, + const uint64_t retrievePriority, + const uint64_t minRetrieveRequestAge, + const uint64_t maxDrivesAllowed, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + if(mountPolicyExists(conn, name)) { + throw exception::UserError(std::string("Cannot create mount policy ") + name + + " because a mount policy with the same name already exists"); + } + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO MOUNT_POLICY(" + "MOUNT_POLICY_NAME," + + "ARCHIVE_PRIORITY," + "ARCHIVE_MIN_REQUEST_AGE," + + "RETRIEVE_PRIORITY," + "RETRIEVE_MIN_REQUEST_AGE," + + "MAX_DRIVES_ALLOWED," + + "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(" + ":MOUNT_POLICY_NAME," + + ":ARCHIVE_PRIORITY," + ":ARCHIVE_MIN_REQUEST_AGE," + + ":RETRIEVE_PRIORITY," + ":RETRIEVE_MIN_REQUEST_AGE," + + ":MAX_DRIVES_ALLOWED," + + ":USER_COMMENT," + + ":CREATION_LOG_USER_NAME," + ":CREATION_LOG_HOST_NAME," + ":CREATION_LOG_TIME," + + ":LAST_UPDATE_USER_NAME," + ":LAST_UPDATE_HOST_NAME," + ":LAST_UPDATE_TIME)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":MOUNT_POLICY_NAME", name); + + stmt->bindUint64(":ARCHIVE_PRIORITY", archivePriority); + stmt->bindUint64(":ARCHIVE_MIN_REQUEST_AGE", minArchiveRequestAge); + + stmt->bindUint64(":RETRIEVE_PRIORITY", retrievePriority); + stmt->bindUint64(":RETRIEVE_MIN_REQUEST_AGE", minRetrieveRequestAge); + + stmt->bindUint64(":MAX_DRIVES_ALLOWED", maxDrivesAllowed); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createRequesterMountRule +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createRequesterMountRule( + const common::dataStructures::SecurityIdentity &admin, + const std::string &mountPolicyName, + const std::string &diskInstanceName, + const std::string &requesterName, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + std::unique_ptr<common::dataStructures::MountPolicy> mountPolicy(getRequesterMountPolicy(conn, diskInstanceName, + requesterName)); + if(nullptr != mountPolicy.get()) { + throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName + + " to requester " + diskInstanceName + ":" + requesterName + + " because the requester is already assigned to mount-policy " + mountPolicy->name); + } + if(!mountPolicyExists(conn, mountPolicyName)) { + throw exception::UserError(std::string("Cannot create a rule to assign mount-policy ") + mountPolicyName + + " to requester " + diskInstanceName + ":" + requesterName + " because mount-policy " + mountPolicyName + + " does not exist"); + } + const uint64_t now = time(nullptr); + const char *const sql = + "INSERT INTO REQUESTER_MOUNT_RULE(" + "DISK_INSTANCE_NAME," + "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(" + ":DISK_INSTANCE_NAME," + ":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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicyName); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getRequesterMountRules +//------------------------------------------------------------------------------ +std::list<common::dataStructures::RequesterMountRule> RdbmsCatalogue::getRequesterMountRules() const { + try { + std::list<common::dataStructures::RequesterMountRule> rules; + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "REQUESTER_NAME AS REQUESTER_NAME," + "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "REQUESTER_MOUNT_RULE " + "ORDER BY " + "DISK_INSTANCE_NAME, REQUESTER_NAME, MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while(rset->next()) { + common::dataStructures::RequesterMountRule rule; + + rule.diskInstance = rset->columnString("DISK_INSTANCE_NAME"); + rule.name = rset->columnString("REQUESTER_NAME"); + rule.mountPolicy = rset->columnString("MOUNT_POLICY_NAME"); + rule.comment = rset->columnString("USER_COMMENT"); + rule.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + rule.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + rule.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + rule.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + rule.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + rule.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + rules.push_back(rule); + } + + return rules; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteRequesterMountRule +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteRequesterMountRule(const std::string &diskInstanceName, const std::string &requesterName) { + try { + const char *const sql = + "DELETE FROM " + "REQUESTER_MOUNT_RULE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_NAME = :REQUESTER_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete mount rule for requester ") + diskInstanceName + ":" + requesterName + + " because the rule does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// createRequesterGroupMountRule +//------------------------------------------------------------------------------ +void RdbmsCatalogue::createRequesterGroupMountRule( + const common::dataStructures::SecurityIdentity &admin, + const std::string &mountPolicyName, + const std::string &diskInstanceName, + const std::string &requesterGroupName, + const std::string &comment) { + try { + auto conn = m_connPool.getConn(); + std::unique_ptr<common::dataStructures::MountPolicy> mountPolicy( + getRequesterGroupMountPolicy(conn, diskInstanceName, requesterGroupName)); + if(nullptr != mountPolicy.get()) { + throw exception::UserError(std::string("Cannot create rule to assign mount-policy ") + mountPolicyName + + " to requester-group " + diskInstanceName + ":" + requesterGroupName + + " because a rule already exists assigning the requester-group to mount-policy " + mountPolicy->name); + } + if(!mountPolicyExists(conn, mountPolicyName)) { + throw exception::UserError(std::string("Cannot assign mount-policy ") + mountPolicyName + " to requester-group " + + diskInstanceName + ":" + requesterGroupName + " because mount-policy " + mountPolicyName + " does not exist"); + } + const uint64_t now = time(nullptr); + const char *const sql = + "INSERT INTO REQUESTER_GROUP_MOUNT_RULE(" + "DISK_INSTANCE_NAME," + "REQUESTER_GROUP_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(" + ":DISK_INSTANCE_NAME," + ":REQUESTER_GROUP_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)"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicyName); + + stmt->bindString(":USER_COMMENT", comment); + + stmt->bindString(":CREATION_LOG_USER_NAME", admin.username); + stmt->bindString(":CREATION_LOG_HOST_NAME", admin.host); + stmt->bindUint64(":CREATION_LOG_TIME", now); + + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getRequesterGroupMountPolicy +//------------------------------------------------------------------------------ +common::dataStructures::MountPolicy *RdbmsCatalogue::getRequesterGroupMountPolicy( + rdbms::PooledConn &conn, + const std::string &diskInstanceName, + const std::string &requesterGroupName) 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_GROUP_MOUNT_RULE " + "ON " + "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME " + "WHERE " + "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + auto rset = stmt->executeQuery(); + if(rset->next()) { + auto policy = cta::make_unique<common::dataStructures::MountPolicy>(); + + policy->name = rset->columnString("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->columnString("USER_COMMENT"); + policy->creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + policy->creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + policy->creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + policy->lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + policy->lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + policy->lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + return policy.release(); + } else { + return nullptr; + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getRequesterGroupMountRules +//------------------------------------------------------------------------------ +std::list<common::dataStructures::RequesterGroupMountRule> RdbmsCatalogue::getRequesterGroupMountRules() const { + try { + std::list<common::dataStructures::RequesterGroupMountRule> rules; + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME," + "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "REQUESTER_GROUP_MOUNT_RULE " + "ORDER BY " + "DISK_INSTANCE_NAME, REQUESTER_GROUP_NAME, MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while(rset->next()) { + common::dataStructures::RequesterGroupMountRule rule; + + rule.diskInstance = rset->columnString("DISK_INSTANCE_NAME"); + rule.name = rset->columnString("REQUESTER_GROUP_NAME"); + rule.mountPolicy = rset->columnString("MOUNT_POLICY_NAME"); + + rule.comment = rset->columnString("USER_COMMENT"); + rule.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + rule.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + rule.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + rule.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + rule.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + rule.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + rules.push_back(rule); + } + + return rules; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// deleteRequesterGroupMountRule +//------------------------------------------------------------------------------ +void RdbmsCatalogue::deleteRequesterGroupMountRule(const std::string &diskInstanceName, + const std::string &requesterGroupName) { + try { + const char *const sql = + "DELETE FROM " + "REQUESTER_GROUP_MOUNT_RULE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete the mount rule for requester group ") + diskInstanceName + ":" + + requesterGroupName + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// mountPolicyExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::mountPolicyExists(rdbms::PooledConn &conn, const std::string &mountPolicyName) const { + try { + const char *const sql = + "SELECT " + "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME " + "FROM " + "MOUNT_POLICY " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":MOUNT_POLICY_NAME", mountPolicyName); + auto rset = stmt->executeQuery(); + return rset->next(); + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// requesterMountRuleExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::requesterMountRuleExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &requesterName) const { + try { + const char *const sql = + "SELECT " + "REQUESTER_NAME AS REQUESTER_NAME " + "FROM " + "REQUESTER_MOUNT_RULE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_NAME = :REQUESTER_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + auto 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( + rdbms::PooledConn &conn, + const std::string &diskInstanceName, + 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_MOUNT_RULE " + "ON " + "MOUNT_POLICY.MOUNT_POLICY_NAME = REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME " + "WHERE " + "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + auto rset = stmt->executeQuery(); + if(rset->next()) { + auto policy = cta::make_unique<common::dataStructures::MountPolicy>(); + + policy->name = rset->columnString("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->columnString("USER_COMMENT"); + + policy->creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + policy->creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + policy->creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + + common::dataStructures::EntryLog updateLog; + policy->lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + policy->lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + policy->lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + return policy.release(); + } else { + return nullptr; + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// requesterGroupMountRuleExists +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::requesterGroupMountRuleExists(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &requesterGroupName) const { + try { + const char *const sql = + "SELECT " + "DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME, " + "REQUESTER_GROUP_NAME AS REQUESTER_GROUP_NAME " + "FROM " + "REQUESTER_GROUP_MOUNT_RULE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + auto 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) { + try { + const char *const sql = "DELETE FROM MOUNT_POLICY WHERE MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot delete mount policy ") + name + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getMountPolicies +//------------------------------------------------------------------------------ +std::list<common::dataStructures::MountPolicy> RdbmsCatalogue::getMountPolicies() const { + try { + std::list<common::dataStructures::MountPolicy> policies; + const char *const sql = + "SELECT " + "MOUNT_POLICY_NAME AS MOUNT_POLICY_NAME," + + "ARCHIVE_PRIORITY AS ARCHIVE_PRIORITY," + "ARCHIVE_MIN_REQUEST_AGE AS ARCHIVE_MIN_REQUEST_AGE," + + "RETRIEVE_PRIORITY AS RETRIEVE_PRIORITY," + "RETRIEVE_MIN_REQUEST_AGE AS RETRIEVE_MIN_REQUEST_AGE," + + "MAX_DRIVES_ALLOWED AS MAX_DRIVES_ALLOWED," + + "USER_COMMENT AS USER_COMMENT," + + "CREATION_LOG_USER_NAME AS CREATION_LOG_USER_NAME," + "CREATION_LOG_HOST_NAME AS CREATION_LOG_HOST_NAME," + "CREATION_LOG_TIME AS CREATION_LOG_TIME," + + "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 " + "MOUNT_POLICY " + "ORDER BY " + "MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); + while (rset->next()) { + common::dataStructures::MountPolicy policy; + + policy.name = rset->columnString("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->columnString("USER_COMMENT"); + + policy.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + policy.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + policy.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + + policy.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + policy.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + policy.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + policies.push_back(policy); + } + + return policies; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyArchivePriority +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyArchivePriority(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t archivePriority) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "ARCHIVE_PRIORITY = :ARCHIVE_PRIORITY," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":ARCHIVE_PRIORITY", archivePriority); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyArchiveMinRequestAge +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyArchiveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t minArchiveRequestAge) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "ARCHIVE_MIN_REQUEST_AGE = :ARCHIVE_MIN_REQUEST_AGE," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":ARCHIVE_MIN_REQUEST_AGE", minArchiveRequestAge); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyRetrievePriority +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyRetrievePriority(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t retrievePriority) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "RETRIEVE_PRIORITY = :RETRIEVE_PRIORITY," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":RETRIEVE_PRIORITY", retrievePriority); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyRetrieveMinRequestAge +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyRetrieveMinRequestAge(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t minRetrieveRequestAge) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "RETRIEVE_MIN_REQUEST_AGE = :RETRIEVE_MIN_REQUEST_AGE," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":RETRIEVE_MIN_REQUEST_AGE", minRetrieveRequestAge); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyMaxDrivesAllowed +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyMaxDrivesAllowed(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const uint64_t maxDrivesAllowed) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "MAX_DRIVES_ALLOWED = :MAX_DRIVES_ALLOWED," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindUint64(":MAX_DRIVES_ALLOWED", maxDrivesAllowed); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// modifyMountPolicyComment +//------------------------------------------------------------------------------ +void RdbmsCatalogue::modifyMountPolicyComment(const common::dataStructures::SecurityIdentity &admin, + const std::string &name, const std::string &comment) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE MOUNT_POLICY SET " + "USER_COMMENT = :USER_COMMENT," + "LAST_UPDATE_USER_NAME = :LAST_UPDATE_USER_NAME," + "LAST_UPDATE_HOST_NAME = :LAST_UPDATE_HOST_NAME," + "LAST_UPDATE_TIME = :LAST_UPDATE_TIME " + "WHERE " + "MOUNT_POLICY_NAME = :MOUNT_POLICY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":USER_COMMENT", comment); + stmt->bindString(":LAST_UPDATE_USER_NAME", admin.username); + stmt->bindString(":LAST_UPDATE_HOST_NAME", admin.host); + stmt->bindUint64(":LAST_UPDATE_TIME", now); + stmt->bindString(":MOUNT_POLICY_NAME", name); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify mount policy ") + name + " because they do not exist"); + } + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// insertArchiveFile +//------------------------------------------------------------------------------ +void RdbmsCatalogue::insertArchiveFile(rdbms::PooledConn &conn, const rdbms::Stmt::AutocommitMode autocommitMode, + const ArchiveFileRow &row) { + try { + if(!storageClassExists(conn, row.diskInstance, row.storageClassName)) { + throw exception::UserError(std::string("Storage class ") + row.diskInstance + ":" + row.storageClassName + + " does not exist"); + } + + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO ARCHIVE_FILE(" + "ARCHIVE_FILE_ID," + "DISK_INSTANCE_NAME," + "DISK_FILE_ID," + "DISK_FILE_PATH," + "DISK_FILE_USER," + "DISK_FILE_GROUP," + "DISK_FILE_RECOVERY_BLOB," + "SIZE_IN_BYTES," + "CHECKSUM_TYPE," + "CHECKSUM_VALUE," + "STORAGE_CLASS_NAME," + "CREATION_TIME," + "RECONCILIATION_TIME)" + "VALUES(" + ":ARCHIVE_FILE_ID," + ":DISK_INSTANCE_NAME," + ":DISK_FILE_ID," + ":DISK_FILE_PATH," + ":DISK_FILE_USER," + ":DISK_FILE_GROUP," + ":DISK_FILE_RECOVERY_BLOB," + ":SIZE_IN_BYTES," + ":CHECKSUM_TYPE," + ":CHECKSUM_VALUE," + ":STORAGE_CLASS_NAME," + ":CREATION_TIME," + ":RECONCILIATION_TIME)"; + auto stmt = conn.createStmt(sql, autocommitMode); + + stmt->bindUint64(":ARCHIVE_FILE_ID", row.archiveFileId); + stmt->bindString(":DISK_INSTANCE_NAME", row.diskInstance); + stmt->bindString(":DISK_FILE_ID", row.diskFileId); + stmt->bindString(":DISK_FILE_PATH", row.diskFilePath); + stmt->bindString(":DISK_FILE_USER", row.diskFileUser); + stmt->bindString(":DISK_FILE_GROUP", row.diskFileGroup); + stmt->bindString(":DISK_FILE_RECOVERY_BLOB", row.diskFileRecoveryBlob); + stmt->bindUint64(":SIZE_IN_BYTES", row.size); + stmt->bindString(":CHECKSUM_TYPE", row.checksumType); + stmt->bindString(":CHECKSUM_VALUE", row.checksumValue); + stmt->bindString(":STORAGE_CLASS_NAME", row.storageClassName); + stmt->bindUint64(":CREATION_TIME", now); + stmt->bindUint64(":RECONCILIATION_TIME", now); + + stmt->executeNonQuery(); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getArchiveFileItor +//------------------------------------------------------------------------------ +std::unique_ptr<ArchiveFileItor> RdbmsCatalogue::getArchiveFileItor(const TapeFileSearchCriteria &searchCriteria, + const uint64_t nbArchiveFilesToPrefetch) const { + + checkTapeFileSearchCriteria(searchCriteria); + + try { + return cta::make_unique<ArchiveFileItorImpl>(*this, nbArchiveFilesToPrefetch, searchCriteria); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// checkTapeFileSearchCriteria +//------------------------------------------------------------------------------ +void RdbmsCatalogue::checkTapeFileSearchCriteria(const TapeFileSearchCriteria &searchCriteria) const { + auto conn = m_connPool.getConn(); + + if(searchCriteria.archiveFileId) { + if(!archiveFileIdExists(conn, searchCriteria.archiveFileId.value())) { + throw exception::UserError(std::string("Archive file with ID ") + + std::to_string(searchCriteria.archiveFileId.value()) + " does not exist"); + } + } + + if(searchCriteria.diskFileGroup && !searchCriteria.diskInstance) { + throw exception::UserError(std::string("Disk file group ") + searchCriteria.diskFileGroup.value() + " is ambiguous " + "without disk instance name"); + } + + if(searchCriteria.diskInstance && searchCriteria.diskFileGroup) { + if(!diskFileGroupExists(conn, searchCriteria.diskInstance.value(), searchCriteria.diskFileGroup.value())) { + throw exception::UserError(std::string("Disk file group ") + searchCriteria.diskInstance.value() + "::" + + searchCriteria.diskFileGroup.value() + " does not exist"); + } + } + + if(searchCriteria.diskFileId && !searchCriteria.diskInstance) { + throw exception::UserError(std::string("Disk file ID ") + searchCriteria.diskFileId.value() + " is ambiguous " + "without disk instance name"); + } + + if(searchCriteria.diskInstance && searchCriteria.diskFileId) { + if(!diskFileIdExists(conn, searchCriteria.diskInstance.value(), searchCriteria.diskFileId.value())) { + throw exception::UserError(std::string("Disk file ID ") + searchCriteria.diskInstance.value() + "::" + + searchCriteria.diskFileId.value() + " does not exist"); + } + } + + if(searchCriteria.diskFilePath && !searchCriteria.diskInstance) { + throw exception::UserError(std::string("Disk file path ") + searchCriteria.diskFilePath.value() + " is ambiguous " + "without disk instance name"); + } + + if(searchCriteria.diskInstance && searchCriteria.diskFilePath) { + if(!diskFilePathExists(conn, searchCriteria.diskInstance.value(), searchCriteria.diskFilePath.value())) { + throw exception::UserError(std::string("Disk file path ") + searchCriteria.diskInstance.value() + "::" + + searchCriteria.diskFilePath.value() + " does not exist"); + } + } + + if(searchCriteria.diskFileUser && !searchCriteria.diskInstance) { + throw exception::UserError(std::string("Disk file user ") + searchCriteria.diskFileUser.value() + " is ambiguous " + "without disk instance name"); + } + + if(searchCriteria.diskInstance && searchCriteria.diskFileUser) { + if(!diskFileUserExists(conn, searchCriteria.diskInstance.value(), searchCriteria.diskFileUser.value())) { + throw exception::UserError(std::string("Disk file user ") + searchCriteria.diskInstance.value() + "::" + + searchCriteria.diskFileUser.value() + " does not exist"); + } + } + + if(searchCriteria.storageClass && !searchCriteria.diskInstance) { + throw exception::UserError(std::string("Storage class ") + searchCriteria.storageClass.value() + " is ambiguous " + "without disk instance name"); + } + + if(searchCriteria.diskInstance && searchCriteria.storageClass) { + if(!storageClassExists(conn, searchCriteria.diskInstance.value(), searchCriteria.storageClass.value())) { + throw exception::UserError(std::string("Storage class ") + searchCriteria.diskInstance.value() + "::" + + searchCriteria.storageClass.value() + " does not exist"); + } + } + + if(searchCriteria.tapePool) { + if(!tapePoolExists(conn, searchCriteria.tapePool.value())) { + throw exception::UserError(std::string("Tape pool ") + searchCriteria.tapePool.value() + " does not exist"); + } + } + + if(searchCriteria.vid) { + if(!tapeExists(conn, searchCriteria.vid.value())) { + throw exception::UserError(std::string("Tape ") + searchCriteria.vid.value() + " does not exist"); + } + } +} + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +RdbmsCatalogue::ArchiveFileItorImpl::ArchiveFileItorImpl( + const RdbmsCatalogue &catalogue, + const uint64_t nbArchiveFilesToPrefetch, + const TapeFileSearchCriteria &searchCriteria): + m_catalogue(catalogue), + m_nbArchiveFilesToPrefetch(nbArchiveFilesToPrefetch), + m_searchCriteria(searchCriteria), + m_nextArchiveFileId(1) { + try { + if(1 > m_nbArchiveFilesToPrefetch) { + exception::Exception ex; + ex.getMessage() << "nbArchiveFilesToPrefetch must equal to or greater than 1: actual=" << + m_nbArchiveFilesToPrefetch; + throw ex; + } + m_prefechedArchiveFiles = m_catalogue.getArchiveFilesForItor(m_nextArchiveFileId, m_nbArchiveFilesToPrefetch, + m_searchCriteria); + if(!m_prefechedArchiveFiles.empty()) { + m_nextArchiveFileId = m_prefechedArchiveFiles.back().archiveFileID + 1; + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " +ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// destructor +//------------------------------------------------------------------------------ +RdbmsCatalogue::ArchiveFileItorImpl::~ArchiveFileItorImpl() { +} + +//------------------------------------------------------------------------------ +// hasMore +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::ArchiveFileItorImpl::hasMore() const { + return !m_prefechedArchiveFiles.empty(); +} + +//------------------------------------------------------------------------------ +// next +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFile RdbmsCatalogue::ArchiveFileItorImpl::next() { + try { + if(m_prefechedArchiveFiles.empty()) { + throw exception::Exception("No more archive files to iterate over"); + } + + common::dataStructures::ArchiveFile archiveFile = m_prefechedArchiveFiles.front(); + m_prefechedArchiveFiles.pop_front(); + + if(m_prefechedArchiveFiles.empty()) { + m_prefechedArchiveFiles = m_catalogue.getArchiveFilesForItor(m_nextArchiveFileId, m_nbArchiveFilesToPrefetch, + m_searchCriteria); + if(!m_prefechedArchiveFiles.empty()) { + m_nextArchiveFileId = m_prefechedArchiveFiles.back().archiveFileID + 1; + } + } + + return archiveFile; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getArchiveFilesForItor +//------------------------------------------------------------------------------ +std::list<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFilesForItor( + const uint64_t startingArchiveFileId, + const uint64_t maxNbArchiveFiles, + const TapeFileSearchCriteria &searchCriteria) const { + try { + std::string sql = + "SELECT " + "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID," + "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "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.SIZE_IN_BYTES AS SIZE_IN_BYTES," + "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_IN_BYTES AS COMPRESSED_SIZE_IN_BYTES," + "TAPE_FILE.COPY_NB AS COPY_NB," + "TAPE_FILE.CREATION_TIME AS TAPE_FILE_CREATION_TIME, " + "TAPE.TAPE_POOL_NAME AS TAPE_POOL_NAME " + "FROM " + "ARCHIVE_FILE " + "LEFT OUTER JOIN TAPE_FILE ON " + "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID " + "LEFT OUTER JOIN TAPE ON " + "TAPE_FILE.VID = TAPE.VID " + "WHERE " + "ARCHIVE_FILE.ARCHIVE_FILE_ID >= :STARTING_ARCHIVE_FILE_ID"; + if(searchCriteria.archiveFileId) { + sql += " AND ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID"; + } + if(searchCriteria.diskInstance) { + sql += " AND ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME"; + } + if(searchCriteria.diskFileId) { + sql += " AND ARCHIVE_FILE.DISK_FILE_ID = :DISK_FILE_ID"; + } + if(searchCriteria.diskFilePath) { + sql += " AND ARCHIVE_FILE.DISK_FILE_PATH = :DISK_FILE_PATH"; + } + if(searchCriteria.diskFileUser) { + sql += " AND ARCHIVE_FILE.DISK_FILE_USER = :DISK_FILE_USER"; + } + if(searchCriteria.diskFileGroup) { + sql += " AND ARCHIVE_FILE.DISK_FILE_GROUP = :DISK_FILE_GROUP"; + } + if(searchCriteria.storageClass) { + sql += " AND ARCHIVE_FILE.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + } + if(searchCriteria.vid) { + sql += " AND TAPE_FILE.VID = :VID"; + } + if(searchCriteria.tapeFileCopyNb) { + sql += " AND TAPE_FILE.COPY_NB = :TAPE_FILE_COPY_NB"; + } + if(searchCriteria.tapePool) { + sql += " AND TAPE.TAPE_POOL_NAME = :TAPE_POOL_NAME"; + } + sql += " ORDER BY ARCHIVE_FILE.ARCHIVE_FILE_ID, TAPE_FILE.COPY_NB"; + + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindUint64(":STARTING_ARCHIVE_FILE_ID", startingArchiveFileId); + if(searchCriteria.archiveFileId) { + stmt->bindUint64(":ARCHIVE_FILE_ID", searchCriteria.archiveFileId.value()); + } + if(searchCriteria.diskInstance) { + stmt->bindString(":DISK_INSTANCE_NAME", searchCriteria.diskInstance.value()); + } + if(searchCriteria.diskFileId) { + stmt->bindString(":DISK_FILE_ID", searchCriteria.diskFileId.value()); + } + if(searchCriteria.diskFilePath) { + stmt->bindString(":DISK_FILE_PATH", searchCriteria.diskFilePath.value()); + } + if(searchCriteria.diskFileUser) { + stmt->bindString(":DISK_FILE_USER", searchCriteria.diskFileUser.value()); + } + if(searchCriteria.diskFileGroup) { + stmt->bindString(":DISK_FILE_GROUP", searchCriteria.diskFileGroup.value()); + } + if(searchCriteria.storageClass) { + stmt->bindString(":STORAGE_CLASS_NAME", searchCriteria.storageClass.value()); + } + if(searchCriteria.vid) { + stmt->bindString(":VID", searchCriteria.vid.value()); + } + if(searchCriteria.tapeFileCopyNb) { + stmt->bindUint64(":TAPE_FILE_COPY_NB", searchCriteria.tapeFileCopyNb.value()); + } + if(searchCriteria.tapePool) { + stmt->bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value()); + } + auto rset = stmt->executeQuery(); + std::list<common::dataStructures::ArchiveFile> archiveFiles; + + // While the prefetch limit has not been exceeded + while(archiveFiles.size() <= maxNbArchiveFiles) { + + // Break the archive file loop if there are no more tape files + if(!rset->next()) { + break; + } + + const uint64_t archiveFileId = rset->columnUint64("ARCHIVE_FILE_ID"); + + // If the current tape file is for the next archive file + if(archiveFiles.empty() || archiveFiles.back().archiveFileID != archiveFileId) { + + // Break the archive file loop if creating the next archive file would exceed the prefetch limit + if(archiveFiles.size() == maxNbArchiveFiles) { + break; + } + + common::dataStructures::ArchiveFile archiveFile; + + archiveFile.archiveFileID = archiveFileId; + archiveFile.diskInstance = rset->columnString("DISK_INSTANCE_NAME"); + archiveFile.diskFileId = rset->columnString("DISK_FILE_ID"); + archiveFile.diskFileInfo.path = rset->columnString("DISK_FILE_PATH"); + archiveFile.diskFileInfo.owner = rset->columnString("DISK_FILE_USER"); + archiveFile.diskFileInfo.group = rset->columnString("DISK_FILE_GROUP"); + archiveFile.diskFileInfo.recoveryBlob = rset->columnString("DISK_FILE_RECOVERY_BLOB"); + archiveFile.fileSize = rset->columnUint64("SIZE_IN_BYTES"); + archiveFile.checksumType = rset->columnString("CHECKSUM_TYPE"); + archiveFile.checksumValue = rset->columnString("CHECKSUM_VALUE"); + archiveFile.storageClass = rset->columnString("STORAGE_CLASS_NAME"); + archiveFile.creationTime = rset->columnUint64("ARCHIVE_FILE_CREATION_TIME"); + archiveFile.reconciliationTime = rset->columnUint64("RECONCILIATION_TIME"); + + archiveFiles.push_back(archiveFile); + } + + common::dataStructures::ArchiveFile &archiveFile = archiveFiles.back(); + + // If there is a tape file + if(!rset->columnIsNull("VID")) { + common::dataStructures::TapeFile tapeFile; + tapeFile.vid = rset->columnString("VID"); + tapeFile.fSeq = rset->columnUint64("FSEQ"); + tapeFile.blockId = rset->columnUint64("BLOCK_ID"); + tapeFile.compressedSize = rset->columnUint64("COMPRESSED_SIZE_IN_BYTES"); + tapeFile.copyNb = rset->columnUint64("COPY_NB"); + tapeFile.creationTime = rset->columnUint64("TAPE_FILE_CREATION_TIME"); + tapeFile.checksumType = archiveFile.checksumType; // Duplicated for convenience + tapeFile.checksumValue = archiveFile.checksumValue; // Duplicated for convenience + + archiveFile.tapeFiles[rset->columnUint64("COPY_NB")] = tapeFile; + } + } + + return archiveFiles; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapeFileSummary +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFileSummary RdbmsCatalogue::getTapeFileSummary( + const TapeFileSearchCriteria &searchCriteria) const { + try { + std::string sql = + "SELECT " + "COALESCE(SUM(ARCHIVE_FILE.SIZE_IN_BYTES), 0) AS TOTAL_BYTES," + "COALESCE(SUM(TAPE_FILE.COMPRESSED_SIZE_IN_BYTES), 0) AS TOTAL_COMPRESSED_BYTES," + "COUNT(ARCHIVE_FILE.ARCHIVE_FILE_ID) AS TOTAL_FILES " + "FROM " + "ARCHIVE_FILE " + "INNER JOIN TAPE_FILE ON " + "ARCHIVE_FILE.ARCHIVE_FILE_ID = TAPE_FILE.ARCHIVE_FILE_ID " + "INNER JOIN TAPE ON " + "TAPE_FILE.VID = TAPE.VID"; + + if( + searchCriteria.archiveFileId || + searchCriteria.diskInstance || + searchCriteria.diskFileId || + searchCriteria.diskFilePath || + searchCriteria.diskFileUser || + searchCriteria.diskFileGroup || + searchCriteria.storageClass || + searchCriteria.vid || + searchCriteria.tapeFileCopyNb || + searchCriteria.tapePool) { + sql += " WHERE "; + } + + bool addedAWhereConstraint = false; + + if(searchCriteria.archiveFileId) { + sql += " ARCHIVE_FILE.ARCHIVE_FILE_ID = :ARCHIVE_FILE_ID"; + addedAWhereConstraint = true; + } + if(searchCriteria.diskInstance) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME"; + addedAWhereConstraint = true; + } + if(searchCriteria.diskFileId) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.DISK_FILE_ID = :DISK_FILE_ID"; + addedAWhereConstraint = true; + } + if(searchCriteria.diskFilePath) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.DISK_FILE_PATH = :DISK_FILE_PATH"; + addedAWhereConstraint = true; + } + if(searchCriteria.diskFileUser) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.DISK_FILE_USER = :DISK_FILE_USER"; + addedAWhereConstraint = true; + } + if(searchCriteria.diskFileGroup) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.DISK_FILE_GROUP = :DISK_FILE_GROUP"; + addedAWhereConstraint = true; + } + if(searchCriteria.storageClass) { + if(addedAWhereConstraint) sql += " AND "; + sql += "ARCHIVE_FILE.STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + addedAWhereConstraint = true; + } + if(searchCriteria.vid) { + if(addedAWhereConstraint) sql += " AND "; + sql += "TAPE_FILE.VID = :VID"; + addedAWhereConstraint = true; + } + if(searchCriteria.tapeFileCopyNb) { + if(addedAWhereConstraint) sql += " AND "; + sql += "TAPE_FILE.COPY_NB = :TAPE_FILE_COPY_NB"; + addedAWhereConstraint = true; + } + if(searchCriteria.tapePool) { + if(addedAWhereConstraint) sql += " AND "; + sql += "TAPE.TAPE_POOL_NAME = :TAPE_POOL_NAME"; + } + + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + if(searchCriteria.archiveFileId) { + stmt->bindUint64(":ARCHIVE_FILE_ID", searchCriteria.archiveFileId.value()); + } + if(searchCriteria.diskInstance) { + stmt->bindString(":DISK_INSTANCE_NAME", searchCriteria.diskInstance.value()); + } + if(searchCriteria.diskFileId) { + stmt->bindString(":DISK_FILE_ID", searchCriteria.diskFileId.value()); + } + if(searchCriteria.diskFilePath) { + stmt->bindString(":DISK_FILE_PATH", searchCriteria.diskFilePath.value()); + } + if(searchCriteria.diskFileUser) { + stmt->bindString(":DISK_FILE_USER", searchCriteria.diskFileUser.value()); + } + if(searchCriteria.diskFileGroup) { + stmt->bindString(":DISK_FILE_GROUP", searchCriteria.diskFileGroup.value()); + } + if(searchCriteria.storageClass) { + stmt->bindString(":STORAGE_CLASS_NAME", searchCriteria.storageClass.value()); + } + if(searchCriteria.vid) { + stmt->bindString(":VID", searchCriteria.vid.value()); + } + if(searchCriteria.tapeFileCopyNb) { + stmt->bindUint64(":TAPE_FILE_COPY_NB", searchCriteria.tapeFileCopyNb.value()); + } + if(searchCriteria.tapePool) { + stmt->bindString(":TAPE_POOL_NAME", searchCriteria.tapePool.value()); + } + auto rset = stmt->executeQuery(); + if(!rset->next()) { + throw exception::Exception("SELECT COUNT statement did not returned a row"); + } + + common::dataStructures::ArchiveFileSummary summary; + summary.totalBytes = rset->columnUint64("TOTAL_BYTES"); + summary.totalCompressedBytes = rset->columnUint64("TOTAL_COMPRESSED_BYTES"); + summary.totalFiles = rset->columnUint64("TOTAL_FILES"); + return summary; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getArchiveFileById +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFile RdbmsCatalogue::getArchiveFileById(const uint64_t id) { + try { + auto conn = m_connPool.getConn(); + std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile(getArchiveFile(conn, id)); + + // Throw an exception if the archive file does not exist + if(nullptr == archiveFile.get()) { + exception::Exception ex; + ex.getMessage() << "No such archive file with ID " << id; + throw (ex); + } + + return *archiveFile; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// tapeLabelled +//------------------------------------------------------------------------------ +void RdbmsCatalogue::tapeLabelled(const std::string &vid, const std::string &drive, const bool lbpIsOn) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LABEL_DRIVE = :LABEL_DRIVE," + "LABEL_TIME = :LABEL_TIME," + "LBP_IS_ON = :LBP_IS_ON " + "WHERE " + "VID = :VID"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":LABEL_DRIVE", drive); + stmt->bindUint64(":LABEL_TIME", now); + stmt->bindBool(":LBP_IS_ON", lbpIsOn); + stmt->bindString(":VID", vid); + stmt->executeNonQuery(); + + if(0 == stmt->getNbAffectedRows()) { + throw exception::UserError(std::string("Cannot modify tape ") + vid + " because it does not exist"); + } + } catch(exception::UserError &) { + throw; + } catch (exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// prepareForNewFile +//------------------------------------------------------------------------------ +common::dataStructures::ArchiveFileQueueCriteria RdbmsCatalogue::prepareForNewFile(const std::string &diskInstanceName, + const std::string &storageClassName, const common::dataStructures::UserIdentity &user) { + try { + auto conn = m_connPool.getConn(); + const common::dataStructures::TapeCopyToPoolMap copyToPoolMap = getTapeCopyToPoolMap(conn, diskInstanceName, + storageClassName); + const uint64_t expectedNbRoutes = getExpectedNbArchiveRoutes(conn, diskInstanceName, storageClassName); + + // Check that the number of archive routes is correct + if(copyToPoolMap.empty()) { + exception::Exception ex; + ex.getMessage() << "Storage class " << diskInstanceName << ":" << storageClassName << " has no archive routes"; + throw ex; + } + if(copyToPoolMap.size() != expectedNbRoutes) { + exception::Exception ex; + ex.getMessage() << "Storage class " << diskInstanceName << ":" << storageClassName << " does not have the" + " expected number of archive routes routes: expected=" << expectedNbRoutes << ", actual=" << + copyToPoolMap.size(); + throw ex; + } + + const RequesterAndGroupMountPolicies mountPolicies = getMountPolicies(conn, diskInstanceName, user.name, + user.group); + // Requester mount policies overrule requester group mount policies + common::dataStructures::MountPolicy mountPolicy; + if(!mountPolicies.requesterMountPolicies.empty()) { + mountPolicy = mountPolicies.requesterMountPolicies.front(); + } else if(!mountPolicies.requesterGroupMountPolicies.empty()) { + mountPolicy = mountPolicies.requesterGroupMountPolicies.front(); + } else { + exception::UserError ue; + ue.getMessage() << "Cannot archive file because there are no mount rules for the requester or their group:" + " storageClass=" << storageClassName << " requester=" << diskInstanceName << ":" << user.name << ":" << + user.group; + throw ue; + } + + // Now that we have both the archive routes and the mount policy it's safe to + // consume an archive file identifierarchiveFileId + const uint64_t archiveFileId = getNextArchiveFileId(conn); + + return common::dataStructures::ArchiveFileQueueCriteria(archiveFileId, copyToPoolMap, mountPolicy); + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapeCopyToPoolMap +//------------------------------------------------------------------------------ +common::dataStructures::TapeCopyToPoolMap RdbmsCatalogue::getTapeCopyToPoolMap(rdbms::PooledConn &conn, + const std::string &diskInstanceName, const std::string &storageClassName) const { + try { + common::dataStructures::TapeCopyToPoolMap copyToPoolMap; + const char *const sql = + "SELECT " + "COPY_NB AS COPY_NB," + "TAPE_POOL_NAME AS TAPE_POOL_NAME " + "FROM " + "ARCHIVE_ROUTE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + auto rset = stmt->executeQuery(); + while (rset->next()) { + const uint64_t copyNb = rset->columnUint64("COPY_NB"); + const std::string tapePoolName = rset->columnString("TAPE_POOL_NAME"); + copyToPoolMap[copyNb] = tapePoolName; + } + + return copyToPoolMap; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getExpectedNbArchiveRoutes +//------------------------------------------------------------------------------ +uint64_t RdbmsCatalogue::getExpectedNbArchiveRoutes(rdbms::PooledConn &conn, const std::string &diskInstanceName, + const std::string &storageClassName) const { + try { + const char *const sql = + "SELECT " + "COUNT(*) AS NB_ROUTES " + "FROM " + "ARCHIVE_ROUTE " + "WHERE " + "DISK_INSTANCE_NAME = :DISK_INSTANCE_NAME AND " + "STORAGE_CLASS_NAME = :STORAGE_CLASS_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":STORAGE_CLASS_NAME", storageClassName); + auto rset = stmt->executeQuery(); + if(!rset->next()) { + throw exception::Exception("Result set of SELECT COUNT(*) is empty"); + } + return rset->columnUint64("NB_ROUTES"); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// updateTape +//------------------------------------------------------------------------------ +void RdbmsCatalogue::updateTape( + rdbms::PooledConn &conn, + const rdbms::Stmt::AutocommitMode autocommitMode, + const std::string &vid, + const uint64_t lastFSeq, + const uint64_t compressedBytesWritten, + const std::string &tapeDrive) { + try { + const time_t now = time(nullptr); + const char *const sql = + "UPDATE TAPE SET " + "LAST_FSEQ = :LAST_FSEQ," + "DATA_IN_BYTES = DATA_IN_BYTES + :DATA_IN_BYTES," + "LAST_WRITE_DRIVE = :LAST_WRITE_DRIVE," + "LAST_WRITE_TIME = :LAST_WRITE_TIME " + "WHERE " + "VID = :VID"; + auto stmt = conn.createStmt(sql, autocommitMode); + stmt->bindString(":VID", vid); + stmt->bindUint64(":LAST_FSEQ", lastFSeq); + stmt->bindUint64(":DATA_IN_BYTES", compressedBytesWritten); + stmt->bindString(":LAST_WRITE_DRIVE", tapeDrive); + stmt->bindUint64(":LAST_WRITE_TIME", now); + stmt->executeNonQuery(); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// throwIfCommonEventDataMismatch +//------------------------------------------------------------------------------ +void RdbmsCatalogue::throwIfCommonEventDataMismatch(const common::dataStructures::ArchiveFile &expected, + const TapeFileWritten &actual) const { + // Throw an exception if the common disk information of this tape file + // written event does not match the previous + if(expected.diskFileId != actual.diskFileId) { + exception::Exception ex; + ex.getMessage() << "Disk file ID mismatch: expected=" << expected.diskFileId << " actual=" << + actual.diskFileId; + throw ex; + } + if(expected.fileSize != actual.size) { + exception::Exception ex; + ex.getMessage() << "File size mismatch: expected=" << expected.fileSize << " actual=" << actual.size; + throw ex; + } + if(expected.storageClass != actual.storageClassName) { + exception::Exception ex; + ex.getMessage() << "Storage class mismatch: expected=" << expected.storageClass << " actual=" << + actual.storageClassName; + throw ex; + } + if(expected.diskInstance != actual.diskInstance) { + exception::Exception ex; + ex.getMessage() << "Disk instance mismatch: expected=" << expected.diskInstance << " actual=" << + actual.diskInstance; + throw ex; + } + if(expected.diskFileInfo.path != actual.diskFilePath) { + exception::Exception ex; + ex.getMessage() << "Disk file path mismatch: expected=" << expected.diskFileInfo.path << " actual=" << + actual.diskFilePath; + throw ex; + } + if(expected.diskFileInfo.owner != actual.diskFileUser) { + exception::Exception ex; + ex.getMessage() << "Disk file user mismatch: expected=" << expected.diskFileInfo.owner << " actual=" << + actual.diskFileUser; + throw ex; + } + if(expected.diskFileInfo.group != actual.diskFileGroup) { + exception::Exception ex; + ex.getMessage() << "Disk file group mismatch: expected=" << expected.diskFileInfo.group << " actual=" << + actual.diskFileGroup; + throw ex; + } + if(expected.diskFileInfo.group != actual.diskFileGroup) { + exception::Exception ex; + ex.getMessage() << "Disk recovery blob mismatch"; + throw ex; + } + +} + +//------------------------------------------------------------------------------ +// prepareToRetrieveFile +//------------------------------------------------------------------------------ +common::dataStructures::RetrieveFileQueueCriteria RdbmsCatalogue::prepareToRetrieveFile( + const std::string &diskInstanceName, + const uint64_t archiveFileId, + const common::dataStructures::UserIdentity &user) { + try { + auto conn = m_connPool.getConn(); + std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile = getArchiveFile(conn, archiveFileId); + if(nullptr == archiveFile.get()) { + exception::Exception ex; + ex.getMessage() << "Archive file with ID " << archiveFileId << " does not exist"; + throw ex; + } + + if(diskInstanceName != archiveFile->diskInstance) { + exception::UserError ue; + ue.getMessage() << "Cannot retrieve file because the disk instance of the request does not match that of the" + " archived file: archiveFileId=" << archiveFileId << " path=" << archiveFile->diskFileInfo.path << + " requestDiskInstance=" << diskInstanceName << " archiveFileDiskInstance=" << archiveFile->diskInstance; + throw ue; + } + + const RequesterAndGroupMountPolicies mountPolicies = getMountPolicies(conn, diskInstanceName, user.name, + user.group); + // Requester mount policies overrule requester group mount policies + common::dataStructures::MountPolicy mountPolicy; + if(!mountPolicies.requesterMountPolicies.empty()) { + mountPolicy = mountPolicies.requesterMountPolicies.front(); + } else if(!mountPolicies.requesterGroupMountPolicies.empty()) { + mountPolicy = mountPolicies.requesterGroupMountPolicies.front(); + } else { + exception::UserError ue; + ue.getMessage() << "Cannot retrieve file because there are no mount rules for the requester or their group:" << + " archiveFileId=" << archiveFileId << " path=" << archiveFile->diskFileInfo.path << " requester=" << + diskInstanceName << ":" << user.name << ":" << user.group; + throw ue; + } + + common::dataStructures::RetrieveFileQueueCriteria criteria; + criteria.archiveFile = *archiveFile; + criteria.mountPolicy = mountPolicy; + return criteria; + } catch(exception::UserError &) { + throw; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getMountPolicies +//------------------------------------------------------------------------------ +RequesterAndGroupMountPolicies RdbmsCatalogue::getMountPolicies( + rdbms::PooledConn &conn, + const std::string &diskInstanceName, + const std::string &requesterName, + const std::string &requesterGroupName) const { + try { + const char *const sql = + "SELECT " + "'REQUESTER' AS RULE_TYPE," + "REQUESTER_MOUNT_RULE.REQUESTER_NAME AS ASSIGNEE," + + "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 " + "REQUESTER_MOUNT_RULE " + "INNER JOIN " + "MOUNT_POLICY " + "ON " + "REQUESTER_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME " + "WHERE " + "REQUESTER_MOUNT_RULE.DISK_INSTANCE_NAME = :REQUESTER_DISK_INSTANCE_NAME AND " + "REQUESTER_MOUNT_RULE.REQUESTER_NAME = :REQUESTER_NAME " + "UNION " + "SELECT " + "'REQUESTER_GROUP' AS RULE_TYPE," + "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME AS ASSIGNEE," + + "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 " + "REQUESTER_GROUP_MOUNT_RULE " + "INNER JOIN " + "MOUNT_POLICY " + "ON " + "REQUESTER_GROUP_MOUNT_RULE.MOUNT_POLICY_NAME = MOUNT_POLICY.MOUNT_POLICY_NAME " + "WHERE " + "REQUESTER_GROUP_MOUNT_RULE.DISK_INSTANCE_NAME = :GROUP_DISK_INSTANCE_NAME AND " + "REQUESTER_GROUP_MOUNT_RULE.REQUESTER_GROUP_NAME = :REQUESTER_GROUP_NAME"; + + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":REQUESTER_DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":GROUP_DISK_INSTANCE_NAME", diskInstanceName); + stmt->bindString(":REQUESTER_NAME", requesterName); + stmt->bindString(":REQUESTER_GROUP_NAME", requesterGroupName); + auto rset = stmt->executeQuery(); + + RequesterAndGroupMountPolicies policies; + while(rset->next()) { + common::dataStructures::MountPolicy policy; + + policy.name = rset->columnString("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->columnString("USER_COMMENT"); + policy.creationLog.username = rset->columnString("CREATION_LOG_USER_NAME"); + policy.creationLog.host = rset->columnString("CREATION_LOG_HOST_NAME"); + policy.creationLog.time = rset->columnUint64("CREATION_LOG_TIME"); + policy.lastModificationLog.username = rset->columnString("LAST_UPDATE_USER_NAME"); + policy.lastModificationLog.host = rset->columnString("LAST_UPDATE_HOST_NAME"); + policy.lastModificationLog.time = rset->columnUint64("LAST_UPDATE_TIME"); + + if(rset->columnString("RULE_TYPE") == "REQUESTER") { + policies.requesterMountPolicies.push_back(policy); + } else { + policies.requesterGroupMountPolicies.push_back(policy); + } + } + + return policies; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// isAdmin +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::isAdmin(const common::dataStructures::SecurityIdentity &admin) const { + auto conn = m_connPool.getConn(); + return userIsAdmin(conn, admin.username) && hostIsAdmin(conn, admin.host); +} + +//------------------------------------------------------------------------------ +// userIsAdmin +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::userIsAdmin(rdbms::PooledConn &conn, const std::string &userName) const { + const char *const sql = + "SELECT " + "ADMIN_USER_NAME AS ADMIN_USER_NAME " + "FROM " + "ADMIN_USER " + "WHERE " + "ADMIN_USER_NAME = :ADMIN_USER_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":ADMIN_USER_NAME", userName); + auto rset = stmt->executeQuery(); + return rset->next(); +} + +//------------------------------------------------------------------------------ +// hostIsAdmin +//------------------------------------------------------------------------------ +bool RdbmsCatalogue::hostIsAdmin(rdbms::PooledConn &conn, const std::string &hostName) const { + const char *const sql = + "SELECT " + "ADMIN_HOST_NAME AS ADMIN_HOST_NAME " + "FROM " + "ADMIN_HOST " + "WHERE " + "ADMIN_HOST_NAME = :ADMIN_HOST_NAME"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":ADMIN_HOST_NAME", hostName); + auto rset = stmt->executeQuery(); + return rset->next(); +} + +//------------------------------------------------------------------------------ +// getTapesForWriting +//------------------------------------------------------------------------------ +std::list<TapeForWriting> RdbmsCatalogue::getTapesForWriting(const std::string &logicalLibraryName) const { + try { + std::list<TapeForWriting> tapes; + const char *const sql = + "SELECT " + "VID AS VID," + "TAPE_POOL_NAME AS TAPE_POOL_NAME," + "CAPACITY_IN_BYTES AS CAPACITY_IN_BYTES," + "DATA_IN_BYTES AS DATA_IN_BYTES," + "LAST_FSEQ AS LAST_FSEQ " + "FROM " + "TAPE " + "WHERE " +// "LBP_IS_ON IS NOT NULL AND " // Set when the tape has been labelled +// "LABEL_DRIVE IS NOT NULL AND " // Set when the tape has been labelled +// "LABEL_TIME IS NOT NULL AND " // Set when the tape has been labelled + "IS_DISABLED = 0 AND " + "IS_FULL = 0 AND " + "LOGICAL_LIBRARY_NAME = :LOGICAL_LIBRARY_NAME"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":LOGICAL_LIBRARY_NAME", logicalLibraryName); + auto rset = stmt->executeQuery(); + while (rset->next()) { + TapeForWriting tape; + tape.vid = rset->columnString("VID"); + tape.tapePool = rset->columnString("TAPE_POOL_NAME"); + tape.capacityInBytes = rset->columnUint64("CAPACITY_IN_BYTES"); + tape.dataOnTapeInBytes = rset->columnUint64("DATA_IN_BYTES"); + tape.lastFSeq = rset->columnUint64("LAST_FSEQ"); + + tapes.push_back(tape); + } + + return tapes; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// insertTapeFile +//------------------------------------------------------------------------------ +void RdbmsCatalogue::insertTapeFile( + rdbms::PooledConn &conn, + const rdbms::Stmt::AutocommitMode autocommitMode, + const common::dataStructures::TapeFile &tapeFile, + const uint64_t archiveFileId) { + try { + const time_t now = time(nullptr); + const char *const sql = + "INSERT INTO TAPE_FILE(" + "VID," + "FSEQ," + "BLOCK_ID," + "COMPRESSED_SIZE_IN_BYTES," + "COPY_NB," + "CREATION_TIME," + "ARCHIVE_FILE_ID)" + "VALUES(" + ":VID," + ":FSEQ," + ":BLOCK_ID," + ":COMPRESSED_SIZE_IN_BYTES," + ":COPY_NB," + ":CREATION_TIME," + ":ARCHIVE_FILE_ID)"; + auto stmt = conn.createStmt(sql, autocommitMode); + + stmt->bindString(":VID", tapeFile.vid); + stmt->bindUint64(":FSEQ", tapeFile.fSeq); + stmt->bindUint64(":BLOCK_ID", tapeFile.blockId); + stmt->bindUint64(":COMPRESSED_SIZE_IN_BYTES", tapeFile.compressedSize); + stmt->bindUint64(":COPY_NB", tapeFile.copyNb); + stmt->bindUint64(":CREATION_TIME", now); + stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + + stmt->executeNonQuery(); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// setTapeLastFseq +//------------------------------------------------------------------------------ +void RdbmsCatalogue::setTapeLastFSeq(rdbms::PooledConn &conn, const std::string &vid, const uint64_t lastFSeq) { + try { + threading::MutexLocker locker(m_mutex); + + const uint64_t currentValue = getTapeLastFSeq(conn, vid); + if(lastFSeq != currentValue + 1) { + exception::Exception ex; + ex.getMessage() << "The last FSeq MUST be incremented by exactly one: currentValue=" << currentValue << + ",nextValue=" << lastFSeq; + throw ex; + } + const char *const sql = + "UPDATE TAPE SET " + "LAST_FSEQ = :LAST_FSEQ " + "WHERE " + "VID=:VID"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::ON); + stmt->bindString(":VID", vid); + stmt->bindUint64(":LAST_FSEQ", lastFSeq); + stmt->executeNonQuery(); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getTapeLastFSeq +//------------------------------------------------------------------------------ +uint64_t RdbmsCatalogue::getTapeLastFSeq(rdbms::PooledConn &conn, const std::string &vid) const { + try { + const char *const sql = + "SELECT " + "LAST_FSEQ AS LAST_FSEQ " + "FROM " + "TAPE " + "WHERE " + "VID = :VID"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindString(":VID", vid); + auto rset = stmt->executeQuery(); + if(rset->next()) { + return rset->columnUint64("LAST_FSEQ"); + } else { + throw exception::Exception(std::string("No such tape with vid=") + vid); + } + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// getArchiveFile +//------------------------------------------------------------------------------ +std::unique_ptr<common::dataStructures::ArchiveFile> RdbmsCatalogue::getArchiveFile(rdbms::PooledConn &conn, + const uint64_t archiveFileId) const { + try { + const char *const sql = + "SELECT " + "ARCHIVE_FILE.ARCHIVE_FILE_ID AS ARCHIVE_FILE_ID," + "ARCHIVE_FILE.DISK_INSTANCE_NAME AS DISK_INSTANCE_NAME," + "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.SIZE_IN_BYTES AS SIZE_IN_BYTES," + "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_IN_BYTES AS COMPRESSED_SIZE_IN_BYTES," + "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"; + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + stmt->bindUint64(":ARCHIVE_FILE_ID", archiveFileId); + auto rset = stmt->executeQuery(); + std::unique_ptr<common::dataStructures::ArchiveFile> archiveFile; + while (rset->next()) { + if(nullptr == archiveFile.get()) { + archiveFile = cta::make_unique<common::dataStructures::ArchiveFile>(); + + archiveFile->archiveFileID = rset->columnUint64("ARCHIVE_FILE_ID"); + archiveFile->diskInstance = rset->columnString("DISK_INSTANCE_NAME"); + archiveFile->diskFileId = rset->columnString("DISK_FILE_ID"); + archiveFile->diskFileInfo.path = rset->columnString("DISK_FILE_PATH"); + archiveFile->diskFileInfo.owner = rset->columnString("DISK_FILE_USER"); + archiveFile->diskFileInfo.group = rset->columnString("DISK_FILE_GROUP"); + archiveFile->diskFileInfo.recoveryBlob = rset->columnString("DISK_FILE_RECOVERY_BLOB"); + archiveFile->fileSize = rset->columnUint64("SIZE_IN_BYTES"); + archiveFile->checksumType = rset->columnString("CHECKSUM_TYPE"); + archiveFile->checksumValue = rset->columnString("CHECKSUM_VALUE"); + archiveFile->storageClass = rset->columnString("STORAGE_CLASS_NAME"); + archiveFile->creationTime = rset->columnUint64("ARCHIVE_FILE_CREATION_TIME"); + archiveFile->reconciliationTime = rset->columnUint64("RECONCILIATION_TIME"); + } + + // If there is a tape file + if(!rset->columnIsNull("VID")) { + // Add the tape file to the archive file's in-memory structure + common::dataStructures::TapeFile tapeFile; + tapeFile.vid = rset->columnString("VID"); + tapeFile.fSeq = rset->columnUint64("FSEQ"); + tapeFile.blockId = rset->columnUint64("BLOCK_ID"); + tapeFile.compressedSize = rset->columnUint64("COMPRESSED_SIZE_IN_BYTES"); + tapeFile.copyNb = rset->columnUint64("COPY_NB"); + tapeFile.creationTime = rset->columnUint64("TAPE_FILE_CREATION_TIME"); + tapeFile.checksumType = archiveFile->checksumType; // Duplicated for convenience + tapeFile.checksumValue = archiveFile->checksumValue; // Duplicated for convenience + + archiveFile->tapeFiles[rset->columnUint64("COPY_NB")] = tapeFile; + } + } + + return archiveFile; + } catch(exception::Exception &ex) { + throw exception::Exception(std::string(__FUNCTION__) + " failed: " + ex.getMessage().str()); + } +} + +//------------------------------------------------------------------------------ +// ping +//------------------------------------------------------------------------------ +void RdbmsCatalogue::ping() { + const char *const sql = "SELECT COUNT(*) FROM CTA_CATALOGUE"; + auto conn = m_connPool.getConn(); + auto stmt = conn.createStmt(sql, rdbms::Stmt::AutocommitMode::OFF); + auto rset = stmt->executeQuery(); +} + +//------------------------------------------------------------------------------ +// checkTapeWrittenFilesAreSet +//------------------------------------------------------------------------------ +void RdbmsCatalogue::checkTapeFileWrittenFieldsAreSet(const TapeFileWritten &event) { + try { + if(event.diskInstance.empty()) throw exception::Exception("diskInstance is an empty string"); + if(event.diskFileId.empty()) throw exception::Exception("diskFileId is an empty string"); + if(event.diskFilePath.empty()) throw exception::Exception("diskFilePath is an empty string"); + if(event.diskFileUser.empty()) throw exception::Exception("diskFileUser is an empty string"); + if(event.diskFileGroup.empty()) throw exception::Exception("diskFileGroup is an empty string"); + if(event.diskFileRecoveryBlob.empty()) throw exception::Exception("diskFileRecoveryBlob is an empty string"); + if(0 == event.size) throw exception::Exception("size is 0"); + if(event.checksumType.empty()) throw exception::Exception("checksumType is an empty string"); + if(event.checksumValue.empty()) throw exception::Exception("checksumValue is an empty string"); + if(event.storageClassName.empty()) throw exception::Exception("storageClassName is an empty string"); + if(event.vid.empty()) throw exception::Exception("vid is an empty string"); + if(0 == event.fSeq) throw exception::Exception("fSeq is 0"); + if(0 == event.blockId && event.fSeq != 1) throw exception::Exception("blockId is 0 and fSeq is not 1"); + if(0 == event.compressedSize) throw exception::Exception("compressedSize is 0"); + if(0 == event.copyNb) throw exception::Exception("copyNb is 0"); + if(event.tapeDrive.empty()) throw exception::Exception("tapeDrive is an empty string"); + } catch(exception::Exception &ex) { + throw exception::Exception(std::string("TapeFileWrittenEvent is invalid: ") + ex.getMessage().str()); + } +} + +} // namespace catalogue +} // namespace cta diff --git a/catalogue/RdbmsArchiveFileItor.hpp b/catalogue/RdbmsArchiveFileItor.hpp new file mode 100644 index 0000000000000000000000000000000000000000..baa8d89006656ca9e2b916a963d5eff334dee90f --- /dev/null +++ b/catalogue/RdbmsArchiveFileItor.hpp @@ -0,0 +1,88 @@ +/* + * 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 "catalogue/ArchiveFileItor.hpp" + +namespace cta { +namespace catalogue { + +/** + * Rdbms implementation of ArchiveFileItor. + */ +class RdbmsArchiveFileItor: public ArchiveFileItor { +public: + + /** + * Constructor. + * + * @param catalogue The RdbmsCatalogue. + * @param nbArchiveFilesToPrefetch The number of archive files to prefetch. + * @param searchCriteria The search criteria. + */ + ArchiveFileItorImpl( + const RdbmsCatalogue &catalogue, + const uint64_t nbArchiveFilesToPrefetch, + const TapeFileSearchCriteria &searchCriteria); + + /** + * Destructor. + */ + ~ArchiveFileItorImpl() override; + + /** + * Returns true if a call to next would return another archive file. + */ + bool hasMore() const override; + + /** + * Returns the next archive or throws an exception if there isn't one. + */ + common::dataStructures::ArchiveFile next() override; + +private: + + /** + * The RdbmsCatalogue. + */ + const RdbmsCatalogue &m_catalogue; + + /** + * The number of archive files to prefetch. + */ + const uint64_t m_nbArchiveFilesToPrefetch; + + /** + * The search criteria. + */ + TapeFileSearchCriteria m_searchCriteria; + + /** + * The current offset into the list of archive files in the form of an + * archive file ID. + */ + uint64_t m_nextArchiveFileId; + + /** + * The current list of prefetched archive files. + */ + std::list<common::dataStructures::ArchiveFile> m_prefechedArchiveFiles; +}; // class ArchiveFileItorImpl + +} // namespace catalogue