From d4728d0754e1b81361a4a242ce5b8c9d77fd84be Mon Sep 17 00:00:00 2001
From: Lasse Tjernaes Wardenaer <lasse.tjernaes.wardenaer@cern.ch>
Date: Thu, 6 Oct 2022 08:35:41 +0200
Subject: [PATCH] Resolve "Refactor operations cmd line tools to use common
 parsing logic"

---
 ReleaseNotes.md                               |   6 +
 cmdline/CMakeLists.txt                        |  20 +-
 cmdline/CtaCmdOptions.hpp                     |  94 ---------
 cmdline/standalone_cli_tools/CMakeLists.txt   |  49 +++++
 .../CtaSendEvent.cpp                          |  62 +-----
 .../CtaVerifyFile.cpp                         |  90 +++------
 .../common/CmdLineArgs.cpp}                   | 150 +++++++++++----
 .../common/CmdLineArgs.hpp}                   |  46 ++++-
 .../common}/CmdLineTool.cpp                   |  19 +-
 .../common}/CmdLineTool.hpp                   |   9 +-
 .../restore_files/CMakeLists.txt              |   2 +-
 .../restore_files/RestoreFilesCmd.cpp         |  70 ++++---
 .../restore_files/RestoreFilesCmd.hpp         |  23 +--
 .../restore_files/RestoreFilesCmdMain.cpp     |   2 +-
 .../cta-restore-deleted-files.1cta            |   0
 .../cc7/etc/cta/cta-frontend-xrootd.conf      |   2 +-
 .../tests/README_restore_files.md             |  27 +++
 .../tests/common/archive_file.sh              |  87 +++++++++
 .../orchestration/tests/common/delete_file.sh |  58 ++++++
 .../orchestration/tests/restore_files.sh      | 180 ++++++++++++++++++
 .../tests/restore_files_ctafrontend.sh        |  57 ++++++
 21 files changed, 703 insertions(+), 350 deletions(-)
 delete mode 100644 cmdline/CtaCmdOptions.hpp
 create mode 100644 cmdline/standalone_cli_tools/CMakeLists.txt
 rename cmdline/{ => standalone_cli_tools}/CtaSendEvent.cpp (83%)
 rename cmdline/{ => standalone_cli_tools}/CtaVerifyFile.cpp (67%)
 rename cmdline/{restore_files/RestoreFilesCmdLineArgs.cpp => standalone_cli_tools/common/CmdLineArgs.cpp} (51%)
 rename cmdline/{restore_files/RestoreFilesCmdLineArgs.hpp => standalone_cli_tools/common/CmdLineArgs.hpp} (75%)
 rename cmdline/{restore_files => standalone_cli_tools/common}/CmdLineTool.cpp (88%)
 rename cmdline/{restore_files => standalone_cli_tools/common}/CmdLineTool.hpp (92%)
 rename cmdline/{ => standalone_cli_tools}/restore_files/CMakeLists.txt (91%)
 rename cmdline/{ => standalone_cli_tools}/restore_files/RestoreFilesCmd.cpp (94%)
 rename cmdline/{ => standalone_cli_tools}/restore_files/RestoreFilesCmd.hpp (92%)
 rename cmdline/{ => standalone_cli_tools}/restore_files/RestoreFilesCmdMain.cpp (96%)
 rename cmdline/{ => standalone_cli_tools}/restore_files/cta-restore-deleted-files.1cta (100%)
 create mode 100644 continuousintegration/orchestration/tests/README_restore_files.md
 create mode 100644 continuousintegration/orchestration/tests/common/archive_file.sh
 create mode 100644 continuousintegration/orchestration/tests/common/delete_file.sh
 create mode 100755 continuousintegration/orchestration/tests/restore_files.sh
 create mode 100644 continuousintegration/orchestration/tests/restore_files_ctafrontend.sh

diff --git a/ReleaseNotes.md b/ReleaseNotes.md
index fc57ca07ba..5dbbb707f4 100644
--- a/ReleaseNotes.md
+++ b/ReleaseNotes.md
@@ -1,3 +1,9 @@
+# v4.NEXT
+
+## Summary
+### Features
+- cta/CTA#146 - Refactoring of operation tools cmd line parsing 
+
 # v4.7.12-1
 
 ## Summary
diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt
index 91d0a453fe..66d988f3b6 100644
--- a/cmdline/CMakeLists.txt
+++ b/cmdline/CMakeLists.txt
@@ -15,7 +15,7 @@
 
 cmake_minimum_required (VERSION 3.17)
 
-add_subdirectory (restore_files)
+add_subdirectory (standalone_cli_tools)
 
 find_package(xrootdclient REQUIRED)
 find_package(Protobuf3 REQUIRED)
