Imported Upstream version 1.5.1

This commit is contained in:
Mario Fetka
2020-09-22 02:25:22 +02:00
commit 434d6067d9
2103 changed files with 928962 additions and 0 deletions

View File

@@ -0,0 +1,249 @@
/*
* Copyright (c) 2012 by Matthias Noack, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <algorithm>
#include <vector>
#include "common/test_environment.h"
#include "common/test_rpc_server_dir.h"
#include "common/test_rpc_server_mrc.h"
#include "common/test_rpc_server_osd.h"
#include "libxtreemfs/client.h"
#include "libxtreemfs/file_handle.h"
#include "libxtreemfs/options.h"
#include "libxtreemfs/volume.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "rpc/client.h"
#include "xtreemfs/OSDServiceConstants.h"
using namespace std;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
namespace xtreemfs {
namespace rpc {
class AsyncWriteHandlerTest : public ::testing::Test {
protected:
static const int kBlockSize = 1024 * 128;
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
test_env.options.connect_timeout_s = 3;
test_env.options.request_timeout_s = 3;
test_env.options.retry_delay_s = 3;
test_env.options.enable_async_writes = true;
test_env.options.async_writes_max_request_size_kb = 128;
test_env.options.async_writes_max_requests = 8;
test_env.options.periodic_xcap_renewal_interval_s = 2;
ASSERT_TRUE(test_env.Start());
// Open a volume
volume = test_env.client->OpenVolume(
test_env.volume_name_,
NULL, // No SSL options.
test_env.options);
// Open a file.
file = volume->OpenFile(
test_env.user_credentials,
"/test_file",
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));
}
virtual void TearDown() {
//volume->Close();
test_env.Stop();
}
TestEnvironment test_env;
Volume* volume;
FileHandle* file;
};
/** A normal async write with nothing special */
TEST_F(AsyncWriteHandlerTest, NormalWrite) {
size_t blocks = 5;
size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected(blocks);
for (size_t i = 0; i < blocks; ++i) {
expected[i] = WriteEntry(i, 0, kBlockSize);
}
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected.begin(),
expected.end(),
test_env.osds[0]->GetReceivedWrites().end() - blocks));
ASSERT_NO_THROW(file->Close());
}
/** Let the first write request fail. The write should be retried and finally
* succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFail) {
size_t blocks = 5;
size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected_tail(blocks);
for (size_t i = 0; i < blocks; ++i) {
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
}
test_env.osds[0]->AddDropRule(
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected_tail.begin(), expected_tail.end(),
test_env.osds[0]->GetReceivedWrites().end() - expected_tail.size()));
ASSERT_NO_THROW(file->Close());
}
/** Let the last write request fail. The write should be retried and finally
* succeed. */
TEST_F(AsyncWriteHandlerTest, LastWriteFail) {
size_t blocks = 5;
size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected_front(blocks - 1);
vector<WriteEntry> expected_tail(1);
for (size_t i = 0; i < blocks - 1; ++i) {
expected_front[i] = WriteEntry(i, 0, kBlockSize);
}
expected_tail[0] = WriteEntry(blocks - 1, 0, kBlockSize);
test_env.osds[0]->AddDropRule(
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
new SkipMDropNRule(blocks - 1, 1)));
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected_front.begin(),
expected_front.end(),
test_env.osds[0]->GetReceivedWrites().begin()));
EXPECT_TRUE(equal(expected_tail.begin(),
expected_tail.end(),
test_env.osds[0]->GetReceivedWrites().end() -
expected_tail.size()));
ASSERT_NO_THROW(file->Close());
}
/** Let the intermediate write request fail. The write should be retried and
* finally succeed. */
TEST_F(AsyncWriteHandlerTest, IntermediateWriteFail) {
size_t blocks = 5;
size_t buffer_size = kBlockSize * blocks;
size_t middle = blocks / 2;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected_front(middle);
vector<WriteEntry> expected_tail(blocks - middle);
for (size_t i = 0; i < middle; ++i) {
expected_front[i] = WriteEntry(i, 0, kBlockSize);
}
for (size_t i = middle; i < blocks; ++i) {
expected_tail[i - middle] = WriteEntry(i, 0, kBlockSize);
}
test_env.osds[0]->AddDropRule(
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
new SkipMDropNRule(middle, 1)));
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected_front.begin(),
expected_front.end(),
test_env.osds[0]->GetReceivedWrites().begin()));
EXPECT_TRUE(equal(expected_tail.begin(),
expected_tail.end(),
test_env.osds[0]->GetReceivedWrites().end() -
expected_tail.size()));
ASSERT_NO_THROW(file->Close());
}
/** Let the all writes request fail. The write should be retried and finally
* succeed. */
TEST_F(AsyncWriteHandlerTest, AllWritesFail) {
size_t blocks = 5;
size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected_tail(blocks);
for (size_t i = 0; i < blocks; ++i) {
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
}
test_env.osds[0]->AddDropRule(
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE,
new DropNRule(blocks)));
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected_tail.begin(),
expected_tail.end(),
test_env.osds[0]->GetReceivedWrites().end() -
expected_tail.size()));
ASSERT_NO_THROW(file->Close());
}
/** Let the first write request fail when there are more writes than
* writeahead allows. The write should be retried and finally succeed. */
TEST_F(AsyncWriteHandlerTest, FirstWriteFailLong) {
size_t blocks = 2 * test_env.options.async_writes_max_requests;
size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]());
vector<WriteEntry> expected_tail(blocks);
for (size_t i = 0; i < blocks; ++i) {
expected_tail[i] = WriteEntry(i, 0, kBlockSize);
}
test_env.osds[0]->AddDropRule(
new ProcIDFilterRule(xtreemfs::pbrpc::PROC_ID_WRITE, new DropNRule(1)));
ASSERT_NO_THROW(file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Flush());
EXPECT_TRUE(equal(expected_tail.begin(),
expected_tail.end(),
test_env.osds[0]->GetReceivedWrites().end() -
expected_tail.size()));
ASSERT_NO_THROW(file->Close());
}
/** TODO(mno): Maybe let all future requests fail to test the retry count. */
} // namespace rpc
} // namespace xtreemfs

View File

@@ -0,0 +1,180 @@
/*
* Copyright (c) 2014 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
#include <map>
#include "common/test_rpc_server_dir.h"
#include "libxtreemfs/client.h"
#include "libxtreemfs/helper.h"
#include "libxtreemfs/options.h"
#include "libxtreemfs/pbrpc_url.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "xtreemfs/DIR.pb.h"
using namespace std;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
namespace xtreemfs {
class ClientImplementationTest : public ::testing::Test {
protected:
/** Mock-up DIR server where custom address mappings can be registered. */
class TestRPCServerDIRCustomMapping : public xtreemfs::rpc::TestRPCServerDIR {
public:
void AddMapping(const string& uuid, const AddressMapping& mapping) {
mappings_.insert(pair<string, AddressMapping>(uuid, mapping));
}
private:
/** Map from UUID to AddressMapping(s). */
std::multimap<string, AddressMapping> mappings_;
virtual google::protobuf::Message* GetAddressMappingOperation(
const pbrpc::Auth& auth,
const pbrpc::UserCredentials& user_credentials,
const google::protobuf::Message& request,
const char* data,
uint32_t data_len,
boost::scoped_array<char>* response_data,
uint32_t* response_data_len) {
const addressMappingGetRequest* rq
= reinterpret_cast<const addressMappingGetRequest*>(&request);
AddressMappingSet* response = new AddressMappingSet();
pair<multimap<string,AddressMapping>::iterator,
multimap<string,AddressMapping>::iterator> ret;
ret = mappings_.equal_range(rq->uuid());
for (multimap<string,AddressMapping>::iterator it = ret.first;
it != ret.second;
++it) {
AddressMapping* mapping = response->add_mappings();
mapping->CopyFrom(it->second);
}
return response;
}
};
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
kTestUUID_ = "uuid";
kTestPort_ = 12345;
user_credentials_.set_username("ClientImplementationTest");
user_credentials_.add_groups("ClientImplementationTest");
dir_.reset(new TestRPCServerDIRCustomMapping());
ASSERT_TRUE(dir_->Start());
client_.reset(Client::CreateClient(
dir_->GetAddress(),
user_credentials_,
NULL, // No SSL options.
options_));
// Start the client (a connection to the DIR service will be setup).
client_->Start();
}
virtual void TearDown() {
if (dir_.get()) {
client_->Shutdown();
}
if (dir_.get()) {
dir_->Stop();
}
}
void AddAddressMapping(const std::string& match_network,
const std::string& hostname) {
AddressMapping mapping;
mapping.set_uuid(kTestUUID_);
mapping.set_version(0);
mapping.set_protocol(PBRPCURL::GetSchemePBRPC());
mapping.set_address(hostname);
mapping.set_port(kTestPort_);
mapping.set_match_network(match_network);
mapping.set_ttl_s(3600);
mapping.set_uri("");
dir_->AddMapping(kTestUUID_, mapping);
}
std::string ExpectedAddress(const std::string& hostname) {
return hostname + ":" + boost::lexical_cast<string>(kTestPort_);
}
boost::scoped_ptr<TestRPCServerDIRCustomMapping> dir_;
boost::scoped_ptr<Client> client_;
Options options_;
UserCredentials user_credentials_;
/** Test constant used for all operations. */
std::string kTestUUID_;
int kTestPort_;
};
/** For the requested UUID is exactly one default entry available. */
TEST_F(ClientImplementationTest, UUIDToAddressDefaultOnly) {
AddAddressMapping("*", "default");
EXPECT_EQ(ExpectedAddress("default"), client_->UUIDToAddress(kTestUUID_));
}
/** For the requested UUID is no address known. */
TEST_F(ClientImplementationTest, UUIDToAddressNoService) {
AddAddressMapping("*", "default");
ASSERT_THROW(client_->UUIDToAddress("unknown-UUID"),
AddressToUUIDNotFoundException);
}
/** For the requested UUID is a local network and a default one available. */
#ifdef __linux__
TEST_F(ClientImplementationTest, UUIDToAddressLocalNetworkAndDefault) {
boost::unordered_set<string> local_networks = GetNetworks();
// TODO(mberlin): This effectively requires to run the unit test with at
// least one network interface. Put an if around this if this
// results into flaky tests.
ASSERT_GE(local_networks.size(), 1);
AddAddressMapping(*local_networks.begin(), "local-network");
AddAddressMapping("*", "default");
EXPECT_EQ(ExpectedAddress("local-network"),
client_->UUIDToAddress(kTestUUID_));
}
#endif // __linux__
/** Same as "UUIDToAddressLocalNetworkAndDefault", but reverse list order. */
#ifdef __linux__
TEST_F(ClientImplementationTest, UUIDToAddressDefaultAndLocalNetwork) {
boost::unordered_set<string> local_networks = GetNetworks();
// TODO(mberlin): This effectively requires to run the unit test with at
// least one network interface. Put an if around this if this
// results into flaky tests.
ASSERT_GE(local_networks.size(), 1);
AddAddressMapping("*", "default");
AddAddressMapping(*local_networks.begin(), "local-network");
EXPECT_EQ(ExpectedAddress("local-network"),
client_->UUIDToAddress(kTestUUID_));
}
#endif // __linux__
} // namespace xtreemfs

View File

@@ -0,0 +1,92 @@
/*
* Copyright (c) 2014 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <arpa/inet.h>
#include <ifaddrs.h>
#include <netdb.h>
#include <sys/socket.h>
#include <google/protobuf/stubs/common.h>
#include "libxtreemfs/helper.h"
#include "util/logging.h"
using namespace std;
using namespace xtreemfs::util;
namespace xtreemfs {
/** Tests for various helper methods. */
class HelperTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
}
virtual void TearDown() {
shutdown_logger();
atexit(google::protobuf::ShutdownProtobufLibrary);
}
};
#ifdef __linux__
TEST_F(HelperTest, GetNetworkStringUnixIPv4) {
// Linux's "getifaddrs" returns for each network interface a "struct ifaddrs".
// Check that the network is correctly determined based on such a struct.
struct ifaddrs ifaddr = {};
struct addrinfo hints = {};
hints.ai_family = AF_INET;
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo* ai_addr;
ASSERT_EQ(0, getaddrinfo("127.0.0.1", NULL, &hints, &ai_addr));
struct addrinfo* ai_netmask;
ASSERT_EQ(0, getaddrinfo("255.0.0.0", NULL, &hints, &ai_netmask));
ifaddr.ifa_next = NULL;
ifaddr.ifa_name = (char*) "eth0";
ifaddr.ifa_addr = ai_addr->ai_addr;
ifaddr.ifa_netmask = ai_netmask->ai_addr;
EXPECT_EQ("127.0.0.0/8", GetNetworkStringUnix(&ifaddr));
freeaddrinfo(ai_addr);
freeaddrinfo(ai_netmask);
}
#endif // __linux__
#ifdef __linux__
TEST_F(HelperTest, GetNetworkStringUnixIPv6) {
// Linux's "getifaddrs" returns for each network interface a "struct ifaddrs".
// Check that the network is correctly determined based on such a struct.
struct ifaddrs ifaddr = {};
struct addrinfo hints = {};
hints.ai_family = AF_INET6;
hints.ai_flags = AI_NUMERICHOST;
struct addrinfo* ai_addr;
ASSERT_EQ(0, getaddrinfo("fe80::b4ff:fe58:a410", NULL, &hints, &ai_addr));
struct addrinfo* ai_netmask;
ASSERT_EQ(0, getaddrinfo("ffff:ffff:ffff:ffff::", NULL, &hints, &ai_netmask));
ifaddr.ifa_next = NULL;
ifaddr.ifa_name = (char*) "eth0";
ifaddr.ifa_addr = ai_addr->ai_addr;
ifaddr.ifa_netmask = ai_netmask->ai_addr;
EXPECT_EQ("fe80::/64", GetNetworkStringUnix(&ifaddr));
freeaddrinfo(ai_addr);
freeaddrinfo(ai_netmask);
}
#endif // __linux__
} // namespace xtreemfs

View File

@@ -0,0 +1,335 @@
/*
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <stdint.h>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <string>
#include "libxtreemfs/metadata_cache.h"
#include "libxtreemfs/helper.h"
#include "util/logging.h"
#include "xtreemfs/MRC.pb.h"
using namespace std;
using namespace xtreemfs;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
class MetadataCacheTestSize2 : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
metadata_cache_ = new MetadataCache(2, 3600); // Max 2 entries, 1 hour.
}
virtual void TearDown() {
delete metadata_cache_;
google::protobuf::ShutdownProtobufLibrary();
shutdown_logger();
}
MetadataCache* metadata_cache_;
};
class MetadataCacheTestSize1024 : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
metadata_cache_ = new MetadataCache(1024, 3600); // Max 1k entries, 1 hour.
}
virtual void TearDown() {
delete metadata_cache_;
google::protobuf::ShutdownProtobufLibrary();
shutdown_logger();
}
MetadataCache* metadata_cache_;
};
/** If a Stat entry gets updated through UpdateStatTime(), the new timeout must
* be respected in case of an eviction. */
TEST_F(MetadataCacheTestSize2, UpdateStatTimeKeepsSequentialTimeoutOrder) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
metadata_cache_->UpdateStat("/a", a);
metadata_cache_->UpdateStat("/b", b);
// Cache is full now. a would be first item to get evicted.
metadata_cache_->UpdateStatTime("/a", 0, SETATTR_MTIME);
// "b" should be the oldest Stat element now and get evicted.
metadata_cache_->UpdateStat("/c", c);
// Was "a" found or did "b" survive?
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
EXPECT_EQ(0, a.ino());
// "c" is also still there.
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
EXPECT_EQ(2, c.ino());
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize2, UpdateStatKeepsSequentialTimeoutOrder) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
metadata_cache_->UpdateStat("/a", a);
metadata_cache_->UpdateStat("/b", b);
// Cache is full now. a would be first item to get evicted.
metadata_cache_->UpdateStat("/a", a);
// "b" should be the oldest Stat element now and get evicted.
metadata_cache_->UpdateStat("/c", c);
// Was "a" found or did "b" survive?
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/a", &a));
EXPECT_EQ(0, a.ino());
// "c" is also still there.
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat("/c", &c));
EXPECT_EQ(2, c.ino());
}
/** Test if Size is updated correctly after UpdateStat() or Invalidate(). */
TEST_F(MetadataCacheTestSize2, CheckSizeAfterUpdateAndInvalidate) {
Stat a, b, c;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
EXPECT_EQ(0, metadata_cache_->Size());
metadata_cache_->UpdateStat("/a", a);
EXPECT_EQ(1, metadata_cache_->Size());
metadata_cache_->UpdateStat("/b", b);
EXPECT_EQ(2, metadata_cache_->Size());
metadata_cache_->UpdateStat("/c", b);
// Cache has only room for two entries.
EXPECT_EQ(2, metadata_cache_->Size());
metadata_cache_->Invalidate("/b");
EXPECT_EQ(1, metadata_cache_->Size());
metadata_cache_->Invalidate("/c");
EXPECT_EQ(0, metadata_cache_->Size());
}
/** Test (Get|Update)DirEntries methods. */
TEST_F(MetadataCacheTestSize2, UpdateAndGetDirEntries) {
DirectoryEntries dir_entries;
int chunk_size = 1024;
int entry_count = chunk_size;
string dir = "/";
// Fill dir_entries;
for (int i = 0; i < entry_count; i++) {
Stat a;
a.set_ino(i);
dir_entries.add_entries();
dir_entries.mutable_entries(dir_entries.entries_size()-1)
->mutable_stbuf()->CopyFrom(a);
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
dir_entries.mutable_entries(dir_entries.entries_size()-1)
->set_name(path_to_stat);
}
metadata_cache_->UpdateDirEntries(dir, dir_entries);
boost::scoped_ptr<DirectoryEntries> dir_entries_read;
// Read all entries.
dir_entries_read.reset(metadata_cache_->GetDirEntries(dir, 0, entry_count));
EXPECT_EQ(entry_count, dir_entries_read->entries_size());
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
string path_to_stat = dir + boost::lexical_cast<std::string>(i);
EXPECT_EQ(i, dir_entries_read->entries(i).stbuf().ino());
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
}
// Read a subset.
dir_entries_read.reset(metadata_cache_
->GetDirEntries(dir, entry_count/2, entry_count/2-1));
EXPECT_EQ(entry_count/2-1, dir_entries_read->entries_size());
int offset = entry_count/2;
for (int i = 0; i < dir_entries_read->entries_size(); i++) {
string path_to_stat = dir + boost::lexical_cast<std::string>(offset);
EXPECT_EQ(path_to_stat, dir_entries_read->entries(i).name());
EXPECT_EQ(offset, dir_entries_read->entries(i).stbuf().ino());
offset++;
}
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize1024, InvalidatePrefix) {
Stat a, b, c, d;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
InitializeStat(&d);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
d.set_ino(3);
metadata_cache_->UpdateStat("/dir", a);
metadata_cache_->UpdateStat("/dir/file1", b);
metadata_cache_->UpdateStat("/dir.file1", c);
metadata_cache_->UpdateStat("/dirZfile1", d);
metadata_cache_->InvalidatePrefix("/dir");
// Invalidate of all matching entries successful?
EXPECT_EQ(MetadataCache::kStatNotCached,
metadata_cache_->GetStat("/dir", &a));
EXPECT_EQ(MetadataCache::kStatNotCached,
metadata_cache_->GetStat("/dir/file1", &b));
// Similiar entries which do not match the prefix "/dir/" have not been
// invalidated.
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dir.file1", &c));
EXPECT_EQ(2, c.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dirZfile1", &d));
EXPECT_EQ(3, d.ino());
}
/** If a Stat entry gets updated through UpdateStat(), the new timeout must be
* respected in case of an eviction. */
TEST_F(MetadataCacheTestSize1024, RenamePrefix) {
Stat a, b, c, d;
InitializeStat(&a);
InitializeStat(&b);
InitializeStat(&c);
InitializeStat(&d);
a.set_ino(0);
b.set_ino(1);
c.set_ino(2);
d.set_ino(3);
metadata_cache_->UpdateStat("/dir", a);
metadata_cache_->UpdateStat("/dir/file1", b);
metadata_cache_->UpdateStat("/dir.file1", c);
metadata_cache_->UpdateStat("/dirZfile1", d);
EXPECT_EQ(4, metadata_cache_->Size());
metadata_cache_->RenamePrefix("/dir", "/newdir");
EXPECT_EQ(4, metadata_cache_->Size());
// Rename of all matching entries successful?
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/newdir", &a));
EXPECT_EQ(0, a.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/newdir/file1", &b));
EXPECT_EQ(1, b.ino());
// Similiar entries which do not match the prefix "/dir/" have not been
// renamed.
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dir.file1", &c));
EXPECT_EQ(2, c.ino());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat("/dirZfile1", &d));
EXPECT_EQ(3, d.ino());
}
/** Are large nanoseconds values correctly updated by
* UpdateStatAttributes? */
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributes) {
string path = "/file";
Stat stat, update_stat;
InitializeStat(&stat);
InitializeStat(&update_stat);
stat.set_ino(0);
update_stat.set_ino(1);
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
EXPECT_EQ(0, stat.ino());
EXPECT_EQ(0, stat.mtime_ns());
uint64_t time = 1234567890;
time *= 1000000000;
update_stat.set_atime_ns(time);
update_stat.set_mtime_ns(time);
metadata_cache_->UpdateStatAttributes(
path,
update_stat,
static_cast<Setattrs>(SETATTR_ATIME | SETATTR_MTIME));
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached, metadata_cache_->GetStat(path, &stat));
EXPECT_EQ(0, stat.ino());
EXPECT_EQ(time, stat.atime_ns());
EXPECT_EQ(time, stat.mtime_ns());
}
/** Changing the file access mode may only modify the last 12 bits (3 bits for
* sticky bit, set GID and set UID and 3 * 3 bits for the file access mode). */
TEST_F(MetadataCacheTestSize1024, UpdateStatAttributesPreservesModeBits) {
string path = "/file";
Stat stat, cached_stat;
InitializeStat(&stat);
stat.set_ino(0);
stat.set_mode(33188); // Octal: 100644 (regular file + 644).
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(33188, cached_stat.mode());
stat.set_mode(420); // Octal: 644.
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(33188, cached_stat.mode());
stat.set_mode(263076); // Octal: 1001644 (regular file + sticky bit + 644).
metadata_cache_->UpdateStat(path, stat);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(263076, cached_stat.mode());
stat.set_mode(511); // Octal: 0777 (no sticky bit + 777).
metadata_cache_->UpdateStatAttributes(path, stat, SETATTR_MODE);
EXPECT_EQ(1, metadata_cache_->Size());
EXPECT_EQ(MetadataCache::kStatCached,
metadata_cache_->GetStat(path, &cached_stat));
EXPECT_EQ(0, cached_stat.ino());
EXPECT_EQ(262655, cached_stat.mode()); // Octal: 1000777.
}
/** Ideas:
*
* test TTL expiration.
* test invalidateprefix.
*
* test correct setting of maximum ttl for every operation
*
*/

View File

