From 4b555bd125c7fd957139e33bd24a6d62c44109c6 Mon Sep 17 00:00:00 2001
From: mvelosob <miguel.veloso.barros@cern.ch>
Date: Fri, 1 Apr 2022 16:08:25 +0200
Subject: [PATCH] add verification column to tape table (#1043)

---
 catalogue/10.0/oracle_catalogue_schema.sql    |  1 +
 catalogue/10.0/postgres_catalogue_schema.sql  |  1 +
 catalogue/10.0/sqlite_catalogue_schema.sql    |  1 +
 catalogue/AllCatalogueSchema.hpp              |  3 +
 catalogue/Catalogue.hpp                       |  2 +
 catalogue/CatalogueRetryWrapper.hpp           |  5 +-
 catalogue/CatalogueTest.cpp                   | 78 +++++++++++++++++++
 catalogue/DummyCatalogue.hpp                  |  1 +
 catalogue/RdbmsCatalogue.cpp                  | 49 ++++++++++++
 catalogue/RdbmsCatalogue.hpp                  |  1 +
 catalogue/common_catalogue_schema.sql         |  1 +
 .../migrations/liquibase/oracle/4.6to10.0.sql | 33 ++++++++
 cmdline/CtaAdminCmdParse.hpp                  |  4 +-
 common/dataStructures/Tape.hpp                |  1 +
 xroot_plugins/XrdCtaTapeLs.hpp                |  3 +
 xroot_plugins/XrdSsiCtaRequestMessage.cpp     | 26 ++++---
 xrootd-ssi-protobuf-interface                 |  2 +-
 17 files changed, 198 insertions(+), 14 deletions(-)
 create mode 100644 catalogue/migrations/liquibase/oracle/4.6to10.0.sql

diff --git a/catalogue/10.0/oracle_catalogue_schema.sql b/catalogue/10.0/oracle_catalogue_schema.sql
index 327f8145e3..d6957953cf 100644
--- a/catalogue/10.0/oracle_catalogue_schema.sql
+++ b/catalogue/10.0/oracle_catalogue_schema.sql
@@ -278,6 +278,7 @@ CREATE TABLE TAPE(
   LAST_UPDATE_USER_NAME   VARCHAR2(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,
   LAST_UPDATE_HOST_NAME   VARCHAR2(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,
   LAST_UPDATE_TIME        NUMERIC(20, 0)      CONSTRAINT TAPE_LUT_NN  NOT NULL,
+  VERIFICATION_STATUS     VARCHAR2(1000),
   CONSTRAINT TAPE_PK PRIMARY KEY(VID),
   CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),
   CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),
diff --git a/catalogue/10.0/postgres_catalogue_schema.sql b/catalogue/10.0/postgres_catalogue_schema.sql
index bc46f24c78..e8bbac31a9 100644
--- a/catalogue/10.0/postgres_catalogue_schema.sql
+++ b/catalogue/10.0/postgres_catalogue_schema.sql
@@ -260,6 +260,7 @@ CREATE TABLE TAPE(
   LAST_UPDATE_USER_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,
   LAST_UPDATE_HOST_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,
   LAST_UPDATE_TIME        NUMERIC(20, 0)      CONSTRAINT TAPE_LUT_NN  NOT NULL,
+  VERIFICATION_STATUS     VARCHAR(1000),
   CONSTRAINT TAPE_PK PRIMARY KEY(VID),
   CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),
   CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),
diff --git a/catalogue/10.0/sqlite_catalogue_schema.sql b/catalogue/10.0/sqlite_catalogue_schema.sql
index 0191c093ee..46e414bd7b 100644
--- a/catalogue/10.0/sqlite_catalogue_schema.sql
+++ b/catalogue/10.0/sqlite_catalogue_schema.sql
@@ -232,6 +232,7 @@ CREATE TABLE TAPE(
   LAST_UPDATE_USER_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,
   LAST_UPDATE_HOST_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,
   LAST_UPDATE_TIME        INTEGER      CONSTRAINT TAPE_LUT_NN  NOT NULL,
+  VERIFICATION_STATUS     VARCHAR(1000),
   CONSTRAINT TAPE_PK PRIMARY KEY(VID),
   CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),
   CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),
