Commit 1ad142e6 authored by Cedric CAFFY's avatar Cedric CAFFY
Browse files

Implemented Catalogue::createVirtualOrganization() + unit tests

parent 4509fc37
......@@ -53,6 +53,7 @@
#include "common/dataStructures/TapeFile.hpp"
#include "common/dataStructures/UpdateFileInfoRequest.hpp"
#include "common/dataStructures/RequesterIdentity.hpp"
#include "common/dataStructures/VirtualOrganization.hpp"
#include "common/dataStructures/VidToTapeMap.hpp"
#include "common/dataStructures/WriteTestResult.hpp"
#include "disk/DiskSystem.hpp"
......@@ -225,6 +226,13 @@ public:
virtual std::list<common::dataStructures::AdminUser> getAdminUsers() const = 0;
virtual void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) = 0;
/**
* Creates the specified Virtual Organization
* @param admin The administrator.
* @param vo the Virtual Organization
*/
virtual void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) = 0;
/**
* Creates the specified storage class.
*
......
......@@ -115,7 +115,11 @@ public:
void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) override {
return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyAdminUserComment(admin, username, comment);}, m_maxTriesToConnect);
}
void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override {
return retryOnLostConnection(m_log, [&]{return m_catalogue->createVirtualOrganization(admin, vo);}, m_maxTriesToConnect);
}
void createStorageClass(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::StorageClass &storageClass) override {
return retryOnLostConnection(m_log, [&]{return m_catalogue->createStorageClass(admin, storageClass);}, m_maxTriesToConnect);
}
......
......@@ -15286,4 +15286,43 @@ TEST_P(cta_catalogue_CatalogueTest, getSchemaVersion) {
ASSERT_EQ((uint64_t)CTA_CATALOGUE_SCHEMA_VERSION_MINOR,schemaDbVersion.getSchemaVersion<catalogue::SchemaVersion::MajorMinor>().second);
}
 
TEST_P(cta_catalogue_CatalogueTest, createVirtualOrganization) {
using namespace cta;
common::dataStructures::VirtualOrganization vo;
vo.name = "VO";
vo.comment = "Comment";
ASSERT_NO_THROW(m_catalogue->createVirtualOrganization(m_admin,vo));
}
TEST_P(cta_catalogue_CatalogueTest, createVirtualOrganizationAlreadyExists) {
using namespace cta;
common::dataStructures::VirtualOrganization vo;
vo.name = "VO";
vo.comment = "Comment";
ASSERT_NO_THROW(m_catalogue->createVirtualOrganization(m_admin,vo));
ASSERT_THROW(m_catalogue->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
}
TEST_P(cta_catalogue_CatalogueTest, createVirtualOrganizationEmptyComment) {
using namespace cta;
common::dataStructures::VirtualOrganization vo;
vo.name = "VO";
ASSERT_THROW(m_catalogue->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
}
TEST_P(cta_catalogue_CatalogueTest, createVirtualOrganizationEmptyName) {
using namespace cta;
common::dataStructures::VirtualOrganization vo;
vo.comment = "comment";
ASSERT_THROW(m_catalogue->createVirtualOrganization(m_admin,vo),cta::exception::UserError);
}
} // namespace unitTests
......@@ -87,6 +87,8 @@ public:
bool isAdmin(const common::dataStructures::SecurityIdentity& admin) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void modifyActivitiesFairShareWeight(const common::dataStructures::SecurityIdentity& admin, const std::string& diskInstanceName, const std::string& acttivity, double weight, const std::string & comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void modifyAdminUserComment(const common::dataStructures::SecurityIdentity& admin, const std::string& username, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void modifyArchiveRouteComment(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void modifyArchiveRouteTapePoolName(const common::dataStructures::SecurityIdentity& admin, const std::string& storageClassName, const uint32_t copyNb, const std::string& tapePoolName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
void modifyLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &currentName, const std::string &newName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
......
......@@ -137,6 +137,48 @@ uint64_t MysqlCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
}
}
//------------------------------------------------------------------------------
// getNextVirtualOrganizationId
//------------------------------------------------------------------------------
uint64_t MysqlCatalogue::getNextVirtualOrganizationId(rdbms::Conn& conn) {
try {
rdbms::AutoRollback autoRollback(conn);
conn.executeNonQuery("START TRANSACTION");
{
const char *const sql =
"UPDATE VIRTUAL_ORGANIZATION_ID SET ID = LAST_INSERT_ID(ID + 1)";
auto stmt = conn.createStmt(sql);
stmt.executeNonQuery();
}
uint64_t virtualOrganizationId = 0;
{
const char *const sql =
"SELECT LAST_INSERT_ID() AS ID ";
auto stmt = conn.createStmt(sql);
auto rset = stmt.executeQuery();
if(!rset.next()) {
throw exception::Exception("VIRTUAL_ORGANIZATION_ID table is empty");
}
virtualOrganizationId = rset.columnUint64("ID");
if(rset.next()) {
throw exception::Exception("Found more than one ID counter in the VIRTUAL_ORGANIZATION_ID table");
}
}
conn.commit();
return virtualOrganizationId;
} catch(exception::UserError &) {
throw;
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// getNextStorageClassId
//------------------------------------------------------------------------------
......
......@@ -84,6 +84,20 @@ protected:
* library storage class within the catalogue.
*/
uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
/**
* Returns a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*
* This method must be implemented by the sub-classes of RdbmsCatalogue
* because different database technologies propose different solution to the
* problem of generating ever increasing numeric identifiers.
*
* @param conn The database connection
* @return a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*/
uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
/**
* Returns a unique storage class ID that can be used by a new storage class
......
......@@ -202,6 +202,31 @@ uint64_t OracleCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
}
}
//------------------------------------------------------------------------------
// getNextVirtualOrganizationId
//------------------------------------------------------------------------------
uint64_t OracleCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
try {
const char *const sql =
"SELECT "
"VIRTUAL_ORGANIZATION_ID_SEQ.NEXTVAL AS VIRTUAL_ORGANIZATION_ID "
"FROM "
"DUAL";
auto stmt = conn.createStmt(sql);
auto rset = stmt.executeQuery();
if (!rset.next()) {
throw exception::Exception(std::string("Result set is unexpectedly empty"));
}
return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
} catch(exception::UserError &) {
throw;
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// getNextStorageClassId
//------------------------------------------------------------------------------
......
......@@ -84,6 +84,20 @@ public:
* library storage class within the catalogue.
*/
uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
/**
* Returns a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*
* This method must be implemented by the sub-classes of RdbmsCatalogue
* because different database technologies propose different solution to the
* problem of generating ever increasing numeric identifiers.
*
* @param conn The database connection
* @return a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*/
uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
/**
* Returns a unique storage class ID that can be used by a new storage class
......
......@@ -192,6 +192,26 @@ uint64_t PostgresCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
}
}
//------------------------------------------------------------------------------
// getNextVirtualOrganizationId
//------------------------------------------------------------------------------
uint64_t PostgresCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
try {
const char *const sql =
"select NEXTVAL('VIRTUAL_ORGANIZATION_ID_SEQ') AS VIRTUAL_ORGANIZATION_ID";
auto stmt = conn.createStmt(sql);
auto rset = stmt.executeQuery();
if(!rset.next()) {
throw exception::Exception("Result set is unexpectedly empty");
}
return rset.columnUint64("VIRTUAL_ORGANIZATION_ID");
} catch(exception::UserError &) {
throw;
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// getNextStorageClassId
//------------------------------------------------------------------------------
......
......@@ -111,6 +111,20 @@ public:
*/
uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
/**
* Returns a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*
* This method must be implemented by the sub-classes of RdbmsCatalogue
* because different database technologies propose different solution to the
* problem of generating ever increasing numeric identifiers.
*
* @param conn The database connection
* @return a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*/
uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
/**
* Returns a unique storage class ID that can be used by a new storage class
* within the catalogue.
......
......@@ -186,6 +186,30 @@ bool RdbmsCatalogue::adminUserExists(rdbms::Conn &conn, const std::string adminU
}
}
//------------------------------------------------------------------------------
// virtualOrganizationExists
//------------------------------------------------------------------------------
bool RdbmsCatalogue::virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName) const {
try {
const char *const sql =
"SELECT "
"VIRTUAL_ORGANIZATION_NAME AS VIRTUAL_ORGANIZATION_NAME "
"FROM "
"VIRTUAL_ORGANIZATION "
"WHERE "
"VIRTUAL_ORGANIZATION_NAME = :VIRTUAL_ORGANIZATION_NAME";
auto stmt = conn.createStmt(sql);
stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", voName);
auto rset = stmt.executeQuery();
return rset.next();
} catch(exception::UserError &) {
throw;
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// deleteAdminUser
//------------------------------------------------------------------------------
......@@ -301,6 +325,76 @@ void RdbmsCatalogue::modifyAdminUserComment(const common::dataStructures::Securi
}
}
//------------------------------------------------------------------------------
// createVirtualOrganization
//------------------------------------------------------------------------------
void RdbmsCatalogue::createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo){
try{
if(vo.name.empty()){
throw UserSpecifiedAnEmptyStringVo("Cannot create virtual organization because the name is an empty string");
}
if(vo.comment.empty()) {
throw UserSpecifiedAnEmptyStringComment("Cannot create virtual organization because the comment is an empty string");
}
auto conn = m_connPool.getConn();
if(virtualOrganizationExists(conn, vo.name)) {
throw exception::UserError(std::string("Cannot create vo : ") +
vo.name + " because it already exists");
}
const uint64_t virtualOrganizationId = getNextVirtualOrganizationId(conn);
const time_t now = time(nullptr);
const char *const sql =
"INSERT INTO VIRTUAL_ORGANIZATION("
"VIRTUAL_ORGANIZATION_ID,"
"VIRTUAL_ORGANIZATION_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("
":VIRTUAL_ORGANIZATION_ID,"
":VIRTUAL_ORGANIZATION_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);
stmt.bindUint64(":VIRTUAL_ORGANIZATION_ID", virtualOrganizationId);
stmt.bindString(":VIRTUAL_ORGANIZATION_NAME", vo.name);
stmt.bindString(":USER_COMMENT", vo.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) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// createStorageClass
//------------------------------------------------------------------------------
......
......@@ -211,6 +211,13 @@ public:
void deleteAdminUser(const std::string &username) override;
std::list<common::dataStructures::AdminUser> getAdminUsers() const override;
void modifyAdminUserComment(const common::dataStructures::SecurityIdentity &admin, const std::string &username, const std::string &comment) override;
/**
* Creates the specified Virtual Organization
* @param admin The administrator.
* @param vo the Virtual Organization
*/
void createVirtualOrganization(const common::dataStructures::SecurityIdentity &admin, const common::dataStructures::VirtualOrganization &vo) override;
/**
* Creates the specified storage class.
......@@ -802,6 +809,14 @@ protected:
*/
bool adminUserExists(rdbms::Conn &conn, const std::string adminUsername) const;
/**
* Returns true if the specified vo exists.
*
* @param conn The database connection.
* @param voName The name of the vo
* @return True if the vo exists, false otherwise
*/
bool virtualOrganizationExists(rdbms::Conn &conn, const std::string &voName) const;
/**
* Returns true if the specified storage class exists.
*
......@@ -1388,6 +1403,20 @@ protected:
*/
virtual uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) = 0;
/**
* Returns a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*
* This method must be implemented by the sub-classes of RdbmsCatalogue
* because different database technologies propose different solution to the
* problem of generating ever increasing numeric identifiers.
*
* @param conn The database connection
* @return a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*/
virtual uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) = 0;
/**
* Returns a unique storage class ID that can be used by a new storage class
* within the catalogue.
......
......@@ -252,6 +252,35 @@ uint64_t SqliteCatalogue::getNextLogicalLibraryId(rdbms::Conn &conn) {
}
}
//------------------------------------------------------------------------------
// getNextVirtualOrganizationId
//------------------------------------------------------------------------------
uint64_t SqliteCatalogue::getNextVirtualOrganizationId(rdbms::Conn &conn) {
try {
conn.executeNonQuery("INSERT INTO VIRTUAL_ORGANIZATION_ID VALUES(NULL)");
uint64_t virtualOrganizationId = 0;
{
const char *const sql = "SELECT LAST_INSERT_ROWID() AS ID";
auto stmt = conn.createStmt(sql);
auto rset = stmt.executeQuery();
if(!rset.next()) {
throw exception::Exception(std::string("Unexpected empty result set for '") + sql + "\'");
}
virtualOrganizationId = rset.columnUint64("ID");
if(rset.next()) {
throw exception::Exception(std::string("Unexpectedly found more than one row in the result of '") + sql + "\'");
}
}
conn.executeNonQuery("DELETE FROM VIRTUAL_ORGANIZATION_ID");
return virtualOrganizationId;
} catch(exception::UserError &) {
throw;
} catch(exception::Exception &ex) {
ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
throw;
}
}
//------------------------------------------------------------------------------
// getNextStorageClassId
//------------------------------------------------------------------------------
......
......@@ -113,6 +113,20 @@ protected:
* library storage class within the catalogue.
*/
uint64_t getNextLogicalLibraryId(rdbms::Conn &conn) override;
/**
* Returns a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*
* This method must be implemented by the sub-classes of RdbmsCatalogue
* because different database technologies propose different solution to the
* problem of generating ever increasing numeric identifiers.
*
* @param conn The database connection
* @return a unique virtual organization ID that can be used by a new Virtual Organization
* within the catalogue.
*/
uint64_t getNextVirtualOrganizationId(rdbms::Conn &conn) override;
/**
* Returns a unique storage class ID that can be used by a new storage class
......
......@@ -73,6 +73,7 @@ set (COMMON_LIB_SRC_FILES
dataStructures/TestSourceType.cpp
dataStructures/UpdateFileInfoRequest.cpp
dataStructures/UpdateFileStorageClassRequest.cpp
dataStructures/VirtualOrganization.cpp
dataStructures/WriteTestResult.cpp
dataStructures/utils.cpp
checksum/ChecksumBlob.cpp
......
/**
* The CERN Tape Archive (CTA) project
* Copyright © 2018 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 "VirtualOrganization.hpp"
/**
* The CERN Tape Archive (CTA) project
* Copyright © 2018 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 <string>
namespace cta {
namespace common {
namespace dataStructures {
struct VirtualOrganization {
/**
* The name
*/
std::string name;
/**
* The comment.
*/
std::string comment;
};
}}}
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment