]> git.apps.os.sepia.ceph.com Git - ceph.git/commitdiff
Add systest framework. Create rados_list_parallel
authorColin Patrick McCabe <cmccabe@alumni.cmu.edu>
Tue, 5 Jul 2011 23:44:26 +0000 (16:44 -0700)
committerColin Patrick McCabe <cmccabe@alumni.cmu.edu>
Thu, 7 Jul 2011 21:24:52 +0000 (14:24 -0700)
Signed-off-by: Colin McCabe <colin.mccabe@dreamhost.com>
src/Makefile.am
src/test/system/rados_list_parallel.cc [new file with mode: 0644]
src/test/system/systest_runnable.cc [new file with mode: 0644]
src/test/system/systest_runnable.h [new file with mode: 0644]
src/test/system/systest_settings.cc [new file with mode: 0644]
src/test/system/systest_settings.h [new file with mode: 0644]

index 7ce6a941e9222eb178cd3cc6a6457bd22621c355..a74569d6c6b981736ab79ac2fb4c5acd48e6b0d3 100644 (file)
@@ -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 (file)
index 0000000..1d7d8f0
--- /dev/null
@@ -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 <sage@newdream.net>
+*
+* 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 <errno.h>
+#include <pthread.h>
+#include <semaphore.h>
+#include <sstream>
+#include <stdarg.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string>
+#include <time.h>
+#include <vector>
+
+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<int>(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 <std::string> 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 (file)
index 0000000..000022d
--- /dev/null
@@ -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 <errno.h>
+#include <pthread.h>
+#include <sstream>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <string>
+#include <sys/syscall.h>
+#include <sys/types.h>
+#include <vector>
+
+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<void*>(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 (file)
index 0000000..f8077f9
--- /dev/null
@@ -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 <pthread.h>
+#include <stdio.h>
+#include <string>
+#include <vector>
+
+#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 (file)
index 0000000..59b4072
--- /dev/null
@@ -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 <pthread.h>
+#include <stdlib.h>
+
+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 (file)
index 0000000..18abf1c
--- /dev/null
@@ -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