@@ -0,0 +1,292 @@
/*
* Copyright (c) 2013 by Felix Hupfeld.
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <stdint.h>
#include <boost/lexical_cast.hpp>
#include <boost/scoped_ptr.hpp>
#include <string>
#include "common/test_environment.h"
#include "common/test_rpc_server_dir.h"
#include "common/test_rpc_server_mrc.h"
#include "common/test_rpc_server_osd.h"
#include "libxtreemfs/client.h"
#include "libxtreemfs/file_handle.h"
#include "libxtreemfs/helper.h"
#include "libxtreemfs/object_cache.h"
#include "libxtreemfs/options.h"
#include "libxtreemfs/volume.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "rpc/client.h"
#include "rpc/sync_callback.h"
#include "util/logging.h"
#include "xtreemfs/OSDServiceConstants.h"
namespace xtreemfs {
const int kObjectSize = 10;
class FakeOsdFile {
public:
FakeOsdFile() : size_(0), reads_(0), writes_(0) {
data_.reset(new char[500]);
}
int Read(int object_no, char* buffer) {
reads_++;
const int offset = object_no * kObjectSize;
const int bytes_to_read = std::min(kObjectSize, size_ - offset);
if (bytes_to_read <= 0) {
return 0;
}
memcpy(buffer, &data_[offset], bytes_to_read);
return bytes_to_read;
}
void Write(int object_no, const char* data, int bytes_to_write) {
writes_++;
const int offset = object_no * kObjectSize;
memcpy(&data_[offset], data, bytes_to_write);
size_ = std::max(size_, offset + bytes_to_write);
}
void Truncate(int new_size) {
size_ = new_size;
}
boost::scoped_array<char> data_;
int size_;
int reads_;
int writes_;
};
class ObjectCacheTest : public ::testing::Test {
protected:
virtual void SetUp() {
util::initialize_logger(util::LEVEL_WARN);
cache_.reset(new ObjectCache(2, kObjectSize));
reader_ = boost::bind(&FakeOsdFile::Read, &osd_file_, _1, _2);
writer_ = boost::bind(&FakeOsdFile::Write, &osd_file_, _1, _2, _3);
}
virtual void TearDown() {
google::protobuf::ShutdownProtobufLibrary();
util::shutdown_logger();
}
FakeOsdFile osd_file_;
boost::scoped_ptr<ObjectCache> cache_;
ObjectReaderFunction reader_;
ObjectWriterFunction writer_;
};
TEST_F(ObjectCacheTest, BasicUse) {
cache_->Write(0, 0, "TestData", 9, reader_, writer_);
EXPECT_EQ(1, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(0, osd_file_.size_);
char buffer[10];
EXPECT_EQ(9, cache_->Read(0, 0, buffer, 10, reader_, writer_));
EXPECT_EQ(0, strcmp(buffer, "TestData"));
EXPECT_EQ(1, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(0, osd_file_.size_);
cache_->Flush(writer_);
EXPECT_EQ(1, osd_file_.reads_);
EXPECT_EQ(1, osd_file_.writes_);
EXPECT_EQ(9, osd_file_.size_);
cache_->Flush(writer_);
EXPECT_EQ(1, osd_file_.reads_);
EXPECT_EQ(1, osd_file_.writes_);
EXPECT_EQ(9, osd_file_.size_);
}
TEST_F(ObjectCacheTest, Truncate) {
cache_->Write(0, 0, "TestDataTe", 10, reader_, writer_);
cache_->Write(1, 0, "stData", 6, reader_, writer_);
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(0, osd_file_.size_);
// Shrink
cache_->Truncate(12);
osd_file_.Truncate(12);
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(12, osd_file_.size_);
char buffer[17];
EXPECT_EQ(2, cache_->Read(1, 0, buffer, 10, reader_, writer_));
EXPECT_EQ(0, strncmp(buffer, "st", 2));
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(12, osd_file_.size_);
cache_->Flush(writer_);
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(2, osd_file_.writes_);
EXPECT_EQ(12, osd_file_.size_);
// Extend
cache_->Truncate(20);
osd_file_.Truncate(20);
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(2, osd_file_.writes_);
EXPECT_EQ(20, osd_file_.size_);
char buffer2[10];
EXPECT_EQ(10, cache_->Read(1, 0, buffer2, 10, reader_, writer_));
EXPECT_EQ(0, strncmp(buffer2, "st\0\0\0\0\0\0", 10));
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(2, osd_file_.writes_);
EXPECT_EQ(20, osd_file_.size_);
cache_->Flush(writer_);
EXPECT_EQ(2, osd_file_.reads_);
// We need not to flush out shrunk objects as the layer above us will
// take care of it by sending a truncate to the OSD.
EXPECT_EQ(2, osd_file_.writes_);
EXPECT_EQ(20, osd_file_.size_);
}
TEST_F(ObjectCacheTest, WriteBack) {
cache_->Write(0, 0, "TestData", 9, reader_, writer_);
EXPECT_EQ(1, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(0, osd_file_.size_);
cache_->Write(1, 0, "TestData", 9, reader_, writer_);
EXPECT_EQ(2, osd_file_.reads_);
EXPECT_EQ(0, osd_file_.writes_);
EXPECT_EQ(0, osd_file_.size_);
cache_->Write(2, 0, "TestData", 9, reader_, writer_);
EXPECT_EQ(3, osd_file_.reads_);
EXPECT_EQ(1, osd_file_.writes_);
EXPECT_EQ(9, osd_file_.size_);
cache_->Flush(writer_);
EXPECT_EQ(3, osd_file_.reads_);
EXPECT_EQ(3, osd_file_.writes_);
EXPECT_EQ(29, osd_file_.size_);
// Read back in
char buffer[10];
EXPECT_EQ(10, cache_->Read(0, 0, buffer, 10, reader_, writer_));
EXPECT_EQ(0, strncmp(buffer, "TestData\0\0", 10));
EXPECT_EQ(4, osd_file_.reads_);
EXPECT_EQ(3, osd_file_.writes_);
EXPECT_EQ(29, osd_file_.size_);
}
class ObjectCacheEndToEndTest : public ::testing::Test {
protected:
static const int kBlockSize = 1024 * 128;
virtual void SetUp() {
util::initialize_logger(util::LEVEL_WARN);
test_env.options.connect_timeout_s = 15;
test_env.options.request_timeout_s = 5;
test_env.options.retry_delay_s = 5;
test_env.options.object_cache_size = 2;
test_env.options.periodic_xcap_renewal_interval_s = 2;
ASSERT_TRUE(test_env.Start());
// Open a volume
volume = test_env.client->OpenVolume(
test_env.volume_name_,
NULL, // No SSL options.
test_env.options);
// Open a file.
file = volume->OpenFile(
test_env.user_credentials,
"/test_file",
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_TRUNC |
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDWR));
}
virtual void TearDown() {
//volume->Close();
test_env.Stop();
}
void CheckData(char* data, int buffer_size) {
for (int i = 0; i < buffer_size; ++i) {
ASSERT_EQ('0' + (i % 3), data[i]);
}
}
void CheckDataIsNull(char* data, int buffer_size) {
for (int i = 0; i < buffer_size; ++i) {
ASSERT_EQ(0, data[i]);
}
}
void FillData(char* data, int buffer_size) {
for (int i = 0; i < buffer_size; ++i) {
data[i] = '0' + (i % 3);
}
}
TestEnvironment test_env;
Volume* volume;
FileHandle* file;
};
TEST_F(ObjectCacheEndToEndTest, Persistence) {
const size_t blocks = 5;
const size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]);
FillData(write_buf.get(), buffer_size);
EXPECT_EQ(buffer_size, file->Write(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Close());
file = volume->OpenFile(
test_env.user_credentials,
"/test_file",
static_cast<xtreemfs::pbrpc::SYSTEM_V_FCNTL>(
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_CREAT |
xtreemfs::pbrpc::SYSTEM_V_FCNTL_H_O_RDONLY));
memset(write_buf.get(), 0, buffer_size);
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size, 0));
CheckData(write_buf.get(), buffer_size);
ASSERT_NO_THROW(file->Close());
}
TEST_F(ObjectCacheEndToEndTest, NormalWrite) {
const size_t blocks = 5;
const size_t buffer_size = kBlockSize * blocks;
boost::scoped_array<char> write_buf(new char[buffer_size]);
FillData(write_buf.get(), buffer_size);
EXPECT_EQ(0, file->Read(write_buf.get(), buffer_size, 0));
EXPECT_EQ(buffer_size, file->Write(write_buf.get(), buffer_size, 0));
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size + 5, 0));
CheckData(write_buf.get(), buffer_size);
ASSERT_NO_THROW(file->Flush());
ASSERT_NO_THROW(file->Truncate(test_env.user_credentials, 0));
EXPECT_EQ(0, file->Read(write_buf.get(), buffer_size, 0));
ASSERT_NO_THROW(file->Truncate(test_env.user_credentials, buffer_size));
EXPECT_EQ(buffer_size, file->Read(write_buf.get(), buffer_size, 0));
CheckDataIsNull(write_buf.get(), buffer_size);
file->Write(write_buf.get(), buffer_size, 0);
ASSERT_NO_THROW(file->Close());
EXPECT_EQ(5 + 5, test_env.osds[0]->GetReceivedWrites().size());
}
} // namespace xtreemfs

View File

@@ -0,0 +1,91 @@
/*
* Copyright (c) 2014 by Robert Schmidtke, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include "libxtreemfs/options.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "util/logging.h"
using namespace xtreemfs::util;
namespace xtreemfs {
class OptionsTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
}
virtual void TearDown() {
shutdown_logger();
}
xtreemfs::Options options_;
};
TEST_F(OptionsTest, TestFailOnMultipleLogLevelOptions) {
const char* n_argv[] = { "executable", "--log-level=DEBUG", "-dINFO" };
ASSERT_THROW(
options_.ParseCommandLine(3, const_cast<char**>(n_argv)),
InvalidCommandLineParametersException
);
}
TEST_F(OptionsTest, TestExplicitSpecificationOverride) {
const char* n_argv[] = {
"executable",
"--log-level=DEBUG",
"-o", "-d=INFO"
};
options_.ParseCommandLine(4, const_cast<char**>(n_argv));
ASSERT_EQ("DEBUG", options_.log_level_string);
}
TEST_F(OptionsTest, TestExplicitAndAlternativeCombination) {
const char* n_argv[] = {
"executable",
"--log-file-path=/tmp/test.log",
"-o", "-d=INFO"
};
options_.ParseCommandLine(4, const_cast<char**>(n_argv));
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
ASSERT_EQ("INFO", options_.log_level_string);
}
TEST_F(OptionsTest, TestMultipleLongAlternativeOptions) {
const char* n_argv[] = {
"executable",
"-o", "log-file-path=/tmp/test.log",
"-o", "log-level=INFO"
};
options_.ParseCommandLine(5, const_cast<char**>(n_argv));
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
ASSERT_EQ("INFO", options_.log_level_string);
}
TEST_F(OptionsTest, TestCommaSeparatedAlternativeOptions) {
const char* n_argv[] = {
"executable",
"-o", "log-file-path=/tmp/test.log,-d=INFO"
};
options_.ParseCommandLine(3, const_cast<char**>(n_argv));
ASSERT_EQ("/tmp/test.log", options_.log_file_path);
ASSERT_EQ("INFO", options_.log_level_string);
}
TEST_F(OptionsTest, TestFstabExample) {
const char* n_argv[] = {
"executable",
"-o", "_netdev,pkcs12-file-path=/tmp/pkcs12.p12,log-level=DEBUG"
};
options_.ParseCommandLine(3, const_cast<char**>(n_argv));
ASSERT_EQ("/tmp/pkcs12.p12", options_.ssl_pkcs12_path);
ASSERT_EQ("DEBUG", options_.log_level_string);
}
} // namespace xtreemfs

View File

@@ -0,0 +1,222 @@
/*
* Copyright (c) 2012 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <boost/scoped_ptr.hpp>
#include <string>
#include "libxtreemfs/pbrpc_url.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "util/logging.h"
#include "xtreemfs/GlobalTypes.pb.h"
using namespace std;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
namespace xtreemfs {
class PBRPCURLTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
volume_name = "test";
default_scheme = PBRPCURL::GetSchemePBRPC();
default_port = DIR_PBRPC_PORT_DEFAULT;
servers.push_back("localhost");
servers.push_back("127.0.0.1");
servers.push_back("somehost");
ports.push_back(1234);
ports.push_back(4242);
ports.push_back(31337);
pbrpc_url_.reset(new PBRPCURL());
}
virtual void TearDown() {
shutdown_logger();
}
typedef list<string> ServerList;
typedef list<uint16_t> PortList;
ServerList servers;
PortList ports;
string volume_name;
string default_scheme;
uint16_t default_port;
boost::scoped_ptr<PBRPCURL> pbrpc_url_;
};
TEST_F(PBRPCURLTest, URLWithOneAddressAndVolume) {
stringstream url_to_parse;
url_to_parse << servers.front() << '/' << volume_name;
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
EXPECT_FALSE(addresses.empty());
EXPECT_EQ(volume_name, pbrpc_url_->volume());
EXPECT_EQ(PBRPCURL::GetSchemePBRPC(), pbrpc_url_->scheme());
stringstream expected_address;
expected_address << "localhost:" << DIR_PBRPC_PORT_DEFAULT;
EXPECT_EQ(expected_address.str(), addresses.GetAddresses().front());
}
TEST_F(PBRPCURLTest, URLWithMultipleAddressesAndVolume) {
// Build an URL to parse
stringstream url_to_parse;
ServerList::const_iterator servers_it = servers.begin();
for (; servers_it != servers.end(); ++servers_it) {
if(servers_it != servers.begin()) {
url_to_parse << ',';
}
url_to_parse << *servers_it;
}
url_to_parse << '/' << volume_name;
// Parse URL and get addresses
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
// Check expectations
EXPECT_EQ(servers.size(), addresses.size());
EXPECT_EQ(volume_name, pbrpc_url_->volume());
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
servers_it = servers.begin();
stringstream expected_address;
ServiceAddresses::Addresses services = addresses.GetAddresses();
for(ServiceAddresses::Addresses::iterator it = services.begin();
it != services.end();
++it, ++servers_it) {
expected_address.str("");
expected_address << *servers_it << ':' << default_port;
EXPECT_EQ(expected_address.str(), *it);
}
}
TEST_F(PBRPCURLTest, URLWithMultipleAddressesPortsAndVolume) {
// Build an URL to parse
stringstream url_to_parse;
ServerList::const_iterator servers_it = servers.begin();
PortList::const_iterator ports_it = ports.begin();
for (; servers_it != servers.end(); ++servers_it, ++ports_it) {
if(servers_it != servers.begin()) {
url_to_parse << ',';
}
url_to_parse << *servers_it << ':' << *ports_it;
}
url_to_parse << '/' << volume_name;
// Parse URL and get addresses
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
// Check expectations
EXPECT_EQ(servers.size(), addresses.size());
EXPECT_EQ(volume_name, pbrpc_url_->volume());
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
servers_it = servers.begin();
ports_it = ports.begin();
stringstream expected_address;
ServiceAddresses::Addresses services = addresses.GetAddresses();
for(ServiceAddresses::Addresses::iterator it = services.begin();
it != services.end();
++it, ++servers_it, ++ports_it) {
expected_address.str("");
expected_address << *servers_it << ':' << *ports_it;
EXPECT_EQ(expected_address.str(), *it);
}
}
TEST_F(PBRPCURLTest, URLWithMultipleAddressesProtocolsPortsAndVolume) {
// Build an URL to parse
stringstream url_to_parse;
ServerList::const_iterator servers_it = servers.begin();
PortList::const_iterator ports_it = ports.begin();
for (; servers_it != servers.end(); ++servers_it, ++ports_it) {
if(servers_it != servers.begin()) {
url_to_parse << ',';
}
url_to_parse << default_scheme << "://" << *servers_it << ':' << *ports_it;
}
url_to_parse << '/' << volume_name;
// Parse URL and get addresses
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
// Check expectations
EXPECT_EQ(servers.size(), addresses.size());
EXPECT_EQ(volume_name, pbrpc_url_->volume());
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
servers_it = servers.begin();
ports_it = ports.begin();
stringstream expected_address;
ServiceAddresses::Addresses services = addresses.GetAddresses();
for(ServiceAddresses::Addresses::iterator it = services.begin();
it != services.end();
++it, ++servers_it, ++ports_it) {
expected_address.str("");
expected_address << *servers_it << ':' << *ports_it;
EXPECT_EQ(expected_address.str(), *it);
}
}
TEST_F(PBRPCURLTest, URLWithMultipleAddressesProtocolsAndVolume) {
// Build an URL to parse
stringstream url_to_parse;
ServerList::const_iterator servers_it = servers.begin();
for (; servers_it != servers.end(); ++servers_it) {
if(servers_it != servers.begin()) {
url_to_parse << ',';
}
url_to_parse << default_scheme << "://" << *servers_it;
}
url_to_parse << '/' << volume_name;
// Parse URL and get addresses
pbrpc_url_->ParseURL(url_to_parse.str(), default_scheme, default_port);
ServiceAddresses addresses = pbrpc_url_->GetAddresses();
// Check expectations
EXPECT_EQ(servers.size(), addresses.size());
EXPECT_EQ(volume_name, pbrpc_url_->volume());
EXPECT_EQ(default_scheme, pbrpc_url_->scheme());
servers_it = servers.begin();
stringstream expected_address;
ServiceAddresses::Addresses services = addresses.GetAddresses();
for(ServiceAddresses::Addresses::iterator it = services.begin();
it != services.end();
++it, ++servers_it) {
expected_address.str("");
expected_address << *servers_it << ':' << default_port;
EXPECT_EQ(expected_address.str(), *it);
}
}
TEST_F(PBRPCURLTest, URLAddressesWithDifferentProtocols) {
EXPECT_THROW({
pbrpc_url_->ParseURL("pbrpc://localhost,pbrpcg://remote/" + volume_name,
PBRPCURL::GetSchemePBRPC(),
DIR_PBRPC_PORT_DEFAULT);
}, InvalidURLException);
}
} // namespace xtreemfs

View File

@@ -0,0 +1,206 @@
/*
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#ifndef WIN32
#include <gtest/gtest.h>
#include <list>
#include <string>
#include <sys/types.h>
#include <unistd.h>
#include "libxtreemfs/system_user_mapping_unix.h"
#include "util/logging.h"
using namespace std;
using namespace xtreemfs;
using namespace xtreemfs::util;
class UserMappingUnixTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
}
virtual void TearDown() {
shutdown_logger();
}
SystemUserMappingUnix user_mapping_;
};
// Test user functions.
TEST_F(UserMappingUnixTest, UsernameRootIs0) {
// Assuming the entry "root" exists.
EXPECT_EQ(0, user_mapping_.UsernameToUID("root"));
}
TEST_F(UserMappingUnixTest, Username1Is1) {
// Assuming there's no user with name "1111", the number itself should be
// returned.
EXPECT_EQ(1111, user_mapping_.UsernameToUID("1111"));
}
TEST_F(UserMappingUnixTest, Username2Pow32Minus1IsSame) {
// 2^32-2 would overflow in case of int32_t but remains 2^32-1 with uint32_t.
// 2^32-1 is the reserved -1.
EXPECT_EQ(4294967294, user_mapping_.UsernameToUID("4294967294"));
}
TEST_F(UserMappingUnixTest, UsernameMinus1isNobody) {
// -1 is used by chown to tell that uid or gid should not change. However,
// the reverse mapping is not possible and should map to nobody.
// (uid_t)-1 = 4294967295.
EXPECT_EQ(65534, user_mapping_.UsernameToUID("-1"));
}
TEST_F(UserMappingUnixTest, Username2Pow32IsNobody) {
// Out of range of uid_t.
EXPECT_EQ(65534, user_mapping_.UsernameToUID("4294967296"));
}
TEST_F(UserMappingUnixTest, UsernameMinus2Pow31Minus1IsNobody) {
// Negative values should be detected as errors (except -1).
// Even -2^31-1 which must not overflow to 2^32-1.
EXPECT_EQ(65534, user_mapping_.UsernameToUID("-2147483649"));
}
TEST_F(UserMappingUnixTest, Username1aIsNobody) {
// Assuming the user "1a" does not exist.
EXPECT_EQ(65534, user_mapping_.UsernameToUID("1a"));
}
TEST_F(UserMappingUnixTest, UID0isRoot) {
// Assuming the entry "root" exists.
EXPECT_EQ("root", user_mapping_.UIDToUsername(0));
}
TEST_F(UserMappingUnixTest, UID1111Is1111) {
// Assuming user with id 1111 does not exist.
EXPECT_EQ("1111", user_mapping_.UIDToUsername(1111));
}
TEST_F(UserMappingUnixTest, UID2Pow32Minus2IsSame) {
// Assuming the group "4294967294" does not exist in the system.
// 4294967294 is the max allowed id as chown's -1 corresponds to 4294967295.
EXPECT_EQ("4294967294", user_mapping_.UIDToUsername(4294967294));
}
TEST_F(UserMappingUnixTest, UID2Pow32Minus1IsMinus1) {
// (uid_t)-1 = 4294967295.
EXPECT_EQ("-1", user_mapping_.UIDToUsername((uid_t)-1));
}
/** Tests for the range of UIDs from 0 to 2^16, if they do exist on this system
* and check if the mapping back from the user name to the UID does work.
*
* In case there exists no user name for a UID, the UID itself will be
* returned as string. Therefore, this test should work on the complete range
* of UIDs (except for the special ID (uid_t)-1 (= 4294967295).
*
* We only test till 2^10 (1024) and not the complete range (4294967294). */
TEST_F(UserMappingUnixTest, UIDsFrom0To2Pow10MapBackCorrectlyToUsernames) {
for (uid_t uid = 0; uid <= 1024; uid++) {
std::string username_for_uid = user_mapping_.UIDToUsername(uid);
EXPECT_EQ(uid, user_mapping_.UsernameToUID(username_for_uid));
}
}
// Test Group functions.
TEST_F(UserMappingUnixTest, GroupnameRootIs0) {
// Assuming the entry "root" exists.
EXPECT_EQ(0, user_mapping_.GroupnameToGID("root"));
}
TEST_F(UserMappingUnixTest, Groupname1Is1) {
// Assuming there's no user with name "1111", the number itself should be
// returned.
EXPECT_EQ(1111, user_mapping_.GroupnameToGID("1111"));
}
TEST_F(UserMappingUnixTest, Groupname2Pow32Minus1IsSame) {
// 2^32-2 would overflow in case of int32_t but remains 2^32-1 with uint32_t.
// 2^32-1 is the reserved -1.
EXPECT_EQ(4294967294, user_mapping_.GroupnameToGID("4294967294"));
}
TEST_F(UserMappingUnixTest, GroupnameMinus1isNobody) {
// -1 is used by chown to tell that gid or gid should not change. However,
// the reverse mapping is not possible and should map to nobody.
// (gid_t)-1 = 4294967295.
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("-1"));
}
TEST_F(UserMappingUnixTest, Groupname2Pow32IsNobody) {
// Out of range of gid_t.
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("4294967296"));
}
TEST_F(UserMappingUnixTest, GroupnameMinus2Pow31Minus1IsNobody) {
// Negative values should be detected as errors (except -1).
// Even -2^31-1 which must not overflow to 2^32-1.
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("-2147483649"));
}
TEST_F(UserMappingUnixTest, Groupname1aIsNobody) {
// Assuming the user "1a" does not exist.
EXPECT_EQ(65534, user_mapping_.GroupnameToGID("1a"));
}
TEST_F(UserMappingUnixTest, GID0isRoot) {
// Assuming the entry "root" exists.
EXPECT_EQ("root", user_mapping_.GIDToGroupname(0));
}
TEST_F(UserMappingUnixTest, GID1111Is1111) {
// Assuming user with id 1111 does not exist.
EXPECT_EQ("1111", user_mapping_.GIDToGroupname(1111));
}
TEST_F(UserMappingUnixTest, GID2Pow32Minus2IsSame) {
// Assuming the group "4294967294" does not exist in the system.
// 4294967294 is the max allowed id as chown's -1 corresponds to 4294967295.
EXPECT_EQ("4294967294", user_mapping_.GIDToGroupname(4294967294));
}
TEST_F(UserMappingUnixTest, GID2Pow32Minus1IsMinus1) {
// (gid_t)-1 = 4294967295.
EXPECT_EQ("-1", user_mapping_.GIDToGroupname((gid_t)-1));
}
/** Tests for the range of GIDs from 0 to 2^16, if they do exist on this system
* and check if the mapping back from the group name to the GID does work.
*
* In case there exists no group name for a GID, the GID itself will be
* returned as string. Therefore, this test should work on the complete range
* of GIDs (except for the special ID (gid_t)-1 (= 4294967295).
*
* We only test till 2^10 (1024) and not the complete range (4294967294). */
TEST_F(UserMappingUnixTest, GIDsFrom0To2Pow10MapBackCorrectlyToGroupnames) {
for (gid_t gid = 0; gid <= 1024; gid++) {
std::string groupname_for_gid = user_mapping_.GIDToGroupname(gid);
EXPECT_EQ(gid, user_mapping_.GroupnameToGID(groupname_for_gid));
}
}
// GetGroupnames() tests.
/** Test if GetGroupnames returns at least the primary group of the user and
* its the first in list. */
TEST_F(UserMappingUnixTest, GetGroupnamesReturnsPrimaryGroupAtFirst) {
string primary_group_of_current_user =
user_mapping_.GIDToGroupname(getegid());
list<string> groupnames;
user_mapping_.GetGroupnames(0, getegid(), getpid(), &groupnames);
ASSERT_GE(groupnames.size(), 1);
EXPECT_EQ(primary_group_of_current_user,
(*groupnames.begin()));
}
#endif // !WIN32

View File

@@ -0,0 +1,137 @@
/*
* Copyright (c) 2011-2012 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <boost/thread/thread.hpp>
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <list>
#include <string>
#include "libxtreemfs/user_mapping.h"
#include "libxtreemfs/user_mapping_gridmap_globus.h"
#include "util/logging.h"
#include "pbrpc/RPC.pb.h"
using namespace std;
using namespace xtreemfs;
using namespace xtreemfs::pbrpc;
using namespace xtreemfs::util;
class UserMappingGridmapGlobusTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
gridmap_file_path_ = "gridmap_file_globus";
// Check if file already exists.
struct stat stat_buf;
if (!stat(gridmap_file_path_.c_str(), &stat_buf)) {
ASSERT_FALSE("The temporary gridmap file does already exist");
}
// Create a temporary gridmap file in the working directory.
ofstream out(gridmap_file_path_.c_str());
out << "\"/C=DE/O=GridGermany/OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)/OU=CSR/CN=Michael Berlin\" root\n";
out.close();
ASSERT_FALSE(out.fail());
user_mapping_.reset(new UserMappingGridmapGlobus(
gridmap_file_path_,
1)); // Check every second for changes.
user_mapping_->Start();
}
virtual void TearDown() {
user_mapping_->Stop();
// Delete gridmap file.
remove(gridmap_file_path_.c_str());
shutdown_logger();
atexit(google::protobuf::ShutdownProtobufLibrary);
}
std::string gridmap_file_path_;
boost::scoped_ptr<UserMapping> user_mapping_;
};
TEST_F(UserMappingGridmapGlobusTest, TestBasicDNAndOUResolving) {
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", // NOLINT
result);
user_mapping_->GlobalToLocalUsername("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE",
&result);
EXPECT_EQ("root",
result);
// All OUs will be returned as list of groups of UserCredentials.
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(2, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
uc.groups(1));
// Groups in general do not work and always return "root" or 0.
user_mapping_->LocalToGlobalGroupname("CSR", &result);
EXPECT_EQ("root", result);
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
EXPECT_EQ("root", result);
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
EXPECT_EQ("root", result);
// List of groups is empty for unknown users.
UserCredentials uc2;
user_mapping_->GetGroupnames("nobody", &uc2);
ASSERT_EQ(0, uc2.groups_size());
}
TEST_F(UserMappingGridmapGlobusTest, GridmapFileReload) {
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", // NOLINT
result);
user_mapping_->GlobalToLocalUsername("CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE", &result); // NOLINT
EXPECT_EQ("root", result);
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(2, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
uc.groups(1));
// Rewrite file with another entry.
ofstream out(gridmap_file_path_.c_str());
out << "\"/C=DE/O=GridGermany/OU=Dummy OU 1/CN=Dummy Username\" root\n";
out.close();
ASSERT_FALSE(out.fail());
// Wait for reload.
boost::this_thread::sleep(boost::posix_time::seconds(2));
// Old entry is no longer visible.
string dn = "CN=Michael Berlin,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ(dn, result);
// New entry can be seen.
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(new_dn, result);
user_mapping_->GlobalToLocalUsername(new_dn, &result);
EXPECT_EQ("root", result);
UserCredentials uc2;
user_mapping_->GetGroupnames("root", &uc2);
ASSERT_EQ(1, uc2.groups_size());
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
}

View File

@@ -0,0 +1,224 @@
/*
* Copyright (c) 2011-2014 by Michael Berlin, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <boost/thread/thread.hpp>
#include <cstdio>
#include <sys/stat.h>
#include <sys/types.h>
#include <fstream>
#include <list>
#include <string>
#include "libxtreemfs/user_mapping.h"
#include "libxtreemfs/user_mapping_gridmap_unicore.h"
#include "util/logging.h"
#include "pbrpc/RPC.pb.h"
using namespace std;
using namespace xtreemfs;
using namespace xtreemfs::util;
using namespace xtreemfs::pbrpc;
class UserMappingGridmapUnicoreTestGeneral : public ::testing::Test {
protected:
static const int kWaittimeForReload = 2;
virtual string GetGridmapFileContent() = 0;
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
gridmap_file_path_ = "gridmap_file_unicore";
// Check if file already exists.
struct stat stat_buf;
if (!stat(gridmap_file_path_.c_str(), &stat_buf)) {
ASSERT_FALSE("The temporary gridmap file does already exist");
}
// Create a temporary gridmap file in the working directory.
ofstream out(gridmap_file_path_.c_str());
out << GetGridmapFileContent();
out.close();
ASSERT_FALSE(out.fail());
user_mapping_.reset(new UserMappingGridmapUnicore(
gridmap_file_path_,
1)); // Check every second for changes.
user_mapping_->Start();
}
virtual void TearDown() {
user_mapping_->Stop();
// Delete gridmap file.
remove(gridmap_file_path_.c_str());
shutdown_logger();
atexit(google::protobuf::ShutdownProtobufLibrary);
}
std::string gridmap_file_path_;
boost::scoped_ptr<UserMapping> user_mapping_;
};
class UserMappingGridmapUnicore6Test
: public UserMappingGridmapUnicoreTestGeneral {
private:
virtual string GetGridmapFileContent() {
return "225;zib;root:dgms0006:dgls0050;user;mosgrid:lifescience;CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE\n"; // NOLINT
}
};
TEST_F(UserMappingGridmapUnicore6Test, TestBasicDNAndOUResolving) {
string dn = "CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(dn, result);
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ("root", result);
// All OUs will be returned as list of groups.
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(2, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
uc.groups(1));
// Groups in general do not work and always return "root" or 0.
user_mapping_->LocalToGlobalGroupname("CSR", &result);
EXPECT_EQ("root", result);
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
EXPECT_EQ("root", result);
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
EXPECT_EQ("root", result);
// List of groups is empty for unknown users.
UserCredentials uc2;
user_mapping_->GetGroupnames("nobody", &uc2);
ASSERT_EQ(0, uc2.groups_size());
}
TEST_F(UserMappingGridmapUnicore6Test, GridmapFileReload) {
string dn = "CN=Patrick Schaefer,OU=CSR,OU=Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB),O=GridGermany,C=DE"; // NOLINT
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(dn, result);
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ("root", result);
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(2, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
EXPECT_EQ("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)",
uc.groups(1));
// Rewrite file with another entry.
ofstream out(gridmap_file_path_.c_str());
out << "225;zib;root:dgms0006:dgls0050;user;mosgrid:lifescience;CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE\n"; // NOLINT
out.close();
ASSERT_FALSE(out.fail());
// Wait for reload.
boost::this_thread::sleep(boost::posix_time::seconds(kWaittimeForReload));
// Old entry is no longer visible.
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ(dn, result);
// New entry can be seen.
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(new_dn, result);
user_mapping_->GlobalToLocalUsername(new_dn, &result);
EXPECT_EQ("root", result);
UserCredentials uc2;
user_mapping_->GetGroupnames("root", &uc2);
ASSERT_EQ(1, uc2.groups_size());
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
}
// Test for Unicore Version < 6.
class UserMappingGridmapUnicoreTest
: public UserMappingGridmapUnicoreTestGeneral {
private:
virtual string GetGridmapFileContent() {
return "root:dgms0006=CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE\n";
}
};
TEST_F(UserMappingGridmapUnicoreTest, TestBasicDNAndOUResolving) {
string dn = "CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE";
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(dn, result);
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ("root", result);
// All OUs will be returned as list of groups.
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(1, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
// Groups in general do not work and always return "root" or 0.
user_mapping_->LocalToGlobalGroupname("CSR", &result);
EXPECT_EQ("root", result);
user_mapping_->LocalToGlobalGroupname("Konrad-Zuse-Zentrum fuer Informationstechnik Berlin (ZIB)", &result); // NOLINT
EXPECT_EQ("root", result);
user_mapping_->GlobalToLocalGroupname("unknowngroup", &result);
EXPECT_EQ("root", result);
// List of groups is empty for unknown users.
UserCredentials uc2;
user_mapping_->GetGroupnames("nobody", &uc2);
ASSERT_EQ(0, uc2.groups_size());
}
TEST_F(UserMappingGridmapUnicoreTest, GridmapFileReload) {
string dn = "CN=Patrick Schaefer,OU=CSR,O=GridGermany,C=DE";
string result;
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(dn, result);
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ("root", result);
UserCredentials uc;
user_mapping_->GetGroupnames("root", &uc);
ASSERT_EQ(1, uc.groups_size());
EXPECT_EQ("CSR", uc.groups(0));
// Rewrite file with another entry.
ofstream out(gridmap_file_path_.c_str());
out << "root:dgms0006=CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE\n"; // NOLINT
out.close();
ASSERT_FALSE(out.fail());
// Wait for reload.
boost::this_thread::sleep(boost::posix_time::seconds(kWaittimeForReload));
// Old entry is no longer visible.
user_mapping_->GlobalToLocalUsername(dn, &result);
EXPECT_EQ(dn, result);
// New entry can be seen.
string new_dn = "CN=Dummy Username,OU=Dummy OU 1,O=GridGermany,C=DE";
user_mapping_->LocalToGlobalUsername("root", &result);
EXPECT_EQ(new_dn, result);
user_mapping_->GlobalToLocalUsername(new_dn, &result);
EXPECT_EQ("root", result);
UserCredentials uc2;
user_mapping_->GetGroupnames("root", &uc2);
ASSERT_EQ(1, uc2.groups_size());
EXPECT_EQ("Dummy OU 1", uc2.groups(0));
}

View File

@@ -0,0 +1,337 @@
/*
* Copyright (c) 2011 by Michael Berlin, Zuse Institute Berlin
* 2012 by Matthias Noack, Zuse Institute Berlin
*
* Licensed under the BSD License, see LICENSE file for details.
*
*/
#include <gtest/gtest.h>
#include <boost/make_shared.hpp>
#include <boost/scoped_ptr.hpp>
#include <boost/shared_ptr.hpp>
#include <sstream>
#include <string>
#include <vector>
#include "libxtreemfs/stripe_translator.h"
#include "libxtreemfs/uuid_container.h"
#include "libxtreemfs/uuid_item.h"
#include "libxtreemfs/container_uuid_iterator.h"
#include "libxtreemfs/simple_uuid_iterator.h"
#include "libxtreemfs/xtreemfs_exception.h"
#include "util/logging.h"
#include "xtreemfs/GlobalTypes.pb.h"
using namespace std;
using namespace xtreemfs::util;
namespace xtreemfs {
// factory function, works fine with all default constructible iterator types
template<class IteratorType>
UUIDIterator* CreateUUIDIterator() {
return new IteratorType();
}
// functor to encapsulate different uuid adding mechanisms
template<class IteratorType>
struct UUIDAdder;
template<>
struct UUIDAdder<SimpleUUIDIterator> {
void operator()(UUIDIterator* it, string uuid) {
static_cast<SimpleUUIDIterator*>(it)->AddUUID(uuid);
}
};
template<>
struct UUIDAdder<ContainerUUIDIterator> {
void operator()(UUIDIterator* it, string uuid) {
created_items_.push_back(new UUIDItem(uuid));
// safe downcast here
static_cast<ContainerUUIDIterator*>(it)->AddUUIDItem(created_items_.back());
}
typedef vector<UUIDItem*> ItemPtrList;
~UUIDAdder() {
for (ItemPtrList::iterator it = created_items_.begin();
it != created_items_.end();
++it) {
delete *it;
}
created_items_.clear();
}
private:
vector<UUIDItem*> created_items_;
};
template<class IteratorType>
class UUIDIteratorTest : public ::testing::Test {
protected:
virtual void SetUp() {
initialize_logger(LEVEL_WARN);
uuid_iterator_.reset(CreateUUIDIterator<IteratorType>());
}
virtual void TearDown() {
shutdown_logger();
atexit(google::protobuf::ShutdownProtobufLibrary);
}
boost::scoped_ptr<UUIDIterator> uuid_iterator_;
UUIDAdder<IteratorType> adder_;
};
#if GTEST_HAS_TYPED_TEST
using testing::Types;
typedef Types<SimpleUUIDIterator, ContainerUUIDIterator> Implementations;
TYPED_TEST_CASE(UUIDIteratorTest, Implementations);
TYPED_TEST(UUIDIteratorTest, ListWithOneUUID) {
string uuid1 = "uuid1";
string current_uuid;
this->adder_(this->uuid_iterator_.get(), uuid1);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
// If there is only one UUID and it fails, there is no other choice and it
// should be always returned.
for (int i = 0; i < 2; i++) {
this->uuid_iterator_->MarkUUIDAsFailed(current_uuid);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
}
}
TYPED_TEST(UUIDIteratorTest, ClearLeavesEmptyList) {
string uuid1 = "uuid1";
string uuid2 = "uuid2";
this->adder_(this->uuid_iterator_.get(), uuid1);
this->adder_(this->uuid_iterator_.get(), uuid2);
// Clear the list.
this->uuid_iterator_->Clear();
// There should be no element left and GetUUID should fail.
string current_uuid;
EXPECT_THROW({this->uuid_iterator_->GetUUID(&current_uuid);},
UUIDIteratorListIsEmpyException);
}
TYPED_TEST(UUIDIteratorTest, ResetAfterEndOfList) {
string uuid1 = "uuid1";
string uuid2 = "uuid2";
string current_uuid;
// Fill iterator with two UUIDs.
this->adder_(this->uuid_iterator_.get(), uuid1);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
this->adder_(this->uuid_iterator_.get(), uuid2);
// No entry is marked as failed yet, the first UUID is still available.
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
// Mark the current entry (uuid1) as failed. uuid2 now current?
this->uuid_iterator_->MarkUUIDAsFailed(uuid1);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid2, current_uuid);
// Mark uuid1 again as failed - should have no consequences.
this->uuid_iterator_->MarkUUIDAsFailed(uuid1);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid2, current_uuid);
// Also mark uuid2 as failed now. Now all entries have failed. As we did reach
// the end of the list, the status of all entries should be reset and set to
// the first entry in the list.
this->uuid_iterator_->MarkUUIDAsFailed(uuid2);
for (list<UUIDItem*>::iterator it
= this->uuid_iterator_->uuids_.begin();
it != this->uuid_iterator_->uuids_.end();
++it) {
EXPECT_FALSE((*it)->IsFailed());
}
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
}
TYPED_TEST(UUIDIteratorTest, SetCurrentUUID) {
string uuid1 = "uuid1";
string uuid2 = "uuid2";
string uuid3 = "uuid3";
string current_uuid;
this->adder_(this->uuid_iterator_.get(), uuid1);
this->adder_(this->uuid_iterator_.get(), uuid2);
this->adder_(this->uuid_iterator_.get(), uuid3);
// First UUID is current.
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
// Set third as current one.
this->uuid_iterator_->SetCurrentUUID(uuid3);
this->uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid3, current_uuid);
// Fail the third (and last one): current UUID should be the first again.
this->uuid_iterator_->MarkUUIDAsFailed(uuid3);
this->uuid_iterator_->GetUUID(&current_uuid);
for (list<UUIDItem*>::iterator it
= this->uuid_iterator_->uuids_.begin();
it != this->uuid_iterator_->uuids_.end();
++it) {
EXPECT_FALSE((*it)->IsFailed());
}
EXPECT_EQ(uuid1, current_uuid);
}
TYPED_TEST(UUIDIteratorTest, DebugString) {
string uuid1 = "uuid1";
string uuid2 = "uuid2";
string uuid3 = "uuid3";
EXPECT_EQ("[ ]", this->uuid_iterator_->DebugString());
this->adder_(this->uuid_iterator_.get(), uuid1);
EXPECT_EQ("[ [ uuid1, 0] ]", this->uuid_iterator_->DebugString());
this->adder_(this->uuid_iterator_.get(), uuid2);
EXPECT_EQ("[ [ uuid1, 0], [ uuid2, 0] ]", this->uuid_iterator_->DebugString());
this->adder_(this->uuid_iterator_.get(), uuid3);
EXPECT_EQ("[ [ uuid1, 0], [ uuid2, 0], [ uuid3, 0] ]", this->uuid_iterator_->DebugString());
}
#endif // GTEST_HAS_TYPED_TEST
// Tests for SimpleUUIDIterator
class SimpleUUIDIteratorTest : public UUIDIteratorTest<SimpleUUIDIterator> {
public:
void SetUp() {
UUIDIteratorTest<SimpleUUIDIterator>::SetUp();
simple_uuid_iterator_ =
static_cast<SimpleUUIDIterator*>(uuid_iterator_.get());
}
protected:
SimpleUUIDIterator* simple_uuid_iterator_;
};
TEST_F(SimpleUUIDIteratorTest, LaterAddsDoNotBreakIterator) {
string uuid1 = "uuid1";
string uuid2 = "uuid2";
string uuid3 = "uuid3";
string current_uuid;
this->adder_(this->uuid_iterator_.get(), uuid1);
this->adder_(this->uuid_iterator_.get(), uuid2);
// Mark first uuid as failed and the second takes over.
uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
uuid_iterator_->MarkUUIDAsFailed(uuid1);
uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid2, current_uuid);
// Add a third uuid which should be the next element if the second does fail.
this->adder_(this->uuid_iterator_.get(), uuid3);
uuid_iterator_->MarkUUIDAsFailed(uuid2);
uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid3, current_uuid);
// After all uuids have failed, the first will be returned again.
uuid_iterator_->MarkUUIDAsFailed(uuid3);
uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
}
TEST_F(SimpleUUIDIteratorTest, SetCurrentUUIDAddsUnknownUUID) {
string uuid1 = "uuid1";
string current_uuid;
// UUID1 not added so far. Setting it will automatically add it.
uuid_iterator_->SetCurrentUUID(uuid1);
uuid_iterator_->GetUUID(&current_uuid);
EXPECT_EQ(uuid1, current_uuid);
}
// Tests for ContainerUUIDIterator
class ContainerUUIDIteratorTest
: public UUIDIteratorTest<ContainerUUIDIterator> {};
TEST_F(ContainerUUIDIteratorTest, CreateContainerAndGetUUID) {
// Create container from XLocList
pbrpc::XLocSet xlocs;
pbrpc::StripingPolicyType type = pbrpc::STRIPING_POLICY_RAID0;
const int replicaCount = 3;
stringstream sstream;
for (int i = 0; i < replicaCount; ++i) {
pbrpc::Replica* replica = xlocs.add_replicas();
const int stripe_width = i + 2;
for (int j = 0; j < stripe_width; ++j) {
sstream.str("");
sstream << "uuid" << i << j;
replica->add_osd_uuids(sstream.str());
replica->mutable_striping_policy()->set_type(type);
replica->mutable_striping_policy()->set_stripe_size(128); // kb
replica->mutable_striping_policy()->set_width(stripe_width);
}
}
// get all striping policies
StripeTranslator::PolicyContainer striping_policies;
for (int i = 0; i < xlocs.replicas_size(); ++i) {
striping_policies.push_back(&(xlocs.replicas(i).striping_policy()));
}
boost::shared_ptr<UUIDContainer> uuid_container =
boost::make_shared<UUIDContainer>(xlocs);
// NOTE: We assume that all replicas use the same striping policy type and
// that all replicas use the same stripe size.
boost::scoped_ptr<StripeTranslator> translator(new StripeTranslatorRaid0());
// Map offset to corresponding OSDs.
std::vector<ReadOperation> operations;
off_t read_offsets[] = {0, 128 * 1024};
for (int i = 0; i < sizeof(read_offsets) / sizeof(off_t); ++i) {
operations.clear();
translator->TranslateReadRequest(NULL,
128 * 1024,
read_offsets[i],
striping_policies,
&operations);
// Create a UUIDIterator for a specific set of offsets
boost::scoped_ptr<ContainerUUIDIterator> uuid_iterator(
new ContainerUUIDIterator(uuid_container,
operations[0].osd_offsets));
// check results
string actual;
for (int j = 0; j < replicaCount; ++j) {
uuid_iterator->GetUUID(&actual);
uuid_iterator->MarkUUIDAsFailed(actual);
sstream.str("");
sstream << "uuid" << j << operations[0].osd_offsets[j];
EXPECT_EQ(sstream.str(), actual);
}
}
}
} // namespace xtreemfs

File diff suppressed because it is too large Load Diff