From b31ac52a638976d75e7a8b34149d3b927f3fd019 Mon Sep 17 00:00:00 2001
From: Cedric CAFFY <cedric.caffy@cern.ch>
Date: Thu, 5 Mar 2020 11:05:50 +0100
Subject: [PATCH] Added cta-admin virtualorganization management commands (add,
 ch, ls, rm)

---
 cmdline/CtaAdminCmd.cpp                       |  3 +
 cmdline/CtaAdminCmdParse.hpp                  | 13 ++-
 cmdline/CtaAdminTextFormatter.cpp             | 27 ++++++
 cmdline/CtaAdminTextFormatter.hpp             |  2 +
 xroot_plugins/XrdCtaVirtualOrganizationLs.hpp | 92 +++++++++++++++++++
 xroot_plugins/XrdSsiCtaRequestMessage.cpp     | 63 ++++++++++++-
 xroot_plugins/XrdSsiCtaRequestMessage.hpp     |  6 +-
 7 files changed, 202 insertions(+), 4 deletions(-)
 create mode 100644 xroot_plugins/XrdCtaVirtualOrganizationLs.hpp

diff --git a/cmdline/CtaAdminCmd.cpp b/cmdline/CtaAdminCmd.cpp
index 017c11d0e6..39d196cee6 100644
--- a/cmdline/CtaAdminCmd.cpp
+++ b/cmdline/CtaAdminCmd.cpp
@@ -103,6 +103,7 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
          case Data::kTflsItem:      std::cout << Log::DumpProtobuf(&record.tfls_item());    break;
          case Data::kTplsItem:      std::cout << Log::DumpProtobuf(&record.tpls_item());    break;
          case Data::kDslsItem:      std::cout << Log::DumpProtobuf(&record.dsls_item());    break;
+         case Data::kVolsItem:      std::cout << Log::DumpProtobuf(&record.vols_item());   break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
       }
@@ -131,6 +132,7 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
          case Data::kTflsItem:      formattedText.print(record.tfls_item());    break;
          case Data::kTplsItem:      formattedText.print(record.tpls_item());    break;
          case Data::kDslsItem:      formattedText.print(record.dsls_item());    break;
+         case Data::kVolsItem:      formattedText.print(record.vols_item());    break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
    }
@@ -286,6 +288,7 @@ void CtaAdminCmd::send() const
             case HeaderType::TAPEFILE_LS:                  formattedText.printTapeFileLsHeader(); break;
             case HeaderType::TAPEPOOL_LS:                  formattedText.printTapePoolLsHeader(); break;
             case HeaderType::DISKSYSTEM_LS:                formattedText.printDiskSystemLsHeader(); break;
+            case HeaderType::VIRTUALORGANIZATION_LS:       formattedText.printVirtualOrganizationLsHeader(); break;
             case HeaderType::NONE:
             default:                                       break;
          }
diff --git a/cmdline/CtaAdminCmdParse.hpp b/cmdline/CtaAdminCmdParse.hpp
index 00a7ea608b..a1c5ad8440 100644
--- a/cmdline/CtaAdminCmdParse.hpp
+++ b/cmdline/CtaAdminCmdParse.hpp
@@ -214,7 +214,9 @@ const cmdLookup_t cmdLookup = {
    { "tapepool",                AdminCmd::CMD_TAPEPOOL },
    { "tp",                      AdminCmd::CMD_TAPEPOOL },
    { "disksystem",              AdminCmd::CMD_DISKSYSTEM },
-   { "ds",                      AdminCmd::CMD_DISKSYSTEM }
+   { "ds",                      AdminCmd::CMD_DISKSYSTEM },
+   { "virtualorganization",     AdminCmd::CMD_VIRTUALORGANIZATION },
+   { "vo",                      AdminCmd::CMD_VIRTUALORGANIZATION }
 };
 
 
@@ -392,6 +394,7 @@ const std::map<AdminCmd::Cmd, CmdHelp> cmdHelp = {
 			    "   If the targeted free space is reach, the queue will sleep during this amount of seconds."
 			    "\n\n"
 					 }},
+   { AdminCmd::CMD_VIRTUALORGANIZATION,  { "virtualorganization",   "vo",  { "add", "ch", "rm", "ls" } }},
 };
 
 
@@ -597,6 +600,14 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
         opt_targeted_free_space.optional(), opt_sleep_time.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_DISKSYSTEM,           AdminCmd::SUBCMD_RM    }, { opt_disksystem }},
    {{ AdminCmd::CMD_DISKSYSTEM,           AdminCmd::SUBCMD_LS    }, { }},
+   {{ AdminCmd::CMD_VIRTUALORGANIZATION,           AdminCmd::SUBCMD_ADD   },
+      { opt_vo, opt_comment }},
+   {{ AdminCmd::CMD_VIRTUALORGANIZATION,           AdminCmd::SUBCMD_CH   },
+      { opt_vo, opt_comment }},
+   {{ AdminCmd::CMD_VIRTUALORGANIZATION,           AdminCmd::SUBCMD_RM   },
+      { opt_vo }},
+   {{ AdminCmd::CMD_VIRTUALORGANIZATION,           AdminCmd::SUBCMD_LS   },
+      { }},
 };
 
 
diff --git a/cmdline/CtaAdminTextFormatter.cpp b/cmdline/CtaAdminTextFormatter.cpp
index a42957cfb3..d56d1a0574 100644
--- a/cmdline/CtaAdminTextFormatter.cpp
+++ b/cmdline/CtaAdminTextFormatter.cpp
@@ -992,4 +992,31 @@ void TextFormatter::print(const DiskSystemLsItem &dsls_item)
   );
 }
 
+void TextFormatter::printVirtualOrganizationLsHeader(){
+  push_back("HEADER");
+  push_back(
+    "name",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const VirtualOrganizationLsItem& vols_item){
+  push_back(
+    vols_item.name(),
+    vols_item.creation_log().username(),
+    vols_item.creation_log().host(),
+    timeToStr(vols_item.creation_log().time()),
+    vols_item.last_modification_log().username(),
+    vols_item.last_modification_log().host(),
+    timeToStr(vols_item.last_modification_log().time()),
+    vols_item.comment()
+  );
+}
+
 }}
diff --git a/cmdline/CtaAdminTextFormatter.hpp b/cmdline/CtaAdminTextFormatter.hpp
index 7a4192078b..1793ef1092 100644
--- a/cmdline/CtaAdminTextFormatter.hpp
+++ b/cmdline/CtaAdminTextFormatter.hpp
@@ -66,6 +66,7 @@ public:
   void printTapeFileLsHeader();
   void printTapePoolLsHeader();
   void printDiskSystemLsHeader();
+  void printVirtualOrganizationLsHeader();
    
   // Output records
   void print(const AdminLsItem &adls_item);
@@ -90,6 +91,7 @@ public:
   void print(const TapeFileLsItem &tfls_item);
   void print(const TapePoolLsItem &tpls_item);
   void print(const DiskSystemLsItem &dsls_item);
+  void print(const VirtualOrganizationLsItem &vols_item);
   
 
 private:
