From 8380d4b381d4c6f7dbb429aba4c84b25d6fe0353 Mon Sep 17 00:00:00 2001
From: Cedric CAFFY <cedric.caffy@cern.ch>
Date: Wed, 4 Mar 2020 10:13:01 +0100
Subject: [PATCH] Implemented Catalogue::createVirtualOrganization() + unit
 tests

---
 catalogue/Catalogue.hpp                       |  8 ++
 catalogue/CatalogueRetryWrapper.hpp           |  6 +-
 catalogue/CatalogueTest.cpp                   | 39 ++++++++
 catalogue/DummyCatalogue.hpp                  |  2 +
 catalogue/MysqlCatalogue.cpp                  | 42 +++++++++
 catalogue/MysqlCatalogue.hpp                  | 14 +++
 catalogue/OracleCatalogue.cpp                 | 25 +++++
 catalogue/OracleCatalogue.hpp                 | 14 +++
 catalogue/PostgresCatalogue.cpp               | 20 ++++
 catalogue/PostgresCatalogue.hpp               | 14 +++
 catalogue/RdbmsCatalogue.cpp                  | 94 +++++++++++++++++++
 catalogue/RdbmsCatalogue.hpp                  | 29 ++++++
 catalogue/SqliteCatalogue.cpp                 | 29 ++++++
 catalogue/SqliteCatalogue.hpp                 | 14 +++
 common/CMakeLists.txt                         |  1 +
 common/dataStructures/VirtualOrganization.cpp | 19 ++++
 common/dataStructures/VirtualOrganization.hpp | 40 ++++++++
 17 files changed, 409 insertions(+), 1 deletion(-)
 create mode 100644 common/dataStructures/VirtualOrganization.cpp
 create mode 100644 common/dataStructures/VirtualOrganization.hpp

diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index b76a9d0abd..1e0b4613de 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -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.
    *
diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp
index 57d023c857..b82233e422 100644
--- a/catalogue/CatalogueRetryWrapper.hpp
+++ b/catalogue/CatalogueRetryWrapper.hpp
@@ -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);
   }
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index 9ae8ead02c..e5d5897e9f 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -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
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
index 7572c9b944..c2cff5b265 100644
--- a/catalogue/DummyCatalogue.hpp
+++ b/catalogue/DummyCatalogue.hpp
@@ -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"); }
diff --git a/catalogue/MysqlCatalogue.cpp b/catalogue/MysqlCatalogue.cpp
index cec4378fca..a7470bec28 100644
--- a/catalogue/MysqlCatalogue.cpp
+++ b/catalogue/MysqlCatalogue.cpp
@@ -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
 //------------------------------------------------------------------------------
diff --git a/catalogue/MysqlCatalogue.hpp b/catalogue/MysqlCatalogue.hpp
index 4781b4dc3c..211cb387d0 100644
--- a/catalogue/MysqlCatalogue.hpp
+++ b/catalogue/MysqlCatalogue.hpp
@@ -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
diff --git a/catalogue/OracleCatalogue.cpp b/catalogue/OracleCatalogue.cpp
index 79509a6917..2c33efd6a9 100644
--- a/catalogue/OracleCatalogue.cpp
+++ b/catalogue/OracleCatalogue.cpp
@@ -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
 //------------------------------------------------------------------------------
diff --git a/catalogue/OracleCatalogue.hpp b/catalogue/OracleCatalogue.hpp
index 730b63b77c..3d4efbdf8f 100644
--- a/catalogue/OracleCatalogue.hpp
+++ b/catalogue/OracleCatalogue.hpp
@@ -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
diff --git a/catalogue/PostgresCatalogue.cpp b/catalogue/PostgresCatalogue.cpp
index c2b41457ba..c8ed6c7ccd 100644
--- a/catalogue/PostgresCatalogue.cpp
+++ b/catalogue/PostgresCatalogue.cpp
@@ -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
 //------------------------------------------------------------------------------
diff --git a/catalogue/PostgresCatalogue.hpp b/catalogue/PostgresCatalogue.hpp
index 085e4a0695..6db30ef340 100644
--- a/catalogue/PostgresCatalogue.hpp
+++ b/catalogue/PostgresCatalogue.hpp
@@ -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.
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 5d00c3f6ac..37f4cf8bad 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -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
 //------------------------------------------------------------------------------
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index d7f8f4a1cd..7fbbf3d241 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -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.
diff --git a/catalogue/SqliteCatalogue.cpp b/catalogue/SqliteCatalogue.cpp
index 3d4ad28874..9a65f42aa2 100644
--- a/catalogue/SqliteCatalogue.cpp
+++ b/catalogue/SqliteCatalogue.cpp
@@ -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
 //------------------------------------------------------------------------------
diff --git a/catalogue/SqliteCatalogue.hpp b/catalogue/SqliteCatalogue.hpp
index 73589a0370..205d82769b 100644
--- a/catalogue/SqliteCatalogue.hpp
+++ b/catalogue/SqliteCatalogue.hpp
@@ -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
diff --git a/common/CMakeLists.txt b/common/CMakeLists.txt
index f1063e5b02..c574437654 100644
--- a/common/CMakeLists.txt
+++ b/common/CMakeLists.txt
@@ -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
diff --git a/common/dataStructures/VirtualOrganization.cpp b/common/dataStructures/VirtualOrganization.cpp
new file mode 100644
index 0000000000..13141a50b7
--- /dev/null
+++ b/common/dataStructures/VirtualOrganization.cpp
@@ -0,0 +1,19 @@
+/**
+ * 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"
diff --git a/common/dataStructures/VirtualOrganization.hpp b/common/dataStructures/VirtualOrganization.hpp
new file mode 100644
index 0000000000..07c2440130
--- /dev/null
+++ b/common/dataStructures/VirtualOrganization.hpp
@@ -0,0 +1,40 @@
+/**
+ * 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;
+};
+
+}}}
+
+
-- 
GitLab