@@ -36,22 +36,6 @@ add_executable(cta-admin CtaAdminCmd.cpp CtaAdminCmdParse.cpp CtaAdminTextFormat
 target_link_libraries(cta-admin XrdSsiPbEosCta XrdSsiLib XrdUtils ctacommon)
 set_property (TARGET cta-admin APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
 
-#
-# cta-send-event CLOSEW|PREPARE|ABORT_PREPARE
-# injects a workflow event into the CTA Frontend
-#
-add_executable(cta-send-event CtaSendEvent.cpp)
-target_link_libraries(cta-send-event ctacommon XrdSsiPbEosCta XrdSsiLib XrdUtils)
-set_property(TARGET cta-send-event APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
-
-#
-# verify-file <archiveFileId> <vid>
-# recalls a file from tape without writing a disk copy
-#
-add_executable(cta-verify-file CtaVerifyFile.cpp)
-target_link_libraries(cta-verify-file ctacommon XrdSsiPbEosCta XrdSsiLib XrdUtils)
-set_property (TARGET cta-verify-file APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
-
 #
 # cta-wfe-test archive|retrieve|delete <options> allows testing of the SSI WorkFlow Engine hooks
 # without invoking EOS.
@@ -65,7 +49,5 @@ set_property (TARGET cta-wfe-test APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPAT
 
 install(TARGETS cta-admin DESTINATION usr/bin)
 install(FILES cta-admin.1cta DESTINATION usr/share/man/man1)
-install(TARGETS cta-send-event DESTINATION usr/bin)
 install(FILES cta-send-closew.sh DESTINATION usr/bin)
-install(TARGETS cta-verify-file DESTINATION usr/bin)
 install(FILES cta-cli.conf.example DESTINATION ${CMAKE_INSTALL_SYSCONFDIR}/cta)
diff --git a/cmdline/CtaCmdOptions.hpp b/cmdline/CtaCmdOptions.hpp
deleted file mode 100644
index 8a3e5ed164..0000000000
--- a/cmdline/CtaCmdOptions.hpp
+++ /dev/null
@@ -1,94 +0,0 @@
-/*
- * @project      The CERN Tape Archive (CTA)
- * @copyright    Copyright © 2021-2022 CERN
- * @license      This program is free software, distributed under the terms of the GNU General Public
- *               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
- *               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
- *
- *               In applying this licence, CERN does not waive the privileges and immunities
- *               granted to it by virtue of its status as an Intergovernmental Organization or
- *               submit itself to any jurisdiction.
- */
-
-#pragma once
-
-/*!
-* Simple command line option class for simpler commands like cta-verify-file or cta-send-event
-*/
-class StringOption {
-public:
-    /*!
-    * Constructor
-    */
-    StringOption(const std::string &long_opt, const std::string &short_opt, const bool is_optional) :
-            m_long_opt(long_opt),
-            m_short_opt(short_opt),
-            m_is_optional(is_optional),
-            m_is_present(false),
-            m_value("") {}
-
-    /*!
-    * Check if the supplied key matches the option
-    */
-    bool operator==(const std::string &option) const {
-        return option == m_short_opt || option == m_long_opt;
-    }
-
-    /*!
-    * Return whether the option is optional
-    */
-    bool is_optional() const {
-        return m_is_optional;
-    }
-
-    /*!
-    * Return whether the option is present on the command line
-    */
-    bool is_present() const {
-        return m_is_present;
-    }
-
-    /*!
-    * Sets the option as present on the command line
-    */
-    void set_present() {
-        m_is_present = true;
-    }
-
-    /*!
-    * Return the value of the option given from the command line
-    */
-    const std::string &get_value() const {
-        return m_value;
-    }
-
-    /*!
-    * Return the name of the option (it's long command option)
-    */
-    const std::string &get_name() const {
-        return m_long_opt;
-    }
-
-    /*!
-    * Sets the value of the option from the command line
-    */
-    void set_value(const std::string &mValue) {
-        m_value = mValue;
-    }
-
-private:
-
-    //member variables
-    const std::string m_long_opt;      //!< Long command option
-    const std::string m_short_opt;     //!< Short command option
-    const bool        m_is_optional;   //!< Option is optional or compulsory
-    bool              m_is_present;    //!< Option is present on the command line
-    std::string       m_value;         //!< Option value
-};
-
-
diff --git a/cmdline/standalone_cli_tools/CMakeLists.txt b/cmdline/standalone_cli_tools/CMakeLists.txt
new file mode 100644
index 0000000000..1112c75327
--- /dev/null
+++ b/cmdline/standalone_cli_tools/CMakeLists.txt
@@ -0,0 +1,49 @@
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2015-2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+cmake_minimum_required (VERSION 3.17)
+
+add_subdirectory (restore_files)
+
+find_package(xrootdclient REQUIRED)
+find_package(Protobuf3 REQUIRED)
+
+# XRootD SSI
+include_directories(${XROOTD_INCLUDE_DIR} ${XROOTD_INCLUDE_DIR}/private)
+
+# XRootD SSI Protocol Buffer bindings
+include_directories(${XRD_SSI_PB_DIR}/include ${XRD_SSI_PB_DIR}/eos_cta/include)
+
+# Compiled protocol buffers
+include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
+
+#
+# cta-send-event CLOSEW|PREPARE|ABORT_PREPARE
+# injects a workflow event into the CTA Frontend
+#
+add_executable(cta-send-event CtaSendEvent.cpp common/CmdLineArgs.cpp)
+target_link_libraries(cta-send-event ctacommon XrdSsiPbEosCta XrdSsiLib XrdUtils)
+set_property(TARGET cta-send-event APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
+
+#
+# verify-file <archiveFileId> <vid>
+# recalls a file from tape without writing a disk copy
+#
+add_executable(cta-verify-file CtaVerifyFile.cpp common/CmdLineArgs.cpp)
+target_link_libraries(cta-verify-file ctacommon XrdSsiPbEosCta XrdSsiLib XrdUtils)
+set_property (TARGET cta-verify-file APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
+
+install(TARGETS cta-send-event DESTINATION usr/bin)
+install(TARGETS cta-verify-file DESTINATION usr/bin)
\ No newline at end of file
diff --git a/cmdline/CtaSendEvent.cpp b/cmdline/standalone_cli_tools/CtaSendEvent.cpp
similarity index 83%
rename from cmdline/CtaSendEvent.cpp
rename to cmdline/standalone_cli_tools/CtaSendEvent.cpp
index 4ae38728de..987d7c170a 100644
--- a/cmdline/CtaSendEvent.cpp
+++ b/cmdline/standalone_cli_tools/CtaSendEvent.cpp
@@ -25,7 +25,7 @@
 #include <common/checksum/ChecksumBlobSerDeser.hpp>
 #include "CtaFrontendApi.hpp"
 #include "version.h"
-#include "CtaCmdOptions.hpp"
+#include "common/CmdLineArgs.hpp"
 
 const std::string config_file = "/etc/cta/cta-cli.conf";
 
@@ -52,48 +52,6 @@ const std::runtime_error Usage("Usage: eos --json fileinfo /eos/path | cta-send-
                                "-i/--eos.instance <instance> [-e/--eos.endpoint <url>] "
                                "-u/--request.user <user> -g/--request.group <group>");
 
-StringOption option_instance {"--eos.instance", "-i", false};
-StringOption option_endpoint {"--eos.endpoint", "-e", true};
-StringOption option_user {"--request.user", "-u", false};
-StringOption option_group {"--request.group", "-g", false};
-
-
-std::map<std::string, StringOption*> option_map = {
-        {"-i", &option_instance},
-        {"--eos.instance", &option_instance},
-        {"-e", &option_endpoint},
-        {"--eos.endpoint", &option_endpoint},
-        {"-u", &option_user},
-        {"--request.user", &option_user},
-        {"-g", &option_group},
-        {"--request.group", &option_group},
-};
-
-
-void validate_cmd() {
-    for (auto &it: option_map) {
-        auto option = it.second;
-        if (!option->is_optional() && !option->is_present()) {
-            std::cout << "Error: Option " << option->get_name() << " is mandatory but not present." << std::endl;
-            throw Usage;
-        }
-    }
-}
-
-void parse_cmd(const int argc, const char *const *const argv) {
-    for (int i = 2; i < argc; i += 2) {
-        auto search = option_map.find(argv[i]);
-        if (search == option_map.end()) {
-            std::cout << "Error: Unknown option: " << argv[i] << std::endl;
-            throw Usage;
-        }
-        auto option = search->second;
-        option->set_present();
-        option->set_value(argv[i + 1]);
-    }
-}
-
-
 // remove leading spaces and quotes
 void ltrim(std::string &s) {
   s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](int ch) {
@@ -151,16 +109,15 @@ void parseFileInfo(std::istream &in, AttrMap &attr, AttrMap &xattr)
  * @param[in]    wf_command      The workflow command (CLOSEW or PREPARE)
  */
 void fillNotification(cta::eos::Notification &notification, const std::string &wf_command,
-                      const int argc, const char *const *const argv)
+                      const int argc, char *const *const argv)
 {
 
-  parse_cmd(argc, argv);
-  validate_cmd();
+  cta::cliTool::CmdLineArgs cmdLineArgs(argc, argv, cta::cliTool::StandaloneCliTool::CTA_SEND_EVENT);
 
-  const std::string &eos_instance = option_instance.get_value();
-  const std::string &eos_endpoint = option_endpoint.is_present() ?option_endpoint.get_value() : "localhost:1095";
-  const std::string &requester_user = option_user.get_value();
-  const std::string &requester_group = option_group.get_value();
+  const std::string &eos_instance = cmdLineArgs.m_diskInstance.value();
+  const std::string &eos_endpoint = cmdLineArgs.m_eosEndpoint.has_value() ? cmdLineArgs.m_eosEndpoint.value() : "localhost:1095";
+  const std::string &requester_user = cmdLineArgs.m_requestUser.value();
+  const std::string &requester_group = cmdLineArgs.m_requestGroup.value();
 
   // Set the event type
   if(wf_command == "CLOSEW") {
@@ -236,7 +193,7 @@ void fillNotification(cta::eos::Notification &notification, const std::string &w
 /*
  * Sends a Notification to the CTA XRootD SSI server
  */
-int exceptionThrowingMain(int argc, const char *const *const argv)
+int exceptionThrowingMain(int argc, char *const *const argv)
 {
   if(argc % 2) {
     throw Usage;
@@ -299,8 +256,7 @@ int exceptionThrowingMain(int argc, const char *const *const argv)
 /*
  * Start here
  */
-int main(int argc, const char **argv)
-{
+int main(int argc, char *const *const argv) {
   try {    
     return exceptionThrowingMain(argc, argv);
   } catch (XrdSsiPb::PbException &ex) {
diff --git a/cmdline/CtaVerifyFile.cpp b/cmdline/standalone_cli_tools/CtaVerifyFile.cpp
similarity index 67%
rename from cmdline/CtaVerifyFile.cpp
rename to cmdline/standalone_cli_tools/CtaVerifyFile.cpp
index 0c4af66dbf..d740a71d1e 100644
--- a/cmdline/CtaVerifyFile.cpp
+++ b/cmdline/standalone_cli_tools/CtaVerifyFile.cpp
@@ -21,7 +21,9 @@
 
 #include "CtaFrontendApi.hpp"
 #include "version.h"
-#include "CtaCmdOptions.hpp"
+#include "common/CmdLineArgs.hpp"
+
+using namespace cta::cliTool;
 
 const std::string config_file = "/etc/cta/cta-cli.conf";
 
@@ -43,53 +45,6 @@ void RequestCallback<cta::xrd::Alert>::operator()(const cta::xrd::Alert &alert)
 // Attribute map type
 typedef std::map<std::string, std::string> AttrMap;
 
-// Usage exception
-const std::runtime_error Usage("Usage: cta-verify-file <archiveFileID> [-v <vid>] "
-                               "[-i <instance>] [-r.user <user>] [-r.group <group>]\n"
-                               "cta-verify-file <archiveFileID> [--vid <id>] "
-                               "[--instance <instance>] [--request.user <user>] [--request.group <group>]");
-
-StringOption option_instance {"--instance", "-i", true};
-StringOption option_request_user {"--request.user", "-r.user", true};
-StringOption option_request_group {"--request.group", "-r.group", true};
-StringOption option_vid {"--vid", "-v", true};
-
-std::vector<StringOption*> verify_options = {&option_instance, &option_request_user, &option_request_group, &option_vid};
-
-std::map<std::string, StringOption*> option_map = {
-        {"-i", &option_instance},
-        {"--instance", &option_instance},
-        {"-r.user", &option_request_user},
-        {"--request.user", &option_request_user},
-        {"-r.group", &option_request_group},
-        {"--request.group", &option_request_group},
-        {"-v", &option_vid},
-        {"--vid", &option_vid},
-};
-
-void validate_cmd() {
-    for (auto &it: option_map) {
-        auto option = it.second;
-        if (!option->is_optional() && !option->is_present()) {
-            std::cout << "Error: Option " << option->get_name() << " is mandatory but not present." << std::endl;
-            throw Usage;
-        }
-    }
-}
-
-void parse_cmd(const int argc, const char *const *const argv) {
-    for (int i = 2; i < argc; i += 2) {
-        auto search = option_map.find(argv[i]);
-        if (search == option_map.end()) {
-            std::cout << "Error: Unknown option: " << argv[i] << std::endl;
-            throw Usage;
-        }
-        auto option = search->second;
-        option->set_present();
-        option->set_value(argv[i + 1]);
-    }
-}
-
 /*
  * Fill a Notification message from the command-line parameters and stdin
  *
@@ -97,7 +52,7 @@ void parse_cmd(const int argc, const char *const *const argv) {
  * @param[in]    argc            Number of arguments passed on the command line
  * @param[in]    argv            Command line arguments array
  */
-void fillNotification(cta::eos::Notification &notification, const int argc, const char *const *const argv)
+void fillNotification(cta::eos::Notification &notification, const int argc, char *const *const argv, const CmdLineArgs &cmdLineArgs)
 {   
   XrdSsiPb::Config config(config_file, "eos");
   for (const auto &conf_option : std::vector<std::string>({ "instance", "requester.user", "requester.group" })) {
@@ -108,27 +63,31 @@ void fillNotification(cta::eos::Notification &notification, const int argc, cons
   notification.mutable_wf()->mutable_instance()->set_name(config.getOptionValueStr("instance").second);
   notification.mutable_cli()->mutable_user()->set_username(config.getOptionValueStr("requester.user").second);
   notification.mutable_cli()->mutable_user()->set_groupname(config.getOptionValueStr("requester.group").second);
+  
+  if(cmdLineArgs.m_help) { cmdLineArgs.printUsage(std::cout); exit(0); }
 
-  parse_cmd(argc, argv);
-  validate_cmd();
+  if(!cmdLineArgs.m_archiveFileId && !cmdLineArgs.m_vid) { 
+    cmdLineArgs.printUsage(std::cout);
+    throw std::runtime_error("ERROR: Usage");
+  }
 
-  if (option_instance.is_present()) {
-    notification.mutable_wf()->mutable_instance()->set_name(option_instance.get_value());
+  if (cmdLineArgs.m_diskInstance) {
+    notification.mutable_wf()->mutable_instance()->set_name(cmdLineArgs.m_diskInstance.value());
   }
-  if (option_request_user.is_present()) {
-    notification.mutable_cli()->mutable_user()->set_username(option_request_user.get_value());  
+  if (cmdLineArgs.m_requestUser) {
+    notification.mutable_cli()->mutable_user()->set_username(cmdLineArgs.m_requestUser.value());  
   }
-  if (option_request_group.is_present()) {
-    notification.mutable_cli()->mutable_user()->set_groupname(option_request_group.get_value());
+  if (cmdLineArgs.m_requestGroup) {
+    notification.mutable_cli()->mutable_user()->set_groupname(cmdLineArgs.m_requestGroup.value());
   }  
 
-  std::string archiveFileId(argv[1]);
+  const std::string archiveFileId(std::to_string(cmdLineArgs.m_archiveFileId.value()));
 
   // WF
   notification.mutable_wf()->set_event(cta::eos::Workflow::PREPARE);
   notification.mutable_wf()->set_requester_instance("cta-verify-file");
   notification.mutable_wf()->set_verify_only(true);
-  notification.mutable_wf()->set_vid(option_vid.get_value());
+  notification.mutable_wf()->set_vid(cmdLineArgs.m_vid.value());
 
   
   // Transport
@@ -151,13 +110,13 @@ void fillNotification(cta::eos::Notification &notification, const int argc, cons
 /*
  * Sends a Notification to the CTA XRootD SSI server
  */
-int exceptionThrowingMain(int argc, const char *const *const argv)
+int exceptionThrowingMain(int argc, char *const *const argv)
 {
+  using namespace cta::cliTool;
+
   std::string vid;
 
-  if (argc == 1 || argc % 2) { // Since all options need values associated, argc should always be even
-    throw Usage;
-  }
+  cta::cliTool::CmdLineArgs cmdLineArgs(argc, argv, StandaloneCliTool::CTA_VERIFY_FILE);
 
   // Verify that the Google Protocol Buffer header and linked library versions are compatible
   GOOGLE_PROTOBUF_VERIFY_VERSION;
@@ -184,7 +143,7 @@ int exceptionThrowingMain(int argc, const char *const *const argv)
   config.getEnv("log", "XrdSsiPbLogLevel");
 
   // Parse the command line arguments: fill the Notification fields
-  fillNotification(notification, argc, argv);
+  fillNotification(notification, argc, argv, cmdLineArgs);
 
   // Obtain a Service Provider
   XrdSsiPbServiceType cta_service(config);
@@ -215,8 +174,7 @@ int exceptionThrowingMain(int argc, const char *const *const argv)
 /*
  * Start here
  */
-int main(int argc, const char **argv)
-{
+int main(int argc, char *const *const argv) {
   try {
     return exceptionThrowingMain(argc, argv);
   } catch (XrdSsiPb::PbException &ex) {
diff --git a/cmdline/restore_files/RestoreFilesCmdLineArgs.cpp b/cmdline/standalone_cli_tools/common/CmdLineArgs.cpp
similarity index 51%
rename from cmdline/restore_files/RestoreFilesCmdLineArgs.cpp
rename to cmdline/standalone_cli_tools/common/CmdLineArgs.cpp
index d8665b129f..a6523c10aa 100644
--- a/cmdline/restore_files/RestoreFilesCmdLineArgs.cpp
+++ b/cmdline/standalone_cli_tools/common/CmdLineArgs.cpp
@@ -15,50 +15,94 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "RestoreFilesCmdLineArgs.hpp"
+#include "cmdline/standalone_cli_tools/common/CmdLineArgs.hpp"
 #include "common/exception/CommandLineNotParsed.hpp"
 
+#include <climits>
+#include <fstream>
 #include <getopt.h>
-#include <ostream>
-#include <sstream>
-#include <iostream>
+#include <map>
 #include <string>
-#include <limits.h>
-#include <fstream>
-
 
 namespace cta {
-namespace admin{
+namespace cliTool{
+
+
+//------------------------------------------------------------------------------
+// Options for each tool
+//------------------------------------------------------------------------------
+static struct option restoreFilesLongOption[] = {
+  {"id", required_argument, nullptr, 'I'},
+  {"instance", required_argument, nullptr, 'i'},
+  {"fxid", required_argument, nullptr, 'f'},
+  {"fxidfile", required_argument, nullptr, 'F'},
+  {"vid", required_argument, nullptr, 'v'},
+  {"copynb", required_argument, nullptr, 'c'},
+  {"help", no_argument, nullptr, 'h'},
+  {"debug", no_argument, nullptr, 'd'},
+  {nullptr, 0, nullptr, 0}
+};
+
+static struct option sendFileLongOption[] = {
+  {"instance", required_argument, nullptr, 'i'},
+  {"eos.endpoint", required_argument, nullptr, 'e'},
+  {"request.user", required_argument, nullptr, 'u'},
+  {"request.group", required_argument, nullptr, 'g'},
+  {nullptr, 0, nullptr, 0}
+};
+
+static struct option verifyFileLongOption[] = {
+  {"id", required_argument, nullptr, 'I'},
+  {"instance", required_argument, nullptr, 'i'},
+  {"request.user", required_argument, nullptr, 'u'},
+  {"request.group", required_argument, nullptr, 'g'},
+  {"vid", required_argument, nullptr, 'v'},
+  {"help", no_argument, nullptr, 'h'},
+  {nullptr, 0, nullptr, 0}
+};
+
+std::map<StandaloneCliTool, const option*> longopts = {
+  {StandaloneCliTool::RESTORE_FILES, restoreFilesLongOption},
+  {StandaloneCliTool::CTA_SEND_EVENT, sendFileLongOption},
+  {StandaloneCliTool::CTA_VERIFY_FILE, verifyFileLongOption},
+};
+
+std::map<StandaloneCliTool, const char*> shortopts = {
+  {StandaloneCliTool::RESTORE_FILES, "I:i:f:F:v:c:hd:"},
+  {StandaloneCliTool::CTA_SEND_EVENT, "i:e:u:g:"},
+  {StandaloneCliTool::CTA_VERIFY_FILE, "I:i:u:g:v:h:"},
+};
 
 //------------------------------------------------------------------------------
 // constructor
 //------------------------------------------------------------------------------
-RestoreFilesCmdLineArgs::RestoreFilesCmdLineArgs(const int argc, char *const *const argv):
-m_help(false), m_debug(false) {
-
-  static struct option longopts[] = {
-    {"id", required_argument, nullptr, 'I'},
-    {"instance", required_argument, nullptr, 'i'},
-    {"fxid", required_argument, nullptr, 'f'},
-    {"fxidfile", required_argument, nullptr, 'F'},
-    {"vid", required_argument, nullptr, 'v'},
-    {"copynb", required_argument, nullptr, 'c'},
-    {"help", no_argument, nullptr, 'h'},
-    {"debug", no_argument, nullptr, 'd'},
-    {nullptr, 0, nullptr, 0}
-  };
+CmdLineArgs::CmdLineArgs(const int &argc, char *const *const &argv, const StandaloneCliTool &standaloneCliTool):
+m_help(false), m_debug(false), m_standaloneCliTool{standaloneCliTool} {
 
   opterr = 0;
   int opt = 0;
-  int opt_index = 3;
+  int opt_index;
 
-  while ((opt = getopt_long(argc, argv, "I:i:f:F:v:c:hd", longopts, &opt_index)) != -1) {
+  switch (standaloneCliTool) {
+  case StandaloneCliTool::RESTORE_FILES:
+    opt_index = 3;
+    break;
+  case StandaloneCliTool::CTA_SEND_EVENT:
+    opt_index = 3;
+    break;
+  case StandaloneCliTool::CTA_VERIFY_FILE:
+    opt_index = 2;
+    break;
+  default:
+    opt_index = 3;
+    break;
+  }
+
+  while ((opt = getopt_long(argc, argv, shortopts[m_standaloneCliTool], longopts[m_standaloneCliTool], &opt_index)) != -1) {
     switch(opt) {
     case 'I':
       {
-        int64_t archiveId = std::stol(std::string(optarg));
-        if(archiveId < 0) throw std::out_of_range("archive id value cannot be negative");
-        m_archiveFileId = archiveId;
+        m_archiveFileId = std::stol(std::string(optarg));
         break;
       }
     case 'i':
@@ -108,10 +152,25 @@ m_help(false), m_debug(false) {
         m_debug = true;
         break;
       }
+    case 'e':
+      {
+        m_eosEndpoint = std::string(optarg);
+        break;
+      }
+    case 'u':
+      {
+        m_requestUser = std::string(optarg);
+        break;
+      }
+    case 'g':
+      {
+        m_requestGroup = std::string(optarg);
+        break;
+      }
     case ':': // Missing parameter
       {
         exception::CommandLineNotParsed ex;
-        ex.getMessage() << "The -" << (char)optopt << " option requires a parameter";
+        ex.getMessage() << "The -" << static_cast<char>(optopt) << " option requires a parameter";
         throw ex;
       }
     case '?': // Unknown option
@@ -120,8 +179,9 @@ m_help(false), m_debug(false) {
         if(0 == optopt) {
           ex.getMessage() << "Unknown command-line option";
         } else {
-          ex.getMessage() << "Unknown command-line option: -" << (char)optopt;
+          ex.getMessage() << "Unknown command-line option: -" << static_cast<char>(optopt) << std::endl;
         }
+        printUsage(ex.getMessage());
         throw ex;
       }
     default:
@@ -129,7 +189,7 @@ m_help(false), m_debug(false) {
         exception::CommandLineNotParsed ex;
         ex.getMessage() <<
         "getopt_long returned the following unknown value: 0x" <<
-        std::hex << (int)opt;
+        std::hex << static_cast<int>(optopt);
         throw ex;
       }
     }
@@ -139,7 +199,7 @@ m_help(false), m_debug(false) {
 //------------------------------------------------------------------------------
 // readFidListFromFile
 //------------------------------------------------------------------------------
-void RestoreFilesCmdLineArgs::readFidListFromFile(const std::string &filename, std::list<std::uint64_t> &fidList) {
+void CmdLineArgs::readFidListFromFile(const std::string &filename, std::list<std::uint64_t> &fidList) {
   std::ifstream file(filename);
   if (file.fail()) {
     throw std::runtime_error("Unable to open file " + filename);
@@ -182,12 +242,28 @@ void RestoreFilesCmdLineArgs::readFidListFromFile(const std::string &filename, s
 //------------------------------------------------------------------------------
 // printUsage
 //------------------------------------------------------------------------------
-void RestoreFilesCmdLineArgs::printUsage(std::ostream &os) {
-    os << "Usage:" << std::endl <<
-    "  cta-restore-deleted-files [--id/-I <archive_file_id>] [--instance/-i <disk_instance>]" << std::endl <<
-    "                            [--fxid/-f <eos_fxid>] [--fxidfile/-F <filename>]" << std::endl <<
-    "                            [--vid/-v <vid>] [--copynb/-c <copy_number>] [--debug/-d]" << std::endl;
+void CmdLineArgs::printUsage(std::ostream &os) const {
+  switch (m_standaloneCliTool) {
+  case StandaloneCliTool::RESTORE_FILES: 
+    os << "   Usage:" << std::endl <<
+    "      cta-restore-deleted-files [--id/-I <archive_file_id>] [--instance/-i <disk_instance>]" << std::endl <<
+    "                                [--fxid/-f <eos_fxid>] [--fxidfile/-F <filename>]" << std::endl <<
+    "                                [--vid/-v <vid>] [--copynb/-c <copy_number>] [--debug/-d]" << std::endl; 
+    break;
+  case StandaloneCliTool::CTA_SEND_EVENT:
+    os << "    Usage:" << std::endl <<
+    "    eos --json fileinfo /eos/path | cta-send-event CLOSEW|PREPARE " << std::endl <<
+    "                        -i/--eos.instance <instance> [-e/--eos.endpoint <url>]" << std::endl << 
+    "                        -u/--request.user <user> -g/--request.group <group>" << std::endl;
+    break;
+  case StandaloneCliTool::CTA_VERIFY_FILE :
+    os << "    Usage:" << std::endl <<
+    "    cta-verify-file --id/-I <archiveFileID> --vid/-v <vid> [--instance/-i <instance>] [--request.user/-u <user>] [request.group/-g <group>]\n" << std::endl;
+    break;
+  default:
+    break;
+  }
 }
 
 } // namespace admin
-} // namespace cta
+} // namespace cta
\ No newline at end of file
diff --git a/cmdline/restore_files/RestoreFilesCmdLineArgs.hpp b/cmdline/standalone_cli_tools/common/CmdLineArgs.hpp
similarity index 75%
rename from cmdline/restore_files/RestoreFilesCmdLineArgs.hpp
rename to cmdline/standalone_cli_tools/common/CmdLineArgs.hpp
index 40c5997cda..a90bf727b1 100644
--- a/cmdline/restore_files/RestoreFilesCmdLineArgs.hpp
+++ b/cmdline/standalone_cli_tools/common/CmdLineArgs.hpp
@@ -21,15 +21,25 @@
 
 #include <list>
 #include <optional>
+#include <string>
 
 namespace cta {
-namespace admin {
+namespace cliTool {
+
+/**
+* Enum value for each of the tools
+*/
+enum class StandaloneCliTool {
+  RESTORE_FILES,
+  CTA_SEND_EVENT,
+  CTA_VERIFY_FILE
+};
 
 /**
  * Structure to store the command-line arguments of the command-line tool
  * named cta-restore-deleted-archive.
  */
-struct RestoreFilesCmdLineArgs {
+struct CmdLineArgs {
   /**
    * True if the usage message should be printed.
    */
@@ -55,6 +65,11 @@ struct RestoreFilesCmdLineArgs {
    */
   std::optional<std::list<uint64_t>> m_eosFids;
 
+    /**
+   * Fids of the files to restore
+   */
+  std::optional<std::string> m_eosInstance;
+
   /**
    * Vid of the tape of the files to restore
    */
@@ -65,14 +80,35 @@ struct RestoreFilesCmdLineArgs {
    */
   std::optional<uint64_t> m_copyNumber;
 
+  /**
+   * Eos endpoint 
+   */
+  std::optional<std::string> m_eosEndpoint;
+
+  /**
+   * Request user
+   */ 
+  std::optional<std::string> m_requestUser;
+
+  /**
+   * Request user
+   */ 
+  std::optional<std::string> m_requestGroup;
+
+  /**
+   * The tool parsing the arguments
+   */ 
+  StandaloneCliTool m_standaloneCliTool;
+
   /**
    * Constructor that parses the specified command-line arguments.
    *
    * @param argc The number of command-line arguments including the name of the
    * executable.
    * @param argv The vector of command-line arguments.
+   * @param standaloneCliTool The tool calling the constructor
    */
-  RestoreFilesCmdLineArgs(const int argc, char *const *const argv);
+  CmdLineArgs(const int &argc, char *const *const &argv, const StandaloneCliTool &standaloneCliTool);
 
    /**
    * Read a list of eos file ids from a file and write the options to a list
@@ -87,9 +123,9 @@ struct RestoreFilesCmdLineArgs {
    *
    * @param os The output stream to which the usage message is to be printed.
    */
-  static void printUsage(std::ostream &os);
+  void printUsage(std::ostream &os) const;
 
 }; // class RestoreFilesCmdLineArgs
 
 } // namespace admin
-} // namespace cta
+} // namespace cta
\ No newline at end of file
diff --git a/cmdline/restore_files/CmdLineTool.cpp b/cmdline/standalone_cli_tools/common/CmdLineTool.cpp
similarity index 88%
rename from cmdline/restore_files/CmdLineTool.cpp
rename to cmdline/standalone_cli_tools/common/CmdLineTool.cpp
index fab6ead755..5ae584bf85 100644
--- a/cmdline/restore_files/CmdLineTool.cpp
+++ b/cmdline/standalone_cli_tools/common/CmdLineTool.cpp
@@ -15,13 +15,15 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "cmdline/restore_files/CmdLineTool.hpp"
+#include "cmdline/standalone_cli_tools/common/CmdLineTool.hpp"
 #include "common/exception/CommandLineNotParsed.hpp"
 
+#include <iostream>
+#include <string>
 #include <unistd.h>
 
 namespace cta {
-namespace admin {
+namespace cliTool {
 
 //------------------------------------------------------------------------------
 // constructor
@@ -72,14 +74,12 @@ std::string CmdLineTool::getHostname() {
 // main
 //------------------------------------------------------------------------------
 int CmdLineTool::main(const int argc, char *const *const argv) {
-  bool cmdLineNotParsed = false;
   std::string errorMessage;
 
   try {
     return exceptionThrowingMain(argc, argv);
   } catch(exception::CommandLineNotParsed &ue) {
     errorMessage = ue.getMessage().str();
-    cmdLineNotParsed = true;
   } catch(exception::Exception &ex) {
     errorMessage = ex.getMessage().str();
   } catch(std::exception &se) {
@@ -87,15 +87,8 @@ int CmdLineTool::main(const int argc, char *const *const argv) {
   } catch(...) {
     errorMessage = "An unknown exception was thrown";
   }
-
-  // Reaching this point means the command has failed, an exception was throw
-  // and errorMessage has been set accordingly
-
-  m_err << "Aborting: " << errorMessage << std::endl;
-  if(cmdLineNotParsed) {
-    m_err << std::endl;
-    printUsage(m_err);
-  }
+  
+  std::cout << errorMessage << std::endl;
   return 1;
 }
 
diff --git a/cmdline/restore_files/CmdLineTool.hpp b/cmdline/standalone_cli_tools/common/CmdLineTool.hpp
similarity index 92%
rename from cmdline/restore_files/CmdLineTool.hpp
rename to cmdline/standalone_cli_tools/common/CmdLineTool.hpp
index e71ced1fcb..5da1bdb277 100644
--- a/cmdline/restore_files/CmdLineTool.hpp
+++ b/cmdline/standalone_cli_tools/common/CmdLineTool.hpp
@@ -21,7 +21,7 @@
 #include <ostream>
 
 namespace cta {
-namespace admin {
+namespace cliTool {
 
 /**
  * Abstract class implementing common code and data structures for a
@@ -64,13 +64,6 @@ protected:
    */
   virtual int exceptionThrowingMain(const int argc, char *const *const argv) = 0;
 
-  /**
-   * Prints the usage message of the command-line tool.
-   *
-   * @param os The output stream to which the usage message is to be printed.
-   */
-  virtual void printUsage(std::ostream &os) = 0;
-
   /**
    * Standard input stream.
    */
diff --git a/cmdline/restore_files/CMakeLists.txt b/cmdline/standalone_cli_tools/restore_files/CMakeLists.txt
similarity index 91%
rename from cmdline/restore_files/CMakeLists.txt
rename to cmdline/standalone_cli_tools/restore_files/CMakeLists.txt
index dea348c803..20a0246972 100644
--- a/cmdline/restore_files/CMakeLists.txt
+++ b/cmdline/standalone_cli_tools/restore_files/CMakeLists.txt
@@ -27,7 +27,7 @@ include_directories(${XRD_SSI_PB_DIR}/include ${XRD_SSI_PB_DIR}/eos_cta/include)
 # Compiled protocol buffers
 include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
 
-add_executable(cta-restore-deleted-files RestoreFilesCmdLineArgs.cpp RestoreFilesCmdMain.cpp CmdLineTool.cpp RestoreFilesCmd.cpp)
+add_executable(cta-restore-deleted-files RestoreFilesCmdMain.cpp ../common/CmdLineTool.cpp ../common/CmdLineArgs.cpp RestoreFilesCmd.cpp ../../CtaAdminCmdParse.cpp)
 target_link_libraries(cta-restore-deleted-files ${PROTOBUF3_LIBRARIES} ${GRPC_LIBRARY} ${GRPC_GRPC++_LIBRARY} XrdSsiPbEosCta XrdSsiLib XrdUtils ctacommon EosCtaGrpc EosGrpcClient)
 set_property (TARGET cta-restore-deleted-files APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
 
diff --git a/cmdline/restore_files/RestoreFilesCmd.cpp b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.cpp
similarity index 94%
rename from cmdline/restore_files/RestoreFilesCmd.cpp
rename to cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.cpp
index c32cc2fc0f..c50b323dab 100644
--- a/cmdline/restore_files/RestoreFilesCmd.cpp
+++ b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.cpp
@@ -15,8 +15,8 @@
  *               submit itself to any jurisdiction.
  */
 
-#include "cmdline/restore_files/RestoreFilesCmd.hpp"
-#include "cmdline/restore_files/RestoreFilesCmdLineArgs.hpp"
+#include "cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.hpp"
+#include "cmdline/CtaAdminCmdParse.hpp"
 #include "common/utils/utils.hpp"
 #include "common/checksum/ChecksumBlob.hpp"
 #include "CtaFrontendApi.hpp"
@@ -99,7 +99,7 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
 
 
 namespace cta{
-namespace admin {
+namespace cliTool {
 
 /*!
  * RestoreFilesCmdException
@@ -136,9 +136,9 @@ RestoreFilesCmd::RestoreFilesCmd(std::istream &inStream, std::ostream &outStream
 // exceptionThrowingMain
 //------------------------------------------------------------------------------
 int RestoreFilesCmd::exceptionThrowingMain(const int argc, char *const *const argv) {
-  RestoreFilesCmdLineArgs cmdLineArgs(argc, argv);
+  CmdLineArgs cmdLineArgs(argc, argv, StandaloneCliTool::RESTORE_FILES);
   if (cmdLineArgs.m_help) {
-    printUsage(m_out);
+    cmdLineArgs.printUsage(m_out);
     return 0;
   }
 
@@ -190,7 +190,7 @@ int RestoreFilesCmd::exceptionThrowingMain(const int argc, char *const *const ar
 //------------------------------------------------------------------------------
 void RestoreFilesCmd::readAndSetConfiguration(
   const std::string &userName, 
-  const RestoreFilesCmdLineArgs &cmdLineArgs) {
+  const CmdLineArgs &cmdLineArgs) {
   
   m_vid = cmdLineArgs.m_vid;
   m_diskInstance = cmdLineArgs.m_diskInstance;
@@ -291,40 +291,40 @@ void RestoreFilesCmd::listDeletedFilesCta() const {
    
   request.set_client_cta_version(CTA_VERSION);
   request.set_client_xrootd_ssi_protobuf_interface_version(XROOTD_SSI_PROTOBUF_INTERFACE_VERSION);
-  admincmd.set_cmd(AdminCmd::CMD_RECYCLETAPEFILE);
-  admincmd.set_subcmd(AdminCmd::SUBCMD_LS);
+  admincmd.set_cmd(cta::admin::AdminCmd::CMD_RECYCLETAPEFILE);
+  admincmd.set_subcmd(cta::admin::AdminCmd::SUBCMD_LS);
 
   if (m_vid) {
     params.push_back(cta::log::Param("tapeVid", m_vid.value()));
-    auto key = OptionString::VID;
+    auto key = cta::admin::OptionString::VID;
     auto new_opt = admincmd.add_option_str();
     new_opt->set_key(key);
     new_opt->set_value(m_vid.value());
   }
   if (m_diskInstance) {
     params.push_back(cta::log::Param("diskInstance", m_diskInstance.value()));
-    auto key = OptionString::INSTANCE;
+    auto key = cta::admin::OptionString::INSTANCE;
     auto new_opt = admincmd.add_option_str();
     new_opt->set_key(key);
     new_opt->set_value(m_diskInstance.value());
   }
   if (m_archiveFileId) {
     params.push_back(cta::log::Param("archiveFileId", m_archiveFileId.value()));
-    auto key = OptionUInt64::ARCHIVE_FILE_ID;
+    auto key = cta::admin::OptionUInt64::ARCHIVE_FILE_ID;
     auto new_opt = admincmd.add_option_uint64();
     new_opt->set_key(key);
     new_opt->set_value(m_archiveFileId.value());
   }
   if (m_copyNumber) {
     params.push_back(cta::log::Param("copyNb", m_copyNumber.value()));
-    auto key = OptionUInt64::COPY_NUMBER;
+    auto key = cta::admin::OptionUInt64::COPY_NUMBER;
     auto new_opt = admincmd.add_option_uint64();
     new_opt->set_key(key);
     new_opt->set_value(m_copyNumber.value());
   }
   if (m_eosFids) {
     std::stringstream ss;
-    auto key = OptionStrList::FILE_ID;
+    auto key = cta::admin::OptionStrList::FILE_ID;
     auto new_opt = admincmd.add_option_str_list();
     new_opt->set_key(key);
     for (auto &fid : m_eosFids.value()) {
@@ -369,7 +369,7 @@ void RestoreFilesCmd::listDeletedFilesCta() const {
 //------------------------------------------------------------------------------
 // restoreDeletedFileCopyCta
 //------------------------------------------------------------------------------
-void RestoreFilesCmd::restoreDeletedFileCopyCta(const RecycleTapeFileLsItem &file) const {
+void RestoreFilesCmd::restoreDeletedFileCopyCta(const cta::admin::RecycleTapeFileLsItem &file) const {
 
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("userName", getUsername()));
@@ -385,35 +385,35 @@ void RestoreFilesCmd::restoreDeletedFileCopyCta(const RecycleTapeFileLsItem &fil
    
   request.set_client_cta_version(CTA_VERSION);
   request.set_client_xrootd_ssi_protobuf_interface_version(XROOTD_SSI_PROTOBUF_INTERFACE_VERSION);
-  admincmd.set_cmd(AdminCmd::CMD_RECYCLETAPEFILE);
-  admincmd.set_subcmd(AdminCmd::SUBCMD_RESTORE);
+  admincmd.set_cmd(cta::admin::AdminCmd::CMD_RECYCLETAPEFILE);
+  admincmd.set_subcmd(cta::admin::AdminCmd::SUBCMD_RESTORE);
 
   {
-    auto key = OptionString::VID;
+    auto key = cta::admin::OptionString::VID;
     auto new_opt = admincmd.add_option_str();
     new_opt->set_key(key);
     new_opt->set_value(file.vid());
   }
   {
-    auto key = OptionString::INSTANCE;
+    auto key = cta::admin::OptionString::INSTANCE;
     auto new_opt = admincmd.add_option_str();
     new_opt->set_key(key);
     new_opt->set_value(file.disk_instance());
   }
   {
-    auto key = OptionUInt64::ARCHIVE_FILE_ID;
+    auto key = cta::admin::OptionUInt64::ARCHIVE_FILE_ID;
     auto new_opt = admincmd.add_option_uint64();
     new_opt->set_key(key);
     new_opt->set_value(file.archive_file_id());
   }
   {
-    auto key = OptionUInt64::COPY_NUMBER;
+    auto key = cta::admin::OptionUInt64::COPY_NUMBER;
     auto new_opt = admincmd.add_option_uint64();
     new_opt->set_key(key);
     new_opt->set_value(file.copy_nb());
   }
   {
-    auto key = OptionString::FXID;
+    auto key = cta::admin::OptionString::FXID;
     auto new_opt = admincmd.add_option_str();
 
     // Convert diskFileId from base 10 to base 16 before transmitting to CTA
@@ -430,6 +430,9 @@ void RestoreFilesCmd::restoreDeletedFileCopyCta(const RecycleTapeFileLsItem &fil
   }
   m_log(cta::log::DEBUG, "Restoring file copy in CTA catalogue", params);  
 
+  // This validation will also be done at the server side
+  cta::admin::validateCmd(admincmd);
+
   // Send the Request to the Service and get a Response
   cta::xrd::Response response;
   m_serviceProviderPtr->Send(request, response);
@@ -523,7 +526,7 @@ uint64_t RestoreFilesCmd::containerExistsEos(const std::string &diskInstance, co
   return cid;
 }
 
-bool RestoreFilesCmd::fileWasDeletedByRM(const RecycleTapeFileLsItem &file) const {
+bool RestoreFilesCmd::fileWasDeletedByRM(const cta::admin::RecycleTapeFileLsItem &file) const {
   return file.reason_log().rfind("(Deleted using cta-admin tapefile rm)", 0) == 0;
 }
 
@@ -544,10 +547,10 @@ bool RestoreFilesCmd::archiveFileExistsCTA(const uint64_t &archiveFileId) const
    
   request.set_client_cta_version(CTA_VERSION);
   request.set_client_xrootd_ssi_protobuf_interface_version(XROOTD_SSI_PROTOBUF_INTERFACE_VERSION);
-  admincmd.set_cmd(AdminCmd::CMD_TAPEFILE);
-  admincmd.set_subcmd(AdminCmd::SUBCMD_LS);
+  admincmd.set_cmd(cta::admin::AdminCmd::CMD_TAPEFILE);
+  admincmd.set_subcmd(cta::admin::AdminCmd::SUBCMD_LS);
   
-  auto key = OptionUInt64::ARCHIVE_FILE_ID;
+  auto key = cta::admin::OptionUInt64::ARCHIVE_FILE_ID;
   auto new_opt = admincmd.add_option_uint64();
   new_opt->set_key(key);
   new_opt->set_value(archiveFileId);
@@ -642,7 +645,7 @@ void RestoreFilesCmd::getCurrentEosIds(const std::string &diskInstance) const {
 //------------------------------------------------------------------------------
 // restoreDeletedFileEos
 //------------------------------------------------------------------------------
-uint64_t RestoreFilesCmd::restoreDeletedFileEos(const RecycleTapeFileLsItem &rtfls_item) const {
+uint64_t RestoreFilesCmd::restoreDeletedFileEos(const cta::admin::RecycleTapeFileLsItem &rtfls_item) const {
 
   std::list<cta::log::Param> params;
   params.push_back(cta::log::Param("userName", getUsername()));
@@ -733,17 +736,10 @@ uint64_t RestoreFilesCmd::restoreDeletedFileEos(const RecycleTapeFileLsItem &rtf
 
 }
 
-//------------------------------------------------------------------------------
-// printUsage
-//------------------------------------------------------------------------------
-void RestoreFilesCmd::printUsage(std::ostream &os) {
-  RestoreFilesCmdLineArgs::printUsage(os);
-}
-
 //------------------------------------------------------------------------------
 // getFxidFromCTA
 //------------------------------------------------------------------------------
-std::pair<std::string,std::string> RestoreFilesCmd::getInstanceAndFidFromCTA(const RecycleTapeFileLsItem& file) {
+std::pair<std::string,std::string> RestoreFilesCmd::getInstanceAndFidFromCTA(const cta::admin::RecycleTapeFileLsItem& file) {
   {
     std::list<cta::log::Param> params;
     params.push_back(cta::log::Param("archiveFileId", file.archive_file_id()));
@@ -755,10 +751,10 @@ std::pair<std::string,std::string> RestoreFilesCmd::getInstanceAndFidFromCTA(con
 
   request.set_client_cta_version(CTA_VERSION);
   request.set_client_xrootd_ssi_protobuf_interface_version(XROOTD_SSI_PROTOBUF_INTERFACE_VERSION);
-  admincmd.set_cmd(AdminCmd::CMD_TAPEFILE);
-  admincmd.set_subcmd(AdminCmd::SUBCMD_LS);
+  admincmd.set_cmd(cta::admin::AdminCmd::CMD_TAPEFILE);
+  admincmd.set_subcmd(cta::admin::AdminCmd::SUBCMD_LS);
   auto new_opt = admincmd.add_option_uint64();
-  new_opt->set_key(OptionUInt64::ARCHIVE_FILE_ID);
+  new_opt->set_key(cta::admin::OptionUInt64::ARCHIVE_FILE_ID);
   new_opt->set_value(file.archive_file_id());
 
   // Send the Request to the Service and get a Response
diff --git a/cmdline/restore_files/RestoreFilesCmd.hpp b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.hpp
similarity index 92%
rename from cmdline/restore_files/RestoreFilesCmd.hpp
rename to cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.hpp
index d3675103e5..512397cd1d 100644
--- a/cmdline/restore_files/RestoreFilesCmd.hpp
+++ b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmd.hpp
@@ -17,8 +17,8 @@
 
 #pragma once
 
-#include "cmdline/restore_files/CmdLineTool.hpp"
-#include "cmdline/restore_files/RestoreFilesCmdLineArgs.hpp"
+#include "cmdline/standalone_cli_tools/common/CmdLineTool.hpp"
+#include "cmdline/standalone_cli_tools/common/CmdLineArgs.hpp"
 #include "eos_grpc_client/GrpcEndpoint.hpp"
 #include "catalogue/Catalogue.hpp"
 #include "common/log/StdoutLogger.hpp"
@@ -29,7 +29,7 @@
 #include "CtaFrontendApi.hpp"
 
 namespace cta {
-namespace admin {
+namespace cliTool {
 
 class RestoreFilesCmd: public CmdLineTool {
 public:
@@ -60,7 +60,7 @@ public:
    * @param username The name of the user running the command-line tool.
    * @param cmdLineArgs The arguments parsed from the command line.
    */
-  void readAndSetConfiguration(const std::string &userName, const RestoreFilesCmdLineArgs &cmdLineArgs);
+  void readAndSetConfiguration(const std::string &userName, const CmdLineArgs &cmdLineArgs);
 
   /**
    * Populate the namespace endpoint configuration from a keytab file
@@ -84,7 +84,7 @@ public:
    * Restores the deleted file present in the cta catalogue recycle bin
    * @param file the deleted tape file
    */
-  void restoreDeletedFileCopyCta(const RecycleTapeFileLsItem &file) const;
+  void restoreDeletedFileCopyCta(const cta::admin::RecycleTapeFileLsItem &file) const;
 
   /**
    * Adds a container in the eos namespace, if it does not exist
@@ -117,7 +117,7 @@ public:
    * @param diskInstance eos disk instance
    * @param diskFileId the eos file id to check
    */
-  bool fileWasDeletedByRM(const RecycleTapeFileLsItem &file) const;
+  bool fileWasDeletedByRM(const cta::admin::RecycleTapeFileLsItem &file) const;
 
   /**
    * Returns true (i.e. not zero) if an archive file with given id exists in the cta catalogue
@@ -136,14 +136,7 @@ public:
    * Restores the deleted file present in the eos namespace
    * @param file the deleted tape file
    */
-  uint64_t restoreDeletedFileEos(const RecycleTapeFileLsItem &file) const;
-
-  /**
-   * Prints the usage message of the command-line tool.
-   *
-   * @param os The output stream to which the usage message is to be printed.
-   */
-  void printUsage(std::ostream &os) override;
+  uint64_t restoreDeletedFileEos(const cta::admin::RecycleTapeFileLsItem &file) const;
 
 private:
   /**
@@ -152,7 +145,7 @@ private:
    * @param file  The restored tape file in CTA
    * @return      Tuple of EOS disk instance and file ID (fid) as a decimal string
    */
-  std::pair<std::string,std::string> getInstanceAndFidFromCTA(const RecycleTapeFileLsItem& file);
+  std::pair<std::string,std::string> getInstanceAndFidFromCTA(const cta::admin::RecycleTapeFileLsItem& file);
 
   /**
    * Query EOS for the archiveFileId and checksum of the restored file
diff --git a/cmdline/restore_files/RestoreFilesCmdMain.cpp b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmdMain.cpp
similarity index 96%
rename from cmdline/restore_files/RestoreFilesCmdMain.cpp
rename to cmdline/standalone_cli_tools/restore_files/RestoreFilesCmdMain.cpp
index d19f11440b..f22ca7ac5e 100644
--- a/cmdline/restore_files/RestoreFilesCmdMain.cpp
+++ b/cmdline/standalone_cli_tools/restore_files/RestoreFilesCmdMain.cpp
@@ -40,7 +40,7 @@ int main(const int argc, char *const *const argv) {
   }
   cta::log::StdoutLogger log(hostName, "cta-restore-deleted-files");
 
-  cta::admin::RestoreFilesCmd cmd(std::cin, std::cout, std::cerr, log);
+  cta::cliTool::RestoreFilesCmd cmd(std::cin, std::cout, std::cerr, log);
   int ret = cmd.main(argc, argv);
   // Delete all global objects allocated by libprotobuf
   google::protobuf::ShutdownProtobufLibrary();
diff --git a/cmdline/restore_files/cta-restore-deleted-files.1cta b/cmdline/standalone_cli_tools/restore_files/cta-restore-deleted-files.1cta
similarity index 100%
rename from cmdline/restore_files/cta-restore-deleted-files.1cta
rename to cmdline/standalone_cli_tools/restore_files/cta-restore-deleted-files.1cta
diff --git a/continuousintegration/docker/ctafrontend/cc7/etc/cta/cta-frontend-xrootd.conf b/continuousintegration/docker/ctafrontend/cc7/etc/cta/cta-frontend-xrootd.conf
index eac3b76818..54bbaceeca 100644
--- a/continuousintegration/docker/ctafrontend/cc7/etc/cta/cta-frontend-xrootd.conf
+++ b/continuousintegration/docker/ctafrontend/cc7/etc/cta/cta-frontend-xrootd.conf
@@ -33,7 +33,7 @@ cta.log.ssi warning
 cta.verification.mount_policy verification
 
 # Keytab containing gRPC endpoints and tokens for each disk instance
-#cta.ns.config /etc/cta/eos.grpc.keytab
+cta.ns.config /etc/cta/eos.grpc.keytab
 
 #
 # XRootD/SSI options
diff --git a/continuousintegration/orchestration/tests/README_restore_files.md b/continuousintegration/orchestration/tests/README_restore_files.md
new file mode 100644
index 0000000000..745ddbce75
--- /dev/null
+++ b/continuousintegration/orchestration/tests/README_restore_files.md
@@ -0,0 +1,27 @@
+# Restore Files Unit Test
+The test checks if the restore tool restores a deleted file from the recycle bin. The steps are:
+- Creates a new file 
+- Deletes the file
+- Restores the file 
+- Checks consistency: archive id, checksum and file size for deleted and restored file 
+- Checks consistency: fxid for restored file in EOS and CTA
+
+## How to Run Test
+The test can be run with the following command: 
+
+`./restore_files.sh -n <kubernetes-instance>`
+
+In addition, the tool must be run from the folder:
+
+`~/CTA/continuousintegration/orchestration/tests`
+
+There must also be a keytab file provided in `/etc/cta/eos.grpc.keytab`. Use the same token as on the eos side. The file should be on the form:
+
+``` 
+# disk instance  endpoint (host:port)           gRPC token
+eosdev           <eoshostname>.cern.ch:50051    <token>
+```
+
+In `continuousintegration/docker/ctafrontend/cc7/etc/cta/cta-frontend-xrootd.conf` an option should specify the path to the file:
+
+`cta.ns.config /etc/cta/eos.grpc.keytab`
\ No newline at end of file
diff --git a/continuousintegration/orchestration/tests/common/archive_file.sh b/continuousintegration/orchestration/tests/common/archive_file.sh
new file mode 100644
index 0000000000..bd9a88da51
--- /dev/null
+++ b/continuousintegration/orchestration/tests/common/archive_file.sh
@@ -0,0 +1,87 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+EOSINSTANCE=ctaeos
+
+usage() { cat <<EOF 1>&2
+Usage: $0 -f <filename>
+EOF
+exit 1
+}
+
+while getopts "n:" o; do
+  case "${o}" in
+    n)
+      TEST_FILE_NAME=${OPTARG}
+      ;;
+    *)
+      usage
+      ;;
+    esac
+done
+shift $((OPTIND-1))
+
+if [ -z "${TEST_FILE_NAME}" ]; then
+  usage
+fi
+
+if [ ! -z "${error}" ]; then
+  echo -e "ERROR:\n${error}"
+  exit 1
+fi
+
+# get some common useful helpers for krb5
+. /root/client_helper.sh
+
+eospower_kdestroy
+eospower_kinit
+
+echo "xrdcp /etc/group root://${EOSINSTANCE}//eos/ctaeos/cta/${TEST_FILE_NAME}"
+xrdcp /etc/group root://${EOSINSTANCE}//eos/ctaeos/cta/${TEST_FILE_NAME}
+
+SECONDS_PASSED=0
+WAIT_FOR_ARCHIVED_FILE_TIMEOUT=90
+while test 0 == `eos root://${EOSINSTANCE} info /eos/ctaeos/cta/${TEST_FILE_NAME} | awk '{print $4;}' | grep tape | wc -l`; do
+  echo "Waiting for file to be archived to tape: Seconds passed = ${SECONDS_PASSED}"
+  sleep 1
+  let SECONDS_PASSED=SECONDS_PASSED+1
+
+  if test ${SECONDS_PASSED} == ${WAIT_FOR_ARCHIVED_FILE_TIMEOUT}; then
+    echo "Timed out after ${WAIT_FOR_ARCHIVED_FILE_TIMEOUT} seconds waiting for file to be archived to tape"
+    exit 1
+  fi
+done
+
+echo
+echo "FILE ARCHIVED TO TAPE"
+echo
+eos root://${EOSINSTANCE} info /eos/ctaeos/cta/${TEST_FILE_NAME}
+echo
+echo "Information about the testing file:"
+echo "********"
+  eos root://${EOSINSTANCE} attr ls /eos/ctaeos/cta/${TEST_FILE_NAME}
+  eos root://${EOSINSTANCE} ls -l /eos/ctaeos/cta/${TEST_FILE_NAME}
+  eos root://${EOSINSTANCE} info /eos/ctaeos/cta/${TEST_FILE_NAME}
+echo
+echo "Removing disk replica as poweruser1:powerusers (12001:1200)"
+
+XrdSecPROTOCOL=sss eos -r 0 0 root://${EOSINSTANCE} file drop /eos/ctaeos/cta/${TEST_FILE_NAME} 1
+echo
+echo "Information about the testing file without disk replica"
+  eos root://${EOSINSTANCE} ls -l /eos/ctaeos/cta/${TEST_FILE_NAME}
+  eos root://${EOSINSTANCE} info /eos/ctaeos/cta/${TEST_FILE_NAME}
+echo
diff --git a/continuousintegration/orchestration/tests/common/delete_file.sh b/continuousintegration/orchestration/tests/common/delete_file.sh
new file mode 100644
index 0000000000..c34048922e
--- /dev/null
+++ b/continuousintegration/orchestration/tests/common/delete_file.sh
@@ -0,0 +1,58 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+usage() { cat <<EOF 1>&2
+Usage: $0 -i <eosinstance> -f <filename>
+EOF
+exit 1
+}
+
+while getopts "i:f:" o; do
+  case "${o}" in
+    i)
+      EOSINSTANCE=${OPTARG}
+      ;;
+    f)
+      TEST_FILE_NAME=${OPTARG}
+      ;;
+    *)
+      usage
+      ;;
+  esac
+done
+shift $((OPTIND-1))
+
+if [ ! -z "${error}" ]; then
+  echo -e "ERROR:\n${error}"
+  exit 1
+fi
+
+echo "bash eos root://${EOSINSTANCE} rm /eos/ctaeos/cta/${TEST_FILE_NAME}"
+eos root://${EOSINSTANCE} rm /eos/ctaeos/cta/${TEST_FILE_NAME}
+
+SECONDS_PASSED=0
+WAIT_FOR_RETRIEVED_FILE_TIMEOUT=10
+while test true = `xrdfs root://${EOSINSTANCE} query prepare 0 /eos/ctaeos/${TEST_FILE_NAME} | jq . | jq '.responses[0] | .path_exists'`; do
+  echo "Waiting for file to be deleted from tape: Seconds passed = ${SECONDS_PASSED}"
+  sleep 1
+  let SECONDS_PASSED=SECONDS_PASSED+1
+
+  if test ${SECONDS_PASSED} == ${WAIT_FOR_RETRIEVED_FILE_TIMEOUT}; then
+    echo "Timed out after ${WAIT_FOR_RETRIEVED_FILE_TIMEOUT} seconds waiting for file to be deleted"
+    exit 1
+  fi
+done
diff --git a/continuousintegration/orchestration/tests/restore_files.sh b/continuousintegration/orchestration/tests/restore_files.sh
new file mode 100755
index 0000000000..3ab6378cf3
--- /dev/null
+++ b/continuousintegration/orchestration/tests/restore_files.sh
@@ -0,0 +1,180 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+EOSINSTANCE=ctaeos
+LOGFILE_PATH=/var/log/restore_files.log
+TEST_FILE_NAME=`uuidgen`
+WAIT_FOR_RETRIEVED_FILE_TIMEOUT=10
+
+usage() { cat <<EOF 1>&2
+Usage: $0 -n <namespace>
+EOF
+exit 1
+}
+
+while getopts "n:" o; do
+  case "${o}" in
+    n)
+      NAMESPACE=${OPTARG}
+      ;;
+    *)
+      usage
+      ;;
+  esac
+done
+shift $((OPTIND-1))
+
+if [ -z "${NAMESPACE}" ]; then
+  usage
+fi
+
+if [ ! -z "${error}" ]; then
+  echo -e "ERROR:\n${error}"
+  exit 1
+fi
+
+FRONTEND_IP=$(kubectl -n ${NAMESPACE} get pods ctafrontend -o json | jq .status.podIP | tr -d '"')
+
+echo
+echo "ADD FRONTEND GATEWAY TO EOS"
+echo "kubectl -n ${NAMESPACE} exec ctaeos -- bash eos root://${EOSINSTANCE} -r 0 0 vid add gateway ${FRONTEND_IP} grpc"
+kubectl -n ${NAMESPACE} exec ctaeos -- eos -r 0 0 vid add gateway ${FRONTEND_IP} grpc
+eos vid set map -grpc key:<your key aks any uuid string> vuid:2 vgid:2
+
+echo 
+echo "eos vid ls"
+kubectl -n ${NAMESPACE} exec ctaeos -- eos root://${EOSINSTANCE} vid ls
+
+echo
+echo "Launching restore_files_client.sh on client pod"
+echo " Archiving file: xrdcp as user1"
+kubectl -n ${NAMESPACE} cp common/archive_file.sh client:/root/archive_file.sh
+kubectl -n ${NAMESPACE} cp client_helper.sh client:/root/client_helper.sh
+kubectl -n ${NAMESPACE} exec client -- bash /root/archive_file.sh -n ${TEST_FILE_NAME} || exit 1
+
+echo
+METADATA_FILE_PATH=$(mktemp -d).json
+echo "SEND FILE METADATA TO JSON FILE: ${METADATA_FILE_PATH}"
+touch ${METADATA_FILE_PATH}
+kubectl -n ${NAMESPACE} exec client -- eos -j root://${EOSINSTANCE} file info /eos/ctaeos/cta/${TEST_FILE_NAME} | jq . > ${METADATA_FILE_PATH}
+
+# Extract values from the meta data used for restoring and testing
+FXID=$(jq -r '.fxid' ${METADATA_FILE_PATH})
+FILE_SIZE=$(jq -r '.size' ${METADATA_FILE_PATH})
+ARCHIVE_FILE_ID=$(jq -r '.xattr | .["sys.archive.file_id"]' ${METADATA_FILE_PATH})
+CHECKSUM=$(jq -r '.checksumvalue' ${METADATA_FILE_PATH})
+
+echo
+echo "DELETE ARCHIVED FILE"
+kubectl -n ${NAMESPACE} cp common/delete_file.sh client:/root/delete_file.sh
+kubectl -n ${NAMESPACE} exec client -- bash /root/delete_file.sh -i ${EOSINSTANCE} -f ${TEST_FILE_NAME}
+
+echo
+echo "VALIDATE THAT THE FILE IS IN THE RECYCLE BIN"
+echo "kubectl -n ${NAMESPACE} exec ctacli -- cta-admin rtf ls --fxid ${FXID} || exit 1"
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin rtf ls --fxid ${FXID} || exit 1
+
+echo 
+echo "COPY REQUIRED FILES TO FRONTEND POD"
+echo "sudo kubectl cp ${NAMESPACE}/ctacli:/etc/cta/cta-cli.conf /etc/cta/cta-cli.conf"
+echo "sudo kubectl cp /etc/cta/cta-cli.conf ${NAMESPACE}/ctafrontend:/etc/cta/cta-cli.conf"
+sudo kubectl cp ${NAMESPACE}/ctacli:/etc/cta/cta-cli.conf /etc/cta/cta-cli.conf
+sudo kubectl cp /etc/cta/cta-cli.conf ${NAMESPACE}/ctafrontend:/etc/cta/cta-cli.conf
+
+echo
+echo "ENABLE CTAFRONTEND TO EXECUTE CTA ADMIN COMMANDS"
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin add --username ctafrontend --comment "for restore files test"
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin add --username ctaeos --comment "for restore files test"
+
+echo 
+echo "RESTORE FILES"
+kubectl -n ${NAMESPACE} cp client_helper.sh ctafrontend:/root/client_helper.sh
+kubectl cp ~/CTA-build/cmdline/standalone_cli_tools/restore_files/cta-restore-deleted-files ${NAMESPACE}/ctafrontend:/usr/bin/cta-restore-deleted-files
+kubectl cp restore_files_ctafrontend.sh ${NAMESPACE}/ctafrontend:/root/restore_files_ctafrontend.sh
+kubectl -n ${NAMESPACE} exec ctafrontend -- chmod +x /root/restore_files_ctafrontend.sh
+kubectl -n ${NAMESPACE} exec ctafrontend -- bash -c "XrdSecPROTOCOL=sss XrdSecSSSKT=/etc/cta/eos.sss.keytab /root/restore_files_ctafrontend.sh -I ${ARCHIVE_FILE_ID} -f ${TEST_FILE_NAME} -i ${EOSINSTANCE}"
+
+SECONDS_PASSED=0
+WAIT_FOR_RETRIEVED_FILE_TIMEOUT=10
+while kubectl -n ${NAMESPACE} exec client -- test `false = xrdfs root://${EOSINSTANCE} query prepare 0 /eos/ctaeos/cta/${TEST_FILE_NAME} | jq . | jq '.responses[0] | .path_exists'`; do
+  echo "Waiting for file with name:${TEST_FILE_NAME} to be restored on EOS side: Seconds passed = ${SECONDS_PASSED}"
+  sleep 1
+  let SECONDS_PASSED=SECONDS_PASSED+1
+
+  if test ${SECONDS_PASSED} == ${WAIT_FOR_RETRIEVED_FILE_TIMEOUT}; then
+    echo "Timed out after ${WAIT_FOR_RETRIEVED_FILE_TIMEOUT} seconds waiting for file to be restored at EOS side"
+    exit 1
+  fi
+done
+
+echo
+METADATA_FILE_AFTER_RESTORE_PATH=$(mktemp -d).json
+echo "SEND FILE METADATA TO JSON FILE: ${METADATA_FILE_AFTER_RESTORE_PATH}"
+touch ${METADATA_FILE_AFTER_RESTORE_PATH}
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin --json tf ls --id ${ARCHIVE_FILE_ID} --instance ${EOSINSTANCE} | jq '.[0]' |& tee ${METADATA_FILE_AFTER_RESTORE_PATH}
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin --json tf ls --id ${ARCHIVE_FILE_ID} --instance ${EOSINSTANCE} | jq '.[0]' | sudo tee -a ${LOGFILE_PATH}
+
+# Extract values from the meta data from the restored file
+FILE_SIZE_AFTER_RESTORE=$(jq -r '.af | .["size"]' ${METADATA_FILE_AFTER_RESTORE_PATH})
+ARCHIVE_FILE_ID_AFTER_RESTORE=$(jq -r '.af | .["archiveId"]' ${METADATA_FILE_AFTER_RESTORE_PATH})
+CHECKSUM_AFTER_RESTORE=$(jq -r '.af | .checksum[0] | .["value"]' ${METADATA_FILE_AFTER_RESTORE_PATH})
+FXID_AFTER_RESTORE=$(jq -r '.df | .["diskId"]' ${METADATA_FILE_AFTER_RESTORE_PATH})
+
+EOS_METADATA_AFTER_RESTORE_PATH=$(mktemp -d).json
+echo "SEND EOS METADATA TO JSON FILE: ${EOS_METADATA_AFTER_RESTORE_PATH}"
+touch ${EOS_METADATA_AFTER_RESTORE_PATH}
+kubectl -n ${NAMESPACE} exec client -- eos -j root://${EOSINSTANCE} file info /eos/ctaeos/cta/${TEST_FILE_NAME} | jq . |& tee ${EOS_METADATA_AFTER_RESTORE_PATH}
+kubectl -n ${NAMESPACE} exec client -- eos -j root://${EOSINSTANCE} file info /eos/ctaeos/cta/${TEST_FILE_NAME} | jq . | sudo tee -a ${LOGFILE_PATH}
+
+EOS_NS_FXID_AFTER_RESTORE=$(jq -r '.fxid' ${EOS_METADATA_AFTER_RESTORE_PATH})
+EOS_NS_FXID_AFTER_RESTORE_DEC=$(( 16#$EOS_NS_FXID_AFTER_RESTORE ))
+
+if test ${CHECKSUM} != ${CHECKSUM_AFTER_RESTORE}; then
+  echo "ERROR: Checksum for original file and restored file does match"
+  exit 1
+fi
+
+if test ${ARCHIVE_FILE_ID} != ${ARCHIVE_FILE_ID_AFTER_RESTORE}; then
+  echo "ERROR: archive file id for original file and restored file does match"
+  exit 1
+fi
+
+if test ${FILE_SIZE} != ${FILE_SIZE_AFTER_RESTORE}; then
+  echo "ERROR: file size for original file and restored file does match"
+  exit 1
+fi
+
+echo "CTA fxid: ${FXID_AFTER_RESTORE}"
+echo "EOS fxid: ${EOS_NS_FXID_AFTER_RESTORE_DEC}"
+if test ${FXID_AFTER_RESTORE} != ${EOS_NS_FXID_AFTER_RESTORE_DEC}; then
+  echo "ERROR: fxid does not match in EOS and CTA"
+  echo "${FXID_AFTER_RESTORE} does not match ${EOS_NS_FXID_AFTER_RESTORE_DEC}"
+  exit 1
+fi
+
+echo 
+echo "ALL TESTS PASSED FOR cta-restore-deleted-files"
+
+echo
+echo "CLEAN UP TEMPORARY FILES AND REMOVE TEMPORARY CTA ADMIN ACCESS"
+echo "kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin rm --username ctafrontend"
+echo "kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin rm --username ctaeos"
+sudo rm ${METADATA_FILE_AFTER_RESTORE_PATH}
+sudo rm ${METADATA_FILE_PATH}
+sudo rm ${EOS_METADATA_AFTER_RESTORE_PATH}
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin rm --username ctafrontend
+kubectl -n ${NAMESPACE} exec ctacli -- cta-admin admin rm --username ctaeos
diff --git a/continuousintegration/orchestration/tests/restore_files_ctafrontend.sh b/continuousintegration/orchestration/tests/restore_files_ctafrontend.sh
new file mode 100644
index 0000000000..5b581cc818
--- /dev/null
+++ b/continuousintegration/orchestration/tests/restore_files_ctafrontend.sh
@@ -0,0 +1,57 @@
+#!/bin/bash
+
+# @project      The CERN Tape Archive (CTA)
+# @copyright    Copyright © 2022 CERN
+# @license      This program is free software, distributed under the terms of the GNU General Public
+#               Licence version 3 (GPL Version 3), copied verbatim in the file "COPYING". You can
+#               redistribute it and/or modify it under the terms of the GPL Version 3, 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.
+#
+#               In applying this licence, CERN does not waive the privileges and immunities
+#               granted to it by virtue of its status as an Intergovernmental Organization or
+#               submit itself to any jurisdiction.
+
+EOSINSTANCE=ctaeos
+
+usage() { cat <<EOF 1>&2
+Usage: $0 -f <archive_file_id>
+EOF
+exit 1
+}
+
+while getopts "I:f:i:" o; do
+  case "${o}" in
+    I)
+      ARCHIVE_FILE_ID=${OPTARG}
+      ;;
+    f)
+      TEST_FILE_NAME=${OPTARG}
+      ;;
+    i)
+      EOSINSTANCE=${OPTARG}
+      ;;
+    *)
+      usage
+      ;;
+  esac    
+done
+shift $((OPTIND-1))
+
+if [ -z "${ARCHIVE_FILE_ID}" ]; then
+  usage
+fi
+
+if [ ! -z "${error}" ]; then
+  echo -e "ERROR:\n${error}"
+  exit 1
+fi
+
+echo
+echo "RESTORE DELETED FILE"
+echo "./cta-restore-deleted-files --id ${ARCHIVE_FILE_ID} --copynb 1 --debug"
+chmod +x /usr/bin/cta-restore-deleted-files
+./usr/bin/cta-restore-deleted-files --id ${ARCHIVE_FILE_ID} --copynb 1 --debug
-- 
GitLab