diff --git a/catalogue/AllCatalogueSchema.hpp b/catalogue/AllCatalogueSchema.hpp
index dd25b23f47..dda1c81adc 100644
--- a/catalogue/AllCatalogueSchema.hpp
+++ b/catalogue/AllCatalogueSchema.hpp
@@ -1212,6 +1212,7 @@ namespace catalogue{
   "  LAST_UPDATE_USER_NAME   VARCHAR2(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,"
   "  LAST_UPDATE_HOST_NAME   VARCHAR2(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,"
   "  LAST_UPDATE_TIME        NUMERIC(20, 0)      CONSTRAINT TAPE_LUT_NN  NOT NULL,"
+  "  VERIFICATION_STATUS     VARCHAR2(1000),"
   "  CONSTRAINT TAPE_PK PRIMARY KEY(VID),"
   "  CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),"
   "  CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),"
@@ -1687,6 +1688,7 @@ namespace catalogue{
   "  LAST_UPDATE_USER_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,"
   "  LAST_UPDATE_HOST_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,"
   "  LAST_UPDATE_TIME        INTEGER      CONSTRAINT TAPE_LUT_NN  NOT NULL,"
+  "  VERIFICATION_STATUS     VARCHAR(1000),"
   "  CONSTRAINT TAPE_PK PRIMARY KEY(VID),"
   "  CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),"
   "  CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),"
@@ -2187,6 +2189,7 @@ namespace catalogue{
   "  LAST_UPDATE_USER_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,"
   "  LAST_UPDATE_HOST_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,"
   "  LAST_UPDATE_TIME        NUMERIC(20, 0)      CONSTRAINT TAPE_LUT_NN  NOT NULL,"
+  "  VERIFICATION_STATUS     VARCHAR(1000),"
   "  CONSTRAINT TAPE_PK PRIMARY KEY(VID),"
   "  CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),"
   "  CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),"
diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index b705fd16a3..2165c516fc 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -699,6 +699,8 @@ public:
   virtual void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &logicalLibraryName) = 0;
   virtual void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &tapePoolName) = 0;
   virtual void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &encryptionKeyName) = 0;
