From aece153a61db199134116dedf39287f730a5e2e2 Mon Sep 17 00:00:00 2001 From: Colin Patrick McCabe Date: Tue, 5 Jul 2011 16:44:26 -0700 Subject: [PATCH] Add systest framework. Create rados_list_parallel Signed-off-by: Colin McCabe --- src/Makefile.am | 15 +- src/test/system/rados_list_parallel.cc | 224 +++++++++++++++++++++++++ src/test/system/systest_runnable.cc | 168 +++++++++++++++++++ src/test/system/systest_runnable.h | 80 +++++++++ src/test/system/systest_settings.cc | 50 ++++++ src/test/system/systest_settings.h | 32 ++++ 6 files changed, 568 insertions(+), 1 deletion(-) create mode 100644 src/test/system/rados_list_parallel.cc create mode 100644 src/test/system/systest_runnable.cc create mode 100644 src/test/system/systest_runnable.h create mode 100644 src/test/system/systest_settings.cc create mode 100644 src/test/system/systest_settings.h diff --git a/src/Makefile.am b/src/Makefile.am index 7ce6a941e9222..a74569d6c6b98 100644 --- a/src/Makefile.am +++ b/src/Makefile.am @@ -375,6 +375,17 @@ libhadoopcephfs_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-r lib_LTLIBRARIES += libhadoopcephfs.la endif +## System tests +libsystest_la_SOURCES = \ + test/system/systest_runnable.cc \ + test/system/systest_settings.cc +libsystest_la_LIBADD = libglobal.la +noinst_LTLIBRARIES += libsystest.la + +rados_list_parallel_SOURCES = test/system/rados_list_parallel.cc +rados_list_parallel_LDADD = libsystest.la librados.la +bin_DEBUGPROGRAMS += rados_list_parallel + ## unit tests # target to build but not run the unit tests @@ -1150,7 +1161,9 @@ noinst_HEADERS = \ tools/gui_resources.h\ test/osd/RadosModel.h\ global/pidfile.h\ - common/sync_filesystem.h + common/sync_filesystem.h \ + test/system/systest_runnable.h \ + test/system/systest_settings.h all_sources = $(cmon_SOURCES) $(ceph_SOURCES) $(cephfs_SOURCES) $(librados_config_SOURCES) $(cauthtool_SOURCES) $(monmaptool_SOURCES) \ $(crushtool_SOURCES) $(osdmaptool_SOURCES) $(cconf_SOURCES) $(mount_ceph_SOURCES) $(cmds_SOURCES) \ diff --git a/src/test/system/rados_list_parallel.cc b/src/test/system/rados_list_parallel.cc new file mode 100644 index 0000000000000..1d7d8f0e480db --- /dev/null +++ b/src/test/system/rados_list_parallel.cc @@ -0,0 +1,224 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* +* Ceph - scalable distributed file system +* +* Copyright (C) 2004-2006 Sage Weil +* +* This is free software; you can redistribute it and/or +* modify it under the terms of the GNU Lesser General Public +* License version 2.1, as published by the Free Software +* Foundation. See file COPYING. +* +*/ + +#include "include/rados/librados.h" +#include "systest_runnable.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::ostringstream; +using std::string; +using std::vector; + +static const int RLP_NUM_OBJECTS = 50; //16384; +static const int RLP_OBJECT_SZ_MAX = 256; + +static std::string get_random_buf(void) +{ + ostringstream oss; + int size = rand() % RLP_OBJECT_SZ_MAX; // yep, it's not very random + for (int i = 0; i < size; ++i) { + oss << "."; + } + return oss.str(); +} + +sem_t pool_setup_sem; +sem_t modify_sem; + +class RadosCreateBigPoolR : public SysTestRunnable +{ +public: + RadosCreateBigPoolR() + : SysTestRunnable() + { + } + + ~RadosCreateBigPoolR() + { + } + + int run(void) + { + rados_t cl; + RETURN_IF_NONZERO(rados_create(&cl, NULL)); + RETURN_IF_NONZERO(rados_conf_read_file(cl, NULL)); + RETURN_IF_NONZERO(rados_connect(cl)); + int ret = rados_pool_delete(cl, "foo"); + if (!((ret == 0) || (ret == -ENOENT))) { + printf("%s: rados_pool_delete error %d\n", get_id_str(), ret); + return ret; + } + RETURN_IF_NONZERO(rados_pool_create(cl, "foo")); + rados_ioctx_t io_ctx; + RETURN_IF_NONZERO(rados_ioctx_create(cl, "foo", &io_ctx)); + + for (int i = 0; i < RLP_NUM_OBJECTS; ++i) { + char oid[128]; + snprintf(oid, sizeof(oid), "%d.obj", i); + std::string buf(get_random_buf()); + ret = rados_write(io_ctx, oid, buf.c_str(), buf.size(), 0); + if (ret < static_cast(buf.size())) { + printf("%s: rados_write error %d\n", get_id_str(), ret); + return ret; + } + } + sem_post(&pool_setup_sem); + sem_post(&pool_setup_sem); + rados_ioctx_destroy(cl); + return 0; + } +}; + +class RadosListObjectsR : public SysTestRunnable +{ +public: + RadosListObjectsR() + : SysTestRunnable() + { + } + + ~RadosListObjectsR() + { + } + + int run(void) + { + rados_t cl; + RETURN_IF_NONZERO(rados_create(&cl, NULL)); + RETURN_IF_NONZERO(rados_conf_read_file(cl, NULL)); + RETURN_IF_NONZERO(rados_connect(cl)); + sem_wait(&pool_setup_sem); + + rados_ioctx_t io_ctx; + RETURN_IF_NONZERO(rados_ioctx_create(cl, "foo", &io_ctx)); + + int ret, saw = 0; + const char *obj_name; + char tmp[RLP_OBJECT_SZ_MAX]; + rados_list_ctx_t h; + RETURN_IF_NONZERO(rados_objects_list_open(io_ctx, &h)); + while (true) { + ret = rados_objects_list_next(h, &obj_name); + if (ret == -ENOENT) { + break; + } + else if (ret != 0) { + printf("%s: rados_objects_list_next error: %d\n", get_id_str(), ret); + return ret; + } + int len = strlen(obj_name); + if (len > RLP_OBJECT_SZ_MAX) + len = RLP_OBJECT_SZ_MAX; + memcpy(tmp, obj_name, strlen(obj_name)); + ++saw; + if (saw == RLP_NUM_OBJECTS / 2) + sem_wait(&modify_sem); + } + rados_objects_list_close(h); + + printf("%s: saw %d objects\n", get_id_str(), saw); + + rados_ioctx_destroy(cl); + + return 0; + } +}; + +class RadosModifyPoolR : public SysTestRunnable +{ +public: + RadosModifyPoolR() + : SysTestRunnable() + { + } + + ~RadosModifyPoolR() + { + } + + int run(void) + { + int ret; + rados_t cl; + RETURN_IF_NONZERO(rados_create(&cl, NULL)); + RETURN_IF_NONZERO(rados_conf_read_file(cl, NULL)); + RETURN_IF_NONZERO(rados_connect(cl)); + sem_wait(&pool_setup_sem); + + rados_ioctx_t io_ctx; + RETURN_IF_NONZERO(rados_ioctx_create(cl, "foo", &io_ctx)); + + std::vector to_delete; + for (int i = 0; i < RLP_NUM_OBJECTS; ++i) { + char oid[128]; + snprintf(oid, sizeof(oid), "%d.obj", i); + to_delete.push_back(oid); + } + + int removed = 0; + while (true) { + if (to_delete.empty()) + break; + int r = rand() % to_delete.size(); + std::string oid(to_delete[r]); + ret = rados_remove(io_ctx, oid.c_str()); + if (ret != 0) { + printf("%s: rados_remove(%s) failed with error %d\n", + get_id_str(), oid.c_str(), ret); + return ret; + } + ++removed; + if (removed == RLP_NUM_OBJECTS / 2) + sem_post(&modify_sem); + } + + printf("%s: removed %d objects\n", get_id_str(), removed); + + rados_ioctx_destroy(cl); + + return 0; + } +}; + +int main(int argc, const char **argv) +{ + sem_init(&pool_setup_sem, 1, 0); + sem_init(&modify_sem, 1, 0); + + RadosCreateBigPoolR r1; + RadosListObjectsR r2; + RadosModifyPoolR r3; + vector < SysTestRunnable* > vec; + vec.push_back(&r1); + vec.push_back(&r2); + vec.push_back(&r3); + std::string error = SysTestRunnable::run_until_finished(vec); + if (!error.empty()) { + printf("got error: %s\n", error.c_str()); + return EXIT_FAILURE; + } + + printf("******* SUCCESS **********\n"); + return EXIT_SUCCESS; +} diff --git a/src/test/system/systest_runnable.cc b/src/test/system/systest_runnable.cc new file mode 100644 index 0000000000000..000022d772eb7 --- /dev/null +++ b/src/test/system/systest_runnable.cc @@ -0,0 +1,168 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "include/atomic.h" +#include "systest_runnable.h" +#include "systest_settings.h" + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +using std::ostringstream; +using std::string; + +static pid_t do_gettid(void) +{ + return static_cast < pid_t >(syscall(SYS_gettid)); +} + +ceph::atomic_t m_highest_id(0); + +SysTestRunnable:: +SysTestRunnable() +{ + m_started = false; + m_id = m_highest_id.inc(); + memset(&m_pthread, 0, sizeof(m_pthread)); + m_pid = 0; + update_id_str(false); +} + +SysTestRunnable:: +~SysTestRunnable() +{ +} + +const char* SysTestRunnable:: +get_id_str(void) const +{ + return m_id_str; +} + +int SysTestRunnable:: +start() +{ + if (m_started) { + return -EDOM; + } + bool use_threads = SysTestSettings::inst().use_threads(); + if (use_threads) { + int ret = pthread_create(&m_pthread, NULL, systest_runnable_pthread_helper, + static_cast(this)); + if (ret) + return ret; + m_started = true; + return 0; + } + else { + // TODO: implement + // m_pid = ??? + return -ENOTSUP; + } +} + +std::string SysTestRunnable:: +join() +{ + if (!m_started) { + return "SysTestRunnable was never started."; + } + bool use_threads = SysTestSettings::inst().use_threads(); + if (use_threads) { + void *ptrretval; + int ret = pthread_join(m_pthread, &ptrretval); + if (ret) { + ostringstream oss; + oss << "pthread_join failed with error " << ret; + return oss.str(); + } + int retval = (int)(uintptr_t)ptrretval; + if (retval != 0) { + ostringstream oss; + oss << "ERROR " << retval; + return oss.str(); + } + return ""; + } + else { + // TODO: implement + // m_pid = ??? + return "processes not supported yet"; + } +} + +void SysTestRunnable:: +update_id_str(bool started) +{ + bool use_threads = SysTestSettings::inst().use_threads(); + char extra[128]; + extra[0] = '\0'; + + if (started) { + if (use_threads) + snprintf(extra, sizeof(extra), " [%d]", do_gettid()); + else + snprintf(extra, sizeof(extra), " [%d]", getpid()); + } + if (use_threads) + snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "thread %d%s", m_id, extra); + else + snprintf(m_id_str, SysTestRunnable::ID_STR_SZ, "process %d%s", m_id, extra); +} + +std::string SysTestRunnable:: +run_until_finished(std::vector < SysTestRunnable * > &runnables) +{ + int ret, index = 0; + for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin(); + r != runnables.end(); ++r) { + ret = (*r)->start(); + if (ret) { + ostringstream oss; + oss << "run_until_finished: got error " << ret + << " when starting runnable " << index; + return oss.str(); + } + ++index; + } + + for (std::vector < SysTestRunnable * >::const_iterator r = runnables.begin(); + r != runnables.end(); ++r) { + std::string rstr = (*r)->join(); + if (!rstr.empty()) { + ostringstream oss; + oss << "run_until_finished: runnable " << (*r)->get_id_str() + << ": got error " << rstr; + return oss.str(); + } + } + return ""; +} + +void *systest_runnable_pthread_helper(void *arg) +{ + SysTestRunnable *st = static_cast < SysTestRunnable * >(arg); + st->update_id_str(true); + int ret = st->run(); + return (void*)(uintptr_t)ret; +} diff --git a/src/test/system/systest_runnable.h b/src/test/system/systest_runnable.h new file mode 100644 index 0000000000000..f8077f9562940 --- /dev/null +++ b/src/test/system/systest_runnable.h @@ -0,0 +1,80 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef CEPH_SYSTEM_TEST_H +#define CEPH_SYSTEM_TEST_H + +#include +#include +#include +#include + +#define RETURN_IF_NOT_VAL(expected, expr) \ + do {\ + int _rinv_ret = expr;\ + if (_rinv_ret) {\ + printf("%s: file %s, line %d: expected %d, got %d",\ + get_id_str(), __FILE__, __LINE__, expected, _rinv_ret);\ + return _rinv_ret;\ + }\ + } while(0); + +#define RETURN_IF_NONZERO(expr) \ + RETURN_IF_NOT_VAL(0, expr) + +extern void* systest_runnable_pthread_helper(void *arg); + +/* Represents a single test thread / process. + * + * Inherit from this class and implement the test body in run(). +*/ +class SysTestRunnable +{ +public: + static const int ID_STR_SZ = 128; + + SysTestRunnable(); + virtual ~SysTestRunnable(); + + /* Returns 0 on success; error code otherwise. */ + virtual int run(void) = 0; + + /* Return a string identifying the runnable. */ + const char* get_id_str(void) const; + + /* Start the Runnable */ + int start(); + + /* Wait until the Runnable is finished. Returns an error string on failure. */ + std::string join(); + + /* Starts a bunch of SystemTestRunnables and waits until they're done. + * + * Returns an error string on failure. */ + static std::string run_until_finished(std::vector < SysTestRunnable * >& + runnables); + +private: + void update_id_str(bool started); + + friend void* systest_runnable_pthread_helper(void *arg); + + bool m_started; + int m_id; + pthread_t m_pthread; + int m_pid; + char m_id_str[ID_STR_SZ]; +}; + +#endif diff --git a/src/test/system/systest_settings.cc b/src/test/system/systest_settings.cc new file mode 100644 index 0000000000000..59b4072cfcadb --- /dev/null +++ b/src/test/system/systest_settings.cc @@ -0,0 +1,50 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#include "systest_settings.h" + +#include +#include + +pthread_mutex_t g_system_test_settings_lock = PTHREAD_MUTEX_INITIALIZER; + +SysTestSettings& SysTestSettings:: +inst() +{ + pthread_mutex_lock(&g_system_test_settings_lock); + if (!m_inst) + m_inst = new SysTestSettings(); + pthread_mutex_unlock(&g_system_test_settings_lock); + return *m_inst; +} + +bool SysTestSettings:: +use_threads() const +{ + return m_use_threads; +} + +SysTestSettings* SysTestSettings:: +m_inst = NULL; + +SysTestSettings:: +SysTestSettings() +{ + m_use_threads = !!getenv("USE_THREADS"); +} + +SysTestSettings:: +~SysTestSettings() +{ +} diff --git a/src/test/system/systest_settings.h b/src/test/system/systest_settings.h new file mode 100644 index 0000000000000..18abf1c134de1 --- /dev/null +++ b/src/test/system/systest_settings.h @@ -0,0 +1,32 @@ +// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*- +// vim: ts=8 sw=2 smarttab +/* + * Ceph - scalable distributed file system + * + * Copyright (C) 2011 New Dream Network + * + * This is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License version 2.1, as published by the Free Software + * Foundation. See file COPYING. + * + */ + +#ifndef CEPH_SYSTEM_TEST_SETTINGS_H +#define CEPH_SYSTEM_TEST_SETTINGS_H + +/* Singleton with settings grabbed from environment variables */ +class SysTestSettings +{ +public: + static SysTestSettings& inst(); + bool use_threads() const; +private: + static SysTestSettings* m_inst; + SysTestSettings(); + ~SysTestSettings(); + + bool m_use_threads; +}; + +#endif -- 2.39.5