diff --git a/cmdline/CMakeLists.txt b/cmdline/CMakeLists.txt
index 3a9fc2e3af96eb0f8da5749fa0ae9052426e3633..9098c080de5c08551c43c311c821febcf6f42739 100644
--- a/cmdline/CMakeLists.txt
+++ b/cmdline/CMakeLists.txt
@@ -32,7 +32,12 @@ include_directories(${CMAKE_BINARY_DIR}/eos_cta ${PROTOBUF3_INCLUDE_DIRS})
 #
 # cta-admin <admin_command> is the SSI version of "cta <admin_command>"
 #
-add_executable(cta-admin CtaAdminCmd.cpp CtaAdminCmdParse.cpp CtaAdminTextFormatter.cpp)
+add_executable(cta-admin
+               CtaAdminCmd.cpp
+               CtaAdminCmdParse.cpp
+               CtaAdminTextFormatter.cpp
+               ../common/dataStructures/DriveStatus.cpp
+               ../common/dataStructures/MountType.cpp)
 target_link_libraries(cta-admin XrdSsiPbEosCta XrdSsi-4 XrdSsiLib XrdUtils)
 set_property (TARGET cta-admin APPEND PROPERTY INSTALL_RPATH ${PROTOBUF3_RPATH})
 
diff --git a/cmdline/CtaAdminCmd.cpp b/cmdline/CtaAdminCmd.cpp
index 592507b405ad3ee9f9a390d5ace5381dde3f4838..a65c5bfa02c10c206ee9aaad4191c936684bdedc 100644
--- a/cmdline/CtaAdminCmd.cpp
+++ b/cmdline/CtaAdminCmd.cpp
@@ -75,15 +75,22 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
          case Data::kAflsItem:      std::cout << Log::DumpProtobuf(&record.afls_item());    break;
          case Data::kAflsSummary:   std::cout << Log::DumpProtobuf(&record.afls_summary()); break;
          case Data::kArlsItem:      std::cout << Log::DumpProtobuf(&record.arls_item());    break;
+         case Data::kDrlsItem:      std::cout << Log::DumpProtobuf(&record.drls_item());    break;
          case Data::kFrlsItem:      std::cout << Log::DumpProtobuf(&record.frls_item());    break;
          case Data::kFrlsSummary:   std::cout << Log::DumpProtobuf(&record.frls_summary()); break;
+         case Data::kGmrlsItem:     std::cout << Log::DumpProtobuf(&record.gmrls_item());   break;
          case Data::kLpaItem:       std::cout << Log::DumpProtobuf(&record.lpa_item());     break;
          case Data::kLpaSummary:    std::cout << Log::DumpProtobuf(&record.lpa_summary());  break;
          case Data::kLprItem:       std::cout << Log::DumpProtobuf(&record.lpr_item());     break;
          case Data::kLprSummary:    std::cout << Log::DumpProtobuf(&record.lpr_summary());  break;
-         case Data::kTplsItem:      std::cout << Log::DumpProtobuf(&record.tpls_item());    break;
-         case Data::kTalsItem:      std::cout << Log::DumpProtobuf(&record.tals_item());    break;
+         case Data::kLllsItem:      std::cout << Log::DumpProtobuf(&record.llls_item());    break;
+         case Data::kMplsItem:      std::cout << Log::DumpProtobuf(&record.mpls_item());    break;
          case Data::kRelsItem:      std::cout << Log::DumpProtobuf(&record.rels_item());    break;
+         case Data::kRmrlsItem:     std::cout << Log::DumpProtobuf(&record.rmrls_item());   break;
+         case Data::kSqItem:        std::cout << Log::DumpProtobuf(&record.sq_item());      break;
+         case Data::kSclsItem:      std::cout << Log::DumpProtobuf(&record.scls_item());    break;
+         case Data::kTalsItem:      std::cout << Log::DumpProtobuf(&record.tals_item());    break;
+         case Data::kTplsItem:      std::cout << Log::DumpProtobuf(&record.tpls_item());    break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
       }
@@ -94,15 +101,22 @@ void IStreamBuffer<cta::xrd::Data>::DataCallback(cta::xrd::Data record) const
          case Data::kAflsItem:      formattedText.print(record.afls_item());    break;
          case Data::kAflsSummary:   formattedText.print(record.afls_summary()); break;
          case Data::kArlsItem:      formattedText.print(record.arls_item());    break;
+         case Data::kDrlsItem:      formattedText.print(record.drls_item());    break;
          case Data::kFrlsItem:      formattedText.print(record.frls_item());    break;
          case Data::kFrlsSummary:   formattedText.print(record.frls_summary()); break;
+         case Data::kGmrlsItem:     formattedText.print(record.gmrls_item());   break;
          case Data::kLpaItem:       formattedText.print(record.lpa_item());     break;
          case Data::kLpaSummary:    formattedText.print(record.lpa_summary());  break;
          case Data::kLprItem:       formattedText.print(record.lpr_item());     break;
          case Data::kLprSummary:    formattedText.print(record.lpr_summary());  break;
-         case Data::kTplsItem:      formattedText.print(record.tpls_item());    break;
-         case Data::kTalsItem:      formattedText.print(record.tals_item());    break;
+         case Data::kLllsItem:      formattedText.print(record.llls_item());    break;
+         case Data::kMplsItem:      formattedText.print(record.mpls_item());    break;
          case Data::kRelsItem:      formattedText.print(record.rels_item());    break;
+         case Data::kRmrlsItem:     formattedText.print(record.rmrls_item());   break;
+         case Data::kSqItem:        formattedText.print(record.sq_item());      break;
+         case Data::kSclsItem:      formattedText.print(record.scls_item());    break;
+         case Data::kTalsItem:      formattedText.print(record.tals_item());    break;
+         case Data::kTplsItem:      formattedText.print(record.tpls_item());    break;
          default:
             throw std::runtime_error("Received invalid stream data from CTA Frontend.");
    }
@@ -236,19 +250,26 @@ void CtaAdminCmd::send() const
          std::cout << response.message_txt();
          // Print streaming response header
          if(!isJson()) switch(response.show_header()) {
-            case HeaderType::ADMIN_LS:                     formattedText.printAdLsHeader(); break;
-            case HeaderType::ARCHIVEFILE_LS:               formattedText.printAfLsHeader(); break;
-            case HeaderType::ARCHIVEFILE_LS_SUMMARY:       formattedText.printAfLsSummaryHeader(); break;
-            case HeaderType::ARCHIVEROUTE_LS:              formattedText.printArLsHeader(); break;
-            case HeaderType::FAILEDREQUEST_LS:             formattedText.printFrLsHeader(); break;
-            case HeaderType::FAILEDREQUEST_LS_SUMMARY:     formattedText.printFrLsSummaryHeader(); break;
-            case HeaderType::LISTPENDINGARCHIVES:          formattedText.printLpaHeader(); break;
-            case HeaderType::LISTPENDINGARCHIVES_SUMMARY:  formattedText.printLpaSummaryHeader(); break;
-            case HeaderType::LISTPENDINGRETRIEVES:         formattedText.printLprHeader(); break;
-            case HeaderType::LISTPENDINGRETRIEVES_SUMMARY: formattedText.printLprSummaryHeader(); break;
-            case HeaderType::TAPEPOOL_LS:                  formattedText.printTapePoolLsHeader(); break;
-            case HeaderType::TAPE_LS:                      formattedText.printTapeLsHeader(); break;
+            case HeaderType::ADMIN_LS:                     formattedText.printAdminLsHeader(); break;
+            case HeaderType::ARCHIVEFILE_LS:               formattedText.printArchiveFileLsHeader(); break;
+            case HeaderType::ARCHIVEFILE_LS_SUMMARY:       formattedText.printArchiveFileLsSummaryHeader(); break;
+            case HeaderType::ARCHIVEROUTE_LS:              formattedText.printArchiveRouteLsHeader(); break;
+            case HeaderType::DRIVE_LS:                     formattedText.printDriveLsHeader(); break;
+            case HeaderType::FAILEDREQUEST_LS:             formattedText.printFailedRequestLsHeader(); break;
+            case HeaderType::FAILEDREQUEST_LS_SUMMARY:     formattedText.printFailedRequestLsSummaryHeader(); break;
+            case HeaderType::GROUPMOUNTRULE_LS:            formattedText.printGroupMountRuleLsHeader(); break;
+            case HeaderType::LISTPENDINGARCHIVES:          formattedText.printListPendingArchivesHeader(); break;
+            case HeaderType::LISTPENDINGARCHIVES_SUMMARY:  formattedText.printListPendingArchivesSummaryHeader(); break;
+            case HeaderType::LISTPENDINGRETRIEVES:         formattedText.printListPendingRetrievesHeader(); break;
+            case HeaderType::LISTPENDINGRETRIEVES_SUMMARY: formattedText.printListPendingRetrievesSummaryHeader(); break;
+            case HeaderType::LOGICALLIBRARY_LS:            formattedText.printLogicalLibraryLsHeader(); break;
+            case HeaderType::MOUNTPOLICY_LS:               formattedText.printMountPolicyLsHeader(); break;
             case HeaderType::REPACK_LS:                    formattedText.printRepackLsHeader(); break;
+            case HeaderType::REQUESTERMOUNTRULE_LS:        formattedText.printRequesterMountRuleLsHeader(); break;
+            case HeaderType::SHOWQUEUES:                   formattedText.printShowQueuesHeader(); break;
+            case HeaderType::STORAGECLASS_LS:              formattedText.printStorageClassLsHeader(); break;
+            case HeaderType::TAPE_LS:                      formattedText.printTapeLsHeader(); break;
+            case HeaderType::TAPEPOOL_LS:                  formattedText.printTapePoolLsHeader(); break;
             case HeaderType::NONE:
             default:                                       break;
          }
diff --git a/cmdline/CtaAdminCmdParse.hpp b/cmdline/CtaAdminCmdParse.hpp
index cbfb82211698f8468d2904239c6afdb6cab9db22..5e4c146d70cbf4f238c9d3b0f808e6b8e9d2c8be 100644
--- a/cmdline/CtaAdminCmdParse.hpp
+++ b/cmdline/CtaAdminCmdParse.hpp
@@ -248,7 +248,6 @@ const std::map<std::string, OptionBoolean::Key> boolOptions = {
    // hasOption options
    { "--checkchecksum",         OptionBoolean::CHECK_CHECKSUM },
    { "--extended",              OptionBoolean::EXTENDED },
-   { "--header",                OptionBoolean::SHOW_HEADER },
    { "--justarchive",           OptionBoolean::JUSTARCHIVE },
    { "--justmove",              OptionBoolean::JUSTMOVE },
    { "--justaddcopies",         OptionBoolean::JUSTADDCOPIES },
@@ -373,7 +372,6 @@ const Option opt_firstfseq            { Option::OPT_UINT, "--firstfseq",
 const Option opt_force                { Option::OPT_BOOL, "--force",                 "-f",   " <\"true\" or \"false\">" };
 const Option opt_force_flag           { Option::OPT_FLAG, "--force",                 "-f",   "" };
 const Option opt_group                { Option::OPT_STR,  "--group",                 "-g",   " <group>" };
-const Option opt_header               { Option::OPT_FLAG, "--header",                "-h",   "" };
 const Option opt_hostname_alias       { Option::OPT_STR,  "--name",                  "-n",   " <host_name>",
                                         "--hostname" };
 const Option opt_input                { Option::OPT_STR,  "--input",                 "-i",   " <\"zero\" or \"urandom\">" };
@@ -430,10 +428,10 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_ADMIN,                AdminCmd::SUBCMD_ADD   }, { opt_username, opt_comment }},
    {{ AdminCmd::CMD_ADMIN,                AdminCmd::SUBCMD_CH    }, { opt_username, opt_comment }},
    {{ AdminCmd::CMD_ADMIN,                AdminCmd::SUBCMD_RM    }, { opt_username }},
-   {{ AdminCmd::CMD_ADMIN,                AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_ADMIN,                AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_ARCHIVEFILE,          AdminCmd::SUBCMD_LS    },
-      { opt_header.optional(), opt_archivefileid.optional(), opt_diskid.optional(), opt_copynb.optional(),
+      { opt_archivefileid.optional(), opt_diskid.optional(), opt_copynb.optional(),
         opt_vid.optional(), opt_tapepool.optional(), opt_owner.optional(), opt_group.optional(),
         opt_storageclass.optional(), opt_path.optional(), opt_instance.optional(), opt_all.optional(),
         opt_summary.optional() }},
@@ -443,7 +441,7 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_ARCHIVEROUTE,         AdminCmd::SUBCMD_CH    },
       { opt_instance, opt_storageclass, opt_copynb, opt_tapepool.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_ARCHIVEROUTE,         AdminCmd::SUBCMD_RM    }, { opt_instance, opt_storageclass, opt_copynb }},
-   {{ AdminCmd::CMD_ARCHIVEROUTE,         AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_ARCHIVEROUTE,         AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_DRIVE,                AdminCmd::SUBCMD_UP    }, { opt_drivename_cmd }},
    {{ AdminCmd::CMD_DRIVE,                AdminCmd::SUBCMD_DOWN  }, { opt_drivename_cmd, opt_force_flag.optional() }},
@@ -451,7 +449,7 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_DRIVE,                AdminCmd::SUBCMD_RM    }, { opt_drivename_cmd, opt_force_flag.optional() }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_FAILEDREQUEST,        AdminCmd::SUBCMD_LS    },
-      { opt_header.optional(), opt_justarchive.optional(), opt_justretrieve.optional(), opt_tapepool.optional(),
+      { opt_justarchive.optional(), opt_justretrieve.optional(), opt_tapepool.optional(),
         opt_vid.optional(), opt_log.optional(), opt_summary.optional() }},
    {{ AdminCmd::CMD_FAILEDREQUEST,        AdminCmd::SUBCMD_SHOW  }, { opt_copynb.optional() }},
    {{ AdminCmd::CMD_FAILEDREQUEST,        AdminCmd::SUBCMD_RETRY }, { opt_copynb.optional() }},
@@ -462,20 +460,20 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_GROUPMOUNTRULE,       AdminCmd::SUBCMD_CH    },
       { opt_instance, opt_username_alias, opt_mountpolicy.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_GROUPMOUNTRULE,       AdminCmd::SUBCMD_RM    }, { opt_instance, opt_username_alias }},
