diff --git a/catalogue/Catalogue.hpp b/catalogue/Catalogue.hpp
index 7dffa6b371f445b880480a546508ff61ea27fa5f..94efb5edd7de0897fea2010e97440751e19b6fa1 100644
--- a/catalogue/Catalogue.hpp
+++ b/catalogue/Catalogue.hpp
@@ -70,6 +70,7 @@
 #include "disk/DiskSystem.hpp"
 #include "RecyleTapeFileSearchCriteria.hpp"
 #include "CreateMountPolicyAttributes.hpp"
+#include "TapePoolSearchCriteria.hpp"
 
 #include <list>
 #include <map>
@@ -92,6 +93,7 @@ CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentArchiveRoute);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentLogicalLibrary);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTape);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentTapePool);
+CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedANonExistentVirtualOrganization);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringComment);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringDiskSystemName);
 CTA_GENERATE_USER_EXCEPTION_CLASS(UserSpecifiedAnEmptyStringFileRegexp);
@@ -488,7 +490,7 @@ public:
   */
   virtual void deleteTapePool(const std::string &name) = 0;
 
-  virtual std::list<TapePool> getTapePools() const = 0;
+  virtual std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria = TapePoolSearchCriteria()) const = 0;
 
   /**
    * @return The tape pool with the specified name.
diff --git a/catalogue/CatalogueRetryWrapper.hpp b/catalogue/CatalogueRetryWrapper.hpp
index 506d475b23bb61d4b36c6496657ada9b15b0d666..2e1ffa70f7dd93638aaf347ca33aa84d4d982dad 100644
--- a/catalogue/CatalogueRetryWrapper.hpp
+++ b/catalogue/CatalogueRetryWrapper.hpp
@@ -241,8 +241,8 @@ public:
     return retryOnLostConnection(m_log, [&]{return m_catalogue->deleteTapePool(name);}, m_maxTriesToConnect);
   }
 
-  std::list<TapePool> getTapePools() const override {
-    return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapePools();}, m_maxTriesToConnect);
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override {
+    return retryOnLostConnection(m_log, [&]{return m_catalogue->getTapePools(searchCriteria);}, m_maxTriesToConnect);
   }
 
   cta::optional<TapePool> getTapePool(const std::string &tapePoolName) const override {
diff --git a/catalogue/CatalogueTest.cpp b/catalogue/CatalogueTest.cpp
index ba7ddf2326d9126f8403532f975cf5e4053de901..cd94aa12cd9d0b993910c85acae531b1b2416907 100644
--- a/catalogue/CatalogueTest.cpp
+++ b/catalogue/CatalogueTest.cpp
@@ -2942,6 +2942,250 @@ TEST_P(cta_catalogue_CatalogueTest, modifyTapePoolSupply_nonExistentTapePool) {
   ASSERT_THROW(m_catalogue->modifyTapePoolSupply(m_admin, tapePoolName, supply), exception::UserError);
 }
 
+TEST_P(cta_catalogue_CatalogueTest, getTapePools_filterName) {
+  using namespace cta;
+
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const cta::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const cta::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes, firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes, secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = tapePoolName;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = secondTapePoolName;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = "no pool with such name";
+
+    ASSERT_THROW(m_catalogue->getTapePools(criteria), catalogue::UserSpecifiedANonExistentTapePool);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.name = "";
+
+    ASSERT_THROW(m_catalogue->getTapePools(criteria), exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_CatalogueTest, getTapePools_filterVO) {
+  using namespace cta;
+
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const cta::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const cta::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes, firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes, secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = m_vo.name;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = m_anotherVo.name;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = "no vo with such name";
+
+    ASSERT_THROW(m_catalogue->getTapePools(criteria), catalogue::UserSpecifiedANonExistentVirtualOrganization);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.vo = "";
+
+    ASSERT_THROW(m_catalogue->getTapePools(criteria), exception::UserError);
+  }
+}
+
+TEST_P(cta_catalogue_CatalogueTest, getTapePools_filterEncrypted) {
+  using namespace cta;
+
+  const std::string tapePoolName = "tape_pool";
+  const std::string secondTapePoolName = "tape_pool_2";
+
+  const uint64_t nbFirstPoolPartialTapes = 2;
+  const uint64_t nbSecondPoolPartialTapes = 3;
+
+  const bool firstPoolIsEncrypted = true;
+  const bool secondPoolIsEncrypted = false;
+
+  const cta::optional<std::string> firstPoolSupply("value for the supply first pool mechanism");
+  const cta::optional<std::string> secondPoolSupply("value for the supply second pool mechanism");
+
+  const std::string firstPoolComment = "Create first tape pool";
+  const std::string secondPoolComment = "Create second tape pool";
+
+  m_catalogue->createVirtualOrganization(m_admin, m_vo);
+  m_catalogue->createVirtualOrganization(m_admin, m_anotherVo);
+
+  m_catalogue->createTapePool(m_admin, tapePoolName, m_vo.name, nbFirstPoolPartialTapes, firstPoolIsEncrypted, firstPoolSupply, firstPoolComment);
+  m_catalogue->createTapePool(m_admin, secondTapePoolName, m_anotherVo.name, nbSecondPoolPartialTapes, secondPoolIsEncrypted, secondPoolSupply, secondPoolComment);
+
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.encrypted = true;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(tapePoolName, pool.name);
+    ASSERT_EQ(m_vo.name, pool.vo.name);
+    ASSERT_EQ(nbFirstPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(firstPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(firstPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+
+  {
+    cta::catalogue::TapePoolSearchCriteria criteria;
+    criteria.encrypted = false;
+    const auto pools = m_catalogue->getTapePools(criteria);
+    ASSERT_EQ(1, pools.size());
+
+    const auto &pool = pools.front();
+    ASSERT_EQ(secondTapePoolName, pool.name);
+    ASSERT_EQ(m_anotherVo.name, pool.vo.name);
+    ASSERT_EQ(nbSecondPoolPartialTapes, pool.nbPartialTapes);
+    ASSERT_EQ(secondPoolIsEncrypted, pool.encryption);
+    ASSERT_TRUE((bool)pool.supply);
+    ASSERT_EQ(0, pool.nbTapes);
+    ASSERT_EQ(0, pool.capacityBytes);
+    ASSERT_EQ(0, pool.dataBytes);
+    ASSERT_EQ(0, pool.nbPhysicalFiles);
+    ASSERT_EQ(secondPoolComment, pool.comment);
+
+    const common::dataStructures::EntryLog creationLog = pool.creationLog;
+    ASSERT_EQ(m_admin.username, creationLog.username);
+    ASSERT_EQ(m_admin.host, creationLog.host);
+  }
+}
+
 TEST_P(cta_catalogue_CatalogueTest, createArchiveRoute) {
   using namespace cta;
 
diff --git a/catalogue/DummyCatalogue.hpp b/catalogue/DummyCatalogue.hpp
index e05502d22aaec145f65bc6eb6396315bd33e2ae4..caad5b8da5da1d469947ed00c663ebaa2662302f 100644
--- a/catalogue/DummyCatalogue.hpp
+++ b/catalogue/DummyCatalogue.hpp
@@ -93,7 +93,7 @@ public:
   std::list<common::dataStructures::RequesterMountRule> getRequesterMountRules() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   std::list<common::dataStructures::StorageClass> getStorageClasses() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   common::dataStructures::ArchiveFileSummary getTapeFileSummary(const TapeFileSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
-  std::list<TapePool> getTapePools() const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   cta::optional<TapePool> getTapePool(const std::string &tapePoolName) const override { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
   std::list<common::dataStructures::Tape> getTapes(const TapeSearchCriteria& searchCriteria) const { throw exception::Exception(std::string("In ")+__PRETTY_FUNCTION__+": not implemented"); }
  // getTapesByVid is implemented below (and works).
diff --git a/catalogue/RdbmsCatalogue.cpp b/catalogue/RdbmsCatalogue.cpp
index 511d100b42843b0de1dbc5b41982acf7bc118796..0288bd9328d8f7ecd8e6eb20b5369e764a88b79c 100644
--- a/catalogue/RdbmsCatalogue.cpp
+++ b/catalogue/RdbmsCatalogue.cpp
@@ -2194,13 +2194,42 @@ void RdbmsCatalogue::deleteTapePool(const std::string &name) {
   }
 }
 
+
 //------------------------------------------------------------------------------
 // getTapePools
 //------------------------------------------------------------------------------
-std::list<TapePool> RdbmsCatalogue::getTapePools() const {
+std::list<TapePool> RdbmsCatalogue::getTapePools(const TapePoolSearchCriteria &searchCriteria) const {
   try {
+    auto conn = m_connPool.getConn();
+    return getTapePools(conn, searchCriteria);
+  } catch(exception::UserError &) {
+    throw;
+  } catch(exception::Exception &ex) {
+    ex.getMessage().str(std::string(__FUNCTION__) + ": " + ex.getMessage().str());
+    throw;
+  }
+}
+
+
+std::list<TapePool> RdbmsCatalogue::getTapePools(rdbms::Conn &conn, const TapePoolSearchCriteria &searchCriteria) const {
+  if (isSetAndEmpty(searchCriteria.name)) throw exception::UserError("Pool name cannot be an empty string");
+  if (isSetAndEmpty(searchCriteria.vo)) throw exception::UserError("Virtual organisation cannot be an empty string");
+  try {
+
+    if (searchCriteria.name && !tapePoolExists(conn, searchCriteria.name.value())) {
+      UserSpecifiedANonExistentTapePool ex;
+      ex.getMessage() << "Cannot list tape pools because tape pool " + searchCriteria.name.value() + " does not exist";
+      throw ex;
+    }
+
+    if (searchCriteria.vo && !virtualOrganizationExists(conn, searchCriteria.vo.value())) {
+      UserSpecifiedANonExistentVirtualOrganization ex;
+      ex.getMessage() << "Cannot list tape pools because virtual organization " + searchCriteria.vo.value() + " does not exist";
+      throw ex;
+    }
+
     std::list<TapePool> pools;
-    const char *const sql =
+    std::string sql =
       "SELECT "
         "TAPE_POOL.TAPE_POOL_NAME AS TAPE_POOL_NAME,"
         "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME AS VO,"
@@ -2233,27 +2262,61 @@ std::list<TapePool> RdbmsCatalogue::getTapePools() const {
       "LEFT OUTER JOIN TAPE ON "
         "TAPE_POOL.TAPE_POOL_ID = TAPE.TAPE_POOL_ID "
       "LEFT OUTER JOIN MEDIA_TYPE ON "
-        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID "
-      "GROUP BY "
-        "TAPE_POOL.TAPE_POOL_NAME,"
-        "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
-        "TAPE_POOL.NB_PARTIAL_TAPES,"
-        "TAPE_POOL.IS_ENCRYPTED,"
-        "TAPE_POOL.SUPPLY,"
-        "TAPE_POOL.USER_COMMENT,"
-        "TAPE_POOL.CREATION_LOG_USER_NAME,"
-        "TAPE_POOL.CREATION_LOG_HOST_NAME,"
-        "TAPE_POOL.CREATION_LOG_TIME,"
-        "TAPE_POOL.LAST_UPDATE_USER_NAME,"
-        "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
-        "TAPE_POOL.LAST_UPDATE_TIME "
-      "ORDER BY "
-        "TAPE_POOL_NAME";
+        "TAPE.MEDIA_TYPE_ID = MEDIA_TYPE.MEDIA_TYPE_ID";
+
+    if (searchCriteria.name || searchCriteria.vo || searchCriteria.encrypted) {
+      sql += " WHERE ";
+    }
+    bool addedAWhereConstraint = false;
+    if (searchCriteria.name) {
+      sql += "TAPE_POOL.TAPE_POOL_NAME = :NAME";
+      addedAWhereConstraint = true;
+    }
+
+    if (searchCriteria.vo) {
+      if (addedAWhereConstraint) sql += " AND ";
+      sql += "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME = :VO";
+      addedAWhereConstraint = true;
+    }
+
+    if (searchCriteria.encrypted) {
+      if (addedAWhereConstraint) sql += " AND ";
+      sql += "TAPE_POOL.IS_ENCRYPTED = :ENCRYPTED";
+    }
+
+    sql +=
+        " GROUP BY "
+           "TAPE_POOL.TAPE_POOL_NAME,"
+           "VIRTUAL_ORGANIZATION.VIRTUAL_ORGANIZATION_NAME,"
+           "TAPE_POOL.NB_PARTIAL_TAPES,"
+           "TAPE_POOL.IS_ENCRYPTED,"
+           "TAPE_POOL.SUPPLY,"
+           "TAPE_POOL.USER_COMMENT,"
+           "TAPE_POOL.CREATION_LOG_USER_NAME,"
+           "TAPE_POOL.CREATION_LOG_HOST_NAME,"
+           "TAPE_POOL.CREATION_LOG_TIME,"
+           "TAPE_POOL.LAST_UPDATE_USER_NAME,"
+           "TAPE_POOL.LAST_UPDATE_HOST_NAME,"
+           "TAPE_POOL.LAST_UPDATE_TIME "
+         "ORDER BY "
+           "TAPE_POOL_NAME";
 
-    auto conn = m_connPool.getConn();
     auto stmt = conn.createStmt(sql);
     stmt.bindString(":STATE_DISABLED",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::DISABLED));
     stmt.bindString(":STATE_ACTIVE",common::dataStructures::Tape::stateToString(common::dataStructures::Tape::ACTIVE));
+
+    if (searchCriteria.name) {
+      stmt.bindString(":NAME", searchCriteria.name.value());
+    }
+
+    if (searchCriteria.vo) {
+      stmt.bindString(":VO", searchCriteria.vo.value());
+    }
+
+    if(searchCriteria.encrypted) {
+      stmt.bindBool(":ENCRYPTED", searchCriteria.encrypted.value());
+    }
+
     auto rset = stmt.executeQuery();
     while (rset.next()) {
       TapePool pool;
diff --git a/catalogue/RdbmsCatalogue.hpp b/catalogue/RdbmsCatalogue.hpp
index 98f59bcf326588dfa447a52f448fc81498168bdf..0c3bcf6d58ccc782cc33b6e27e18c2721936ac20 100644
--- a/catalogue/RdbmsCatalogue.hpp
+++ b/catalogue/RdbmsCatalogue.hpp
@@ -431,7 +431,9 @@ public:
 
   void createTapePool(const common::dataStructures::SecurityIdentity &admin, const std::string &name, const std::string &vo, const uint64_t nbPartialTapes, const bool encryptionValue, const cta::optional<std::string> &supply, const std::string &comment) override;
   void deleteTapePool(const std::string &name) override;
-  std::list<TapePool> getTapePools() const override;
+  std::list<TapePool> getTapePools(const TapePoolSearchCriteria &searchCriteria) const override;
+
+  std::list<TapePool> getTapePools(rdbms::Conn &conn, const TapePoolSearchCriteria &searchCriteria) const;
 
   /**
    * @return The tape pool with the specified name.
diff --git a/catalogue/TapePoolSearchCriteria.hpp b/catalogue/TapePoolSearchCriteria.hpp
new file mode 100644
index 0000000000000000000000000000000000000000..6e38265cd1f1918b33988e48207a8ede577491c6
--- /dev/null
+++ b/catalogue/TapePoolSearchCriteria.hpp
@@ -0,0 +1,57 @@
+/*
+ * @project        The CERN Tape Archive (CTA)
+ * @copyright      Copyright(C) 2015-2021 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 <string>
+#include <vector>
+
+#include <common/optional.hpp>
+
+namespace cta {
+namespace catalogue {
+
+/**
+ * The collection of criteria used to select a set of tapepools.
+ *
+ * A tapepool is selected if it meets all of the specified criteria.
+ *
+ * A criterion is only considered specified if it has been set.
+ *
+ * Please note that no wild cards, for example '*' or '%', are supported.
+ */
+struct TapePoolSearchCriteria {
+
+   /**
+   * The name of the tapepool.
+   */
+    optional<std::string> name;
+
+    /**
+    * The virtual organization of the tapepool.
+    */
+    optional<std::string> vo;
+
+    /**
+    * Set to true if searching for encrypted tape pools.
+    */
+    optional<bool> encrypted;
+
+}; // struct TapePoolSearchCriteria
+
+} // namespace catalogue
+} // namespace cta
\ No newline at end of file
diff --git a/cmdline/CtaAdminCmdParse.hpp b/cmdline/CtaAdminCmdParse.hpp
index a629108e4a353710a6802105f89864fd63b53929..7f18c1bcfe922d83db753791ae88493a9de25008 100644
--- a/cmdline/CtaAdminCmdParse.hpp
+++ b/cmdline/CtaAdminCmdParse.hpp
@@ -297,7 +297,7 @@ const std::map<std::string, OptionUInt64::Key> uint64Options = {
  * Map string options to Protocol Buffer enum values
  */
 const std::map<std::string, OptionString::Key> strOptions = {
-   { "--bufferurl",             OptionString::BUFFERURL }, 
+   { "--bufferurl",             OptionString::BUFFERURL },
    { "--cartridge",             OptionString::CARTRIDGE },
    { "--comment",               OptionString::COMMENT },
    { "--drive",                 OptionString::DRIVE },
@@ -592,7 +592,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    }, { }},
+   {{ AdminCmd::CMD_TAPEPOOL,             AdminCmd::SUBCMD_LS    }, { opt_tapepool.optional(), opt_vo.optional(), opt_encrypted.optional()}},
    /*----------------------------------------------------------------------------------------------------*/
    {{ AdminCmd::CMD_DISKSYSTEM,           AdminCmd::SUBCMD_ADD   },
       { opt_disksystem, opt_file_regexp, opt_free_space_query_url, opt_refresh_interval, opt_targeted_free_space, opt_sleep_time,
diff --git a/xroot_plugins/XrdCtaTapePoolLs.hpp b/xroot_plugins/XrdCtaTapePoolLs.hpp
index 5a6481674476359092ffdd9ee24e00de9a9a5596..ed8f57715a233c249dc62bb17ea40403997585fe 100644
--- a/xroot_plugins/XrdCtaTapePoolLs.hpp
+++ b/xroot_plugins/XrdCtaTapePoolLs.hpp
@@ -19,6 +19,7 @@
 
 #include <xroot_plugins/XrdCtaStream.hpp>
 #include <xroot_plugins/XrdSsiCtaRequestMessage.hpp>
+#include <catalogue/TapePoolSearchCriteria.hpp>
 
 
 namespace cta { namespace xrd {
@@ -63,6 +64,13 @@ TapePoolLsStream::TapePoolLsStream(const RequestMessage &requestMsg, cta::catalo
   using namespace cta::admin;
 
   XrdSsiPb::Log::Msg(XrdSsiPb::Log::DEBUG, LOG_SUFFIX, "TapePoolLsStream() constructor");
+  cta::catalogue::TapePoolSearchCriteria searchCriteria;
+
+  searchCriteria.name = requestMsg.getOptional(OptionString_Key_TAPE_POOL);
+  searchCriteria.vo = requestMsg.getOptional(OptionString_Key_VO);
+  searchCriteria.encrypted = requestMsg.getOptional(OptionBoolean_Key_ENCRYPTED);
+
+  m_tapePoolList = m_catalogue.getTapePools(searchCriteria);
 }
 
 int TapePoolLsStream::fillBuffer(XrdSsiPb::OStreamBuffer<Data> *streambuf) {