diff --git a/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp b/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp
new file mode 100644
index 0000000000..0a1cb57a4f
--- /dev/null
+++ b/xroot_plugins/XrdCtaVirtualOrganizationLs.hpp
@@ -0,0 +1,92 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA TapePool Ls stream implementation
+ * @copyright      Copyright 2019 CERN
+ * @license        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 <xroot_plugins/XrdCtaStream.hpp>
+#include <xroot_plugins/XrdSsiCtaRequestMessage.hpp>
+
+#include "disk/DiskSystem.hpp"
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "virtualorganization ls" command
+ */
+class VirtualOrganizationLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  VirtualOrganizationLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_virtualOrganizationList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::VirtualOrganization> m_virtualOrganizationList;             //!< List of virtual organizations from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "VirtualOrganizationLsStream";    //!< Identifier for log messages
+};
+
+
+VirtualOrganizationLsStream::VirtualOrganizationLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_virtualOrganizationList(catalogue.getVirtualOrganizations())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "VirtualOrganizationLsStream() constructor");
+}
+
+int VirtualOrganizationLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_virtualOrganizationList.empty() && !is_buffer_full; m_virtualOrganizationList.pop_front()) {
+    Data record;
+
+    auto &vo      = m_virtualOrganizationList.front();
+    auto  vo_item = record.mutable_vols_item();
+
+    vo_item->set_name(vo.name);
+    vo_item->mutable_creation_log()->set_username(vo.creationLog.username);
+    vo_item->mutable_creation_log()->set_host(vo.creationLog.host);
+    vo_item->mutable_creation_log()->set_time(vo.creationLog.time);
+    vo_item->mutable_last_modification_log()->set_username(vo.lastModificationLog.username);
+    vo_item->mutable_last_modification_log()->set_host(vo.lastModificationLog.host);
+    vo_item->mutable_last_modification_log()->set_time(vo.lastModificationLog.time);
+    vo_item->set_comment(vo.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.cpp b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
index 26f631403d..5802d4db36 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.cpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
@@ -38,6 +38,7 @@ using XrdSsiPb::PbException;
 #include "XrdCtaStorageClassLs.hpp"
 #include "XrdCtaTapePoolLs.hpp"
 #include "XrdCtaDiskSystemLs.hpp"
+#include "XrdCtaVirtualOrganizationLs.hpp"
 
 namespace cta {
 namespace xrd {
@@ -246,8 +247,19 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                break;  
             case cmd_pair(AdminCmd::CMD_DISKSYSTEM, AdminCmd::SUBCMD_CH):
                processDiskSystem_Ch(response);
-               break;  
-               
+               break;
+           case cmd_pair(AdminCmd::CMD_VIRTUALORGANIZATION, AdminCmd::SUBCMD_ADD):
+               processVirtualOrganization_Add(response);
+               break;
+           case cmd_pair(AdminCmd::CMD_VIRTUALORGANIZATION, AdminCmd::SUBCMD_CH):
+               processVirtualOrganization_Ch(response);
+               break;
+           case cmd_pair(AdminCmd::CMD_VIRTUALORGANIZATION, AdminCmd::SUBCMD_RM):
+               processVirtualOrganization_Rm(response);
+               break;
+           case cmd_pair(AdminCmd::CMD_VIRTUALORGANIZATION,AdminCmd::SUBCMD_LS):
+               processVirtualOrganization_Ls(response, stream);
+               break;
             default:
                throw PbException("Admin command pair <" +
                      AdminCmd_Cmd_Name(request.admincmd().cmd()) + ", " +
@@ -1661,6 +1673,53 @@ void RequestMessage::processDiskSystem_Rm(cta::xrd::Response &response)
   response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
+void RequestMessage::processVirtualOrganization_Add(cta::xrd::Response &response){
+  using namespace cta::admin;
+
+  const auto &name = getRequired(OptionString::VO);
+  const auto &comment = getRequired(OptionString::COMMENT);
+  
+  cta::common::dataStructures::VirtualOrganization vo;
+  vo.name = name;
+  vo.comment = comment;
+  
+  m_catalogue.createVirtualOrganization(m_cliIdentity,vo);
+  
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
+}
+
+void RequestMessage::processVirtualOrganization_Ch(cta::xrd::Response &response){
+  using namespace cta::admin;
+
+  const auto &name = getRequired(OptionString::VO);
+  const auto &comment = getRequired(OptionString::COMMENT);
+  
+  m_catalogue.modifyVirtualOrganizationComment(m_cliIdentity,name,comment);
+  
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
+}
+
+void RequestMessage::processVirtualOrganization_Rm(cta::xrd::Response& response) {
+  using namespace cta::admin;
+  
+  const auto &name = getRequired(OptionString::VO);
+  
+  m_catalogue.deleteVirtualOrganization(name);
+  
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
+}
+
+void RequestMessage::processVirtualOrganization_Ls(cta::xrd::Response &response, XrdSsiStream * & stream){
+  using namespace cta::admin;
+
+  // Create a XrdSsi stream object to return the results
+  stream = new VirtualOrganizationLsStream(*this, m_catalogue, m_scheduler);
+
+  response.set_show_header(HeaderType::VIRTUALORGANIZATION_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
+}
+
+
 std::string RequestMessage::setDriveState(const std::string &regex, DriveState drive_state)
 {
    using namespace cta::admin;
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.hpp b/xroot_plugins/XrdSsiCtaRequestMessage.hpp
index c71640b750..ed6928bead 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.hpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.hpp
@@ -190,7 +190,10 @@ private:
   void processDiskSystem_Add        (cta::xrd::Response &response);
   void processDiskSystem_Ch         (cta::xrd::Response &response);
   void processDiskSystem_Rm         (cta::xrd::Response &response);
-  
+  void processVirtualOrganization_Add(cta::xrd::Response &response);
+  void processVirtualOrganization_Ch(cta::xrd::Response &response);
+  void processVirtualOrganization_Rm(cta::xrd::Response &response);
+
   /*!
    * Process AdminCmd events which can return a stream response
    *
@@ -218,6 +221,7 @@ private:
   admincmdstream_t processTapeFile_Ls;
   admincmdstream_t processRepack_Ls;
   admincmdstream_t processDiskSystem_Ls;
+  admincmdstream_t processVirtualOrganization_Ls;
 
   /*!
    * Log an admin command
-- 
GitLab