-   {{ AdminCmd::CMD_GROUPMOUNTRULE,       AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_GROUPMOUNTRULE,       AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_LISTPENDINGARCHIVES,  AdminCmd::SUBCMD_NONE  },
-      { opt_header.optional(), opt_tapepool.optional(), opt_extended.optional() }},
+      { opt_tapepool.optional(), opt_extended.optional() }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_LISTPENDINGRETRIEVES, AdminCmd::SUBCMD_NONE  },
-      { opt_header.optional(), opt_vid.optional(), opt_extended.optional() }},
+      { opt_vid.optional(), opt_extended.optional() }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_LOGICALLIBRARY,       AdminCmd::SUBCMD_ADD   },
       { opt_logicallibrary_alias, opt_disabled.optional(), opt_comment }},
    {{ AdminCmd::CMD_LOGICALLIBRARY,       AdminCmd::SUBCMD_CH    },
       { opt_logicallibrary_alias, opt_disabled.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_LOGICALLIBRARY,       AdminCmd::SUBCMD_RM    }, { opt_logicallibrary_alias }},
-   {{ AdminCmd::CMD_LOGICALLIBRARY,       AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_LOGICALLIBRARY,       AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_MOUNTPOLICY,          AdminCmd::SUBCMD_ADD   },
       { opt_mountpolicy_alias, opt_archivepriority, opt_minarchiverequestage, opt_retrievepriority,
@@ -485,12 +483,12 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
         opt_retrievepriority.optional(), opt_minretrieverequestage.optional(), opt_maxdrivesallowed.optional(),
         opt_comment.optional() }},
    {{ AdminCmd::CMD_MOUNTPOLICY,          AdminCmd::SUBCMD_RM    }, { opt_mountpolicy_alias }},
-   {{ AdminCmd::CMD_MOUNTPOLICY,          AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_MOUNTPOLICY,          AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_REPACK,               AdminCmd::SUBCMD_ADD   },
       { opt_vid.optional(), opt_vidfile.optional(), opt_bufferurl, opt_justmove.optional(), opt_justaddcopies.optional() }},
    {{ AdminCmd::CMD_REPACK,               AdminCmd::SUBCMD_RM    }, { opt_vid }},
-   {{ AdminCmd::CMD_REPACK,               AdminCmd::SUBCMD_LS    }, { opt_header.optional(), opt_vid.optional() }},
+   {{ AdminCmd::CMD_REPACK,               AdminCmd::SUBCMD_LS    }, { opt_vid.optional() }},
    {{ AdminCmd::CMD_REPACK,               AdminCmd::SUBCMD_ERR   }, { opt_vid }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_REQUESTERMOUNTRULE,   AdminCmd::SUBCMD_ADD   },
@@ -498,16 +496,16 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_REQUESTERMOUNTRULE,   AdminCmd::SUBCMD_CH    },
       { opt_instance, opt_username_alias, opt_mountpolicy.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_REQUESTERMOUNTRULE,   AdminCmd::SUBCMD_RM    }, { opt_instance, opt_username_alias }},
-   {{ AdminCmd::CMD_REQUESTERMOUNTRULE,   AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_REQUESTERMOUNTRULE,   AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
-   {{ AdminCmd::CMD_SHOWQUEUES,           AdminCmd::SUBCMD_NONE  }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_SHOWQUEUES,           AdminCmd::SUBCMD_NONE  }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_STORAGECLASS,         AdminCmd::SUBCMD_ADD   },
       { opt_instance, opt_storageclass_alias, opt_copynb, opt_comment }},
    {{ AdminCmd::CMD_STORAGECLASS,         AdminCmd::SUBCMD_CH    },
       { opt_instance, opt_storageclass_alias, opt_copynb.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_STORAGECLASS,         AdminCmd::SUBCMD_RM    }, { opt_instance, opt_storageclass_alias }},
-   {{ AdminCmd::CMD_STORAGECLASS,         AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_STORAGECLASS,         AdminCmd::SUBCMD_LS    }, { }},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_ADD   },
       { opt_vid, opt_mediatype, opt_vendor, opt_logicallibrary, opt_tapepool, opt_capacity, opt_disabled, opt_full,
@@ -519,7 +517,7 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_RM    }, { opt_vid }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_RECLAIM }, { opt_vid }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_LS    },
-      { opt_header.optional(), opt_vid.optional(), opt_mediatype.optional(), opt_vendor.optional(),
+      { opt_vid.optional(), opt_mediatype.optional(), opt_vendor.optional(),
         opt_logicallibrary.optional(), opt_tapepool.optional(), opt_vo.optional(), opt_capacity.optional(),
         opt_disabled.optional(), opt_full.optional(), opt_all.optional() }},
    {{ AdminCmd::CMD_TAPE,                 AdminCmd::SUBCMD_LABEL },
@@ -531,7 +529,7 @@ const std::map<cmd_key_t, cmd_val_t> cmdOptions = {
       { opt_tapepool_alias, opt_vo.optional(), opt_partialtapes.optional(), opt_encrypted.optional(),
         opt_supply.optional(), opt_comment.optional() }},
    {{ AdminCmd::CMD_TAPEPOOL,             AdminCmd::SUBCMD_RM    }, { opt_tapepool_alias }},
-   {{ AdminCmd::CMD_TAPEPOOL,             AdminCmd::SUBCMD_LS    }, { opt_header.optional() }},
+   {{ AdminCmd::CMD_TAPEPOOL,             AdminCmd::SUBCMD_LS    }, { }},
 };
 
 
diff --git a/cmdline/CtaAdminTextFormatter.cpp b/cmdline/CtaAdminTextFormatter.cpp
index 03021b3e5a92dbc995a192611aece34113a49326..fcfe922240a4b7cf4faed6d16181871b2664bb1c 100644
--- a/cmdline/CtaAdminTextFormatter.cpp
+++ b/cmdline/CtaAdminTextFormatter.cpp
@@ -20,9 +20,11 @@
 #include <iostream>
 #include <iomanip>
 #include <cmdline/CtaAdminTextFormatter.hpp>
+#include <common/dataStructures/DriveStatusSerDeser.hpp>
+#include <common/dataStructures/MountTypeSerDeser.hpp>
 