+  virtual void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &verificationStatus) = 0;
+  
   /**
    * Modify the state of the specified tape
    * @param admin, the person or the system who modified the state of the tape
diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp
index 5fcbdea4cf..f7853acf58 100644
--- a/catalogue/CatalogueRetryWrapper.hpp
+++ b/catalogue/CatalogueRetryWrapper.hpp
@@ -387,7 +387,10 @@ public:
   void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &encryptionKeyName) override {
     return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyTapeEncryptionKeyName(admin, vid, encryptionKeyName);}, m_maxTriesToConnect);
   }
-
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &verificationStatus) override {
+    return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyTapeVerificationStatus(admin, vid, verificationStatus);}, m_maxTriesToConnect);
+  }
+  
   void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const cta::optional<std::string> & stateReason) override {
     return retryOnLostConnection(m_log, [&]{return m_catalogue->modifyTapeState(admin,vid, state, stateReason);}, m_maxTriesToConnect);
   }
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index e079b8635a..dd28d48e16 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -5976,6 +5976,84 @@ TEST_P(cta_catalogue_CatalogueTest, modifyTapeEncryptionKeyName_emptyStringEncry
   }
 }
 
+TEST_P(cta_catalogue_CatalogueTest, modifyTapeVerificationStatus) {
+  using namespace cta;
+
+  const bool logicalLibraryIsDisabled= false;
+  const uint64_t nbPartialTapes = 2;
+  const bool isEncrypted = true;
+  const cta::optional<std::string> supply("value for the supply pool mechanism");
+
+  m_catalogue->createMediaType(m_admin, m_mediaType);
+  m_catalogue->createLogicalLibrary(m_admin, m_tape1.logicalLibraryName, logicalLibraryIsDisabled, "Create logical library");
+
+  m_catalogue->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->createTapePool(m_admin, m_tape1.tapePoolName, m_vo.name, nbPartialTapes, isEncrypted, supply, "Create tape pool");
+
+  m_catalogue->createTape(m_admin, m_tape1);
+
+  {
+    const std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+
+    const common::dataStructures::EntryLog lastModificationLog = tape.lastModificationLog;
+    ASSERT_EQ(creationLog, lastModificationLog);
+    ASSERT_FALSE(tape.verificationStatus);
+  }
+
+  const std::string modifiedVerificationStatus = "verification_status";
+  m_catalogue->modifyTapeVerificationStatus(m_admin, m_tape1.vid, modifiedVerificationStatus);
+
+  {
+    const std::list<common::dataStructures::Tape> tapes = m_catalogue->getTapes();
+
+    ASSERT_EQ(1, tapes.size());
+
+    const common::dataStructures::Tape tape = tapes.front();
+    ASSERT_EQ(m_tape1.vid, tape.vid);
+    ASSERT_EQ(m_tape1.mediaType, tape.mediaType);
+    ASSERT_EQ(m_tape1.vendor, tape.vendor);
+    ASSERT_EQ(m_tape1.logicalLibraryName, tape.logicalLibraryName);
+    ASSERT_EQ(m_tape1.tapePoolName, tape.tapePoolName);
+    ASSERT_EQ(m_vo.name, tape.vo);
+    ASSERT_EQ(m_mediaType.capacityInBytes, tape.capacityInBytes);
+    ASSERT_EQ(m_tape1.full, tape.full);
+
+    ASSERT_FALSE(tape.isFromCastor);
+    ASSERT_EQ(m_tape1.comment, tape.comment);
+    ASSERT_FALSE(tape.labelLog);
+    ASSERT_FALSE(tape.lastReadLog);
+    ASSERT_FALSE(tape.lastWriteLog);
+
+    const common::dataStructures::EntryLog creationLog = tape.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+    ASSERT_EQ(tape.verificationStatus.value(), modifiedVerificationStatus);
+
+  }
+}
+
 TEST_P(cta_catalogue_CatalogueTest, modifyTapeEncryptionKeyName_nonExistentTape) {
   using namespace cta;
 
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
index 5da52bbdd5..f20fade6e8 100644
--- a/catalogue/DummyCatalogue.hpp
+++ b/catalogue/DummyCatalogue.hpp
@@ -160,6 +160,7 @@ public:
   void modifyStorageClassNbCopies(const common::dataStructures::SecurityIdentity& admin, const std::string& name, const uint64_t nbCopies) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyTapeComment(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const cta::optional<std::string> &comment) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& encryptionKeyName) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& verificationStatus) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const cta::optional<std::string> & stateReason) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyTapeMediaType(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& mediaType) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   void modifyTapeVendor(const common::dataStructures::SecurityIdentity& admin, const std::string& vid, const std::string& vendor) override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index c57fe1244f..a245bdd8bf 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -3979,6 +3979,8 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co
         "TAPE.READ_MOUNT_COUNT AS READ_MOUNT_COUNT,"
         "TAPE.WRITE_MOUNT_COUNT AS WRITE_MOUNT_COUNT,"
 
+        "TAPE.VERIFICATION_STATUS AS VERIFICATION_STATUS,"
+
         "TAPE.USER_COMMENT AS USER_COMMENT,"
 
         "TAPE.TAPE_STATE AS TAPE_STATE,"
@@ -4145,6 +4147,8 @@ std::list<common::dataStructures::Tape> RdbmsCatalogue::getTapes(rdbms::Conn &co
         tape.readMountCount = rset.columnUint64("READ_MOUNT_COUNT");
         tape.writeMountCount = rset.columnUint64("WRITE_MOUNT_COUNT");
 
+        tape.verificationStatus =  rset.columnOptionalString("VERIFICATION_STATUS");
+
         auto optionalComment = rset.columnOptionalString("USER_COMMENT");
         tape.comment = optionalComment ? optionalComment.value() : "";
 
@@ -5013,6 +5017,51 @@ void RdbmsCatalogue::modifyTapeEncryptionKeyName(const common::dataStructures::S
   }
 }
 
+//------------------------------------------------------------------------------
+// modifyTapeVerificationStatus
+//------------------------------------------------------------------------------
+void RdbmsCatalogue::modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin,
+  const std::string &vid, const std::string &verificationStatus) {
+  try {
+    const time_t now = time(nullptr);
+    const char *const sql =
+      "UPDATE TAPE SET "
+        "VERIFICATION_STATUS = :VERIFICATION_STATUS,"
+        "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);
+    stmt.bindString(":VERIFICATION_STATUS", verificationStatus);
+    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");
+    }
+
+    log::LogContext lc(m_log);
+    log::ScopedParamContainer spc(lc);
+    spc.add("vid", vid)
+       .add("verificationStatus", verificationStatus)
+       .add("lastUpdateUserName", admin.username)
+       .add("lastUpdateHostName", admin.host)
+       .add("lastUpdateTime", now);
+    lc.log(log::INFO, "Catalogue - user modified tape - verificationStatus");
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
 //------------------------------------------------------------------------------
 // modifyTapeState
 //------------------------------------------------------------------------------
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index 296a38f842..0d4cb14908 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -649,6 +649,7 @@ public:
   void modifyTapeLogicalLibraryName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &logicalLibraryName) override;
   void modifyTapeTapePoolName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &tapePoolName) override;
   void modifyTapeEncryptionKeyName(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &encryptionKeyName) override;
+  void modifyTapeVerificationStatus(const common::dataStructures::SecurityIdentity &admin, const std::string &vid, const std::string &verificationStatus) override;
   void modifyTapeState(const common::dataStructures::SecurityIdentity &admin,const std::string &vid, const common::dataStructures::Tape::State & state, const cta::optional<std::string> & stateReason) override;
   static std::string generateTapeStateModifiedBy(const common::dataStructures::SecurityIdentity & admin);
   /**
diff --git a/catalogue/common_catalogue_schema.sql b/catalogue/common_catalogue_schema.sql
index be57a24ede..7cc5646b99 100644
--- a/catalogue/common_catalogue_schema.sql
+++ b/catalogue/common_catalogue_schema.sql
@@ -211,6 +211,7 @@ CREATE TABLE TAPE(
   LAST_UPDATE_USER_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUUN_NN NOT NULL,
   LAST_UPDATE_HOST_NAME   VARCHAR(100)    CONSTRAINT TAPE_LUHN_NN NOT NULL,
   LAST_UPDATE_TIME        UINT64TYPE      CONSTRAINT TAPE_LUT_NN  NOT NULL,
+  VERIFICATION_STATUS     VARCHAR(1000),
   CONSTRAINT TAPE_PK PRIMARY KEY(VID),
   CONSTRAINT TAPE_LOGICAL_LIBRARY_FK FOREIGN KEY(LOGICAL_LIBRARY_ID) REFERENCES LOGICAL_LIBRARY(LOGICAL_LIBRARY_ID),
   CONSTRAINT TAPE_TAPE_POOL_FK FOREIGN KEY(TAPE_POOL_ID) REFERENCES TAPE_POOL(TAPE_POOL_ID),
diff --git a/catalogue/migrations/liquibase/oracle/4.6to10.0.sql b/catalogue/migrations/liquibase/oracle/4.6to10.0.sql
new file mode 100644
index 0000000000..2b9d453f4b
--- /dev/null
+++ b/catalogue/migrations/liquibase/oracle/4.6to10.0.sql
@@ -0,0 +1,33 @@
+--liquibase formatted sql
+
+--changeset mvelosob:1 failOnError:true dbms:oracle
+--preconditions onFail:HALT onError:HALT
+--precondition-sql-check expectedResult:"4.6" SELECT CONCAT(CONCAT(CAST(SCHEMA_VERSION_MAJOR as VARCHAR(10)),'.'), CAST(SCHEMA_VERSION_MINOR AS VARCHAR(10))) AS CATALOGUE_VERSION FROM CTA_CATALOGUE;
+--precondition-sql-check expectedResult:"0.0" SELECT CONCAT(CONCAT(CAST(SCHEMA_VERSION_MAJOR as VARCHAR(10)),'.'), CAST(SCHEMA_VERSION_MINOR AS VARCHAR(10))) AS CATALOGUE_VERSION FROM CTA_CATALOGUE;
+--!!!THIS FALSE PRECONDITION IS HERE TO BLOCK AN UPGRADE WHILE THE DEVELOPMENT OF THE NEW CATALOGUE VERSION IS BEING DEVELOPED!!!
+UPDATE CTA_CATALOGUE SET STATUS='UPGRADING';
+UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MAJOR=10;
+UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MINOR=0;
+--rollback UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MAJOR=NULL;
+--rollback UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MINOR=NULL;
+--rollback UPDATE CTA_CATALOGUE SET STATUS='PRODUCTION';
+
+--changeset mvelosob:2 failOnError:true dbms:oracle
+--preconditions onFail:HALT onError:HALT
+--precondition-sql-check expectedResult:"4.6" SELECT CONCAT(CONCAT(CAST(SCHEMA_VERSION_MAJOR as VARCHAR(10)),'.'), CAST(SCHEMA_VERSION_MINOR AS VARCHAR(10))) AS CATALOGUE_VERSION FROM CTA_CATALOGUE;
+ALTER TABLE TAPE ADD (VERIFICATION_STATUS VARCHAR2(1000));
+--rollback ALTER TABLE TAPE DROP COLUMN VERIFICATION_STATUS
+
+--changeset mvelosob:3 failOnError:true dbms:oracle
+--preconditions onFail:HALT onError:HALT
+--precondition-sql-check expectedResult:"4.6" SELECT CONCAT(CONCAT(CAST(SCHEMA_VERSION_MAJOR as VARCHAR(10)),'.'), CAST(SCHEMA_VERSION_MINOR AS VARCHAR(10))) AS CATALOGUE_VERSION FROM CTA_CATALOGUE;
+UPDATE CTA_CATALOGUE SET STATUS='PRODUCTION';
+UPDATE CTA_CATALOGUE SET SCHEMA_VERSION_MAJOR=10;
+UPDATE CTA_CATALOGUE SET SCHEMA_VERSION_MINOR=0;
+UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MAJOR=NULL;
+UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MINOR=NULL;
+--rollback UPDATE CTA_CATALOGUE SET STATUS='UPGRADING';
+--rollback UPDATE CTA_CATALOGUE SET SCHEMA_VERSION_MAJOR=4;
+--rollback UPDATE CTA_CATALOGUE SET SCHEMA_VERSION_MINOR=6;
+--rollback UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MAJOR=10;
+--rollback UPDATE CTA_CATALOGUE SET NEXT_SCHEMA_VERSION_MINOR=0;
diff --git a/cmdline/CtaAdminCmdParse.hpp b/cmdline/CtaAdminCmdParse.hpp
index 3f9d076ca0..a4d51ecf82 100644
--- a/cmdline/CtaAdminCmdParse.hpp
+++ b/cmdline/CtaAdminCmdParse.hpp
@@ -345,6 +345,7 @@ const std::map<std::string, OptionString::Key> strOptions = {
    {  "--activityregex",        OptionString::ACTIVITY_REGEX},
    { "--diskinstance",          OptionString::DISK_INSTANCE },
    { "--diskinstancespace",     OptionString::DISK_INSTANCE_SPACE },
+   { "--verificationstatus",    OptionString::VERIFICATION_STATUS },
 };
 
 
@@ -544,6 +545,7 @@ const Option opt_diskinstance         { Option::OPT_STR,  "--diskinstance",
 const Option opt_diskinstance_alias   { Option::OPT_STR,  "--name",                 "-n",    " <disk_instance_name>", "--diskinstance" };
 const Option opt_diskinstancespace    { Option::OPT_STR,  "--diskinstancespace",         "--dis",  " <disk_instance_space_name>" };
 const Option opt_diskinstancespace_alias { Option::OPT_STR,  "--name",                 "-n",    " <disk_instance_space_name>", "--diskinstancespace" };
+const Option opt_verificationstatus   { Option::OPT_STR,  "--verificationstatus",     "--vs",   " <verification_status>" };
 
 /*!
  * Map valid options to commands
@@ -630,7 +632,7 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
         opt_state.optional(), opt_reason.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_CH    },
       { opt_vid, opt_mediatype.optional(), opt_vendor.optional(), opt_logicallibrary.optional(),
-        opt_tapepool.optional(), opt_encryptionkeyname.optional(), opt_full.optional(), 
+        opt_tapepool.optional(), opt_encryptionkeyname.optional(), opt_full.optional(), opt_verificationstatus.optional(),
         opt_state.optional(), opt_reason.optional(), opt_comment.optional(), opt_dirtybit.optional() }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_RM    }, { opt_vid }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_RECLAIM }, { opt_vid }},
diff --git a/common/dataStructures/Tape.hpp b/common/dataStructures/Tape.hpp
index 6c1dd57c49..cdb48ad2c5 100644
--- a/common/dataStructures/Tape.hpp
+++ b/common/dataStructures/Tape.hpp
@@ -110,6 +110,7 @@ struct Tape {
   optional<std::string> stateReason;
   std::string stateModifiedBy;
   time_t stateUpdateTime;
+  optional<std::string> verificationStatus;
   
   bool isDisabled() const;
   
diff --git a/xroot_plugins/XrdCtaTapeLs.hpp b/xroot_plugins/XrdCtaTapeLs.hpp
index 2d40ee288c..3be29a6248 100644
--- a/xroot_plugins/XrdCtaTapeLs.hpp
+++ b/xroot_plugins/XrdCtaTapeLs.hpp
@@ -146,6 +146,9 @@ int TapeLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
     tape_item->set_state_reason(tape.stateReason ? tape.stateReason.value() : "");
     tape_item->set_state_update_time(tape.stateUpdateTime);
     tape_item->set_state_modified_by(tape.stateModifiedBy);
+    if (tape.verificationStatus) {
+      tape_item->set_verification_status(tape.verificationStatus.value());
+    }
     
     is_buffer_full = streambuf->Push(record);
   }
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.cpp b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
index dafc9c00a2..291b6bfb51 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.cpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
@@ -1921,17 +1921,18 @@ void RequestMessage::processTape_Ch(cta::xrd::Response &response)
 {
    using namespace cta::admin;
 
-   auto &vid               = getRequired(OptionString::VID);
-   auto  mediaType         = getOptional(OptionString::MEDIA_TYPE);
-   auto  vendor            = getOptional(OptionString::VENDOR);
-   auto  logicallibrary    = getOptional(OptionString::LOGICAL_LIBRARY);
-   auto  tapepool          = getOptional(OptionString::TAPE_POOL);
-   auto  comment           = getOptional(OptionString::COMMENT);
-   auto  encryptionkeyName = getOptional(OptionString::ENCRYPTION_KEY_NAME);
-   auto  full              = getOptional(OptionBoolean::FULL);
-   auto  state             = getOptional(OptionString::STATE);
-   auto  stateReason       = getOptional(OptionString::REASON);
-   auto  dirty             = getOptional(OptionBoolean::DIRTY_BIT);
+   auto &vid                = getRequired(OptionString::VID);
+   auto  mediaType          = getOptional(OptionString::MEDIA_TYPE);
+   auto  vendor             = getOptional(OptionString::VENDOR);
+   auto  logicallibrary     = getOptional(OptionString::LOGICAL_LIBRARY);
+   auto  tapepool           = getOptional(OptionString::TAPE_POOL);
+   auto  comment            = getOptional(OptionString::COMMENT);
+   auto  encryptionkeyName  = getOptional(OptionString::ENCRYPTION_KEY_NAME);
+   auto  full               = getOptional(OptionBoolean::FULL);
+   auto  state              = getOptional(OptionString::STATE);
+   auto  stateReason        = getOptional(OptionString::REASON);
+   auto  dirty              = getOptional(OptionBoolean::DIRTY_BIT);
+   auto  verificationStatus = getOptional(OptionString::VERIFICATION_STATUS);
 
    if(mediaType) {
       m_catalogue.modifyTapeMediaType(m_cliIdentity, vid, mediaType.value());
@@ -1965,6 +1966,9 @@ void RequestMessage::processTape_Ch(cta::xrd::Response &response)
    if (dirty) {
       m_catalogue.setTapeDirty(m_cliIdentity, vid, dirty.value());
    }
+   if (verificationStatus) {
+      m_catalogue.modifyTapeVerificationStatus(m_cliIdentity, vid, verificationStatus.value());
+   }
 
    response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
diff --git a/xrootd-ssi-protobuf-interface b/xrootd-ssi-protobuf-interface
index 8df884a2ed..6a4cbde3b1 160000
--- a/xrootd-ssi-protobuf-interface
+++ b/xrootd-ssi-protobuf-interface
@@ -1 +1 @@
-Subproject commit 8df884a2ed9bef45f3d43e8e6ae7c0374f560338
+Subproject commit 6a4cbde3b1af09f2cab46c68c23c262e0f037b4b
-- 
GitLab