diff --git a/CMakeLists.txt b/CMakeLists.txt index 1dacac66c9c5865510f8dc92f20428b2fd6f9cae..e3f7b2bca5d6be5020bfbef5a3938d2ecb1c5e0b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -2,6 +2,9 @@ cmake_minimum_required (VERSION 2.6) project(cta) +set(CMAKE_C_FLAGS "-fPIC -pedantic -Wall -Wextra -Werror -Wno-unused-parameter") +set(CMAKE_CXX_FLAGS "-fPIC -pedantic -Wall -Wextra -Werror -Wno-unused-parameter") + # Explicitly setting the C and C++ compiler flags for the RelWithDebInfo build # in order to prevent the -O2 flag from being used. set(CMAKE_C_FLAGS_RELWITHDEBINFO "-g") @@ -26,7 +29,7 @@ set(CMAKE_DISABLE_IN_SOURCE_BUILD ON) list(APPEND CMAKE_MODULE_PATH ${PROJECT_SOURCE_DIR}/cmake) add_subdirectory(libs) -add_subdirectory(unit_tests) +add_subdirectory(objectstore) +add_subdirectory(tests) add_subdirectory(xroot_clients) add_subdirectory(xroot_plugins) -add_subdirectory(objectstore) diff --git a/libs/client/CMakeLists.txt b/libs/client/CMakeLists.txt index e128d00bfd392c59a3b5838f4bd7e7c3d87a0b3e..81dbace149c72de52c2cd6f666f59a52e45b1188 100644 --- a/libs/client/CMakeLists.txt +++ b/libs/client/CMakeLists.txt @@ -5,9 +5,10 @@ set (CLIENT_LIB_SRC_FILES DirectoryEntry.cpp DirectoryIterator.cpp Exception.cpp + FileAttribute.cpp MockClientAPI.cpp - MockAPITest.cpp - StorageClass.cpp) + StorageClass.cpp + UserIdentity.cpp) add_library (ctaclient SHARED ${CLIENT_LIB_SRC_FILES}) diff --git a/libs/client/ClientAPI.hpp b/libs/client/ClientAPI.hpp index 7de9755ac68a09c9718c1403eb7b712a01567f90..92a967c346c708bf1344b502049917a18f5ca3fe 100644 --- a/libs/client/ClientAPI.hpp +++ b/libs/client/ClientAPI.hpp @@ -2,7 +2,7 @@ #include "DirectoryIterator.hpp" #include "StorageClass.hpp" -#include "StorageClassList.hpp" +#include "UserIdentity.hpp" #include <list> #include <stdint.h> @@ -11,7 +11,8 @@ namespace cta { /** - * Abstract class that specifies the client ClientAPI of the CERN Tape Archive project. + * Abstract class that specifies the client ClientAPI of the CERN Tape Archive + * project. */ class ClientAPI { public: @@ -21,38 +22,121 @@ public: */ virtual ~ClientAPI() throw() = 0; + /** + * Creates the specified administrator. + * + * @param requester The identity of the user requesting the creation of the + * administrator. + * @param admin The identity of the administrator. + */ + virtual void createAdminUser( + const UserIdentity &requester, + const UserIdentity &admin) = 0; + + /** + * Deletes the specified administrator. + * + * @param requester The identity of the user requesting the deletion of the + * administrator. + * @param admin The identity of the administrator. + */ + virtual void deleteAdminUser( + const UserIdentity &requester, + const UserIdentity &admin) = 0; + + /** + * Returns the current list of administrators. + * + * @param requester The identity of the user requesting the list. + */ + virtual std::list<UserIdentity> getAdminUsers(const UserIdentity &requester) + const = 0; + + /** + * Creates the specified administration host. + * + * @param requester The identity of the user requesting the creation of the + * administration host. + * @param adminHost The network name of the administration host. + */ + virtual void createAdminHost( + const UserIdentity &requester, + const std::string &adminHost) = 0; + + /** + * Deletes the specified administration host. + * + * @param requester The identity of the user requesting the deletion of the + * administration host. + * @param adminHost The network name of the administration host. + */ + virtual void deleteAdminHost( + const UserIdentity &requester, + const std::string &adminHost) = 0; + + /** + * Returns the current list of administration hosts. + * + * @param requester The identity of the user requesting the list. + */ + virtual std::list<std::string> getAdminHosts(const UserIdentity &requester) + const = 0; + /** * Creates the specified storage class. * + * @param requester The identity of the user requesting the creation of the + * storage class. * @param name The name of the storage class. * @param nbCopies The number of copies a file associated with this storage * class should have on tape. */ virtual void createStorageClass( + const UserIdentity &requester, const std::string &name, const uint8_t nbCopies) = 0; /** * Deletes the specified storage class. * + * @param requester The identity of the user requesting the deletion of the + * storage class. * @param name The name of the storage class. */ - virtual void deleteStorageClass(const std::string &name) = 0; + virtual void deleteStorageClass( + const UserIdentity &requester, + const std::string &name) = 0; /** * Gets the current list of storage classes in lexicographical order. * + * @param requester The identity of the user requesting list. * @return The current list of storage classes in lexicographical order. */ - virtual StorageClassList getStorageClasses() const = 0; + virtual std::list<StorageClass> getStorageClasses( + const UserIdentity &requester) const = 0; /** - * Gets an iterator over the entries of the specified directory. + * Creates the specified directory. * + * @param requester The identity of the user requesting the creation of the + * directory. + * @param dirPath The full path of the directory. + */ + virtual void createDirectory( + const UserIdentity &requester, + const std::string &dirPath) = 0; + + /** + * Gets the contents of the specified directory. + * + * @param requester The identity of the user requesting the contents of the + * directory. * @param dirPath The full path of the directory. - * @return The iterator. + * @return An iterator over the contents of the directory. */ - virtual DirectoryIterator getDirectoryIterator( + virtual DirectoryIterator getDirectoryContents( + const UserIdentity &requester, const std::string &dirPath) const = 0; /** @@ -68,11 +152,14 @@ public: * The storage class of the archived file will be inherited from its * destination directory. * + * @param requester The identity of the user requesting the archival. * @param srcUrls List of one or more source files. * @param dst Destination file or directory within the archive namespace. * @return The identifier of the archive job. */ - virtual std::string archiveToTape(const std::list<std::string> &srcUrls, + virtual std::string archiveToTape( + const UserIdentity &requester, + const std::list<std::string> &srcUrls, std::string dst) = 0; }; // class ClientAPI diff --git a/libs/client/DirectoryEntry.cpp b/libs/client/DirectoryEntry.cpp index 27f03539f9d75aeec825be3bd587557ee2d05cd4..74041e52db9b75231b30187438d1b106d34c7b91 100644 --- a/libs/client/DirectoryEntry.cpp +++ b/libs/client/DirectoryEntry.cpp @@ -1 +1,26 @@ #include "DirectoryEntry.hpp" + +//------------------------------------------------------------------------------ +// typeToString +//------------------------------------------------------------------------------ +const char *cta::DirectoryEntry::typeToString(const EntryType enumValue) + throw() { + switch(enumValue) { + case NONE : return "NONE"; + case FILE_ENTRY : return "FILE_ENTRY"; + case DIRECTORY_ENTRY: return "DIRECTORY_ENTRY"; + default : return "UNKNOWN"; + } +} + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +cta::DirectoryEntry::DirectoryEntry(): + entryType(NONE), + ownerId(0), + groupId(0), + ownerPerms(0), + groupPerms(0), + otherPerms(0) { +} diff --git a/libs/client/DirectoryEntry.hpp b/libs/client/DirectoryEntry.hpp index 25269a0ba2286dedaf28b2af7445a68f2d99e921..86e0c189c5809724738016e0db062cbed3e28bfa 100644 --- a/libs/client/DirectoryEntry.hpp +++ b/libs/client/DirectoryEntry.hpp @@ -1,11 +1,87 @@ #pragma once +#include "FileAttribute.hpp" + +#include <list> +#include <stdint.h> #include <string> namespace cta { +/** + * The structure of a directory entry. + */ struct DirectoryEntry { + + /** + * Enumeration of the different types of directory entry. + */ + enum EntryType { + NONE, + FILE_ENTRY, + DIRECTORY_ENTRY + }; + + /** + * Thread safe method that returns the string representation of the + * specified enumeration (integer) value. + * + * @param enumValue The enumeration (integer) value. + * @return The string value. + */ + static const char *typeToString(const EntryType enumValue) throw(); + + /** + * The type of the directory entry. + */ + EntryType entryType; + + /** + * The name of the directory. + */ std::string name; + + /** + * The user ID of the file's owner. + */ + uint32_t ownerId; + + /** + * The group ID of the file. + */ + uint32_t groupId; + + /** + * Access permissions for the file's owner "a la" posix. For example a value + * of 7 means read, write and execute permissions. + */ + uint8_t ownerPerms; + + /** + * Access permissions for the file's group "a la" posix. For example a + * value of 7 means read, write and execute permissions. + */ + uint8_t groupPerms; + + /** + * Access permissions for users other than the owner or members of the file's + * group. + */ + uint8_t otherPerms; + + /** + * The attributes of the directory. + */ + std::list<FileAttribute> attributes; + + /** + * Constructor. + * + * Initialises the entry type of the DirectoryEntry to NONE and all integer + * member-variables to 0. + */ + DirectoryEntry(); + }; // DirectoryEntry } // namespace cta diff --git a/libs/client/DirectoryIterator.cpp b/libs/client/DirectoryIterator.cpp index 85bafa9388215b5b5f04c42d7c84a6666d1f7600..dbff1d5e3112f5fb722cba28bf9ff03114a5bdd1 100644 --- a/libs/client/DirectoryIterator.cpp +++ b/libs/client/DirectoryIterator.cpp @@ -1,4 +1,11 @@ #include "DirectoryIterator.hpp" +#include "Exception.hpp" + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +cta::DirectoryIterator::DirectoryIterator() { +} //------------------------------------------------------------------------------ // constructor @@ -12,3 +19,23 @@ cta::DirectoryIterator::DirectoryIterator( //------------------------------------------------------------------------------ cta::DirectoryIterator::~DirectoryIterator() throw() { } + +//------------------------------------------------------------------------------ +// hasMore +//------------------------------------------------------------------------------ +bool cta::DirectoryIterator::hasMore() { + return !m_entries.empty(); +} + +//------------------------------------------------------------------------------ +// next +//------------------------------------------------------------------------------ +const cta::DirectoryEntry cta::DirectoryIterator::next() { + if(m_entries.empty()) { + throw Exception("Out of bounds: There are no more directory entries"); + } + + DirectoryEntry entry = m_entries.front(); + m_entries.pop_front(); + return entry; +} diff --git a/libs/client/DirectoryIterator.hpp b/libs/client/DirectoryIterator.hpp index 5fdaf83533db887abe305665bfc1e1818f24b667..232efa492abcfa41700c920dacd7c8cdcf225442 100644 --- a/libs/client/DirectoryIterator.hpp +++ b/libs/client/DirectoryIterator.hpp @@ -23,6 +23,11 @@ namespace cta { class DirectoryIterator { public: + /** + * Constructor. + */ + DirectoryIterator(); + /** * Constructor. * diff --git a/libs/client/FileAttribute.cpp b/libs/client/FileAttribute.cpp index d934deb9c14ce20c3cfbe3824609f59f8a53cd83..84432ac75c2197652070f75c8e54c1c83308f7a7 100644 --- a/libs/client/FileAttribute.cpp +++ b/libs/client/FileAttribute.cpp @@ -3,12 +3,12 @@ //------------------------------------------------------------------------------ // constructor //------------------------------------------------------------------------------ -cta::FileAtribute::FileAtribute() { +cta::FileAttribute::FileAttribute() { } //------------------------------------------------------------------------------ // constructor //------------------------------------------------------------------------------ -cta::FileAtribute::FileAtribute(const std::string &name, +cta::FileAttribute::FileAttribute(const std::string &name, const std::string &value): name(name), value(value) { } diff --git a/libs/client/FileAttribute.hpp b/libs/client/FileAttribute.hpp index 6b8f1d927424f57d7b28a90154abe831de9a8d7c..299913948f37cecd1e75ea0786cc9efab0212529 100644 --- a/libs/client/FileAttribute.hpp +++ b/libs/client/FileAttribute.hpp @@ -7,7 +7,7 @@ namespace cta { /** * Class representing a file atrribute. */ -struct FileAtribute { +struct FileAttribute { /** * The name of the attribute. @@ -22,7 +22,7 @@ struct FileAtribute { /** * Constructor. */ - FileAtribute(); + FileAttribute(); /** * Constructor. @@ -30,8 +30,8 @@ struct FileAtribute { * @param name The name of the attribute. * @param value value of the attribute. */ - FileAtribute(const std::string &name, const std::string &value); + FileAttribute(const std::string &name, const std::string &value); -}; // struct FileAtribute +}; // struct FileAttribute } // namespace cta diff --git a/libs/client/MockAPITest.cpp b/libs/client/MockAPITest.cpp deleted file mode 100644 index 441bc29d823f2bd4161b725d41e318e0850ddb9e..0000000000000000000000000000000000000000 --- a/libs/client/MockAPITest.cpp +++ /dev/null @@ -1,123 +0,0 @@ -#include "MockClientAPI.hpp" - -#include <gtest/gtest.h> - -namespace unitTests { - -class cta_client_MockClientAPITest: public ::testing::Test { -protected: - - virtual void SetUp() { - } - - virtual void TearDown() { - } -}; - -TEST_F(cta_client_MockClientAPITest, createStorageClass_new) { - using namespace cta; - - MockClientAPI api; - - ASSERT_TRUE(api.getStorageClasses().empty()); - - const std::string name = "TestStorageClass"; - const uint8_t nbCopies = 2; - ASSERT_NO_THROW(api.createStorageClass(name, nbCopies)); - - ASSERT_EQ(1, api.getStorageClasses().size()); - cta::StorageClass storageClass; - ASSERT_NO_THROW(storageClass = api.getStorageClasses().front()); - ASSERT_EQ(name, storageClass.name); - ASSERT_EQ(nbCopies, storageClass.nbCopies); -} - -TEST_F(cta_client_MockClientAPITest, createStorageClass_already_existing) { - using namespace cta; - - MockClientAPI api; - - ASSERT_TRUE(api.getStorageClasses().empty()); - - const std::string name = "TestStorageClass"; - const uint8_t nbCopies = 2; - ASSERT_NO_THROW(api.createStorageClass(name, nbCopies)); - - ASSERT_EQ(1, api.getStorageClasses().size()); - cta::StorageClass storageClass; - ASSERT_NO_THROW(storageClass = api.getStorageClasses().front()); - ASSERT_EQ(name, storageClass.name); - ASSERT_EQ(nbCopies, storageClass.nbCopies); - - ASSERT_THROW(api.createStorageClass(name, nbCopies), std::exception); -} - -TEST_F(cta_client_MockClientAPITest, createStorageClass_lexicographical_order) { - using namespace cta; - - MockClientAPI api; - - ASSERT_TRUE(api.getStorageClasses().empty()); - - ASSERT_NO_THROW(api.createStorageClass("d", 1)); - ASSERT_NO_THROW(api.createStorageClass("b", 1)); - ASSERT_NO_THROW(api.createStorageClass("a", 1)); - ASSERT_NO_THROW(api.createStorageClass("c", 1)); - - ASSERT_EQ(4, api.getStorageClasses().size()); - - cta::StorageClassList storageClasses = api.getStorageClasses(); - - ASSERT_EQ(std::string("a"), storageClasses.front().name); - storageClasses.pop_front(); - ASSERT_EQ(std::string("b"), storageClasses.front().name); - storageClasses.pop_front(); - ASSERT_EQ(std::string("c"), storageClasses.front().name); - storageClasses.pop_front(); - ASSERT_EQ(std::string("d"), storageClasses.front().name); -} - -TEST_F(cta_client_MockClientAPITest, deleteStorageClass_existing) { - using namespace cta; - - MockClientAPI api; - - ASSERT_TRUE(api.getStorageClasses().empty()); - - const std::string name = "TestStorageClass"; - const uint8_t nbCopies = 2; - ASSERT_NO_THROW(api.createStorageClass(name, nbCopies)); - - ASSERT_EQ(1, api.getStorageClasses().size()); - cta::StorageClass storageClass; - ASSERT_NO_THROW(storageClass = api.getStorageClasses().front()); - ASSERT_EQ(name, storageClass.name); - ASSERT_EQ(nbCopies, storageClass.nbCopies); - - ASSERT_NO_THROW(api.deleteStorageClass(name)); - - ASSERT_TRUE(api.getStorageClasses().empty()); -} - -TEST_F(cta_client_MockClientAPITest, deleteStorageClass_non_existing) { - using namespace cta; - - MockClientAPI api; - - ASSERT_TRUE(api.getStorageClasses().empty()); - - const std::string name = "TestStorageClass"; - ASSERT_THROW(api.deleteStorageClass(name), std::exception); - - ASSERT_TRUE(api.getStorageClasses().empty()); -} - -TEST_F(cta_client_MockClientAPITest, getDirectoryIterator_empty) { - using namespace cta; - - MockClientAPI api; - - ASSERT_NO_THROW(api.getDirectoryIterator("/")); -} - -} // namespace unitTests diff --git a/libs/client/MockClientAPI.cpp b/libs/client/MockClientAPI.cpp index b7ca0d1468259ec7179968d33d4e05bef67837e8..f39d9e8ffe0bdadaf7b1516a22ac573e9b24c41d 100644 --- a/libs/client/MockClientAPI.cpp +++ b/libs/client/MockClientAPI.cpp @@ -1,5 +1,5 @@ -#include "MockClientAPI.hpp" #include "Exception.hpp" +#include "MockClientAPI.hpp" #include <sstream> @@ -15,11 +15,59 @@ cta::MockClientAPI::MockClientAPI() { cta::MockClientAPI::~MockClientAPI() throw() { } +//------------------------------------------------------------------------------ +// createAdminUser +//------------------------------------------------------------------------------ +void cta::MockClientAPI::createAdminUser( + const UserIdentity &requester, + const UserIdentity &admin) { +} + +//------------------------------------------------------------------------------ +// deleteAdminUser +//------------------------------------------------------------------------------ +void cta::MockClientAPI::deleteAdminUser( + const UserIdentity &requester, + const UserIdentity &admin) { +} + +//------------------------------------------------------------------------------ +// getAdminUsers +//------------------------------------------------------------------------------ +std::list<cta::UserIdentity> cta::MockClientAPI::getAdminUsers( + const UserIdentity &requester) const { + return m_adminUsers; +} + +//------------------------------------------------------------------------------ +// createAdminHost +//------------------------------------------------------------------------------ +void cta::MockClientAPI::createAdminHost( + const UserIdentity &requester, + const std::string &adminHost) { +} + +//------------------------------------------------------------------------------ +// deleteAdminHost +//------------------------------------------------------------------------------ +void cta::MockClientAPI::deleteAdminHost( + const UserIdentity &requester, + const std::string &adminHost) { +} + +//------------------------------------------------------------------------------ +// getAdminHosts +//------------------------------------------------------------------------------ +std::list<std::string> cta::MockClientAPI::getAdminHosts( + const UserIdentity &requester) const { + return m_adminHosts; +} + //------------------------------------------------------------------------------ // createStorageClass //------------------------------------------------------------------------------ -void cta::MockClientAPI::createStorageClass(const std::string &name, - const uint8_t nbCopies) { +void cta::MockClientAPI::createStorageClass(const UserIdentity &requester, + const std::string &name, const uint8_t nbCopies) { try { checkStorageClassDoesNotAlreadyExist(name); StorageClass storageClass(name, nbCopies); @@ -47,7 +95,8 @@ void cta::MockClientAPI::checkStorageClassDoesNotAlreadyExist( //------------------------------------------------------------------------------ // deleteStorageClass //------------------------------------------------------------------------------ -void cta::MockClientAPI::deleteStorageClass(const std::string &name) { +void cta::MockClientAPI::deleteStorageClass(const UserIdentity &requester, + const std::string &name) { try { checkStorageClassExists(name); m_storageClasses.erase(name); @@ -74,7 +123,8 @@ void cta::MockClientAPI::checkStorageClassExists( //------------------------------------------------------------------------------ // getStorageClasses //------------------------------------------------------------------------------ -std::list<cta::StorageClass> cta::MockClientAPI::getStorageClasses() const { +std::list<cta::StorageClass> cta::MockClientAPI::getStorageClasses( + const UserIdentity &requester) const { std::list<StorageClass> storageClasses; for(std::map<std::string, StorageClass>::const_iterator itor = m_storageClasses.begin(); itor != m_storageClasses.end(); itor++) { @@ -84,11 +134,107 @@ std::list<cta::StorageClass> cta::MockClientAPI::getStorageClasses() const { } //------------------------------------------------------------------------------ -// getDirectoryIterator +// createDirectory +//------------------------------------------------------------------------------ +void cta::MockClientAPI::createDirectory(const UserIdentity &requester, + const std::string &dirPath) { + checkAbsolutePathSyntax(dirPath); +} + +//------------------------------------------------------------------------------ +// checkAbsolutePathSyntax //------------------------------------------------------------------------------ -cta::DirectoryIterator cta::MockClientAPI:: - getDirectoryIterator(const std::string &dirPath) const { +void cta::MockClientAPI::checkAbsolutePathSyntax(const std::string &path) { + try { + checkPathStartsWithASlash(path); + checkPathContainsValidChars(path); + checkPathDoesContainConsecutiveSlashes(path); + } catch(std::exception &ex) { + std::ostringstream message; + message << "Absolute path \"" << path << "\" contains a syntax error: " << + ex.what(); + throw Exception(message.str()); + } +} +//------------------------------------------------------------------------------ +// checkPathStartsWithASlash +//------------------------------------------------------------------------------ +void cta::MockClientAPI::checkPathStartsWithASlash(const std::string &path) { + if(path.empty()) { + throw Exception("Path is an empty string"); + } + + if('/' != path[0]) { + throw Exception("Path does not start with a '/' character"); + } +} + +//------------------------------------------------------------------------------ +// checkPathContainsValidChars +//------------------------------------------------------------------------------ +void cta::MockClientAPI::checkPathContainsValidChars(const std::string &path) { + for(std::string::const_iterator itor = path.begin(); itor != path.end(); + itor++) { + checkValidPathChar(*itor); + } +} + +//------------------------------------------------------------------------------ +// checkValidPathChar +//------------------------------------------------------------------------------ +void cta::MockClientAPI::checkValidPathChar(const char c) { + if(!isValidPathChar(c)) { + std::ostringstream message; + message << "The '" << c << "' character cannot be used within a path"; + throw Exception(message.str()); + } +} + +//------------------------------------------------------------------------------ +// isValidPathChar +//------------------------------------------------------------------------------ +bool cta::MockClientAPI::isValidPathChar(const char c) { + return ('0' <= c && c <= '9') || + ('A' <= c && c <= 'Z') || + ('a' <= c && c <= 'z') || + c == '_' || + c == '/'; +} + +//------------------------------------------------------------------------------ +// checkPathDoesContainConsecutiveSlashes +//------------------------------------------------------------------------------ +void cta::MockClientAPI::checkPathDoesContainConsecutiveSlashes( + const std::string &path) { + char previousChar = '\0'; + + for(std::string::const_iterator itor = path.begin(); itor != path.end(); + itor++) { + const char ¤tChar = *itor; + if(previousChar == '/' && currentChar == '/') { + throw Exception("Path contains consecutive slashes"); + } + previousChar = currentChar; + } +} + +//------------------------------------------------------------------------------ +// getEnclosingDirPath +//------------------------------------------------------------------------------ +std::string cta::MockClientAPI::getEnclosingDirPath(const std::string &path) { + const std::string::size_type last_slash_idx = path.find_last_of('/'); + if(std::string::npos == last_slash_idx) { + throw Exception("Path does not contain a slash"); + } + return path.substr(0, last_slash_idx); +} + +//------------------------------------------------------------------------------ +// getDirectoryContents +//------------------------------------------------------------------------------ +cta::DirectoryIterator cta::MockClientAPI::getDirectoryContents( + const UserIdentity &requester, const std::string &dirPath) const { std::list<DirectoryEntry> entries; DirectoryIterator itor(entries); return itor; @@ -97,7 +243,7 @@ cta::DirectoryIterator cta::MockClientAPI:: //------------------------------------------------------------------------------ // archiveToTape //------------------------------------------------------------------------------ -std::string cta::MockClientAPI::archiveToTape( +std::string cta::MockClientAPI::archiveToTape(const UserIdentity &requester, const std::list<std::string> &srcUrls, std::string dst) { return "Funny_Job_ID"; } diff --git a/libs/client/MockClientAPI.hpp b/libs/client/MockClientAPI.hpp index 1e115f959f6f55ce87501677af1ac3daa7e6cc1d..324e8482d976bca6d045b5a41518f0f3c6541c35 100644 --- a/libs/client/MockClientAPI.hpp +++ b/libs/client/MockClientAPI.hpp @@ -22,38 +22,119 @@ public: */ ~MockClientAPI() throw(); + /** + * Creates the specified administrator. + * + * @param requester The identity of the user requesting the creation of the + * administrator. + * @param admin The identity of the administrator. + */ + void createAdminUser( + const UserIdentity &requester, + const UserIdentity &admin); + + /** + * Deletes the specified administrator. + * + * @param requester The identity of the user requesting the deletion of the + * administrator. + * @param admin The identity of the administrator. + */ + void deleteAdminUser( + const UserIdentity &requester, + const UserIdentity &admin); + + /** + * Returns the current list of administrators. + * + * @param requester The identity of the user requesting the list. + */ + std::list<UserIdentity> getAdminUsers(const UserIdentity &requester) const; + + /** + * Creates the specified administration host. + * + * @param requester The identity of the user requesting the creation of the + * administration host. + * @param adminHost The network name of the administration host. + */ + void createAdminHost( + const UserIdentity &requester, + const std::string &adminHost); + + /** + * Deletes the specified administration host. + * + * @param requester The identity of the user requesting the deletion of the + * administration host. + * @param adminHost The network name of the administration host. + */ + void deleteAdminHost( + const UserIdentity &requester, + const std::string &adminHost); + + /** + * Returns the current list of administration hosts. + * + * @param requester The identity of the user requesting the list. + */ + std::list<std::string> getAdminHosts(const UserIdentity &requester) const; + /** * Creates the specified storage class. * + * @param requester The identity of the user requesting the creation of the + * storage class. * @param name The name of the storage class. * @param nbCopies The number of copies a file associated with this storage * class should have on tape. */ void createStorageClass( + const UserIdentity &requester, const std::string &name, const uint8_t nbCopies); /** * Deletes the specified storage class. * + * @param requester The identity of the user requesting the deletion of the + * storage class. * @param name The name of the storage class. */ - void deleteStorageClass(const std::string &name); + void deleteStorageClass( + const UserIdentity &requester, + const std::string &name); /** * Gets the current list of storage classes in lexicographical order. * + * @param requester The identity of the user requesting the list. * @return The current list of storage classes in lexicographical order. */ - StorageClassList getStorageClasses() const; + std::list<StorageClass> getStorageClasses( + const UserIdentity &requester) const; /** - * Gets an iterator over the entries of the specified directory. + * Creates the specified directory. * + * @param requester The identity of the user requesting the creation of the + * directory. * @param dirPath The full path of the directory. - * @return The iterator. */ - DirectoryIterator getDirectoryIterator( + void createDirectory( + const UserIdentity &requester, + const std::string &dirPath); + + /** + * Gets the contents of the specified directory. + * + * @param requester The identity of the user requesting the contents of the + * directory. + * @param dirPath The full path of the directory. + * @return An iterator over the contents of the directory. + */ + DirectoryIterator getDirectoryContents( + const UserIdentity &requester, const std::string &dirPath) const; /** @@ -69,14 +150,27 @@ public: * The storage class of the archived file will be inherited from its * destination directory. * + * @param requester The identity of the user requesting the archival. * @param srcUrls List of one or more source files. * @param dst Destination file or directory within the archive namespace. * @return The identifier of the archive job. */ - std::string archiveToTape(const std::list<std::string> &srcUrls, + std::string archiveToTape( + const UserIdentity &requester, + const std::list<std::string> &srcUrls, std::string dst); -private: +protected: + + /** + * The current list of administrators. + */ + std::list<UserIdentity> m_adminUsers; + + /** + * The current list of administration hosts. + */ + std::list<std::string> m_adminHosts; /** * The current list of storage classes. @@ -93,10 +187,69 @@ private: /** * Throws an exception if the specified storage class does not exist. * - * @param name The name of teh storage class. + * @param name The name of the storage class. */ void checkStorageClassExists(const std::string &name) const; + /** + * Throws an exception if the specified absolute path constains a + * syntax error. + * + * @param path The Absolute path. + */ + void checkAbsolutePathSyntax(const std::string &path); + + /** + * Throws an exception if the specified path does not start with a slash. + * + * @param path The path. + */ + void checkPathStartsWithASlash(const std::string &path); + + /** + * Throws an exception if the specified path does not contain valid + * characters. + * + * @param path The path. + */ + void checkPathContainsValidChars(const std::string &path); + + /** + * Throws an exception if the specified character cannot be used within a + * path. + * + * @param c The character to be tested. + */ + void checkValidPathChar(const char c); + + /** + * Returns true of the specified character can be used within a path. + */ + bool isValidPathChar(const char c); + + /** + * Throws an exception if the specified path contains consective slashes. For + * example the path "/just_before_consectuive_slashes//file" would cause this + * method to throw an exception. + * + * @param path The path. + */ + void checkPathDoesContainConsecutiveSlashes(const std::string &path); + + /** + * Returns the path of the enclosing directory of the specified path. + * + * For example: + * + * * path="/grandparent/parent/child" would return "/grandparent/parent" + * * path="/grandparent" would return "/grandparent" + * * path="/" would return "" where empty string means no enclosing directoyr + * + * @param path The path. + * @return The path of the enclosing directory. + */ + std::string getEnclosingDirPath(const std::string &path); + }; // class MockClientAPI } // namespace cta diff --git a/libs/client/MockClientAPITest.cpp b/libs/client/MockClientAPITest.cpp new file mode 100644 index 0000000000000000000000000000000000000000..0f06e71693005d8a44cc19f4bd669abffbd30a4f --- /dev/null +++ b/libs/client/MockClientAPITest.cpp @@ -0,0 +1,428 @@ +#include "TestingMockClientAPI.hpp" + +#include <gtest/gtest.h> + +namespace unitTests { + +class cta_client_MockClientAPITest: public ::testing::Test { +protected: + + virtual void SetUp() { + } + + virtual void TearDown() { + } +}; + +TEST_F(cta_client_MockClientAPITest, createAdminUser_new) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } + + const uint16_t adminUser1Uid = 1234; + const uint16_t adminUser1Gid = 5678; + const UserIdentity adminUser1(adminUser1Uid, adminUser1Gid, ""); + ASSERT_NO_THROW(api.createAdminUser(requester, adminUser1)); + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_EQ(1, adminUsers.size()); + + ASSERT_EQ(adminUser1Uid, adminUsers.front().uid); + ASSERT_EQ(adminUser1Gid, adminUsers.front().gid); + } +} + +TEST_F(cta_client_MockClientAPITest, createAdminUser_already_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } + + const uint16_t adminUser1Uid = 1234; + const uint16_t adminUser1Gid = 5678; + const UserIdentity adminUser1(adminUser1Uid, adminUser1Gid, ""); + ASSERT_NO_THROW(api.createAdminUser(requester, adminUser1)); + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_EQ(1, adminUsers.size()); + + ASSERT_EQ(adminUser1Uid, adminUsers.front().uid); + ASSERT_EQ(adminUser1Gid, adminUsers.front().gid); + } + + ASSERT_THROW(api.createAdminUser(requester, adminUser1), std::exception); + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_EQ(1, adminUsers.size()); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteAdminUser_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } + + const uint16_t adminUser1Uid = 1234; + const uint16_t adminUser1Gid = 5678; + const UserIdentity adminUser1(adminUser1Uid, adminUser1Gid, ""); + ASSERT_NO_THROW(api.createAdminUser(requester, adminUser1)); + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_EQ(1, adminUsers.size()); + + ASSERT_EQ(adminUser1Uid, adminUsers.front().uid); + ASSERT_EQ(adminUser1Gid, adminUsers.front().gid); + } + + ASSERT_NO_THROW(api.deleteAdminUser(requester, adminUser1)); + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteAdminUser_non_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } + + const uint16_t adminUser1Uid = 1234; + const uint16_t adminUser1Gid = 5678; + const UserIdentity adminUser1(adminUser1Uid, adminUser1Gid, ""); + ASSERT_THROW(api.deleteAdminUser(requester, adminUser1), std::exception); + + { + std::list<UserIdentity> adminUsers; + ASSERT_NO_THROW(adminUsers = api.getAdminUsers(requester)); + ASSERT_TRUE(adminUsers.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, createAdminHost_new) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_TRUE(adminHosts.empty()); + } + + const std::string adminHost1 = "adminHost1"; + ASSERT_NO_THROW(api.createAdminHost(requester, adminHost1)); + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_EQ(1, adminHosts.size()); + + ASSERT_EQ(adminHost1, adminHosts.front()); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteAdminHost_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_TRUE(adminHosts.empty()); + } + + const std::string adminHost1 = "adminHost1"; + ASSERT_NO_THROW(api.createAdminHost(requester, adminHost1)); + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_EQ(1, adminHosts.size()); + + ASSERT_EQ(adminHost1, adminHosts.front()); + } + + ASSERT_NO_THROW(api.deleteAdminHost(requester, adminHost1)); + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_TRUE(adminHosts.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteAdminHost_non_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_TRUE(adminHosts.empty()); + } + + const std::string adminHost1 = "adminHost1"; + ASSERT_THROW(api.deleteAdminHost(requester, adminHost1), std::exception); + + { + std::list<std::string> adminHosts; + ASSERT_NO_THROW(adminHosts = api.getAdminHosts(requester)); + ASSERT_TRUE(adminHosts.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, createStorageClass_new) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint8_t nbCopies = 2; + ASSERT_NO_THROW(api.createStorageClass(requester, name, nbCopies)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.name); + ASSERT_EQ(nbCopies, storageClass.nbCopies); + } +} + +TEST_F(cta_client_MockClientAPITest, createStorageClass_already_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint8_t nbCopies = 2; + ASSERT_NO_THROW(api.createStorageClass(requester, name, nbCopies)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.name); + ASSERT_EQ(nbCopies, storageClass.nbCopies); + } + + ASSERT_THROW(api.createStorageClass(requester, name, nbCopies), + std::exception); +} + +TEST_F(cta_client_MockClientAPITest, createStorageClass_lexicographical_order) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + ASSERT_NO_THROW(api.createStorageClass(requester, "d", 1)); + ASSERT_NO_THROW(api.createStorageClass(requester, "b", 1)); + ASSERT_NO_THROW(api.createStorageClass(requester, "a", 1)); + ASSERT_NO_THROW(api.createStorageClass(requester, "c", 1)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_EQ(4, storageClasses.size()); + + ASSERT_EQ(std::string("a"), storageClasses.front().name); + storageClasses.pop_front(); + ASSERT_EQ(std::string("b"), storageClasses.front().name); + storageClasses.pop_front(); + ASSERT_EQ(std::string("c"), storageClasses.front().name); + storageClasses.pop_front(); + ASSERT_EQ(std::string("d"), storageClasses.front().name); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteStorageClass_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + const uint8_t nbCopies = 2; + ASSERT_NO_THROW(api.createStorageClass(requester, name, nbCopies)); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_EQ(1, storageClasses.size()); + + StorageClass storageClass; + ASSERT_NO_THROW(storageClass = storageClasses.front()); + ASSERT_EQ(name, storageClass.name); + ASSERT_EQ(nbCopies, storageClass.nbCopies); + + ASSERT_NO_THROW(api.deleteStorageClass(requester, name)); + } + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, deleteStorageClass_non_existing) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } + + const std::string name = "TestStorageClass"; + ASSERT_THROW(api.deleteStorageClass(requester, name), std::exception); + + { + std::list<StorageClass> storageClasses; + ASSERT_NO_THROW(storageClasses = api.getStorageClasses(requester)); + ASSERT_TRUE(storageClasses.empty()); + } +} + +TEST_F(cta_client_MockClientAPITest, getDirectoryContents_root_dir_is_empty) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + const std::string dirPath = "/"; + + DirectoryIterator itor; + ASSERT_NO_THROW(itor = api.getDirectoryContents(requester,"/")); + ASSERT_FALSE(itor.hasMore()); +} + +TEST_F(cta_client_MockClientAPITest, createDirectory_empty_string) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + const std::string dirPath = ""; + + ASSERT_THROW(api.createDirectory(requester, dirPath), std::exception); +} + +TEST_F(cta_client_MockClientAPITest, createDirectory_consecutive_slashes) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + const std::string dirPath = "//"; + + ASSERT_THROW(api.createDirectory(requester, dirPath), std::exception); +} + +TEST_F(cta_client_MockClientAPITest, createDirectory_invalid_chars) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + const std::string dirPath = "/grandparent/?parent"; + + ASSERT_THROW(api.createDirectory(requester, dirPath), std::exception); +} + +TEST_F(cta_client_MockClientAPITest, createDirectory_top_level) { + using namespace cta; + + TestingMockClientAPI api; + const UserIdentity requester; + const std::string dirPath = "/grandparent"; + + ASSERT_NO_THROW(api.createDirectory(requester, dirPath)); + + DirectoryIterator itor; + + ASSERT_NO_THROW(itor = api.getDirectoryContents(requester, "/")); + + ASSERT_TRUE(itor.hasMore()); + + DirectoryEntry entry; + + ASSERT_NO_THROW(entry = itor.next()); + + ASSERT_EQ(std::string("grandparent"), entry.name); +} + +} // namespace unitTests diff --git a/libs/client/StorageClassList.hpp b/libs/client/StorageClassList.hpp deleted file mode 100644 index 3e24d1be0fb1e51f2ce5b5d85521fed0b0ed5177..0000000000000000000000000000000000000000 --- a/libs/client/StorageClassList.hpp +++ /dev/null @@ -1,11 +0,0 @@ -#pragma once - -#include "StorageClass.hpp" - -#include <list> - -namespace cta { - -typedef std::list<StorageClass> StorageClassList; - -} // namespace cta diff --git a/libs/client/UserIdentity.cpp b/libs/client/UserIdentity.cpp new file mode 100644 index 0000000000000000000000000000000000000000..66a02e5d4e3ec071a51a4bd437678ec6f97d1bda --- /dev/null +++ b/libs/client/UserIdentity.cpp @@ -0,0 +1,22 @@ +#include "UserIdentity.hpp" + + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +cta::UserIdentity::UserIdentity() throw(): + uid(0), + gid(0) { +} + +//------------------------------------------------------------------------------ +// constructor +//------------------------------------------------------------------------------ +cta::UserIdentity::UserIdentity( + const uint32_t uid, + const uint32_t gid, + const std::string &host) throw(): + uid(uid), + gid(gid), + host(host) { +} diff --git a/libs/client/UserIdentity.hpp b/libs/client/UserIdentity.hpp new file mode 100644 index 0000000000000000000000000000000000000000..1b9d36ec15bd98789d72550f7b3b81884e8b03fa --- /dev/null +++ b/libs/client/UserIdentity.hpp @@ -0,0 +1,47 @@ +#pragma once + +#include <stdint.h> +#include <string> + +namespace cta { + +/** + * Class reprsenting the identity of a user. + */ +struct UserIdentity { + + /** + * The user ID of the user. + */ + uint32_t uid; + + /** + * The group ID of the user. + */ + uint32_t gid; + + /** + * The host from which the user is communicating. + */ + std::string host; + + /** + * Constructor. + * + * Initialises all integer member-variables to 0. + */ + UserIdentity() throw(); + + /** + * Constructor. + * + * @param uid The user ID of the user. + * @param gid The group ID of the user. + * @param host The host from which the user is communicating. + */ + UserIdentity(const uint32_t uid, const uint32_t gid, const std::string &host) + throw(); + +}; // struct UserIdentity + +} // namespace cta diff --git a/objectstore/RecallJob.hpp b/objectstore/RecallJob.hpp index 5769b020d87cfda023667a2ee8a9ea76472f8c2f..bca006c2885914e5c13dad4419fbabb40fa8da8f 100644 --- a/objectstore/RecallJob.hpp +++ b/objectstore/RecallJob.hpp @@ -48,4 +48,4 @@ public: } }; -}} \ No newline at end of file +}} diff --git a/unit_tests/CMakeLists.txt b/tests/CMakeLists.txt similarity index 84% rename from unit_tests/CMakeLists.txt rename to tests/CMakeLists.txt index 4cda2e4d98d9ea9b6a4c19ba36551043132efa79..d6d778a39420c5b2c7563dc0e8206399ddbc76ed 100644 --- a/unit_tests/CMakeLists.txt +++ b/tests/CMakeLists.txt @@ -5,7 +5,7 @@ include_directories(${PROJECT_SOURCE_DIR}/libs/ctacommon) include_directories(${PROJECT_SOURCE_DIR}/libs/ctaclient) add_executable(unittests - ${PROJECT_SOURCE_DIR}/libs/client/MockAPITest.cpp + ${PROJECT_SOURCE_DIR}/libs/client/MockClientAPITest.cpp unit_tests.cpp) target_link_libraries(unittests diff --git a/unit_tests/unit_tests.cpp b/tests/unit_tests.cpp similarity index 100% rename from unit_tests/unit_tests.cpp rename to tests/unit_tests.cpp diff --git a/xroot_clients/CTACmd.cpp b/xroot_clients/CTACmd.cpp index eb1e47d7f88fd8250197b380fc5feb65e956f22a..7d9611cb5f8fd3684ab44673acecc9c31a60c974 100644 --- a/xroot_clients/CTACmd.cpp +++ b/xroot_clients/CTACmd.cpp @@ -20,12 +20,13 @@ void CTACmd::usage(std::ostream &os) const throw() { os << "Usage:\n" "\t" << m_programName << " archive <source_file1> [<source_file2> [<source_file3> [...]]] <destination_path>\n" - "\t" << m_programName << " create-storage-class <storage_class_name> <number_of_tape_copies>\n" - "\t" << m_programName << " change-storage-class <directory_name> <storage_class_name>\n" - "\t" << m_programName << " delete-storage-class <storage_class_name>\n" - "\t" << m_programName << " list-storage-class\n" + "\t" << m_programName << " mkclass <storage_class_name> <number_of_tape_copies>\n" + "\t" << m_programName << " chclass <directory_name> <storage_class_name>\n" + "\t" << m_programName << " rmclass <storage_class_name>\n" + "\t" << m_programName << " lsclass\n" "\t" << m_programName << " mkdir <directory_name>\n" - "\t" << m_programName << " rmdir <directory_name>\n"; + "\t" << m_programName << " rmdir <directory_name>\n" + "\t" << m_programName << " ls <directory_name>\n"; } //------------------------------------------------------------------------------ @@ -57,13 +58,14 @@ int CTACmd::executeCommand(const int argc, char **argv) { XrdCl::FileSystem fs(XrdCl::URL("localhost")); std::string queryString = "/"; queryString += argv[1]; - queryString += "?"; - for(int i=2; i<argc-1; i++) { - queryString += argv[i]; - queryString += "+"; - } - queryString += argv[argc-1]; - + queryString += "?"; + if(argc > 2) { + for(int i=2; i<argc-1; i++) { + queryString += argv[i]; + queryString += "+"; + } + queryString += argv[argc-1]; + } XrdCl::Buffer arg; arg.FromString(queryString.c_str()); XrdCl::Buffer* response = 0; diff --git a/xroot_plugins/CMakeLists.txt b/xroot_plugins/CMakeLists.txt index c33b24a1f9c5d119e6ae3d8f0801d2c3a1969bf0..c68aefd0003dec650b499938fa7d8a8c1aa68085 100644 --- a/xroot_plugins/CMakeLists.txt +++ b/xroot_plugins/CMakeLists.txt @@ -5,3 +5,4 @@ find_package (xrootd REQUIRED) include_directories (${XROOTD_INCLUDE_DIR} ${XROOTD_PRIVATE_INCLUDE_DIR} ${CMAKE_SOURCE_DIR}) add_library (XrdProFst MODULE XrdProFilesystem.cpp ParsedRequest.cpp) +target_link_libraries (XrdProFst ctaclient) \ No newline at end of file diff --git a/xroot_plugins/XrdProFilesystem.cpp b/xroot_plugins/XrdProFilesystem.cpp index 9453fc26eb82cd6a1032de546e7aea86a1099fd0..c183236aefb0649104dfba4c60fbced15518ea6c 100644 --- a/xroot_plugins/XrdProFilesystem.cpp +++ b/xroot_plugins/XrdProFilesystem.cpp @@ -3,6 +3,7 @@ #include "XrdOuc/XrdOucString.hh" #include "XrdSec/XrdSecEntity.hh" #include "XrdVersion.hh" +#include "libs/client/Exception.hpp" #include <iostream> #include <pwd.h> @@ -99,82 +100,186 @@ int XrdProFilesystem::executeArchiveCommand(ParsedRequest &req, XrdOucErrInfo &e eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Requested archival of the following files:\n"; - for(int i=0; i<req.args.size()-1; i++) { + try { + std::list<std::string> sourceFiles; + std::string destinationPath = req.args.at(req.args.size()-1); + for(int i=0; i<req.args.size()-1; i++) { + sourceFiles.push_back(req.args.at(i)); + } + cta::UserIdentity requester; + std::string jobID = m_clientAPI->archiveToTape(requester, sourceFiles, destinationPath); + std::string response = "[OK] Requested archival of the following files:\n"; + for(std::list<std::string>::iterator it = sourceFiles.begin(); it != sourceFiles.end(); it++) { + response += "[OK]\t"; + response += *it; + response += "\n"; + } + response += "[OK] To the following directory:\n"; response += "[OK]\t"; - response += req.args.at(i); - response += "\n"; - } - response += "[OK] To the following directory:\n"; - response += "[OK]\t"; - response += req.args.at(req.args.size()-1); - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + response += destinationPath; + response += "\n[OK] JobID: "; + response += jobID; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ // executeCreateStorageClassCommand //------------------------------------------------------------------------------ -int XrdProFilesystem::executeCreateStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { +int XrdProFilesystem::executeMkclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { if(req.args.size() != 2) { std::string response = "[ERROR] Wrong number of arguments provided"; eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Created storage class "; - response += req.args.at(0); - response += " with "; - response += req.args.at(1); - response += " tape copies"; - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + uint8_t numberOfCopies; + std::istringstream ss(req.args.at(1)); + ss >> numberOfCopies; + cta::UserIdentity requester; + m_clientAPI->createStorageClass(requester, req.args.at(0), numberOfCopies); + std::string response = "[OK] Created storage class "; + response += req.args.at(0); + response += " with "; + response += req.args.at(1); + response += " tape copies"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ // executeCreateStorageClassCommand //------------------------------------------------------------------------------ -int XrdProFilesystem::executeChangeStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { +int XrdProFilesystem::executeChclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { if(req.args.size() != 2) { std::string response = "[ERROR] Wrong number of arguments provided"; eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Changed storage class of directory "; - response += req.args.at(0); - response += " to "; - response += req.args.at(1); - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + std::string response = "[OK] Changed storage class of directory "; + response += req.args.at(0); + response += " to "; + response += req.args.at(1); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ // executeDeleteStorageClassCommand //------------------------------------------------------------------------------ -int XrdProFilesystem::executeDeleteStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { +int XrdProFilesystem::executeRmclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { if(req.args.size() != 1) { std::string response = "[ERROR] Wrong number of arguments provided"; eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Storage class "; - response += req.args.at(0); - response += " deleted"; - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + cta::UserIdentity requester; + m_clientAPI->deleteStorageClass(requester, req.args.at(0)); + std::string response = "[OK] Storage class "; + response += req.args.at(0); + response += " deleted"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ // executeListStorageClassCommand //------------------------------------------------------------------------------ -int XrdProFilesystem::executeListStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { +int XrdProFilesystem::executeLsclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { if(req.args.size() != 0) { std::string response = "[ERROR] Wrong number of arguments provided"; eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Requested listing of the storage classes"; - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + cta::UserIdentity requester; + std::list<cta::StorageClass> stgList = m_clientAPI->getStorageClasses(requester); + std::string response = "[OK] Listing of the storage class names and no of copies:"; + for(std::list<cta::StorageClass>::iterator it = stgList.begin(); it != stgList.end(); it++) { + response += "\n"; + response += it->name; + response += " "; + response += it->nbCopies; + } + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ @@ -186,11 +291,29 @@ int XrdProFilesystem::executeMkdirCommand(ParsedRequest &req, XrdOucErrInfo &eIn eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Directory "; - response += req.args.at(0); - response += " created"; - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + cta::UserIdentity requester; + m_clientAPI->createDirectory(requester, req.args.at(0)); + std::string response = "[OK] Directory "; + response += req.args.at(0); + response += " created"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ @@ -202,11 +325,84 @@ int XrdProFilesystem::executeRmdirCommand(ParsedRequest &req, XrdOucErrInfo &eIn eInfo.setErrInfo(response.length(), response.c_str()); return SFS_DATA; } - std::string response = "[OK] Directory "; - response += req.args.at(0); - response += " removed"; - eInfo.setErrInfo(response.length(), response.c_str()); - return SFS_DATA; + try { + std::string response = "[OK] Directory "; + response += req.args.at(0); + response += " removed"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } +} + +//------------------------------------------------------------------------------ +// executeLsCommand +//------------------------------------------------------------------------------ +int XrdProFilesystem::executeLsCommand(ParsedRequest &req, XrdOucErrInfo &eInfo) { + if(req.args.size() != 1) { + std::string response = "[ERROR] Wrong number of arguments provided"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } + try { + cta::UserIdentity requester; + std::string response; + cta::DirectoryIterator itor = m_clientAPI->getDirectoryContents(requester, req.args.at(0)); + while(itor.hasMore()) { + const cta::DirectoryEntry &entry = itor.next(); + response += "\n"; + switch(entry.entryType) { + case cta::DirectoryEntry::FILE_ENTRY: + response += "-"; + break; + case cta::DirectoryEntry::DIRECTORY_ENTRY: + response += "d"; + break; + case cta::DirectoryEntry::NONE: + default: + response += "n"; + break; + } + response += entry.ownerPerms; + response += entry.groupPerms; + response += entry.otherPerms; + response += " "; + response += entry.ownerId; + response += " "; + response += entry.groupId; + response += " "; + response += entry.name; + } + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (cta::Exception &ex) { + std::string response = "[ERROR] CTA exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (std::exception &ex) { + std::string response = "[ERROR] Exception caught: "; + response += ex.what(); + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } catch (...) { + std::string response = "[ERROR] Unknown exception caught!"; + eInfo.setErrInfo(response.length(), response.c_str()); + return SFS_DATA; + } } //------------------------------------------------------------------------------ @@ -222,21 +418,21 @@ int XrdProFilesystem::dispatchRequest(XrdSfsFSctl &args, XrdOucErrInfo &eInfo) { { return executeArchiveCommand(req, eInfo); } - else if(strcmp(req.cmd.c_str(), "/create-storage-class") == 0) + else if(strcmp(req.cmd.c_str(), "/mkclass") == 0) { - return executeCreateStorageClassCommand(req, eInfo); + return executeMkclassCommand(req, eInfo); } - else if(strcmp(req.cmd.c_str(), "/change-storage-class") == 0) + else if(strcmp(req.cmd.c_str(), "/chclass") == 0) { - return executeChangeStorageClassCommand(req, eInfo); + return executeChclassCommand(req, eInfo); } - else if(strcmp(req.cmd.c_str(), "/delete-storage-class") == 0) + else if(strcmp(req.cmd.c_str(), "/rmclass") == 0) { - return executeDeleteStorageClassCommand(req, eInfo); + return executeRmclassCommand(req, eInfo); } - else if(strcmp(req.cmd.c_str(), "/list-storage-class") == 0) + else if(strcmp(req.cmd.c_str(), "/lsclass") == 0) { - return executeListStorageClassCommand(req, eInfo); + return executeLsclassCommand(req, eInfo); } else if(strcmp(req.cmd.c_str(), "/mkdir") == 0) { @@ -245,6 +441,10 @@ int XrdProFilesystem::dispatchRequest(XrdSfsFSctl &args, XrdOucErrInfo &eInfo) { else if(strcmp(req.cmd.c_str(), "/rmdir") == 0) { return executeRmdirCommand(req, eInfo); + } + else if(strcmp(req.cmd.c_str(), "/ls") == 0) + { + return executeLsCommand(req, eInfo); } else { @@ -449,9 +649,13 @@ void XrdProFilesystem::EnvInfo(XrdOucEnv *envP) //------------------------------------------------------------------------------ // constructor //------------------------------------------------------------------------------ -XrdProFilesystem::XrdProFilesystem() {} +XrdProFilesystem::XrdProFilesystem() { + m_clientAPI = new cta::MockClientAPI(); +} //------------------------------------------------------------------------------ // destructor //------------------------------------------------------------------------------ -XrdProFilesystem::~XrdProFilesystem() {} \ No newline at end of file +XrdProFilesystem::~XrdProFilesystem() { + delete m_clientAPI; +} diff --git a/xroot_plugins/XrdProFilesystem.hpp b/xroot_plugins/XrdProFilesystem.hpp index d679c3e9327d99a5187c11bfd46f5a9a36979d1e..b45287319aebbf8563da6ffe9890762ca018604d 100644 --- a/xroot_plugins/XrdProFilesystem.hpp +++ b/xroot_plugins/XrdProFilesystem.hpp @@ -1,5 +1,6 @@ #pragma once +#include "libs/client/MockClientAPI.hpp" #include "XrdSfs/XrdSfsInterface.hh" #include "ParsedRequest.hpp" @@ -29,6 +30,11 @@ public: ~XrdProFilesystem(); protected: + + /** + * Pointer to the client API object + */ + cta::ClientAPI *m_clientAPI; /** * Parses the query into the request structure @@ -65,7 +71,7 @@ protected: * @param eInfo Error information * @return SFS_DATA */ - int executeCreateStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + int executeMkclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); /** * Executes the command contained within the request structure @@ -74,7 +80,7 @@ protected: * @param eInfo Error information * @return SFS_DATA */ - int executeChangeStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + int executeChclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); /** * Executes the command contained within the request structure @@ -83,7 +89,7 @@ protected: * @param eInfo Error information * @return SFS_DATA */ - int executeDeleteStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + int executeRmclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); /** * Executes the command contained within the request structure @@ -92,7 +98,7 @@ protected: * @param eInfo Error information * @return SFS_DATA */ - int executeListStorageClassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + int executeLsclassCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); /** * Executes the command contained within the request structure @@ -112,6 +118,15 @@ protected: */ int executeRmdirCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + /** + * Executes the command contained within the request structure + * + * @param req parsed request + * @param eInfo Error information + * @return SFS_DATA + */ + int executeLsCommand(ParsedRequest &req, XrdOucErrInfo &eInfo); + /** * Dispatches the request based on the query *