-
-namespace cta { namespace admin {
+namespace cta {
+namespace admin {
 
 /**
  ** Generic utility methods
@@ -109,7 +111,7 @@ void TextFormatter::flush() {
  ** Output for specific commands
  **/
 
-void TextFormatter::printAdLsHeader() {
+void TextFormatter::printAdminLsHeader() {
   push_back("HEADER");
   push_back(
     "user",
@@ -123,7 +125,7 @@ void TextFormatter::printAdLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::AdminLsItem &adls_item) {
+void TextFormatter::print(const AdminLsItem &adls_item) {
   push_back(
     adls_item.user(),
     adls_item.creation_log().username(),
@@ -136,7 +138,7 @@ void TextFormatter::print(const cta::admin::AdminLsItem &adls_item) {
   );
 }
 
-void TextFormatter::printAfLsHeader() {
+void TextFormatter::printArchiveFileLsHeader() {
   push_back("HEADER");
   push_back(
     "archive id",
@@ -159,7 +161,7 @@ void TextFormatter::printAfLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ArchiveFileLsItem &afls_item) {
+void TextFormatter::print(const ArchiveFileLsItem &afls_item) {
   push_back(
     afls_item.af().archive_id(),
     afls_item.copy_nb(),
@@ -181,7 +183,23 @@ void TextFormatter::print(const cta::admin::ArchiveFileLsItem &afls_item) {
   );
 }
 
-void TextFormatter::printArLsHeader() {
+void TextFormatter::printArchiveFileLsSummaryHeader() {
+  push_back("HEADER");
+  push_back(
+    "total files",
+    "total size"
+  );
+}
+
+void TextFormatter::print(const ArchiveFileLsSummary &afls_summary)
+{
+  push_back(
+    afls_summary.total_files(),
+    dataSizeToStr(afls_summary.total_size())
+  );
+}
+
+void TextFormatter::printArchiveRouteLsHeader() {
   push_back("HEADER");
   push_back(
     "instance",
@@ -198,7 +216,7 @@ void TextFormatter::printArLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ArchiveRouteLsItem &arls_item) {
+void TextFormatter::print(const ArchiveRouteLsItem &arls_item) {
   push_back(
     arls_item.instance(),
     arls_item.storage_class(),
@@ -214,23 +232,83 @@ void TextFormatter::print(const cta::admin::ArchiveRouteLsItem &arls_item) {
   );
 }
 
-void TextFormatter::printAfLsSummaryHeader() {
+
+void TextFormatter::printDriveLsHeader() {
   push_back("HEADER");
   push_back(
-    "total files",
-    "total size"
+    "library",
+    "drive",
+    "host",
+    "desired",
+    "request",
+    "status",
+    "since",
+    "vid",
+    "tapepool",
+    "files",
+    "data",
+    "MB/s",
+    "session",
+    "priority",
+    "activity",
+    "age"
   );
 }
 
-void TextFormatter::print(const cta::admin::ArchiveFileLsSummary &afls_summary)
+void TextFormatter::print(const DriveLsItem &drls_item)
 {
+  //using namespace cta::common::dataStructures;
+
+  const int DRIVE_TIMEOUT = 600; // Time after which a drive will be marked as STALE
+
+  std::string driveStatusSince;
+  std::string filesTransferredInSession;
+  std::string bytesTransferredInSession;
+  std::string latestBandwidth;
+  std::string sessionId;
+  std::string timeSinceLastUpdate;
+
+
+  if(drls_item.drive_status() != DriveLsItem::UNKNOWN_DRIVE_STATUS) {
+    driveStatusSince = std::to_string(drls_item.drive_status_since());
+  }
+
+  if(drls_item.drive_status() == DriveLsItem::TRANSFERRING) {
+    filesTransferredInSession = std::to_string(drls_item.files_transferred_in_session());
+    bytesTransferredInSession = dataSizeToStr(drls_item.bytes_transferred_in_session());
+    latestBandwidth = std::to_string(drls_item.latest_bandwidth());
+  }
+
+  if(drls_item.drive_status() != DriveLsItem::UP &&
+     drls_item.drive_status() != DriveLsItem::DOWN &&
+     drls_item.drive_status() != DriveLsItem::UNKNOWN_DRIVE_STATUS) {
+    sessionId = std::to_string(drls_item.session_id());
+  }
+
+  timeSinceLastUpdate = std::to_string(drls_item.time_since_last_update()) +
+    (drls_item.time_since_last_update() > DRIVE_TIMEOUT ? " [STALE]" : "");
+
   push_back(
-    afls_summary.total_files(),
-    dataSizeToStr(afls_summary.total_size())
+    drls_item.logical_library(),
+    drls_item.drive_name(),
+    drls_item.host(),
+    (drls_item.desired_drive_state() == DriveLsItem::UP ? "Up" : "Down"),
+    toString(ProtobufToMountType(drls_item.mount_type())),
+    toString(ProtobufToDriveStatus(drls_item.drive_status())),
+    driveStatusSince,
+    drls_item.vid(),
+    drls_item.tapepool(),
+    filesTransferredInSession,
+    bytesTransferredInSession,
+    latestBandwidth,
+    sessionId,
+    drls_item.current_priority(),
+    drls_item.current_activity(),
+    timeSinceLastUpdate
   );
 }
 
-void TextFormatter::printFrLsHeader() {
+void TextFormatter::printFailedRequestLsHeader() {
   push_back("HEADER");
   push_back(
     "request type",
@@ -242,7 +320,7 @@ void TextFormatter::printFrLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::FailedRequestLsItem &frls_item) {
+void TextFormatter::print(const FailedRequestLsItem &frls_item) {
   std::string request_type;
   std::string tapepool_vid;
 
@@ -272,7 +350,7 @@ void TextFormatter::print(const cta::admin::FailedRequestLsItem &frls_item) {
   //       displayed in the text output, only in JSON.
 }
 
-void TextFormatter::printFrLsSummaryHeader() {
+void TextFormatter::printFailedRequestLsSummaryHeader() {
   push_back("HEADER");
   push_back(
     "request type",
@@ -281,10 +359,10 @@ void TextFormatter::printFrLsSummaryHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::FailedRequestLsSummary &frls_summary) {
+void TextFormatter::print(const FailedRequestLsSummary &frls_summary) {
   std::string request_type =
-    frls_summary.request_type() == cta::admin::RequestType::ARCHIVE_REQUEST  ? "archive" :
-    frls_summary.request_type() == cta::admin::RequestType::RETRIEVE_REQUEST ? "retrieve" : "total";
+    frls_summary.request_type() == RequestType::ARCHIVE_REQUEST  ? "archive" :
+    frls_summary.request_type() == RequestType::RETRIEVE_REQUEST ? "retrieve" : "total";
 
   push_back(
     request_type,
@@ -293,7 +371,38 @@ void TextFormatter::print(const cta::admin::FailedRequestLsSummary &frls_summary
   );
 }
 
-void TextFormatter::printLpaHeader() {
+void TextFormatter::printGroupMountRuleLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "instance",
+    "group",
+    "policy",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const GroupMountRuleLsItem &gmrls_item) {
+  push_back(
+    gmrls_item.disk_instance(),
+    gmrls_item.group_mount_rule(),
+    gmrls_item.mount_policy(),
+    gmrls_item.creation_log().username(),
+    gmrls_item.creation_log().host(),
+    timeToStr(gmrls_item.creation_log().time()),
+    gmrls_item.last_modification_log().username(),
+    gmrls_item.last_modification_log().host(),
+    timeToStr(gmrls_item.last_modification_log().time()),
+    gmrls_item.comment()
+  );
+}
+
+void TextFormatter::printListPendingArchivesHeader() {
   push_back("HEADER");
   push_back(
     "tapepool",
@@ -311,7 +420,7 @@ void TextFormatter::printLpaHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ListPendingArchivesItem &lpa_item) {
+void TextFormatter::print(const ListPendingArchivesItem &lpa_item) {
   push_back(
     lpa_item.tapepool(),
     lpa_item.af().archive_id(),
@@ -328,7 +437,7 @@ void TextFormatter::print(const cta::admin::ListPendingArchivesItem &lpa_item) {
   );
 }
 
-void TextFormatter::printLpaSummaryHeader() {
+void TextFormatter::printListPendingArchivesSummaryHeader() {
   push_back("HEADER");
   push_back(
     "tapepool",
@@ -337,7 +446,7 @@ void TextFormatter::printLpaSummaryHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ListPendingArchivesSummary &lpa_summary) {
+void TextFormatter::print(const ListPendingArchivesSummary &lpa_summary) {
   push_back(
     lpa_summary.tapepool(),
     lpa_summary.total_files(),
@@ -345,7 +454,7 @@ void TextFormatter::print(const cta::admin::ListPendingArchivesSummary &lpa_summ
   );
 }
 
-void TextFormatter::printLprHeader() {
+void TextFormatter::printListPendingRetrievesHeader() {
   push_back("HEADER");
   push_back(
     "vid",
@@ -360,7 +469,7 @@ void TextFormatter::printLprHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ListPendingRetrievesItem &lpr_item) {
+void TextFormatter::print(const ListPendingRetrievesItem &lpr_item) {
   push_back(
     lpr_item.tf().vid(),
     lpr_item.af().archive_id(),
@@ -374,7 +483,7 @@ void TextFormatter::print(const cta::admin::ListPendingRetrievesItem &lpr_item)
   );
 }
 
-void TextFormatter::printLprSummaryHeader() {
+void TextFormatter::printListPendingRetrievesSummaryHeader() {
   push_back("HEADER");
   push_back(
     "vid",
@@ -383,7 +492,7 @@ void TextFormatter::printLprSummaryHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::ListPendingRetrievesSummary &lpr_summary) {
+void TextFormatter::print(const ListPendingRetrievesSummary &lpr_summary) {
   push_back(
     lpr_summary.vid(),
     lpr_summary.total_files(),
@@ -391,13 +500,248 @@ void TextFormatter::print(const cta::admin::ListPendingRetrievesSummary &lpr_sum
   );
 }
 
+void TextFormatter::printLogicalLibraryLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "library",
+    "disabled",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const LogicalLibraryLsItem &llls_item) {
+  push_back(
+    llls_item.name(),
+    llls_item.is_disabled(),
+    llls_item.creation_log().username(),
+    llls_item.creation_log().host(),
+    timeToStr(llls_item.creation_log().time()),
+    llls_item.last_modification_log().username(),
+    llls_item.last_modification_log().host(),
+    timeToStr(llls_item.last_modification_log().time()),
+    llls_item.comment()
+  );
+}
+
+void TextFormatter::printMountPolicyLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "mount policy",
+    "a.priority",
+    "a.minAge",
+    "r.priority",
+    "r.minAge",
+    "max drives",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const MountPolicyLsItem &mpls_item) {
+  push_back(
+    mpls_item.name(),
+    mpls_item.archive_priority(),
+    mpls_item.archive_min_request_age(),
+    mpls_item.retrieve_priority(),
+    mpls_item.retrieve_min_request_age(),
+    mpls_item.max_drives_allowed(),
+    mpls_item.creation_log().username(),
+    mpls_item.creation_log().host(),
+    timeToStr(mpls_item.creation_log().time()),
+    mpls_item.last_modification_log().username(),
+    mpls_item.last_modification_log().host(),
+    timeToStr(mpls_item.last_modification_log().time()),
+    mpls_item.comment()
+  );
+}
+
+void TextFormatter::printRepackLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "vid",
+    "repackBufferURL",
+    "userProvidedFiles",
+    "totalFilesToRetrieve",
+    "totalBytesToRetrieve",
+    "totalFilesToArchive",
+    "totalBytesToArchive",
+    "retrievedFiles",
+    "archivedFiles",
+    "failedToRetrieveFiles",
+    "failedToRetrieveBytes",
+    "failedToArchiveFiles",
+    "failedToArchiveBytes",
+    "lastExpandedFSeq",
+    "status"
+  );
+}
+
+void TextFormatter::print(const RepackLsItem &rels_item) {
+  push_back(
+   rels_item.vid(),
+   rels_item.repack_buffer_url(),
+   rels_item.user_provided_files(),
+   rels_item.total_files_to_retrieve(),
+   dataSizeToStr(rels_item.total_bytes_to_retrieve()),
+   rels_item.total_files_to_archive(),
+   dataSizeToStr(rels_item.total_bytes_to_archive()),
+   rels_item.retrieved_files(),
+   rels_item.archived_files(),
+   rels_item.failed_to_retrieve_files(),
+   dataSizeToStr(rels_item.failed_to_retrieve_bytes()),
+   rels_item.failed_to_archive_files(),
+   dataSizeToStr(rels_item.failed_to_retrieve_bytes()),
+   rels_item.last_expanded_fseq(),
+   rels_item.status()
+  );
+}
+
+void TextFormatter::printRequesterMountRuleLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "instance",
+    "username",
+    "policy",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const RequesterMountRuleLsItem &rmrls_item) {
+  push_back(
+    rmrls_item.disk_instance(),
+    rmrls_item.requester_mount_rule(),
+    rmrls_item.mount_policy(),
+    rmrls_item.creation_log().username(),
+    rmrls_item.creation_log().host(),
+    timeToStr(rmrls_item.creation_log().time()),
+    rmrls_item.last_modification_log().username(),
+    rmrls_item.last_modification_log().host(),
+    timeToStr(rmrls_item.last_modification_log().time()),
+    rmrls_item.comment()
+  );
+}
+
+void TextFormatter::printShowQueuesHeader() {
+  push_back("HEADER");
+  push_back(
+    "type",
+    "tapepool",
+    "logical library",
+    "vid",
+    "files queued",
+    "data queued",
+    "oldest age",
+    "priority",
+    "min age",
+    "max drives",
+    "cur. mounts",
+    "cur. files",
+    "cur. data",
+    "MB/s",
+    "next mounts",
+    "tapes capacity",
+    "files on tapes",
+    "data on tapes",
+    "full tapes",
+    "empty tapes",
+    "disabled tapes",
+    "writable tapes"
+  );
+}
+
+void TextFormatter::print(const ShowQueuesItem &sq_item) {
+  std::string priority;
+  std::string minAge;
+  std::string maxDrivesAllowed;
+
+  if(sq_item.mount_type() == ARCHIVE_FOR_USER ||
+     sq_item.mount_type() == RETRIEVE) {
+    priority         = std::to_string(sq_item.priority());
+    minAge           = std::to_string(sq_item.min_age());
+    maxDrivesAllowed = std::to_string(sq_item.max_drives());
+  }
+
+  push_back(
+    toString(ProtobufToMountType(sq_item.mount_type())),
+    sq_item.tapepool(),
+    sq_item.logical_library(),
+    sq_item.vid(),
+    sq_item.queued_files(),
+    dataSizeToStr(sq_item.queued_bytes()),
+    sq_item.oldest_age(),
+    priority,
+    minAge,
+    maxDrivesAllowed,
+    sq_item.cur_mounts(),
+    sq_item.cur_files(),
+    dataSizeToStr(sq_item.cur_bytes()),
+    sq_item.bytes_per_second() / 1000000,
+    sq_item.next_mounts(),
+    dataSizeToStr(sq_item.tapes_capacity()),
+    sq_item.tapes_files(),
+    dataSizeToStr(sq_item.tapes_bytes()),
+    sq_item.full_tapes(),
+    sq_item.empty_tapes(),
+    sq_item.disabled_tapes(),
+    sq_item.writable_tapes()
+  );
+}
+
+void TextFormatter::printStorageClassLsHeader() {
+  push_back("HEADER");
+  push_back(
+    "instance",
+    "storage class",
+    "number of copies",
+    "c.user",
+    "c.host",
+    "c.time",
+    "m.user",
+    "m.host",
+    "m.time",
+    "comment"
+  );
+}
+
+void TextFormatter::print(const StorageClassLsItem &scls_item) {
+  push_back(
+    scls_item.disk_instance(),
+    scls_item.name(),
+    scls_item.nb_copies(),
+    scls_item.creation_log().username(),
+    scls_item.creation_log().host(),
+    timeToStr(scls_item.creation_log().time()),
+    scls_item.last_modification_log().username(),
+    scls_item.last_modification_log().host(),
+    timeToStr(scls_item.last_modification_log().time()),
+    scls_item.comment()
+  );
+}
+
 void TextFormatter::printTapeLsHeader() {
   push_back("HEADER");
   push_back(
     "vid",
     "media type",
     "vendor",
-    "logical library",
+    "library",
     "tapepool",
     "vo",
     "encryption key",
@@ -421,7 +765,7 @@ void TextFormatter::printTapeLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::TapeLsItem &tals_item) {
+void TextFormatter::print(const TapeLsItem &tals_item) {
   push_back(
     tals_item.vid(),
     tals_item.media_type(),
@@ -450,47 +794,6 @@ void TextFormatter::print(const cta::admin::TapeLsItem &tals_item) {
   );
 }
 
-void TextFormatter::printRepackLsHeader() {
-  push_back("HEADER");
-  push_back(
-    "vid",
-    "repackBufferURL",
-    "userProvidedFiles",
-    "totalFilesToRetrieve",
-    "totalBytesToRetrieve",
-    "totalFilesToArchive",
-    "totalBytesToArchive",
-    "retrievedFiles",
-    "archivedFiles",
-    "failedToRetrieveFiles",
-    "failedToRetrieveBytes",
-    "failedToArchiveFiles",
-    "failedToArchiveBytes",
-    "lastExpandedFSeq",
-    "status"
-  );
-}
-
-void TextFormatter::print(const cta::admin::RepackLsItem &rels_item) {
-  push_back(
-   rels_item.vid(),
-   rels_item.repack_buffer_url(),
-   rels_item.user_provided_files(),
-   rels_item.total_files_to_retrieve(),
-   dataSizeToStr(rels_item.total_bytes_to_retrieve()),
-   rels_item.total_files_to_archive(),
-   dataSizeToStr(rels_item.total_bytes_to_archive()),
-   rels_item.retrieved_files(),
-   rels_item.archived_files(),
-   rels_item.failed_to_retrieve_files(),
-   dataSizeToStr(rels_item.failed_to_retrieve_bytes()),
-   rels_item.failed_to_archive_files(),
-   dataSizeToStr(rels_item.failed_to_retrieve_bytes()),
-   rels_item.last_expanded_fseq(),
-   rels_item.status()
-  );
-}
-
 void TextFormatter::printTapePoolLsHeader() {
   push_back("HEADER");
   push_back(
@@ -515,7 +818,7 @@ void TextFormatter::printTapePoolLsHeader() {
   );
 }
 
-void TextFormatter::print(const cta::admin::TapePoolLsItem &tpls_item)
+void TextFormatter::print(const TapePoolLsItem &tpls_item)
 {
   uint64_t avail = tpls_item.capacity_bytes() > tpls_item.data_bytes() ?
     tpls_item.capacity_bytes()-tpls_item.data_bytes() : 0; 
diff --git a/cmdline/CtaAdminTextFormatter.hpp b/cmdline/CtaAdminTextFormatter.hpp
index 29eedde121aa15c5e25482c68f1fed3e5102f92c..533e534f5d414e4a322175f45890fbf57a20d1a0 100644
--- a/cmdline/CtaAdminTextFormatter.hpp
+++ b/cmdline/CtaAdminTextFormatter.hpp
@@ -44,34 +44,48 @@ public:
   }
 
   // Output headers
-  void printAdLsHeader();
-  void printAfLsHeader();
-  void printAfLsSummaryHeader();
-  void printArLsHeader();
-  void printFrLsHeader();
-  void printFrLsSummaryHeader();
-  void printLpaHeader();
-  void printLpaSummaryHeader();
-  void printLprHeader();
-  void printLprSummaryHeader();
-  void printTapePoolLsHeader();
-  void printTapeLsHeader();
+  void printAdminLsHeader();
+  void printArchiveFileLsHeader();
+  void printArchiveFileLsSummaryHeader();
+  void printArchiveRouteLsHeader();
+  void printDriveLsHeader();
+  void printFailedRequestLsHeader();
+  void printFailedRequestLsSummaryHeader();
+  void printGroupMountRuleLsHeader();
+  void printListPendingArchivesHeader();
+  void printListPendingArchivesSummaryHeader();
+  void printListPendingRetrievesHeader();
+  void printListPendingRetrievesSummaryHeader();
+  void printLogicalLibraryLsHeader();
+  void printMountPolicyLsHeader();
   void printRepackLsHeader();
+  void printRequesterMountRuleLsHeader();
+  void printShowQueuesHeader();
+  void printStorageClassLsHeader();
+  void printTapeLsHeader();
+  void printTapePoolLsHeader();
    
   // Output records
   void print(const AdminLsItem &adls_item);
   void print(const ArchiveFileLsItem &afls_item);
   void print(const ArchiveFileLsSummary &afls_summary);
   void print(const ArchiveRouteLsItem &afls_item);
+  void print(const DriveLsItem &drls_item);
   void print(const FailedRequestLsItem &frls_item);
   void print(const FailedRequestLsSummary &frls_summary);
+  void print(const GroupMountRuleLsItem &gmrls_item);
   void print(const ListPendingArchivesItem &lpa_item);
   void print(const ListPendingArchivesSummary &lpa_summary);
   void print(const ListPendingRetrievesItem &lpr_item);
   void print(const ListPendingRetrievesSummary &lpr_summary);
-  void print(const TapePoolLsItem &tpls_item);
-  void print(const TapeLsItem &tals_item);
+  void print(const LogicalLibraryLsItem &llls_item);
+  void print(const MountPolicyLsItem &mpls_item);
   void print(const RepackLsItem &rels_item);
+  void print(const RequesterMountRuleLsItem &rmrls_item);
+  void print(const ShowQueuesItem &sq_item);
+  void print(const StorageClassLsItem &scls_item);
+  void print(const TapeLsItem &tals_item);
+  void print(const TapePoolLsItem &tpls_item);
 
 private:
   //! Add a line to the buffer
diff --git a/common/dataStructures/DriveStatusSerDeser.hpp b/common/dataStructures/DriveStatusSerDeser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..d6d4be35e9751b302f5297e4610130da9e457569
--- /dev/null
+++ b/common/dataStructures/DriveStatusSerDeser.hpp
@@ -0,0 +1,68 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          Convert common::dataStructures::DriveStatus to/from admin::DriveLsItem::DriveStatus
+ * @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 <common/dataStructures/DriveStatus.hpp>
+#include "cta_admin.pb.h"
+
+namespace cta {
+namespace admin {
+
+common::dataStructures::DriveStatus ProtobufToDriveStatus(DriveLsItem::DriveStatus driveStatus) {
+  using namespace common::dataStructures;
+
+  switch(driveStatus) {
+    case DriveLsItem::DOWN:                    return DriveStatus::Down;
+    case DriveLsItem::UP:                      return DriveStatus::Up;
+    case DriveLsItem::PROBING:                 return DriveStatus::Probing;
+    case DriveLsItem::STARTING:                return DriveStatus::Starting;
+    case DriveLsItem::MOUNTING:                return DriveStatus::Mounting;
+    case DriveLsItem::TRANSFERRING:            return DriveStatus::Transferring;
+    case DriveLsItem::UNLOADING:               return DriveStatus::Unloading;
+    case DriveLsItem::UNMOUNTING:              return DriveStatus::Unmounting;
+    case DriveLsItem::DRAINING_TO_DISK:        return DriveStatus::DrainingToDisk;
+    case DriveLsItem::CLEANING_UP:             return DriveStatus::CleaningUp;
+    case DriveLsItem::SHUTDOWN:                return DriveStatus::Shutdown;
+    case DriveLsItem::UNKNOWN_DRIVE_STATUS:
+    default:
+                                               return DriveStatus::Unknown;
+  }
+}
+
+DriveLsItem::DriveStatus DriveStatusToProtobuf(common::dataStructures::DriveStatus driveStatus) {
+  using namespace common::dataStructures;
+
+  switch(driveStatus) {
+    case DriveStatus::Down:                    return DriveLsItem::DOWN;
+    case DriveStatus::Up:                      return DriveLsItem::UP;
+    case DriveStatus::Probing:                 return DriveLsItem::PROBING;
+    case DriveStatus::Starting:                return DriveLsItem::STARTING;
+    case DriveStatus::Mounting:                return DriveLsItem::MOUNTING;
+    case DriveStatus::Transferring:            return DriveLsItem::TRANSFERRING;
+    case DriveStatus::Unloading:               return DriveLsItem::UNLOADING;
+    case DriveStatus::Unmounting:              return DriveLsItem::UNMOUNTING;
+    case DriveStatus::DrainingToDisk:          return DriveLsItem::DRAINING_TO_DISK;
+    case DriveStatus::CleaningUp:              return DriveLsItem::CLEANING_UP;
+    case DriveStatus::Shutdown:                return DriveLsItem::SHUTDOWN;
+    case DriveStatus::Unknown:                 return DriveLsItem::UNKNOWN_DRIVE_STATUS;
+  }
+  return DriveLsItem::UNKNOWN_DRIVE_STATUS;
+}
+
+}} // cta::admin
diff --git a/common/dataStructures/MountType.hpp b/common/dataStructures/MountType.hpp
index 520023b2422e66e31ff77ba635d9e6620ccd7c0d..b10bc44baa4db205d616e9566b4288a2fd032a7a 100644
--- a/common/dataStructures/MountType.hpp
+++ b/common/dataStructures/MountType.hpp
@@ -23,6 +23,7 @@
 namespace cta {
 namespace common {
 namespace dataStructures {
+
 enum class MountType: uint32_t {
   ArchiveForUser = 1,
   ArchiveForRepack = 2,
diff --git a/common/dataStructures/MountTypeSerDeser.hpp b/common/dataStructures/MountTypeSerDeser.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..0e6855cd8797b612784e9d0141616f5d9e2b251b
--- /dev/null
+++ b/common/dataStructures/MountTypeSerDeser.hpp
@@ -0,0 +1,57 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          Convert common::dataStructures::MountType to/from admin::DriveLsItem::MountType
+ * @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 <common/dataStructures/MountType.hpp>
+#include "cta_admin.pb.h"
+
+namespace cta {
+namespace admin {
+
+common::dataStructures::MountType ProtobufToMountType(MountType mountType) {
+  using namespace common::dataStructures;
+
+  switch(mountType) {
+    case NO_MOUNT:              return common::dataStructures::MountType::NoMount;
+    case ARCHIVE_FOR_USER:      return common::dataStructures::MountType::ArchiveForUser;
+    case ARCHIVE_FOR_REPACK:    return common::dataStructures::MountType::ArchiveForRepack;
+    case ARCHIVE_ALL_TYPES:     return common::dataStructures::MountType::ArchiveAllTypes;
+    case RETRIEVE:              return common::dataStructures::MountType::Retrieve;
+    case LABEL:                 return common::dataStructures::MountType::Label;
+    case UNKNOWN_MOUNT_TYPE:
+    default:
+      throw std::runtime_error("In ProtobufToMountType(): unknown mount type " + std::to_string(mountType));
+  }
+}
+
+MountType MountTypeToProtobuf(common::dataStructures::MountType mountType) {
+  using namespace common::dataStructures;
+
+  switch(mountType) {
+    case common::dataStructures::MountType::NoMount:             return NO_MOUNT;
+    case common::dataStructures::MountType::ArchiveForUser:      return ARCHIVE_FOR_USER;
+    case common::dataStructures::MountType::ArchiveForRepack:    return ARCHIVE_FOR_REPACK;
+    case common::dataStructures::MountType::ArchiveAllTypes:     return ARCHIVE_ALL_TYPES;
+    case common::dataStructures::MountType::Retrieve:            return RETRIEVE;
+    case common::dataStructures::MountType::Label:               return LABEL;
+  }
+  return UNKNOWN_MOUNT_TYPE;
+}
+
+}} // cta::admin
diff --git a/xroot_plugins/XrdCtaAdminLs.hpp b/xroot_plugins/XrdCtaAdminLs.hpp
index 1ad3dd61e57aa5b652b25c58b0aa9c39fcca3d32..4ef232b49ad44fc26b43b00eebdabfcfac812c7c 100644
--- a/xroot_plugins/XrdCtaAdminLs.hpp
+++ b/xroot_plugins/XrdCtaAdminLs.hpp
@@ -70,7 +70,6 @@ int AdminLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
   for(bool is_buffer_full = false; !m_adminList.empty() && !is_buffer_full; m_adminList.pop_front()) {
     Data record;
 
-    // TapePool
     auto &ad      = m_adminList.front();
     auto  ad_item = record.mutable_adls_item();
 
diff --git a/xroot_plugins/XrdCtaArchiveRouteLs.hpp b/xroot_plugins/XrdCtaArchiveRouteLs.hpp
index 9eb9acbb2148a1d8b27b132907ccc8e4cea45fa7..3ae5e1e3ffa6096d7521dee72d5fc22c025eb6da 100644
--- a/xroot_plugins/XrdCtaArchiveRouteLs.hpp
+++ b/xroot_plugins/XrdCtaArchiveRouteLs.hpp
@@ -70,7 +70,6 @@ int ArchiveRouteLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
   for(bool is_buffer_full = false; !m_archiveRouteList.empty() && !is_buffer_full; m_archiveRouteList.pop_front()) {
     Data record;
 
-    // TapePool
     auto &ar      = m_archiveRouteList.front();
     auto  ar_item = record.mutable_arls_item();
 
diff --git a/xroot_plugins/XrdCtaDriveLs.hpp b/xroot_plugins/XrdCtaDriveLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..58aeb8000aa711978e99cd0eefdd512c9ea91e92
--- /dev/null
+++ b/xroot_plugins/XrdCtaDriveLs.hpp
@@ -0,0 +1,151 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Drive 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 <common/dataStructures/DriveStatusSerDeser.hpp>
+#include <common/dataStructures/MountTypeSerDeser.hpp>
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class DriveLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  DriveLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler,
+    const cta::common::dataStructures::SecurityIdentity &clientID, log::LogContext &lc);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_driveList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::DriveState> m_driveList;      //!< List of drives from the scheduler
+
+  static constexpr const char* const LOG_SUFFIX  = "DriveLsStream";    //!< Identifier for log messages
+};
+
+
+DriveLsStream::DriveLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue,
+  cta::Scheduler &scheduler, const cta::common::dataStructures::SecurityIdentity &clientID,
+  log::LogContext &lc) :
+  XrdCtaStream(catalogue, scheduler),
+  m_driveList(scheduler.getDriveStates(clientID, lc))
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "DriveLsStream() constructor");
+
+  auto driveRegexOpt = requestMsg.getOptional(OptionString::DRIVE);
+
+  // Dump all drives unless we specified a drive
+  if(driveRegexOpt) {
+    std::string driveRegexStr = '^' + driveRegexOpt.value() + '$';
+    utils::Regex driveRegex(driveRegexStr.c_str());
+
+    // Remove non-matching drives from the list
+    for(auto dr_it = m_driveList.begin(); dr_it != m_driveList.end(); ) {
+      if(driveRegex.has_match(dr_it->driveName)) {
+        ++dr_it;
+      } else {
+        auto erase_it = dr_it;
+        ++dr_it;
+        m_driveList.erase(erase_it);
+      }
+    }
+
+    if(m_driveList.empty()) {
+      throw exception::UserError(std::string("No such drive: ") + driveRegexOpt.value());
+    }
+  }
+
+  // Sort drives in the result set into lexicographic order
+  typedef decltype(*m_driveList.begin()) dStateVal_t;
+  m_driveList.sort([](const dStateVal_t &a, const dStateVal_t &b){ return a.driveName < b.driveName; });
+}
+
+int DriveLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  using namespace cta::admin;
+
+  for(bool is_buffer_full = false; !m_driveList.empty() && !is_buffer_full; m_driveList.pop_front()) {
+    Data record;
+
+    auto &dr      = m_driveList.front();
+    auto  dr_item = record.mutable_drls_item();
+
+    dr_item->set_logical_library(dr.logicalLibrary);
+    dr_item->set_drive_name(dr.driveName);
+    dr_item->set_host(dr.host);
+    dr_item->set_desired_drive_state(dr.desiredDriveState.up ? DriveLsItem::UP : DriveLsItem::DOWN);
+    dr_item->set_mount_type(MountTypeToProtobuf(dr.mountType));
+    dr_item->set_drive_status(DriveStatusToProtobuf(dr.driveStatus));
+    dr_item->set_vid(dr.currentVid);
+    dr_item->set_tapepool(dr.currentTapePool);
+    dr_item->set_files_transferred_in_session(dr.filesTransferredInSession);
+    dr_item->set_bytes_transferred_in_session(dr.bytesTransferredInSession);
+    dr_item->set_latest_bandwidth(dr.latestBandwidth);
+    dr_item->set_session_id(dr.sessionId);
+    dr_item->set_time_since_last_update(time(nullptr)-dr.lastUpdateTime);
+    dr_item->set_current_priority(dr.currentPriority);
+    dr_item->set_current_activity(dr.currentActivityAndWeight ? dr.currentActivityAndWeight.value().activity : "");
+
+    // set the time spent in the current state
+    uint64_t drive_time = time(nullptr);
+
+    switch(dr.driveStatus) {
+      using namespace cta::common::dataStructures;
+
+      case DriveStatus::Probing:           drive_time -= dr.probeStartTime;    break;
+      case DriveStatus::Up:                drive_time -= dr.downOrUpStartTime; break;
+      case DriveStatus::Down:              drive_time -= dr.downOrUpStartTime; break;
+      case DriveStatus::Starting:          drive_time -= dr.startStartTime;    break;
+      case DriveStatus::Mounting:          drive_time -= dr.mountStartTime;    break;
+      case DriveStatus::Transferring:      drive_time -= dr.transferStartTime; break;
+      case DriveStatus::CleaningUp:        drive_time -= dr.cleanupStartTime;  break;
+      case DriveStatus::Unloading:         drive_time -= dr.unloadStartTime;   break;
+      case DriveStatus::Unmounting:        drive_time -= dr.unmountStartTime;  break;
+      case DriveStatus::DrainingToDisk:    drive_time -= dr.drainingStartTime; break;
+      case DriveStatus::Shutdown:          drive_time -= dr.shutdownTime;      break;
+      case DriveStatus::Unknown:                                               break;
+    }
+    dr_item->set_drive_status_since(drive_time);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaGroupMountRuleLs.hpp b/xroot_plugins/XrdCtaGroupMountRuleLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..38f27b5c5076574cce45cf3adcf32f91c4aa271f
--- /dev/null
+++ b/xroot_plugins/XrdCtaGroupMountRuleLs.hpp
@@ -0,0 +1,92 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Group Mount Rule 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>
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class GroupMountRuleLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  GroupMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_groupMountRuleList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::RequesterGroupMountRule> m_groupMountRuleList;    //!< List of group mount rules from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "GroupMountRuleLsStream";               //!< Identifier for log messages
+};
+
+
+GroupMountRuleLsStream::GroupMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_groupMountRuleList(catalogue.getRequesterGroupMountRules())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "GroupMountRuleLsStream() constructor");
+}
+
+int GroupMountRuleLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_groupMountRuleList.empty() && !is_buffer_full; m_groupMountRuleList.pop_front()) {
+    Data record;
+
+    auto &gmr      = m_groupMountRuleList.front();
+    auto  gmr_item = record.mutable_gmrls_item();
+
+    gmr_item->set_disk_instance(gmr.diskInstance);
+    gmr_item->set_group_mount_rule(gmr.name);
+    gmr_item->set_mount_policy(gmr.mountPolicy);
+    gmr_item->mutable_creation_log()->set_username(gmr.creationLog.username);
+    gmr_item->mutable_creation_log()->set_host(gmr.creationLog.host);
+    gmr_item->mutable_creation_log()->set_time(gmr.creationLog.time);
+    gmr_item->mutable_last_modification_log()->set_username(gmr.lastModificationLog.username);
+    gmr_item->mutable_last_modification_log()->set_host(gmr.lastModificationLog.host);
+    gmr_item->mutable_last_modification_log()->set_time(gmr.lastModificationLog.time);
+    gmr_item->set_comment(gmr.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaLogicalLibraryLs.hpp b/xroot_plugins/XrdCtaLogicalLibraryLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..7df9a93e69c7e329d6c4cc335fe2053d2ed066ef
--- /dev/null
+++ b/xroot_plugins/XrdCtaLogicalLibraryLs.hpp
@@ -0,0 +1,91 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Logical Library 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>
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class LogicalLibraryLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  LogicalLibraryLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_logicalLibraryList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::LogicalLibrary> m_logicalLibraryList;    //!< List of logical libraries from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "LogicalLibraryLsStream";      //!< Identifier for log messages
+};
+
+
+LogicalLibraryLsStream::LogicalLibraryLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_logicalLibraryList(catalogue.getLogicalLibraries())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "LogicalLibraryLsStream() constructor");
+}
+
+int LogicalLibraryLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_logicalLibraryList.empty() && !is_buffer_full; m_logicalLibraryList.pop_front()) {
+    Data record;
+
+    auto &ll      = m_logicalLibraryList.front();
+    auto  ll_item = record.mutable_llls_item();
+
+    ll_item->set_name(ll.name);
+    ll_item->set_is_disabled(ll.isDisabled);
+    ll_item->mutable_creation_log()->set_username(ll.creationLog.username);
+    ll_item->mutable_creation_log()->set_host(ll.creationLog.host);
+    ll_item->mutable_creation_log()->set_time(ll.creationLog.time);
+    ll_item->mutable_last_modification_log()->set_username(ll.lastModificationLog.username);
+    ll_item->mutable_last_modification_log()->set_host(ll.lastModificationLog.host);
+    ll_item->mutable_last_modification_log()->set_time(ll.lastModificationLog.time);
+    ll_item->set_comment(ll.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaMountPolicyLs.hpp b/xroot_plugins/XrdCtaMountPolicyLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..084bad6086ac3926fdf0cd67e6059a3f82c579f9
--- /dev/null
+++ b/xroot_plugins/XrdCtaMountPolicyLs.hpp
@@ -0,0 +1,95 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Mount Policy 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>
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class MountPolicyLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  MountPolicyLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_mountPolicyList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::MountPolicy> m_mountPolicyList;     //!< List of mount policies from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "MountPolicyLsStream";    //!< Identifier for log messages
+};
+
+
+MountPolicyLsStream::MountPolicyLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_mountPolicyList(catalogue.getMountPolicies())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "MountPolicyLsStream() constructor");
+}
+
+int MountPolicyLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_mountPolicyList.empty() && !is_buffer_full; m_mountPolicyList.pop_front()) {
+    Data record;
+
+    auto &mp      = m_mountPolicyList.front();
+    auto  mp_item = record.mutable_mpls_item();
+
+    mp_item->set_name(mp.name);
+    mp_item->set_archive_priority(mp.archivePriority);
+    mp_item->set_archive_min_request_age(mp.archiveMinRequestAge);
+    mp_item->set_retrieve_priority(mp.retrievePriority);
+    mp_item->set_retrieve_min_request_age(mp.retrieveMinRequestAge);
+    mp_item->set_max_drives_allowed(mp.maxDrivesAllowed);
+    mp_item->mutable_creation_log()->set_username(mp.creationLog.username);
+    mp_item->mutable_creation_log()->set_host(mp.creationLog.host);
+    mp_item->mutable_creation_log()->set_time(mp.creationLog.time);
+    mp_item->mutable_last_modification_log()->set_username(mp.lastModificationLog.username);
+    mp_item->mutable_last_modification_log()->set_host(mp.lastModificationLog.host);
+    mp_item->mutable_last_modification_log()->set_time(mp.lastModificationLog.time);
+    mp_item->set_comment(mp.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp b/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..af455befd1f653a60be5f81926e1a1457996a31e
--- /dev/null
+++ b/xroot_plugins/XrdCtaRequesterMountRuleLs.hpp
@@ -0,0 +1,92 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Requester Mount Rule 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>
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class RequesterMountRuleLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  RequesterMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_requesterMountRuleList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::RequesterMountRule> m_requesterMountRuleList;    //!< List of requester mount rules from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "RequesterMountRuleLsStream";          //!< Identifier for log messages
+};
+
+
+RequesterMountRuleLsStream::RequesterMountRuleLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_requesterMountRuleList(catalogue.getRequesterMountRules())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "RequesterMountRuleLsStream() constructor");
+}
+
+int RequesterMountRuleLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_requesterMountRuleList.empty() && !is_buffer_full; m_requesterMountRuleList.pop_front()) {
+    Data record;
+
+    auto &rmr      = m_requesterMountRuleList.front();
+    auto  rmr_item = record.mutable_rmrls_item();
+
+    rmr_item->set_disk_instance(rmr.diskInstance);
+    rmr_item->set_requester_mount_rule(rmr.name);
+    rmr_item->set_mount_policy(rmr.mountPolicy);
+    rmr_item->mutable_creation_log()->set_username(rmr.creationLog.username);
+    rmr_item->mutable_creation_log()->set_host(rmr.creationLog.host);
+    rmr_item->mutable_creation_log()->set_time(rmr.creationLog.time);
+    rmr_item->mutable_last_modification_log()->set_username(rmr.lastModificationLog.username);
+    rmr_item->mutable_last_modification_log()->set_host(rmr.lastModificationLog.host);
+    rmr_item->mutable_last_modification_log()->set_time(rmr.lastModificationLog.time);
+    rmr_item->set_comment(rmr.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaShowQueues.hpp b/xroot_plugins/XrdCtaShowQueues.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..a6addb3a8f1e5fec70993ca27495046a8c710b41
--- /dev/null
+++ b/xroot_plugins/XrdCtaShowQueues.hpp
@@ -0,0 +1,119 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Show Queues 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 <common/dataStructures/MountTypeSerDeser.hpp>
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class ShowQueuesStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  ShowQueuesStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue,
+    cta::Scheduler &scheduler, log::LogContext &lc);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_queuesAndMountsList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::QueueAndMountSummary> m_queuesAndMountsList;    //!< List of queues and mounts from the scheduler
+
+  static constexpr const char* const LOG_SUFFIX  = "ShowQueuesStream";                   //!< Identifier for log messages
+};
+
+ShowQueuesStream::ShowQueuesStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue,
+  cta::Scheduler &scheduler, log::LogContext &lc) :
+  XrdCtaStream(catalogue, scheduler),
+  m_queuesAndMountsList(scheduler.getQueuesAndMountSummaries(lc))
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "ShowQueuesStream() constructor");
+}
+
+int ShowQueuesStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  using namespace cta::admin;
+
+  for(bool is_buffer_full = false; !m_queuesAndMountsList.empty() && !is_buffer_full; m_queuesAndMountsList.pop_front()) {
+    Data record;
+
+    auto &sq      = m_queuesAndMountsList.front();
+    auto  sq_item = record.mutable_sq_item();
+
+    switch(sq.mountType) {
+      case common::dataStructures::MountType::ArchiveForUser:
+        sq_item->set_priority(sq.mountPolicy.archivePriority);
+        sq_item->set_min_age(sq.mountPolicy.archiveMinRequestAge);
+        sq_item->set_max_drives(sq.mountPolicy.maxDrivesAllowed);
+        break;
+      case common::dataStructures::MountType::Retrieve:
+        sq_item->set_priority(sq.mountPolicy.retrievePriority);
+        sq_item->set_min_age(sq.mountPolicy.retrieveMinRequestAge);
+        sq_item->set_max_drives(sq.mountPolicy.maxDrivesAllowed);
+        break;
+      default:
+        break;
+    }
+
+    sq_item->set_mount_type(MountTypeToProtobuf(sq.mountType));
+    sq_item->set_tapepool(sq.tapePool);
+    sq_item->set_logical_library(sq.logicalLibrary);
+    sq_item->set_vid(sq.vid);
+    sq_item->set_queued_files(sq.filesQueued);
+    sq_item->set_queued_bytes(sq.bytesQueued);
+    sq_item->set_oldest_age(sq.oldestJobAge);
+    sq_item->set_cur_mounts(sq.currentMounts);
+    sq_item->set_cur_files(sq.currentFiles);
+    sq_item->set_cur_bytes(sq.currentBytes);
+    sq_item->set_bytes_per_second(sq.latestBandwidth);
+    sq_item->set_next_mounts(sq.nextMounts);
+    sq_item->set_tapes_capacity(sq.tapesCapacity);
+    sq_item->set_tapes_files(sq.filesOnTapes);
+    sq_item->set_tapes_bytes(sq.dataOnTapes);
+    sq_item->set_full_tapes(sq.fullTapes);
+    sq_item->set_empty_tapes(sq.emptyTapes);
+    sq_item->set_disabled_tapes(sq.disabledTapes);
+    sq_item->set_writable_tapes(sq.writableTapes);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaStorageClassLs.hpp b/xroot_plugins/XrdCtaStorageClassLs.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..9490f801cfbfa5353d82dd0c61633d5659ea93db
--- /dev/null
+++ b/xroot_plugins/XrdCtaStorageClassLs.hpp
@@ -0,0 +1,92 @@
+/*!
+ * @project        The CERN Tape Archive (CTA)
+ * @brief          CTA Storage Class 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>
+
+
+namespace cta { namespace xrd {
+
+/*!
+ * Stream object which implements "tapepool ls" command
+ */
+class StorageClassLsStream: public XrdCtaStream{
+public:
+  /*!
+   * Constructor
+   *
+   * @param[in]    requestMsg    RequestMessage containing command-line arguments
+   * @param[in]    catalogue     CTA Catalogue
+   * @param[in]    scheduler     CTA Scheduler
+   */
+  StorageClassLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler);
+
+private:
+  /*!
+   * Can we close the stream?
+   */
+  virtual bool isDone() const {
+    return m_storageClassList.empty();
+  }
+
+  /*!
+   * Fill the buffer
+   */
+  virtual int fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf);
+
+  std::list<cta::common::dataStructures::StorageClass> m_storageClassList;    //!< List of storage classes from the catalogue
+
+  static constexpr const char* const LOG_SUFFIX  = "StorageClassLsStream";    //!< Identifier for log messages
+};
+
+
+StorageClassLsStream::StorageClassLsStream(const RequestMessage &requestMsg, cta::catalogue::Catalogue &catalogue, cta::Scheduler &scheduler) :
+  XrdCtaStream(catalogue, scheduler),
+  m_storageClassList(catalogue.getStorageClasses())
+{
+  using namespace cta::admin;
+
+  XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "StorageClassLsStream() constructor");
+}
+
+int StorageClassLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
+  for(bool is_buffer_full = false; !m_storageClassList.empty() && !is_buffer_full; m_storageClassList.pop_front()) {
+    Data record;
+
+    auto &sc      = m_storageClassList.front();
+    auto  sc_item = record.mutable_scls_item();
+
+    sc_item->set_disk_instance(sc.diskInstance);
+    sc_item->set_name(sc.name);
+    sc_item->set_nb_copies(sc.nbCopies);
+    sc_item->mutable_creation_log()->set_username(sc.creationLog.username);
+    sc_item->mutable_creation_log()->set_host(sc.creationLog.host);
+    sc_item->mutable_creation_log()->set_time(sc.creationLog.time);
+    sc_item->mutable_last_modification_log()->set_username(sc.lastModificationLog.username);
+    sc_item->mutable_last_modification_log()->set_host(sc.lastModificationLog.host);
+    sc_item->mutable_last_modification_log()->set_time(sc.lastModificationLog.time);
+    sc_item->set_comment(sc.comment);
+
+    is_buffer_full = streambuf->Push(record);
+  }
+  return streambuf->Size();
+}
+
+}} // namespace cta::xrd
diff --git a/xroot_plugins/XrdCtaTapePoolLs.hpp b/xroot_plugins/XrdCtaTapePoolLs.hpp
index 4cd527fd2fb2afc0ce61cf26c7122c2cbbb81d1b..f429df10ea137630f82b7acadb82f1adbb27085b 100644
--- a/xroot_plugins/XrdCtaTapePoolLs.hpp
+++ b/xroot_plugins/XrdCtaTapePoolLs.hpp
@@ -70,7 +70,6 @@ int TapePoolLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {
   for(bool is_buffer_full = false; !m_tapePoolList.empty() && !is_buffer_full; m_tapePoolList.pop_front()) {
     Data record;
 
-    // TapePool
     auto &tp      = m_tapePoolList.front();
     auto  tp_item = record.mutable_tpls_item();
 
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.cpp b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
index 626fbdd4bc9e70fb7f1b0af333c47bb303903c4e..049366a0dd3806d3df09acc192195b24c0d236d7 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.cpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.cpp
@@ -1,7 +1,7 @@
 /*!
  * @project        The CERN Tape Archive (CTA)
  * @brief          XRootD EOS Notification handler
- * @copyright      Copyright 2017 CERN
+ * @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
@@ -16,25 +16,26 @@
  *                 along with this program.  If not, see <http://www.gnu.org/licenses/>.
  */
 
-#include <iomanip> // for setw
-
-#include <common/utils/utils.hpp>
-#include <common/utils/Regex.hpp>
-
 #include <XrdSsiPbException.hpp>
 using XrdSsiPb::PbException;
 
 #include <cmdline/CtaAdminCmdParse.hpp>
+#include "XrdSsiCtaRequestMessage.hpp"
 #include "XrdCtaAdminLs.hpp"
 #include "XrdCtaArchiveFileLs.hpp"
 #include "XrdCtaArchiveRouteLs.hpp"
+#include "XrdCtaDriveLs.hpp"
 #include "XrdCtaFailedRequestLs.hpp"
+#include "XrdCtaGroupMountRuleLs.hpp"
 #include "XrdCtaListPendingQueue.hpp"
-#include "XrdCtaTapePoolLs.hpp"
-#include "XrdSsiCtaRequestMessage.hpp"
-#include "XrdCtaTapeLs.hpp"
+#include "XrdCtaLogicalLibraryLs.hpp"
+#include "XrdCtaMountPolicyLs.hpp"
 #include "XrdCtaRepackLs.hpp"
-
+#include "XrdCtaRequesterMountRuleLs.hpp"
+#include "XrdCtaShowQueues.hpp"
+#include "XrdCtaTapeLs.hpp"
+#include "XrdCtaStorageClassLs.hpp"
+#include "XrdCtaTapePoolLs.hpp"
 
 namespace cta {
 namespace xrd {
@@ -44,7 +45,6 @@ const char* const TEXT_RED    = "\x1b[31;1m";
 const char* const TEXT_NORMAL = "\x1b[0m\n";
 
 
-
 /*
  * Convert AdminCmd <Cmd, SubCmd> pair to an integer so that it can be used in a switch statement
  */
@@ -53,33 +53,6 @@ constexpr unsigned int cmd_pair(cta::admin::AdminCmd::Cmd cmd, cta::admin::Admin
 }
 
 
-
-/*
- * Helper function to convert time to string
- */
-static std::string timeToString(const time_t &time)
-{
-   std::string timeString(ctime(&time));
-   timeString.resize(timeString.size()-1); //remove newline
-   return timeString;
-}
-
-
-
-/*
- * Helper function to convert bytes to Mb and return the result as a string
- */
-static std::string bytesToMbString(uint64_t bytes)
-{
-   long double mBytes = static_cast<long double>(bytes)/(1000.0*1000.0);
-
-   std::ostringstream oss;
-   oss << std::setprecision(2) << std::fixed << mBytes;
-   return oss.str();
-}
-
-
-
 void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Response &response, XrdSsiStream* &stream)
 {
    // Branch on the Request payload type
@@ -138,7 +111,7 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processDrive_Down(response);
                break;
             case cmd_pair(AdminCmd::CMD_DRIVE, AdminCmd::SUBCMD_LS):
-               processDrive_Ls(response);
+               processDrive_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_DRIVE, AdminCmd::SUBCMD_RM):
                processDrive_Rm(response);
@@ -156,7 +129,7 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processGroupMountRule_Rm(response);
                break;
             case cmd_pair(AdminCmd::CMD_GROUPMOUNTRULE, AdminCmd::SUBCMD_LS):
-               processGroupMountRule_Ls(response);
+               processGroupMountRule_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_LISTPENDINGARCHIVES, AdminCmd::SUBCMD_NONE):
                processListPendingArchives(response, stream);
@@ -174,7 +147,7 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processLogicalLibrary_Rm(response);
                break;
             case cmd_pair(AdminCmd::CMD_LOGICALLIBRARY, AdminCmd::SUBCMD_LS):
-               processLogicalLibrary_Ls(response);
+               processLogicalLibrary_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_MOUNTPOLICY, AdminCmd::SUBCMD_ADD):
                processMountPolicy_Add(response);
@@ -186,7 +159,7 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processMountPolicy_Rm(response);
                break;
             case cmd_pair(AdminCmd::CMD_MOUNTPOLICY, AdminCmd::SUBCMD_LS):
-               processMountPolicy_Ls(response);
+               processMountPolicy_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_REPACK, AdminCmd::SUBCMD_ADD):
                processRepack_Add(response);
@@ -210,10 +183,10 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processRequesterMountRule_Rm(response);
                break;
             case cmd_pair(AdminCmd::CMD_REQUESTERMOUNTRULE, AdminCmd::SUBCMD_LS):
-               processRequesterMountRule_Ls(response);
+               processRequesterMountRule_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_SHOWQUEUES, AdminCmd::SUBCMD_NONE):
-               processShowQueues(response);
+               processShowQueues(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_STORAGECLASS, AdminCmd::SUBCMD_ADD):
                processStorageClass_Add(response);
@@ -225,7 +198,7 @@ void RequestMessage::process(const cta::xrd::Request &request, cta::xrd::Respons
                processStorageClass_Rm(response);
                break;
             case cmd_pair(AdminCmd::CMD_STORAGECLASS, AdminCmd::SUBCMD_LS):
-               processStorageClass_Ls(response);
+               processStorageClass_Ls(response, stream);
                break;
             case cmd_pair(AdminCmd::CMD_TAPE, AdminCmd::SUBCMD_ADD):
                processTape_Add(response);
@@ -801,112 +774,15 @@ void RequestMessage::processDrive_Down(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processDrive_Ls(cta::xrd::Response &response)
+void RequestMessage::processDrive_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   const int DRIVE_TIMEOUT = 600;
-
-   std::stringstream cmdlineOutput;
-
-   // Dump all drives unless we specified a drive
-   bool hasRegex   = false;
-   bool driveFound = false;
-
-   auto driveRegexOpt = getOptional(OptionString::DRIVE, &hasRegex);
-   std::string driveRegexStr = hasRegex ? '^' + driveRegexOpt.value() + '$' : ".";
-   utils::Regex driveRegex(driveRegexStr.c_str());
-
-   auto driveStates = m_scheduler.getDriveStates(m_cliIdentity, m_lc);
-   if (!driveStates.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> headers = {
-         "library","drive","host","desired","request","status","since","vid","tapepool","files",
-         "MBytes","MB/s","session","priority","activity","age"
-      };
-      responseTable.push_back(headers);
-
-      typedef decltype(*driveStates.begin()) dStateVal_t;
-      driveStates.sort([](const dStateVal_t &a, const dStateVal_t &b){ return a.driveName < b.driveName; });
-
-      for (auto ds: driveStates)
-      {
-         if(!driveRegex.has_match(ds.driveName)) continue;
-         driveFound = true;
-
-         auto timeSinceLastUpdate_s = time(nullptr) - ds.lastUpdateTime;
-
-         std::vector<std::string> currentRow;
-         currentRow.push_back(ds.logicalLibrary);
-         currentRow.push_back(ds.driveName);
-         currentRow.push_back(ds.host);
-         currentRow.push_back(ds.desiredDriveState.up ? "Up" : "Down");
-         currentRow.push_back(cta::common::dataStructures::toString(ds.mountType));
-         currentRow.push_back(cta::common::dataStructures::toString(ds.driveStatus));
-
-         // print the time spent in the current state
-         unsigned long long drive_time = time(nullptr);
-
-         switch(ds.driveStatus) {
-            using namespace cta::common::dataStructures;
-
-            case DriveStatus::Probing:           drive_time -= ds.probeStartTime;    break;
-            case DriveStatus::Up:
-            case DriveStatus::Down:              drive_time -= ds.downOrUpStartTime; break;
-            case DriveStatus::Starting:          drive_time -= ds.startStartTime;    break;
-            case DriveStatus::Mounting:          drive_time -= ds.mountStartTime;    break;
-            case DriveStatus::Transferring:      drive_time -= ds.transferStartTime; break;
-            case DriveStatus::CleaningUp:        drive_time -= ds.cleanupStartTime;  break;
-            case DriveStatus::Unloading:         drive_time -= ds.unloadStartTime;   break;
-            case DriveStatus::Unmounting:        drive_time -= ds.unmountStartTime;  break;
-            case DriveStatus::DrainingToDisk:    drive_time -= ds.drainingStartTime; break;
-            case DriveStatus::Shutdown:          drive_time -= ds.shutdownTime;      break;
-            case DriveStatus::Unknown:           break;
-         }
-         currentRow.push_back(ds.driveStatus == cta::common::dataStructures::DriveStatus::Unknown ? "-" :
-            std::to_string(drive_time));
-
-         currentRow.push_back(ds.currentVid == "" ? "-" : ds.currentVid);
-         currentRow.push_back(ds.currentTapePool == "" ? "-" : ds.currentTapePool);
-
-         switch (ds.driveStatus) {
-            case cta::common::dataStructures::DriveStatus::Transferring:
-               currentRow.push_back(std::to_string(static_cast<unsigned long long>(ds.filesTransferredInSession)));
-               currentRow.push_back(bytesToMbString(ds.bytesTransferredInSession));
-               currentRow.push_back(bytesToMbString(ds.latestBandwidth));
-               break;
-            default:
-               currentRow.push_back("-");
-               currentRow.push_back("-");
-               currentRow.push_back("-");
-         }
-         switch(ds.driveStatus) {
-            case cta::common::dataStructures::DriveStatus::Up:
-            case cta::common::dataStructures::DriveStatus::Down:
-            case cta::common::dataStructures::DriveStatus::Unknown:
-               currentRow.push_back("-");
-               break;
-            default:
-               currentRow.push_back(std::to_string(static_cast<unsigned long long>(ds.sessionId)));
-         }
-         currentRow.push_back(std::to_string(ds.currentPriority));
-         currentRow.push_back(ds.currentActivityAndWeight?ds.currentActivityAndWeight.value().activity: "-");
-         currentRow.push_back(std::to_string(timeSinceLastUpdate_s) +
-            (timeSinceLastUpdate_s > DRIVE_TIMEOUT ? " [STALE]" : ""));
-         responseTable.push_back(currentRow);
-      }
-
-      if (hasRegex && !driveFound) {
-         throw cta::exception::UserError(std::string("No such drive: ") + driveRegexOpt.value());
-      }
+  using namespace cta::admin;
 
-      m_option_bool[OptionBoolean::SHOW_HEADER] = true;
-      cmdlineOutput<< formatResponse(responseTable);
-   }
+  // Create a XrdSsi stream object to return the results
+  stream = new DriveLsStream(*this, m_catalogue, m_scheduler, m_cliIdentity, m_lc);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::DRIVE_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1020,36 +896,14 @@ void RequestMessage::processGroupMountRule_Rm(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processGroupMountRule_Ls(cta::xrd::Response &response)
+void RequestMessage::processGroupMountRule_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
+  using namespace cta::admin;
 
-   std::list<cta::common::dataStructures::RequesterGroupMountRule> list = m_catalogue.getRequesterGroupMountRules();
+  stream = new GroupMountRuleLsStream(*this, m_catalogue, m_scheduler);
 
-   if(!list.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "instance","group","policy","c.user","c.host","c.time","m.user","m.host","m.time","comment"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);    
-      for(auto it = list.cbegin(); it != list.cend(); it++)
-      {
-         std::vector<std::string> currentRow;
-         currentRow.push_back(it->diskInstance);
-         currentRow.push_back(it->name);
-         currentRow.push_back(it->mountPolicy);
-         addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
-         currentRow.push_back(it->comment);
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
-
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::GROUPMOUNTRULE_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1134,34 +988,15 @@ void RequestMessage::processLogicalLibrary_Rm(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processLogicalLibrary_Ls(cta::xrd::Response &response)
+void RequestMessage::processLogicalLibrary_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
-
-   std::list<cta::common::dataStructures::LogicalLibrary> list = m_catalogue.getLogicalLibraries();
+  using namespace cta::admin;
 
-   if(!list.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "name","disabled","c.user","c.host","c.time","m.user","m.host","m.time","comment"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);    
-      for(auto it = list.cbegin(); it != list.cend(); it++) {
-         std::vector<std::string> currentRow;
-         currentRow.push_back(it->name);
-         currentRow.push_back(std::to_string(it->isDisabled));
-         addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
-         currentRow.push_back(it->comment);
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
+  // Create a XrdSsi stream object to return the results
+  stream = new LogicalLibraryLsStream(*this, m_catalogue, m_scheduler);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::LOGICALLIBRARY_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1235,39 +1070,15 @@ void RequestMessage::processMountPolicy_Rm(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processMountPolicy_Ls(cta::xrd::Response &response)
+void RequestMessage::processMountPolicy_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
-
-   std::list<cta::common::dataStructures::MountPolicy> list= m_catalogue.getMountPolicies();
+  using namespace cta::admin;
 
-   if(!list.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "mount policy","a.priority","a.minAge","r.priority","r.minAge","max drives","c.user","c.host","c.time","m.user","m.host","m.time","comment"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);    
-      for(auto it = list.cbegin(); it != list.cend(); it++)
-      {
-         std::vector<std::string> currentRow;
-         currentRow.push_back(it->name);
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->archivePriority)));
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->archiveMinRequestAge)));
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->retrievePriority)));
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->retrieveMinRequestAge)));
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->maxDrivesAllowed)));
-         addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
-         currentRow.push_back(it->comment);
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
+  // Create a XrdSsi stream object to return the results
+  stream = new MountPolicyLsStream(*this, m_catalogue, m_scheduler);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::MOUNTPOLICY_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1407,100 +1218,27 @@ void RequestMessage::processRequesterMountRule_Rm(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processRequesterMountRule_Ls(cta::xrd::Response &response)
+void RequestMessage::processRequesterMountRule_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
-
-   std::list<cta::common::dataStructures::RequesterMountRule> list = m_catalogue.getRequesterMountRules();
+  using namespace cta::admin;
 
-   if(!list.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "instance","username","policy","c.user","c.host","c.time","m.user","m.host","m.time","comment"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);    
-      for(auto it = list.cbegin(); it != list.cend(); it++) {
-         std::vector<std::string> currentRow;
-         currentRow.push_back(it->diskInstance);
-         currentRow.push_back(it->name);
-         currentRow.push_back(it->mountPolicy);
-         addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
-         currentRow.push_back(it->comment);
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
+  stream = new RequesterMountRuleLsStream(*this, m_catalogue, m_scheduler);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::REQUESTERMOUNTRULE_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
 
-void RequestMessage::processShowQueues(cta::xrd::Response &response)
+void RequestMessage::processShowQueues(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
-
-   auto queuesAndMounts = m_scheduler.getQueuesAndMountSummaries(m_lc);
+  using namespace cta::admin;
 
-   if(!queuesAndMounts.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "type","tapepool","logical library","vid","files queued","MBytes queued","oldest age","priority",
-         "min age","max drives","cur. mounts","cur. files","cur. MBytes","MB/s","next mounts","tapes capacity",
-         "files on tapes","MBytes on tapes","full tapes","empty tapes","disabled tapes","writables tapes"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);
-      for (auto & q: queuesAndMounts)
-      {
-         const uint64_t MBytes = 1000 * 1000;
-
-         std::vector<std::string> currentRow;
-         currentRow.push_back(common::dataStructures::toString(q.mountType));
-         currentRow.push_back(q.tapePool);
-         currentRow.push_back(q.logicalLibrary);
-         currentRow.push_back(q.vid);
-         currentRow.push_back(std::to_string(q.filesQueued));
-         currentRow.push_back(bytesToMbString(q.bytesQueued));
-         currentRow.push_back(std::to_string(q.oldestJobAge));
-         if (common::dataStructures::MountType::ArchiveForUser == q.mountType) {
-            currentRow.push_back(std::to_string(q.mountPolicy.archivePriority));
-            currentRow.push_back(std::to_string(q.mountPolicy.archiveMinRequestAge));
-            currentRow.push_back(std::to_string(q.mountPolicy.maxDrivesAllowed));
-         } else if (common::dataStructures::MountType::Retrieve == q.mountType) {
-            currentRow.push_back(std::to_string(q.mountPolicy.retrievePriority));
-            currentRow.push_back(std::to_string(q.mountPolicy.retrieveMinRequestAge));
-            currentRow.push_back(std::to_string(q.mountPolicy.maxDrivesAllowed));
-         } else {
-            currentRow.push_back("-");
-            currentRow.push_back("-");
-            currentRow.push_back("-");
-         }
-         currentRow.push_back(std::to_string(q.currentMounts));
-         currentRow.push_back(std::to_string(q.currentFiles));
-         currentRow.push_back(bytesToMbString(q.currentBytes));
-         currentRow.push_back(bytesToMbString(q.latestBandwidth));
-         currentRow.push_back(std::to_string(q.nextMounts));
-         currentRow.push_back(std::to_string(q.tapesCapacity/MBytes));
-         currentRow.push_back(std::to_string(q.filesOnTapes));
-         currentRow.push_back(bytesToMbString(q.dataOnTapes));
-         currentRow.push_back(std::to_string(q.fullTapes));
-         currentRow.push_back(std::to_string(q.emptyTapes));
-         currentRow.push_back(std::to_string(q.disabledTapes));
-         currentRow.push_back(std::to_string(q.writableTapes));
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
+  // Create a XrdSsi stream object to return the results
+  stream = new ShowQueuesStream(*this, m_catalogue, m_scheduler, m_lc);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::SHOWQUEUES);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1558,35 +1296,15 @@ void RequestMessage::processStorageClass_Rm(cta::xrd::Response &response)
 
 
 
-void RequestMessage::processStorageClass_Ls(cta::xrd::Response &response)
+void RequestMessage::processStorageClass_Ls(cta::xrd::Response &response, XrdSsiStream* &stream)
 {
-   using namespace cta::admin;
-
-   std::stringstream cmdlineOutput;
-
-   std::list<cta::common::dataStructures::StorageClass> list = m_catalogue.getStorageClasses();
+  using namespace cta::admin;
 
-   if(!list.empty())
-   {
-      std::vector<std::vector<std::string>> responseTable;
-      std::vector<std::string> header = {
-         "instance","storage class","number of copies","c.user","c.host","c.time","m.user","m.host","m.time","comment"
-      };
-      if(has_flag(OptionBoolean::SHOW_HEADER)) responseTable.push_back(header);    
-      for(auto it = list.cbegin(); it != list.cend(); it++) {
-         std::vector<std::string> currentRow;
-         currentRow.push_back(it->diskInstance);
-         currentRow.push_back(it->name);
-         currentRow.push_back(std::to_string(static_cast<unsigned long long>(it->nbCopies)));
-         addLogInfoToResponseRow(currentRow, it->creationLog, it->lastModificationLog);
-         currentRow.push_back(it->comment);
-         responseTable.push_back(currentRow);
-      }
-      cmdlineOutput << formatResponse(responseTable);
-   }
+  // Create a XrdSsi stream object to return the results
+  stream = new StorageClassLsStream(*this, m_catalogue, m_scheduler);
 
-   response.set_message_txt(cmdlineOutput.str());
-   response.set_type(cta::xrd::Response::RSP_SUCCESS);
+  response.set_show_header(HeaderType::STORAGECLASS_LS);
+  response.set_type(cta::xrd::Response::RSP_SUCCESS);
 }
 
 
@@ -1822,52 +1540,6 @@ std::string RequestMessage::setDriveState(const std::string &regex, DriveState d
 }
 
 
-
-std::string RequestMessage::formatResponse(const std::vector<std::vector<std::string>> &responseTable) const
-{
-   bool has_header = has_flag(cta::admin::OptionBoolean::SHOW_HEADER);
-
-   if(responseTable.empty() || responseTable.at(0).empty()) return "";
-
-   std::vector<int> columnSizes;
-
-   for(uint j = 0; j < responseTable.at(0).size(); j++) { //for each column j
-      uint columnSize = 0;
-      for(uint i = 0; i<responseTable.size(); i++) { //for each row i
-         if(responseTable.at(i).at(j).size() > columnSize) {
-            columnSize = responseTable.at(i).at(j).size();
-         }
-      }
-      columnSizes.push_back(columnSize);//loops here
-   }
-   std::stringstream responseSS;
-   for(auto row = responseTable.cbegin(); row != responseTable.cend(); row++) {
-      if(has_header && row == responseTable.cbegin()) responseSS << "\x1b[31;1m";
-      for(uint i = 0; i<row->size(); i++) {
-         responseSS << std::string(i ? "  " : "") << std::setw(columnSizes.at(i)) << row->at(i);
-      }
-      if(has_header && row == responseTable.cbegin()) responseSS << "\x1b[0m" << std::endl;
-      else responseSS << std::endl;
-   }
-   return responseSS.str();
-}
-
-
-
-void RequestMessage::addLogInfoToResponseRow(std::vector<std::string> &responseRow,
-                                             const cta::common::dataStructures::EntryLog &creationLog,
-                                             const cta::common::dataStructures::EntryLog &lastModificationLog) const
-{
-   responseRow.push_back(creationLog.username);
-   responseRow.push_back(creationLog.host);
-   responseRow.push_back(timeToString(creationLog.time));
-   responseRow.push_back(lastModificationLog.username);
-   responseRow.push_back(lastModificationLog.host);
-   responseRow.push_back(timeToString(lastModificationLog.time));
-}
-
-
-
 void RequestMessage::importOptions(const cta::admin::AdminCmd &admincmd)
 {
    // Validate the Protocol Buffer
diff --git a/xroot_plugins/XrdSsiCtaRequestMessage.hpp b/xroot_plugins/XrdSsiCtaRequestMessage.hpp
index 8515f9aba4f0c7a4495989f9a2eb0df24d3249ef..0d08dbcfc1250fc9ebaf870864fbac3108dce032 100644
--- a/xroot_plugins/XrdSsiCtaRequestMessage.hpp
+++ b/xroot_plugins/XrdSsiCtaRequestMessage.hpp
@@ -1,7 +1,7 @@
 /*!
  * @project        The CERN Tape Archive (CTA)
  * @brief          CTA Frontend Message handler
- * @copyright      Copyright 2017 CERN
+ * @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
@@ -24,7 +24,6 @@
 #include "cta_frontend.pb.h"
 
 
-
 namespace cta { namespace xrd {
 
 /*!
@@ -33,264 +32,244 @@ namespace cta { namespace xrd {
 class RequestMessage
 {
 public:
-   RequestMessage(const XrdSsiEntity &client, const XrdSsiCtaServiceProvider *service) :
-      m_scheddb  (service->getSchedDb()),
-      m_catalogue(service->getCatalogue()),
-      m_scheduler(service->getScheduler()),
-      m_lc       (service->getLogContext()) {
-         m_cliIdentity.username = client.name;
-         m_cliIdentity.host     = client.host;
+  RequestMessage(const XrdSsiEntity &client, const XrdSsiCtaServiceProvider *service) :
+    m_scheddb  (service->getSchedDb()),
+    m_catalogue(service->getCatalogue()),
+    m_scheduler(service->getScheduler()),
+    m_lc       (service->getLogContext()) {
+      m_cliIdentity.username = client.name;
+      m_cliIdentity.host     = client.host;
 
-         // Map the client protocol string to an enum value
-         auto proto_it = m_protomap.find(client.prot);
-         m_protocol = proto_it != m_protomap.end() ? proto_it->second : Protocol::OTHER;
-      }
+      // Map the client protocol string to an enum value
+      auto proto_it = m_protomap.find(client.prot);
+      m_protocol = proto_it != m_protomap.end() ? proto_it->second : Protocol::OTHER;
+    }
 
-   /*!
-    * Process a Notification request or an Admin command request
-    *
-    * @param[in]     request
-    * @param[out]    response        Response protocol buffer
-    * @param[out]    stream          Reference to Response stream pointer
-    */
-   void process(const cta::xrd::Request &request, cta::xrd::Response &response, XrdSsiStream* &stream);
+  /*!
+   * Process a Notification request or an Admin command request
+   *
+   * @param[in]     request
+   * @param[out]    response        Response protocol buffer
+   * @param[out]    stream          Reference to Response stream pointer
+   */
+  void process(const cta::xrd::Request &request, cta::xrd::Response &response, XrdSsiStream* &stream);
 
-   /*!
-    * Get a required option
-    */
-   const std::string &getRequired(cta::admin::OptionString::Key key) const {
-      return m_option_str.at(key);
-   }
-   const std::vector<std::string> &getRequired(cta::admin::OptionStrList::Key key) const {
-      return m_option_str_list.at(key);
-   }
-   const uint64_t &getRequired(cta::admin::OptionUInt64::Key key) const {
-      return m_option_uint64.at(key);
-   }
-   const bool &getRequired(cta::admin::OptionBoolean::Key key) const {
-      return m_option_bool.at(key);
-   }
+  /*!
+   * Get a required option
+   */
+  const std::string &getRequired(cta::admin::OptionString::Key key) const {
+    return m_option_str.at(key);
+  }
+  const std::vector<std::string> &getRequired(cta::admin::OptionStrList::Key key) const {
+    return m_option_str_list.at(key);
+  }
+  const uint64_t &getRequired(cta::admin::OptionUInt64::Key key) const {
+    return m_option_uint64.at(key);
+  }
+  const bool &getRequired(cta::admin::OptionBoolean::Key key) const {
+    return m_option_bool.at(key);
+  }
 
-   /*!
-    * Get an optional option
-    *
-    * The has_option parameter is set if the option exists and left unmodified if the option does not
-    * exist. This is provided as a convenience to monitor whether at least one option was set from a
-    * list of optional options.
-    *
-    * @param[in]     key           option key to look up in options
-    * @param[out]    has_option    Set to true if the option exists, unmodified if it does not
-    *
-    * @returns       value of the option if it exists, an object of type nullopt_t if it does not
-    */
-   template<typename K, typename V>
-   optional<V> getOptional(K key, const std::map<K,V> &options, bool *has_option) const
-   {
-      auto it = options.find(key);
+  /*!
+   * Get an optional option
+   *
+   * The has_option parameter is set if the option exists and left unmodified if the option does not
+   * exist. This is provided as a convenience to monitor whether at least one option was set from a
+   * list of optional options.
+   *
+   * @param[in]     key           option key to look up in options
+   * @param[out]    has_option    Set to true if the option exists, unmodified if it does not
+   *
+   * @returns       value of the option if it exists, an object of type nullopt_t if it does not
+   */
+  template<typename K, typename V>
+  optional<V> getOptional(K key, const std::map<K,V> &options, bool *has_option) const
+  {
+    auto it = options.find(key);
 
-      if(it != options.end()) {
-         if(has_option != nullptr) *has_option = true;
-         return it->second;
-      } else {
-         return cta::nullopt;
-      }
-   }
+    if(it != options.end()) {
+      if(has_option != nullptr) *has_option = true;
+      return it->second;
+    } else {
+      return cta::nullopt;
+    }
+  }
 
-   /*!
-    * Overloaded versions of getOptional
-    *
-    * These map the key type to the template specialization of <key,value> pairs
-    */
-   optional<std::string> getOptional(cta::admin::OptionString::Key key, bool *has_option = nullptr) const {
-      return getOptional(key, m_option_str, has_option);
-   }
-   optional<std::vector<std::string>> getOptional(cta::admin::OptionStrList::Key key, bool *has_option = nullptr) const {
-      return getOptional(key, m_option_str_list, has_option);
-   }
-   optional<uint64_t> getOptional(cta::admin::OptionUInt64::Key key, bool *has_option = nullptr) const {
-      return getOptional(key, m_option_uint64, has_option);
-   }
-   optional<bool> getOptional(cta::admin::OptionBoolean::Key key, bool *has_option = nullptr) const {
-      return getOptional(key, m_option_bool, has_option);
-   }
+  /*!
+   * Overloaded versions of getOptional
+   *
+   * These map the key type to the template specialization of <key,value> pairs
+   */
+  optional<std::string> getOptional(cta::admin::OptionString::Key key, bool *has_option = nullptr) const {
+    return getOptional(key, m_option_str, has_option);
+  }
+  optional<std::vector<std::string>> getOptional(cta::admin::OptionStrList::Key key, bool *has_option = nullptr) const {
+    return getOptional(key, m_option_str_list, has_option);
+  }
+  optional<uint64_t> getOptional(cta::admin::OptionUInt64::Key key, bool *has_option = nullptr) const {
+    return getOptional(key, m_option_uint64, has_option);
+  }
+  optional<bool> getOptional(cta::admin::OptionBoolean::Key key, bool *has_option = nullptr) const {
+    return getOptional(key, m_option_bool, has_option);
+  }
 
-   /*!
-    * Check if an optional flag has been set
-    *
-    * This is a simpler version of getOptional for checking flags which are either present
-    * or not. In the case of flags, they should always have the value true if the flag is
-    * present, but we do a redundant check anyway.
-    *
-    * @param[in] option    Optional command line option
-    *
-    * @retval    true      The flag is present in the options map, and its value is true
-    * @retval    false     The flag is either not present or is present and set to false
-    */
-   bool has_flag(cta::admin::OptionBoolean::Key option) const {
-      auto opt_it = m_option_bool.find(option);
-      return opt_it != m_option_bool.end() && opt_it->second;
-   }
+  /*!
+   * Check if an optional flag has been set
+   *
+   * This is a simpler version of getOptional for checking flags which are either present
+   * or not. In the case of flags, they should always have the value true if the flag is
+   * present, but we do a redundant check anyway.
+   *
+   * @param[in] option    Optional command line option
+   *
+   * @retval    true      The flag is present in the options map, and its value is true
+   * @retval    false     The flag is either not present or is present and set to false
+   */
+  bool has_flag(cta::admin::OptionBoolean::Key option) const {
+    auto opt_it = m_option_bool.find(option);
+    return opt_it != m_option_bool.end() && opt_it->second;
+  }
 
 private:
-   /*!
-    * Process Notification events
-    *
-    * @param[in]     notification    Notification request message from EOS WFE
-    * @param[out]    response        Response message to return to EOS
-    */
-   void processOPENW        (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Ignore OPENW event
-   void processCREATE       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< New archive file ID event
-   void processCLOSEW       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Archive file event
-   void processPREPARE      (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Retrieve file event
-   void processABORT_PREPARE(const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Abort retrieve file event
-   void processDELETE       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Delete file event
-
-   /*!
-    * Process AdminCmd events
-    *
-    * @param[out]    response        CTA Admin command response message
-    */
-   void processAdmin_Add             (cta::xrd::Response &response);
-   void processAdmin_Ch              (cta::xrd::Response &response);
-   void processAdmin_Rm              (cta::xrd::Response &response);
-   void processArchiveRoute_Add      (cta::xrd::Response &response);
-   void processArchiveRoute_Ch       (cta::xrd::Response &response);
-   void processArchiveRoute_Rm       (cta::xrd::Response &response);
-   void processDrive_Up              (cta::xrd::Response &response);
-   void processDrive_Down            (cta::xrd::Response &response);
-   void processDrive_Ls              (cta::xrd::Response &response);
-   void processDrive_Rm              (cta::xrd::Response &response);
-   void processGroupMountRule_Add    (cta::xrd::Response &response);
-   void processGroupMountRule_Ch     (cta::xrd::Response &response);
-   void processGroupMountRule_Rm     (cta::xrd::Response &response);
-   void processGroupMountRule_Ls     (cta::xrd::Response &response);
-   void processLogicalLibrary_Add    (cta::xrd::Response &response);
-   void processLogicalLibrary_Ch     (cta::xrd::Response &response);
-   void processLogicalLibrary_Rm     (cta::xrd::Response &response);
-   void processLogicalLibrary_Ls     (cta::xrd::Response &response);
-   void processMountPolicy_Add       (cta::xrd::Response &response);
-   void processMountPolicy_Ch        (cta::xrd::Response &response);
-   void processMountPolicy_Rm        (cta::xrd::Response &response);
-   void processMountPolicy_Ls        (cta::xrd::Response &response);
-   void processRepack_Add            (cta::xrd::Response &response);
-   void processRepack_Rm             (cta::xrd::Response &response);
-   void processRepack_Err            (cta::xrd::Response &response);
-   void processRequesterMountRule_Add(cta::xrd::Response &response);
-   void processRequesterMountRule_Ch (cta::xrd::Response &response);
-   void processRequesterMountRule_Rm (cta::xrd::Response &response);
-   void processRequesterMountRule_Ls (cta::xrd::Response &response);
-   void processShowQueues            (cta::xrd::Response &response);
-   void processStorageClass_Add      (cta::xrd::Response &response);
-   void processStorageClass_Ch       (cta::xrd::Response &response);
-   void processStorageClass_Rm       (cta::xrd::Response &response);
-   void processStorageClass_Ls       (cta::xrd::Response &response);
-   void processTape_Add              (cta::xrd::Response &response);
-   void processTape_Ch               (cta::xrd::Response &response);
-   void processTape_Rm               (cta::xrd::Response &response);
-   void processTape_Reclaim          (cta::xrd::Response &response);
-   void processTape_Label            (cta::xrd::Response &response);
-   void processTapePool_Add          (cta::xrd::Response &response);
-   void processTapePool_Ch           (cta::xrd::Response &response);
-   void processTapePool_Rm           (cta::xrd::Response &response);
-
-   /*!
-    * Process AdminCmd events which can return a stream response
-    *
-    * @param[out]    response    Response protocol buffer message. This is used for response
-    *                            headers or for summary responses.
-    * @param[out]    stream      Reference to Response stream message pointer
-    */
-   typedef void admincmdstream_t(cta::xrd::Response &response, XrdSsiStream* &stream);
+  /*!
+   * Process Notification events
+   *
+   * @param[in]     notification    Notification request message from EOS WFE
+   * @param[out]    response        Response message to return to EOS
+   */
+  void processOPENW        (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Ignore OPENW event
+  void processCREATE       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< New archive file ID event
+  void processCLOSEW       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Archive file event
+  void processPREPARE      (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Retrieve file event
+  void processABORT_PREPARE(const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Abort retrieve file event
+  void processDELETE       (const cta::eos::Notification &notification, cta::xrd::Response &response);    //!< Delete file event
 
-   admincmdstream_t processAdmin_Ls;
-   admincmdstream_t processArchiveFile_Ls;
-   admincmdstream_t processArchiveRoute_Ls;
-   admincmdstream_t processFailedRequest_Ls;
-   admincmdstream_t processListPendingArchives;
-   admincmdstream_t processListPendingRetrieves;
-   admincmdstream_t processTapePool_Ls;
-   admincmdstream_t processTape_Ls;
-   admincmdstream_t processRepack_Ls;
+  /*!
+   * Process AdminCmd events
+   *
+   * @param[out]    response        CTA Admin command response message
+   */
+  void processAdmin_Add             (cta::xrd::Response &response);
+  void processAdmin_Ch              (cta::xrd::Response &response);
+  void processAdmin_Rm              (cta::xrd::Response &response);
+  void processArchiveRoute_Add      (cta::xrd::Response &response);
+  void processArchiveRoute_Ch       (cta::xrd::Response &response);
+  void processArchiveRoute_Rm       (cta::xrd::Response &response);
+  void processDrive_Up              (cta::xrd::Response &response);
+  void processDrive_Down            (cta::xrd::Response &response);
+  void processDrive_Rm              (cta::xrd::Response &response);
+  void processGroupMountRule_Add    (cta::xrd::Response &response);
+  void processGroupMountRule_Ch     (cta::xrd::Response &response);
+  void processGroupMountRule_Rm     (cta::xrd::Response &response);
+  void processLogicalLibrary_Add    (cta::xrd::Response &response);
+  void processLogicalLibrary_Ch     (cta::xrd::Response &response);
+  void processLogicalLibrary_Rm     (cta::xrd::Response &response);
+  void processMountPolicy_Add       (cta::xrd::Response &response);
+  void processMountPolicy_Ch        (cta::xrd::Response &response);
+  void processMountPolicy_Rm        (cta::xrd::Response &response);
+  void processRepack_Add            (cta::xrd::Response &response);
+  void processRepack_Rm             (cta::xrd::Response &response);
+  void processRepack_Err            (cta::xrd::Response &response);
+  void processRequesterMountRule_Add(cta::xrd::Response &response);
+  void processRequesterMountRule_Ch (cta::xrd::Response &response);
+  void processRequesterMountRule_Rm (cta::xrd::Response &response);
+  void processStorageClass_Add      (cta::xrd::Response &response);
+  void processStorageClass_Ch       (cta::xrd::Response &response);
+  void processStorageClass_Rm       (cta::xrd::Response &response);
+  void processTape_Add              (cta::xrd::Response &response);
+  void processTape_Ch               (cta::xrd::Response &response);
+  void processTape_Rm               (cta::xrd::Response &response);
+  void processTape_Reclaim          (cta::xrd::Response &response);
+  void processTape_Label            (cta::xrd::Response &response);
+  void processTapePool_Add          (cta::xrd::Response &response);
+  void processTapePool_Ch           (cta::xrd::Response &response);
+  void processTapePool_Rm           (cta::xrd::Response &response);
 
-   /*!
-    * Log an admin command
-    *
-    * @param[in]    admincmd    CTA Admin command request message
-    * @param[in]    t           CTA Catalogue timer
-    */
-   void logAdminCmd(const std::string &function, const cta::admin::AdminCmd &admincmd, cta::utils::Timer &t);
+  /*!
+   * Process AdminCmd events which can return a stream response
+   *
+   * @param[out]    response    Response protocol buffer message. This is used for response
+   *                            headers or for summary responses.
+   * @param[out]    stream      Reference to Response stream message pointer
+   */
+  typedef void admincmdstream_t(cta::xrd::Response &response, XrdSsiStream* &stream);
 
-   /*!
-    * Drive state enum
-    */
-   enum DriveState { Up, Down };
+  admincmdstream_t processAdmin_Ls;
+  admincmdstream_t processArchiveFile_Ls;
+  admincmdstream_t processArchiveRoute_Ls;
+  admincmdstream_t processDrive_Ls;
+  admincmdstream_t processFailedRequest_Ls;
+  admincmdstream_t processGroupMountRule_Ls;
+  admincmdstream_t processListPendingArchives;
+  admincmdstream_t processListPendingRetrieves;
+  admincmdstream_t processLogicalLibrary_Ls;
+  admincmdstream_t processMountPolicy_Ls;
+  admincmdstream_t processRequesterMountRule_Ls;
+  admincmdstream_t processShowQueues;
+  admincmdstream_t processStorageClass_Ls;
+  admincmdstream_t processTapePool_Ls;
+  admincmdstream_t processTape_Ls;
+  admincmdstream_t processRepack_Ls;
 
-   /*!
-    * Changes state for the drives by a given regex.
-    *
-    * @param[in]     regex          The regex to match drive name(s) to change
-    * @param[in]     drive_state    The desired state for the drives (Up or Down)
-    *
-    * @returns       The result of the operation, to return to the client
-    */
-   std::string setDriveState(const std::string &regex, DriveState drive_state);
+  /*!
+   * Log an admin command
+   *
+   * @param[in]    admincmd    CTA Admin command request message
+   * @param[in]    t           CTA Catalogue timer
+   */
+  void logAdminCmd(const std::string &function, const cta::admin::AdminCmd &admincmd, cta::utils::Timer &t);
 
-   /*!
-    * Returns the response string for admin commands in a tabular format
-    * 
-    * @param[in]     responseTable   The response 2D matrix
-    *
-    * @returns       the response string properly formatted in a table
-    */
-   std::string formatResponse(const std::vector<std::vector<std::string>> &responseTable) const;
+  /*!
+   * Drive state enum
+   */
+  enum DriveState { Up, Down };
 
-   /*!
-    * Adds the creation log and the last modification log to the current response row
-    * 
-    * @param[in,out] responseRow            The current response row to modify
-    * @param[in]     creationLog            the creation log
-    * @param[in]     lastModificationLog    the last modification log
-    */
-   void addLogInfoToResponseRow(std::vector<std::string> &responseRow,
-                                const cta::common::dataStructures::EntryLog &creationLog,
-                                const cta::common::dataStructures::EntryLog &lastModificationLog) const;
+  /*!
+   * Changes state for the drives by a given regex.
+   *
+   * @param[in]     regex          The regex to match drive name(s) to change
+   * @param[in]     drive_state    The desired state for the drives (Up or Down)
+   *
+   * @returns       The result of the operation, to return to the client
+   */
+  std::string setDriveState(const std::string &regex, DriveState drive_state);
 
-   /*!
-    * Import Google Protobuf option fields into maps
-    *
-    * @param[in]     admincmd        CTA Admin command request message
-    */
-   void importOptions(const cta::admin::AdminCmd &admincmd);
+  /*!
+   * Import Google Protobuf option fields into maps
+   *
+   * @param[in]     admincmd        CTA Admin command request message
+   */
+  void importOptions(const cta::admin::AdminCmd &admincmd);
 
-   /*!
-    * Throw an exception for empty protocol buffer strings
-    */
-   void checkIsNotEmptyString(const std::string &value, const std::string &error_txt) {
-      if(value.empty()) throw XrdSsiPb::PbException("Protocol buffer field " + error_txt + " is an empty string.");
-   }
+  /*!
+   * Throw an exception for empty protocol buffer strings
+   */
+  void checkIsNotEmptyString(const std::string &value, const std::string &error_txt) {
+    if(value.empty()) throw XrdSsiPb::PbException("Protocol buffer field " + error_txt + " is an empty string.");
+  }
 
-   // Security protocol used to connect
+  // Security protocol used to connect
 
-   enum class Protocol { SSS, KRB5, OTHER };
+  enum class Protocol { SSS, KRB5, OTHER };
 
-   const std::map<std::string, Protocol> m_protomap = {
-      { "sss",  Protocol::SSS  },
-      { "krb5", Protocol::KRB5 },
-   };
+  const std::map<std::string, Protocol> m_protomap = {
+    { "sss",  Protocol::SSS  },
+    { "krb5", Protocol::KRB5 },
+  };
 
-   // Member variables
+  // Member variables
 
-   Protocol                                              m_protocol;           //!< The protocol the client used to connect
-   cta::common::dataStructures::SecurityIdentity         m_cliIdentity;        //!< Client identity: username/host
-   cta::OStoreDBWithAgent                               &m_scheddb;            //!< Reference to CTA ObjectStore
-   cta::catalogue::Catalogue                            &m_catalogue;          //!< Reference to CTA Catalogue
-   cta::Scheduler                                       &m_scheduler;          //!< Reference to CTA Scheduler
-   cta::log::LogContext                                  m_lc;                 //!< CTA Log Context
-   std::map<cta::admin::OptionBoolean::Key, bool>        m_option_bool;        //!< Boolean options
-   std::map<cta::admin::OptionUInt64::Key, uint64_t>     m_option_uint64;      //!< UInt64 options
-   std::map<cta::admin::OptionString::Key, std::string>  m_option_str;         //!< String options
-   std::map<cta::admin::OptionStrList::Key,
-      std::vector<std::string>>                          m_option_str_list;    //!< String List options
+  Protocol                                              m_protocol;           //!< The protocol the client used to connect
+  cta::common::dataStructures::SecurityIdentity         m_cliIdentity;        //!< Client identity: username/host
+  cta::OStoreDBWithAgent                               &m_scheddb;            //!< Reference to CTA ObjectStore
+  cta::catalogue::Catalogue                            &m_catalogue;          //!< Reference to CTA Catalogue
+  cta::Scheduler                                       &m_scheduler;          //!< Reference to CTA Scheduler
+  cta::log::LogContext                                  m_lc;                 //!< CTA Log Context
+  std::map<cta::admin::OptionBoolean::Key, bool>        m_option_bool;        //!< Boolean options
+  std::map<cta::admin::OptionUInt64::Key, uint64_t>     m_option_uint64;      //!< UInt64 options
+  std::map<cta::admin::OptionString::Key, std::string>  m_option_str;         //!< String options
+  std::map<cta::admin::OptionStrList::Key,
+    std::vector<std::string>>                           m_option_str_list;    //!< String List options
 };
 
 }} // namespace cta::xrd
diff --git a/xrootd-ssi-protobuf-interface b/xrootd-ssi-protobuf-interface
index 17f1f77acc76a15bd9f494cfd65bdab68ee8ac19..602063b47e056ae84b45726fad63162ad6d6975b 160000
--- a/xrootd-ssi-protobuf-interface
+++ b/xrootd-ssi-protobuf-interface
@@ -1 +1 @@
-Subproject commit 17f1f77acc76a15bd9f494cfd65bdab68ee8ac19
+Subproject commit 602063b47e056ae84b45726fad63162ad6d6975b