Commit 1ce732bd authored by Eric Cano's avatar Eric Cano
Browse files

Created a shared unit test for both objectstore backends.

Implemented the new interface on the Rados backend.
Fixed several bugs.
parent 7a1a334d
...@@ -97,6 +97,12 @@ public: ...@@ -97,6 +97,12 @@ public:
* @return pointer to the newly created representation. * @return pointer to the newly created representation.
*/ */
virtual Parameters * getParams() = 0; virtual Parameters * getParams() = 0;
/**
* Return the name of the class. Mostly usefull in tests
* @return name of the class
*/
virtual std::string typeName() = 0;
}; };
}} // end of cta::objectstore }} // end of cta::objectstore
......
#include "BackendAbstractTests.hpp"
#include "BackendVFS.hpp"
#include "BackendRados.hpp"
TEST_P(BackendAbstractTest, BasicReadWrite) {
std::cout << "Type=" << m_os->typeName() << std::endl;
const std::string testValue = "1234";
const std::string testSecondValue = "1234";
const std::string testObjectName = "testObject";
// Check we can verify the absence of an object
ASSERT_EQ(false, m_os->exists(testObjectName));
// Check that an update attempt fails on a non-existing object
ASSERT_THROW(m_os->atomicOverwrite(testObjectName, testSecondValue), cta::exception::Exception);
// Check the creation of the obecjt
m_os->create(testObjectName, testValue);
// Check that re-creating an existing object throws exception
ASSERT_THROW(m_os->create(testObjectName, testValue), cta::exception::Exception);
// Check we can validate the presence of the object
ASSERT_EQ(true, m_os->exists(testObjectName));
// Check that we can read back after creation
ASSERT_EQ(testValue, m_os->read(testObjectName));
m_os->atomicOverwrite(testObjectName, testSecondValue);
// Check that an update goes through
ASSERT_EQ(testSecondValue, m_os->read(testObjectName));
// Check that we read back the value
ASSERT_EQ(testSecondValue, m_os->read(testObjectName));
// Check we can delete the object
ASSERT_NO_THROW(m_os->remove(testObjectName));
// Check that the object is actually gone
ASSERT_EQ(false, m_os->exists(testObjectName));
}
cta::objectstore::BackendVFS osVFS;
cta::objectstore::BackendRados osRados("tapetest", "tapetest");
INSTANTIATE_TEST_CASE_P(BackendTest, BackendAbstractTest, ::testing::Values(&osVFS, &osRados));
\ No newline at end of file
#pragma once
#include <gtest/gtest.h>
#include "Backend.hpp"
class BackendAbstractTest: public ::testing::TestWithParam<cta::objectstore::Backend *> {
protected:
BackendAbstractTest() {}
virtual void SetUp() {
m_os = GetParam();
}
cta::objectstore::Backend * m_os;
};
#pragma once #pragma once
#include "Backend.hpp" #include "Backend.hpp"
#include "exception/Errnum.hpp"
#include <rados/librados.hpp>
#include <sys/syscall.h>
#include <errno.h>
namespace cta { namespace objectstore { namespace cta { namespace objectstore {
/**
class ObjectStoreRados: public Backend { * An implementation of the object store primitives, using Rados.
*/
class BackendRados: public Backend {
public: public:
ObjectStoreRados(std::string path, std::string userId, std::string pool): /**
* The constructor, connecting to the storage pool 'pool' using the user id
* 'userId'
* @param userId
* @param pool
*/
BackendRados(std::string userId, std::string pool):
m_user(userId), m_pool(pool), m_cluster(), m_radosCtx() { m_user(userId), m_pool(pool), m_cluster(), m_radosCtx() {
cta::exception::Errnum::throwOnNonZero(m_cluster.init(userId.c_str()), cta::exception::Errnum::throwOnNonZero(m_cluster.init(userId.c_str()),
"In ObjectStoreRados::ObjectStoreRados, failed to m_cluster.init"); "In ObjectStoreRados::ObjectStoreRados, failed to m_cluster.init");
...@@ -24,7 +36,7 @@ public: ...@@ -24,7 +36,7 @@ public:
throw; throw;
} }
} }
virtual ~ObjectStoreRados() { virtual ~BackendRados() {
m_radosCtx.close(); m_radosCtx.close();
m_cluster.shutdown(); m_cluster.shutdown();
} }
...@@ -37,14 +49,25 @@ public: ...@@ -37,14 +49,25 @@ public:
virtual void create(std::string name, std::string content) { virtual void create(std::string name, std::string content) {
atomicOverwrite(name, content); librados::ObjectWriteOperation wop;
const bool createExclusive = true;
wop.create(createExclusive);
ceph::bufferlist bl;
bl.append(content.c_str(), content.size());
wop.write_full(bl);
cta::exception::Errnum::throwOnNonZero(m_radosCtx.operate(name, &wop),
std::string("In ObjectStoreRados::create, failed to create exclusively or write ")
+ name);
} }
virtual void atomicOverwrite(std::string name, std::string content) { virtual void atomicOverwrite(std::string name, std::string content) {
librados::ObjectWriteOperation wop;
wop.assert_exists();
ceph::bufferlist bl; ceph::bufferlist bl;
bl.append(content.c_str(), content.size()); bl.append(content.c_str(), content.size());
cta::exception::Errnum::throwOnNonZero(m_radosCtx.write_full(name, bl), wop.write_full(bl);
std::string("In ObjectStoreRados::atomicOverwrite, failed to write_full ") cta::exception::Errnum::throwOnNonZero(m_radosCtx.operate(name, &wop),
std::string("In ObjectStoreRados::atomicOverwrite, failed to assert existence or write ")
+ name); + name);
} }
...@@ -67,7 +90,43 @@ public: ...@@ -67,7 +90,43 @@ public:
cta::exception::Errnum::throwOnNegative(m_radosCtx.remove(name)); cta::exception::Errnum::throwOnNegative(m_radosCtx.remove(name));
} }
virtual void lockExclusive(std::string name, ContextHandle & context, std::string where) { virtual bool exists(std::string name) {
uint64_t size;
time_t date;
if (m_radosCtx.stat(name, &size, &date)) {
return false;
} else {
return true;
}
}
class ScopedLock: public Backend::ScopedLock {
friend class BackendRados;
public:
virtual void release() {
if (!m_lockSet) return;
cta::exception::Errnum::throwOnReturnedErrno(
-m_context.unlock(m_oid, "lock", m_clientId),
std::string("In cta::objectstore::ScopedLock::release, failed unlock ")+
m_oid);
m_lockSet = false;
}
virtual ~ScopedLock() { release(); }
private:
ScopedLock(librados::IoCtx & ioCtx): m_lockSet(false), m_context(ioCtx) {}
void set(const std::string & oid, const std::string clientId) {
m_oid = oid;
m_clientId = clientId;\
m_lockSet = true;
}
bool m_lockSet;
librados::IoCtx & m_context;
std::string m_clientId;
std::string m_oid;
};
private:
std::string createUniqueClientId() {
// Build a unique client name: host:thread // Build a unique client name: host:thread
char buff[200]; char buff[200];
cta::exception::Errnum::throwOnMinusOne(gethostname(buff, sizeof(buff)), cta::exception::Errnum::throwOnMinusOne(gethostname(buff, sizeof(buff)),
...@@ -75,60 +134,73 @@ public: ...@@ -75,60 +134,73 @@ public:
pid_t tid = syscall(SYS_gettid); pid_t tid = syscall(SYS_gettid);
std::stringstream client; std::stringstream client;
client << buff << ":" << tid; client << buff << ":" << tid;
return client.str();
}
public:
virtual ScopedLock * lockExclusive(std::string name) {
std::string client = createUniqueClientId();
struct timeval tv; struct timeval tv;
tv.tv_usec = 0; tv.tv_usec = 0;
tv.tv_sec = 10; tv.tv_sec = 10;
int rc; int rc;
std::auto_ptr<ScopedLock> ret(new ScopedLock(m_radosCtx));
do { do {
rc=m_radosCtx.lock_exclusive(name, "lock", client.str(), "", &tv, 0); rc=m_radosCtx.lock_exclusive(name, "lock", client, "", &tv, 0);
} while (-EBUSY==rc); } while (-EBUSY==rc);
cta::exception::Errnum::throwOnReturnedErrno(-rc, cta::exception::Errnum::throwOnReturnedErrno(-rc,
std::string("In ObjectStoreRados::lockExclusive, failed to librados::IoCtx::lock_exclusive: ")+ std::string("In ObjectStoreRados::lockExclusive, failed to librados::IoCtx::lock_exclusive: ")+
name + "/" + "lock" + "/" + client.str() + "//"); name + "/" + "lock" + "/" + client + "//");
context.set(); ret->set(name, client);
//std::cout << "LockedExclusive: " << name << "/" << "lock" << "/" << client.str() << "//@" << where << std::endl; return ret.release();
} }
virtual void lockShared(std::string name, ContextHandle & context, std::string where) { virtual ScopedLock * lockShared(std::string name) {
// Build a unique client name: host:thread std::string client = createUniqueClientId();
char buff[200];
cta::exception::Errnum::throwOnMinusOne(gethostname(buff, sizeof(buff)),
"In ObjectStoreRados::lockExclusive: failed to gethostname");
pid_t tid = syscall(SYS_gettid);
std::stringstream client;
client << buff << ":" << tid;
struct timeval tv; struct timeval tv;
tv.tv_usec = 0; tv.tv_usec = 0;
tv.tv_sec = 10; tv.tv_sec = 10;
int rc; int rc;
std::auto_ptr<ScopedLock> ret(new ScopedLock(m_radosCtx));
do { do {
rc=m_radosCtx.lock_shared(name, "lock", client.str(), "", "", &tv, 0); rc=m_radosCtx.lock_shared(name, "lock", client, "", "", &tv, 0);
} while (-EBUSY==rc); } while (-EBUSY==rc);
cta::exception::Errnum::throwOnReturnedErrno(-rc, cta::exception::Errnum::throwOnReturnedErrno(-rc,
std::string("In ObjectStoreRados::lockShared, failed to librados::IoCtx::lock_shared: ")+ std::string("In ObjectStoreRados::lockShared, failed to librados::IoCtx::lock_shared: ")+
name + "/" + "lock" + "/" + client.str() + "//"); name + "/" + "lock" + "/" + client + "//");
context.set(); ret->set(name, client);
//std::cout << "LockedShared: " << name << "/" << "lock" << "/" << client.str() << "//@" << where << std::endl; return ret.release();
} }
virtual void unlock(std::string name, ContextHandle & context, std::string where) { class Parameters: public Backend::Parameters {
// Build a unique client name: host:thread friend class BackendRados;
char buff[200]; public:
cta::exception::Errnum::throwOnMinusOne(gethostname(buff, sizeof(buff)), /**
"In ObjectStoreRados::lockExclusive: failed to gethostname"); * The standard-issue params to string for logging
pid_t tid = syscall(SYS_gettid); * @return a string representation of the parameters for logging
std::stringstream client; */
client << buff << ":" << tid; virtual std::string toStr() {
cta::exception::Errnum::throwOnReturnedErrno( std::stringstream ret;
-m_radosCtx.unlock(name, "lock", client.str()), ret << "userId=" << m_userId << " pool=" << m_pool;
std::string("In ObjectStoreRados::lockExclusive, failed to lock_exclusive ")+ return ret.str();
name); }
context.reset(); private:
//std::cout << "Unlocked: " << name << "/" << "lock" << "/" << client.str() << "//@" << where << std::endl; std::string m_userId;
std::string m_pool;
};
virtual Parameters * getParams() {
std::auto_ptr<Parameters> ret (new Parameters);
ret->m_pool = m_pool;
ret->m_userId = m_user;
return ret.release();
} }
virtual std::string typeName() {
return "cta::objectstore::BackendRados";
}
private: private:
std::string m_user; std::string m_user;
std::string m_pool; std::string m_pool;
......
...@@ -61,25 +61,29 @@ BackendVFS::~BackendVFS() { ...@@ -61,25 +61,29 @@ BackendVFS::~BackendVFS() {
void BackendVFS::create(std::string name, std::string content) { void BackendVFS::create(std::string name, std::string content) {
std::string path = m_root + "/" + name; std::string path = m_root + "/" + name;
std::string lockPath = m_root + "/." + name + ".lock"; std::string lockPath = m_root + "/." + name + ".lock";
bool fileCreated = false;
bool lockCreated = false;
try { try {
int fd = ::creat(path.c_str(), S_IRWXU); int fd = ::open(path.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
// Create and fill up the path // Create and fill up the path
cta::exception::Errnum::throwOnMinusOne(fd, cta::exception::Errnum::throwOnMinusOne(fd,
"In ObjectStoreVFS::create, failed to creat the file"); "In ObjectStoreVFS::create, failed to open the file");
fileCreated = true;
cta::exception::Errnum::throwOnMinusOne( cta::exception::Errnum::throwOnMinusOne(
::write(fd, content.c_str(), content.size()), ::write(fd, content.c_str(), content.size()),
"In ObjectStoreVFS::create, failed to write to file"); "In ObjectStoreVFS::create, failed to write to file");
cta::exception::Errnum::throwOnMinusOne(::close(fd), cta::exception::Errnum::throwOnMinusOne(::close(fd),
"In ObjectStoreVFS::create, failed to close the file"); "In ObjectStoreVFS::create, failed to close the file");
// Create the lock file // Create the lock file
int fdLock = ::creat(lockPath.c_str(), S_IRWXU); int fdLock = ::open(lockPath.c_str(), O_WRONLY | O_CREAT | O_EXCL, S_IRWXU);
lockCreated = true;
cta::exception::Errnum::throwOnMinusOne(fdLock, cta::exception::Errnum::throwOnMinusOne(fdLock,
"In ObjectStoreVFS::create, failed to creat the lock file"); "In ObjectStoreVFS::create, failed to creat the lock file");
cta::exception::Errnum::throwOnMinusOne(::close(fdLock), cta::exception::Errnum::throwOnMinusOne(::close(fdLock),
"In ObjectStoreVFS::create, failed to close the lock file"); "In ObjectStoreVFS::create, failed to close the lock file");
} catch (...) { } catch (...) {
unlink(path.c_str()); if (fileCreated) unlink(path.c_str());
unlink(lockPath.c_str()); if (lockCreated) unlink(lockPath.c_str());
throw; throw;
} }
} }
......
...@@ -87,6 +87,12 @@ public: ...@@ -87,6 +87,12 @@ public:
}; };
virtual Parameters * getParams(); virtual Parameters * getParams();
virtual std::string typeName() {
return "cta::objectstore::BackendVFS";
}
private: private:
std::string m_root; std::string m_root;
......
...@@ -49,6 +49,11 @@ add_library (CTAObjectStore ...@@ -49,6 +49,11 @@ add_library (CTAObjectStore
# target_link_libraries(createEnvironment # target_link_libraries(createEnvironment
# protobuf rados CTAObjectStore) # protobuf rados CTAObjectStore)
add_executable(unitTests unitTests.cpp BackendVFSTest.cpp) set(ObjectStoreUnitTests
BackendVFSTest.cpp
BackendAbstractTests.cpp)
# RootEntryTest.cpp)
add_executable(unitTests unitTests.cpp ${ObjectStoreUnitTests})
target_link_libraries(unitTests target_link_libraries(unitTests
protobuf rados CTAObjectStore gtest gmock) protobuf rados CTAObjectStore gtest gmock)
\ No newline at end of file
#pragma once #pragma once
#include "BackendStore.hpp.hpp" #include "Backend.hpp"
namespace cta { namespace objectstore { namespace cta { namespace objectstore {
......
...@@ -3,7 +3,7 @@ ...@@ -3,7 +3,7 @@
#include "ObjectOps.hpp" #include "ObjectOps.hpp"
#define USE_RADOS 1 #define USE_RADOS 1
#if USE_RADOS #if USE_RADOS
typedef cta::objectstore::ObjectStoreRados myOS; typedef cta::objectstore::BackendRados myOS;
#else #else
typedef cta::objectstore::BackendVFS myOS; typedef cta::objectstore::BackendVFS myOS;
#endif #endif
\ No newline at end of file
...@@ -2,7 +2,7 @@ ...@@ -2,7 +2,7 @@
#include "objectstore/cta.pb.h" #include "objectstore/cta.pb.h"
#include "BackendStore.hpp.hpp" #include "Backend.hpp"
#include "ObjectOps.hpp" #include "ObjectOps.hpp"
#include "Agent.hpp" #include "Agent.hpp"
......
...@@ -3,6 +3,6 @@ ...@@ -3,6 +3,6 @@
#include "exception/Exception.hpp" #include "exception/Exception.hpp"
#include "RootEntry.hpp" #include "RootEntry.hpp"
TEST(RootEntry.BasicAccess) { TEST(RootEntry, BasicAccess) {
} };
\ No newline at end of file \ No newline at end of file
Supports Markdown
0% or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment