ceph_dencoder_SOURCES = test/encoding/ceph_dencoder.cc ${rgw_dencoder_src}
ceph_dencoder_CXXFLAGS = ${CRYPTO_CXXFLAGS} ${AM_CXXFLAGS}
-ceph_dencoder_LDADD = $(LIBGLOBAL_LDA) libosd.a libmds.a $(LIBOS_LDA) libmon.a
+ceph_dencoder_LDADD = $(LIBGLOBAL_LDA) libcls_lock_client.a libosd.a libmds.a $(LIBOS_LDA) libmon.a
bin_PROGRAMS += ceph-dencoder
mount_ceph_SOURCES = mount/mount.ceph.c common/armor.c common/safe_io.c common/secret.c include/addr_parsing.c
lib_LTLIBRARIES += librbd.la
rados_SOURCES = rados.cc rados_import.cc rados_export.cc rados_sync.cc common/obj_bencher.cc
-rados_LDADD = libglobal.la librados.la $(PTHREAD_LIBS) -lm $(CRYPTO_LIBS) $(EXTRALIBS)
+rados_LDADD = libglobal.la libcls_lock_client.a librados.la $(PTHREAD_LIBS) -lm $(CRYPTO_LIBS) $(EXTRALIBS)
bin_PROGRAMS += rados
if WITH_REST_BENCH
radoslib_LTLIBRARIES = libcls_rbd.la
+# rbd: rados block device class
+libcls_lock_la_SOURCES = cls/lock/cls_lock.cc
+libcls_lock_la_CFLAGS = ${AM_CFLAGS}
+libcls_lock_la_CXXFLAGS= ${AM_CXXFLAGS}
+libcls_lock_la_LIBADD = $(PTHREAD_LIBS) $(EXTRALIBS)
+libcls_lock_la_LDFLAGS = ${AM_LDFLAGS} -version-info 1:0:0 -export-symbols-regex '.*__cls_.*'
+
+radoslib_LTLIBRARIES += libcls_lock.la
+
+
if WITH_RADOSGW
# rgw: rados gateway
libcls_rgw_la_SOURCES = cls_rgw.cc
radoslib_LTLIBRARIES += libcls_rgw.la
endif
+libcls_lock_client_a_SOURCES = \
+ cls/lock/cls_lock_client.cc \
+ cls/lock/cls_lock_types.cc \
+ cls/lock/cls_lock_ops.cc
+noinst_LIBRARIES += libcls_lock_client.a
+
## hadoop client
if WITH_HADOOPCLIENT
JAVA_BASE = /usr/lib/jvm/java-6-sun
test_rados_api_stat_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
bin_DEBUGPROGRAMS += test_rados_api_stat
+test_rados_api_cls_lock_SOURCES = test/rados-api/cls_lock.cc test/rados-api/test.cc
+test_rados_api_cls_lock_LDFLAGS = ${AM_LDFLAGS}
+test_rados_api_cls_lock_LDADD = libcls_lock_client.a librados.la ${UNITTEST_STATIC_LDADD}
+test_rados_api_cls_lock_CXXFLAGS = ${AM_CXXFLAGS} ${UNITTEST_CXXFLAGS}
+bin_DEBUGPROGRAMS += test_rados_api_cls_lock
+
test_rados_api_watch_notify_SOURCES = test/rados-api/watch_notify.cc test/rados-api/test.cc
test_rados_api_watch_notify_LDFLAGS = ${AM_LDFLAGS}
test_rados_api_watch_notify_LDADD = librados.la ${UNITTEST_STATIC_LDADD}
client/ObjecterWriteback.h\
cls_acl.cc\
cls_crypto.cc\
+ cls/lock/cls_lock_types.h\
+ cls/lock/cls_lock_ops.h\
+ cls/lock/cls_lock_client.h\
common/BackTrace.h\
common/RefCountedObj.h\
common/HeartbeatMap.h\
--- /dev/null
+// -*- mode:C++; tab-width:8; c-basic-offset:2; indent-tabs-mode:t -*-
+// vim: ts=8 sw=2 smarttab
+
+/** \file
+ *
+ * This is an OSD class that implements methods for object
+ * advisory locking.
+ *
+ */
+
+#include <algorithm>
+#include <cstring>
+#include <cstdlib>
+#include <errno.h>
+#include <iostream>
+#include <map>
+#include <sstream>
+#include <vector>
+
+#include "include/types.h"
+#include "include/utime.h"
+#include "objclass/objclass.h"
+
+#include "common/Clock.h"
+
+#include "cls/lock/cls_lock_types.h"
+#include "cls/lock/cls_lock_ops.h"
+
+#include "global/global_context.h"
+
+
+CLS_VER(1,0)
+CLS_NAME(lock)
+
+cls_handle_t h_class;
+cls_method_handle_t h_lock_op;
+cls_method_handle_t h_unlock_op;
+cls_method_handle_t h_break_lock;
+cls_method_handle_t h_get_info;
+cls_method_handle_t h_list_locks;
+
+#define LOCK_PREFIX "lock."
+
+typedef struct lock_info_s {
+ map<cls_lock_id_t, cls_lock_locker_info_t> lockers;
+ ClsLockType lock_type;
+ string tag;
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(lockers, bl);
+ uint8_t t = (uint8_t)lock_type;
+ ::encode(t, bl);
+ ::encode(tag, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(lockers, bl);
+ uint8_t t;
+ ::decode(t, bl);
+ lock_type = (ClsLockType)t;
+ ::decode(tag, bl);
+ DECODE_FINISH(bl);
+ }
+ lock_info_s() : lock_type(LOCK_NONE) {}
+} lock_info_t;
+WRITE_CLASS_ENCODER(lock_info_t)
+
+
+static int read_lock(cls_method_context_t hctx, const string& name, lock_info_t *lock)
+{
+ bufferlist bl;
+ string key = LOCK_PREFIX;
+ key.append(name);
+
+ int r = cls_cxx_getxattr(hctx, key.c_str(), &bl);
+ if (r < 0) {
+ if (r == -ENODATA) {
+ *lock = lock_info_t();
+ return 0;
+ }
+ if (r != -ENOENT) {
+ CLS_ERR("error reading xattr %s: %d", key.c_str(), r);
+ }
+ return r;
+ }
+
+ try {
+ bufferlist::iterator it = bl.begin();
+ ::decode(*lock, it);
+ } catch (const buffer::error &err) {
+ CLS_ERR("error decoding %s", key.c_str());
+ return -EIO;
+ }
+
+ /* now trim expired locks */
+
+ utime_t now = ceph_clock_now(g_ceph_context);
+
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator iter = lock->lockers.begin();
+
+ while (iter != lock->lockers.end()) {
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator next = iter;
+ ++next;
+
+ struct cls_lock_locker_info_t& info = iter->second;
+ if (!info.duration.is_zero() && info.duration < now) {
+ CLS_LOG(20, "expiring locker");
+ lock->lockers.erase(iter);
+ }
+
+ iter = next;
+ }
+
+ return 0;
+}
+
+static int write_lock(cls_method_context_t hctx, const string& name, const lock_info_t& lock)
+{
+ string key = LOCK_PREFIX;
+ key.append(name);
+
+ bufferlist lock_bl;
+ ::encode(lock, lock_bl);
+
+ int r = cls_cxx_setxattr(hctx, key.c_str(), &lock_bl);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/**
+ * helper function to add a lock and update disk state.
+ *
+ * Input:
+ * @param name Lock name
+ * @param lock_type Type of lock (exclusive / shared)
+ * @param duration Type of lock (exclusive / shared)
+ * @param flags lock flags
+ * @param cookie The cookie to set in the lock
+ * @param tag The tag to match with the lock (can only lock with matching tags)
+ * @param lock_description The lock description to set (if not empty)
+ * @param locker_description The locker description
+ *
+ * @return 0 on success, or -errno on failure
+ */
+static int lock_obj(cls_method_context_t hctx,
+ const string& name,
+ ClsLockType lock_type,
+ utime_t duration,
+ const string& description,
+ uint8_t flags,
+ const string& cookie,
+ const string& tag)
+{
+ bool exclusive = lock_type == LOCK_EXCLUSIVE;
+ lock_info_t linfo;
+ bool fail_if_exists = (flags & LOCK_FLAG_RENEW) == 0;
+
+ CLS_LOG(20, "requested lock_type=%s fail_if_exists=%d", cls_lock_type_str(lock_type), fail_if_exists);
+ if (lock_type != LOCK_EXCLUSIVE &&
+ lock_type != LOCK_SHARED)
+ return -EINVAL;
+
+ if (name.empty())
+ return -EINVAL;
+
+ // see if there's already a locker
+ int r = read_lock(hctx, name, &linfo);
+ if (r < 0 && r != -ENOENT) {
+ CLS_ERR("Could not read lock info: %s", strerror(r));
+ return r;
+ }
+ map<cls_lock_id_t, cls_lock_locker_info_t>& lockers = linfo.lockers;
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator iter;
+
+ cls_lock_id_t id;
+ id.cookie = cookie;
+ entity_inst_t inst;
+ r = cls_get_request_origin(hctx, &inst);
+ id.locker = inst.name;
+ assert(r == 0);
+
+ /* check this early, before we check fail_if_exists, otherwise we might
+ * remove the locker entry and not check it later */
+ if (lockers.size() && tag != linfo.tag) {
+ CLS_LOG(20, "cannot take lock on object, conflicting tag");
+ return -EBUSY;
+ }
+
+ ClsLockType existing_lock_type = linfo.lock_type;
+ CLS_LOG(20, "existing_lock_type=%s", cls_lock_type_str(existing_lock_type));
+ iter = lockers.find(id);
+ if (iter != lockers.end()) {
+ if (fail_if_exists) {
+ return -EEXIST;
+ } else {
+ lockers.erase(iter); // remove old entry
+ }
+ }
+
+ if (lockers.size()) {
+ if (exclusive) {
+ CLS_LOG(20, "could not exclusive-lock object, already locked");
+ return -EBUSY;
+ }
+
+ if (existing_lock_type != lock_type) {
+ CLS_LOG(20, "cannot take lock on object, conflicting lock type");
+ return -EBUSY;
+ }
+ }
+
+ linfo.lock_type = lock_type;
+ linfo.tag = tag;
+ utime_t duration_abs;
+ if (!duration.is_zero()) {
+ duration_abs = ceph_clock_now(g_ceph_context);
+ duration_abs += duration;
+
+ }
+ struct cls_lock_locker_info_t info(duration_abs, inst.addr, description);
+
+ linfo.lockers[id] = info;
+
+ r = write_lock(hctx, name, linfo);
+ if (r < 0)
+ return r;
+
+ return 0;
+}
+
+/**
+ * Set an exclusive lock on an object for the activating client, if possible.
+ *
+ * Input:
+ * @param cls_lock_lock_op request input
+ *
+ * @returns 0 on success, -EINVAL if it can't decode the lock_cookie,
+ * -EBUSY if the object is already locked, or -errno on (unexpected) failure.
+ */
+static int lock_op(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "lock_op");
+ cls_lock_lock_op op;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error &err) {
+ return -EINVAL;
+ }
+
+ return lock_obj(hctx,
+ op.name, op.type, op.duration, op.description,
+ op.flags, op.cookie, op.tag);
+}
+
+/**
+ * helper function to remove a lock from on disk and clean up state.
+ *
+ * @param name The lock name
+ * @param locker The locker entity name
+ * @param cookie The user-defined cookie associated with the lock.
+ *
+ * @return 0 on success, -ENOENT if there is no such lock (either
+ * entity or cookie is wrong), or -errno on other error.
+ */
+static int remove_lock(cls_method_context_t hctx,
+ const string& name,
+ entity_name_t& locker,
+ const string& cookie)
+{
+ // get current lockers
+ lock_info_t linfo;
+ int r = read_lock(hctx, name, &linfo);
+ if (r < 0) {
+ CLS_ERR("Could not read list of current lockers off disk: %s", strerror(r));
+ return r;
+ }
+
+ map<cls_lock_id_t, cls_lock_locker_info_t>& lockers = linfo.lockers;
+ struct cls_lock_id_t id(locker, cookie);
+
+ // remove named locker from set
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator iter = lockers.find(id);
+ if (iter == lockers.end()) { // no such key
+ return -ENOENT;
+ }
+ lockers.erase(iter);
+
+ r = write_lock(hctx, name, linfo);
+
+ return r;
+}
+
+/**
+ * Unlock an object which the activating client currently has locked.
+ *
+ * Input:
+ * @param cls_lock_unlock_op request input
+ *
+ * @return 0 on success, -EINVAL if it can't decode the cookie, -ENOENT
+ * if there is no such lock (either entity or cookie is wrong), or
+ * -errno on other (unexpected) error.
+ */
+static int unlock_op(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "unlock_op");
+ cls_lock_unlock_op op;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error& err) {
+ return -EINVAL;
+ }
+
+ entity_inst_t inst;
+ int r = cls_get_request_origin(hctx, &inst);
+ assert(r == 0);
+ return remove_lock(hctx, op.name, inst.name, op.cookie);
+}
+
+/**
+ * Break the lock on an object held by any client.
+ *
+ * Input:
+ * @param cls_lock_break_op request input
+ *
+ * @return 0 on success, -EINVAL if it can't decode the locker and
+ * cookie, -ENOENT if there is no such lock (either entity or cookie
+ * is wrong), or -errno on other (unexpected) error.
+ */
+static int break_lock(cls_method_context_t hctx,
+ bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "break_lock");
+ cls_lock_break_op op;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error& err) {
+ return -EINVAL;
+ }
+
+ return remove_lock(hctx, op.name, op.locker, op.cookie);
+}
+
+
+ /**
+ * Retrieve lock info: lockers, tag, exclusive
+ *
+ * Input:
+ * @param cls_lock_list_lockers_op request input
+ *
+ * Output:
+ * @param cls_lock_list_lockers_reply result
+ *
+ * @return 0 on success, -errno on failure.
+ */
+static int get_info(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "get_info");
+ cls_lock_get_info_op op;
+ try {
+ bufferlist::iterator iter = in->begin();
+ ::decode(op, iter);
+ } catch (const buffer::error& err) {
+ return -EINVAL;
+ }
+
+ // get current lockers
+ lock_info_t linfo;
+ int r = read_lock(hctx, op.name, &linfo);
+ if (r < 0) {
+ CLS_ERR("Could not read lock info: %s", strerror(r));
+ return r;
+ }
+
+ struct cls_lock_get_info_reply ret;
+
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator iter;
+ for (iter = linfo.lockers.begin(); iter != linfo.lockers.end(); ++iter) {
+ ret.lockers[iter->first] = iter->second;
+ }
+ ret.lock_type = linfo.lock_type;
+ ret.tag = linfo.tag;
+
+ ::encode(ret, *out);
+
+ return 0;
+}
+
+
+ /**
+ * Retrieve a list of locks for this object
+ *
+ * Input:
+ * @param in is ignored.
+ *
+ * Output:
+ * @param
+ *
+ * @return 0 on success, -errno on failure.
+ */static int list_locks(cls_method_context_t hctx, bufferlist *in, bufferlist *out)
+{
+ CLS_LOG(20, "list_locks");
+
+ map<string, bufferlist> attrs;
+
+ int r = cls_cxx_getxattrs(hctx, &attrs);
+ if (r < 0)
+ return r;
+
+ cls_lock_list_locks_reply ret;
+
+ map<string, bufferlist>::iterator iter;
+ size_t pos = sizeof(LOCK_PREFIX) - 1;
+ for (iter = attrs.begin(); iter != attrs.end(); ++iter) {
+ const string& attr = iter->first;
+ if (attr.substr(0, pos).compare(LOCK_PREFIX) == 0) {
+ ret.locks.push_back(attr.substr(pos));
+ }
+ }
+
+ ::encode(ret, *out);
+
+ return 0;
+}
+
+void __cls_init()
+{
+ CLS_LOG(20, "Loaded lock class!");
+
+ cls_register("lock", &h_class);
+ cls_register_cxx_method(h_class, "lock",
+ CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
+ lock_op, &h_lock_op);
+ cls_register_cxx_method(h_class, "unlock",
+ CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
+ unlock_op, &h_unlock_op);
+ cls_register_cxx_method(h_class, "break_lock",
+ CLS_METHOD_RD | CLS_METHOD_WR | CLS_METHOD_PUBLIC,
+ break_lock, &h_break_lock);
+ cls_register_cxx_method(h_class, "get_info",
+ CLS_METHOD_RD | CLS_METHOD_PUBLIC,
+ get_info, &h_get_info);
+ cls_register_cxx_method(h_class, "list_locks",
+ CLS_METHOD_RD | CLS_METHOD_PUBLIC,
+ list_locks, &h_list_locks);
+
+ return;
+}
--- /dev/null
+// -*- 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/types.h"
+#include "msg/msg_types.h"
+#include "include/rados/librados.hpp"
+
+using namespace librados;
+
+#include <iostream>
+
+#include <errno.h>
+#include <stdlib.h>
+#include <time.h>
+
+#include "cls/lock/cls_lock_types.h"
+#include "cls/lock/cls_lock_ops.h"
+#include "cls/lock/cls_lock_client.h"
+
+namespace rados {
+ namespace cls {
+ namespace lock {
+
+ void lock(ObjectWriteOperation& rados_op,
+ string& name, ClsLockType type,
+ string& cookie, string& tag,
+ string description,
+ utime_t& duration, uint8_t flags)
+ {
+ cls_lock_lock_op op;
+ op.name = name;
+ op.type = type;
+ op.cookie = cookie;
+ op.tag = tag;
+ op.description = description;
+ op.duration = duration;
+ op.flags = flags;
+ bufferlist in;
+ ::encode(op, in);
+ rados_op.exec("lock", "lock", in);
+ }
+
+ int lock(IoCtx& ioctx,
+ string& oid,
+ string& name, ClsLockType type,
+ string& cookie, string& tag,
+ string description, utime_t& duration, uint8_t flags)
+ {
+ ObjectWriteOperation op;
+ lock(op, name, type, cookie, tag, description, duration, flags);
+ return ioctx.operate(oid, &op);
+ }
+
+ void unlock(ObjectWriteOperation& rados_op,
+ string& name, string& cookie)
+ {
+ cls_lock_unlock_op op;
+ op.name = name;
+ op.cookie = cookie;
+ bufferlist in;
+ ::encode(op, in);
+
+ rados_op.exec("lock", "unlock", in);
+ }
+
+ int unlock(IoCtx& ioctx, string& oid,
+ string& name, string& cookie)
+ {
+ ObjectWriteOperation op;
+ unlock(op, name, cookie);
+ return ioctx.operate(oid, &op);
+ }
+
+ void break_lock(ObjectWriteOperation& rados_op,
+ string& name, string& cookie,
+ entity_name_t& locker)
+ {
+ cls_lock_break_op op;
+ op.name = name;
+ op.cookie = cookie;
+ op.locker = locker;
+ bufferlist in;
+ ::encode(op, in);
+ rados_op.exec("lock", "break_lock", in);
+ }
+
+ int break_lock(IoCtx& ioctx, string& oid,
+ string& name, string& cookie,
+ entity_name_t& locker)
+ {
+ ObjectWriteOperation op;
+ break_lock(op, name, cookie, locker);
+ return ioctx.operate(oid, &op);
+ }
+
+ int list_locks(IoCtx& ioctx, string& oid, list<string> *locks)
+ {
+ bufferlist in, out;
+ int r = ioctx.exec(oid, "lock", "list_locks", in, out);
+ if (r < 0)
+ return r;
+
+ cls_lock_list_locks_reply ret;
+ bufferlist::iterator iter = out.begin();
+ try {
+ ::decode(ret, iter);
+ } catch (buffer::error& err) {
+ cerr << __func__ << ": failed to decode response" << std::endl;
+ }
+
+ *locks = ret.locks;
+
+ return 0;
+ }
+
+ int get_lock_info(IoCtx& ioctx, string& oid, string& lock,
+ map<cls_lock_id_t, cls_lock_locker_info_t> *lockers,
+ ClsLockType *lock_type,
+ string *tag)
+ {
+ bufferlist in, out;
+ cls_lock_get_info_op op;
+ op.name = lock;
+ ::encode(op, in);
+ int r = ioctx.exec(oid, "lock", "get_info", in, out);
+ if (r < 0)
+ return r;
+
+ cls_lock_get_info_reply ret;
+ bufferlist::iterator iter = out.begin();
+ try {
+ ::decode(ret, iter);
+ } catch (buffer::error& err) {
+ cerr << __func__ << ": failed to decode response" << std::endl;
+ }
+
+ if (lockers) {
+ *lockers = ret.lockers;
+ }
+
+ if (lock_type) {
+ *lock_type = ret.lock_type;
+ }
+
+ if (tag) {
+ *tag = ret.tag;
+ }
+
+ return 0;
+ }
+
+ void Lock::lock_shared(ObjectWriteOperation& op)
+ {
+ lock(op, name, LOCK_SHARED,
+ cookie, tag, description, duration, flags);
+ }
+
+ int Lock::lock_shared(IoCtx& ioctx, string& oid)
+ {
+ return lock(ioctx, oid, name, LOCK_SHARED,
+ cookie, tag, description, duration, flags);
+ }
+
+ void Lock::lock_exclusive(ObjectWriteOperation& op)
+ {
+ lock(op, name, LOCK_EXCLUSIVE,
+ cookie, tag, description, duration, flags);
+ }
+
+ int Lock::lock_exclusive(IoCtx& ioctx, string& oid)
+ {
+ return lock(ioctx, oid, name, LOCK_EXCLUSIVE,
+ cookie, tag, description, duration, flags);
+ }
+
+ void Lock::unlock(ObjectWriteOperation& op)
+ {
+ rados::cls::lock::unlock(op, name, cookie);
+ }
+
+ int Lock::unlock(IoCtx& ioctx, string& oid)
+ {
+ return rados::cls::lock::unlock(ioctx, oid, name, cookie);
+ }
+
+ void Lock::break_lock(ObjectWriteOperation& op, entity_name_t& locker)
+ {
+ rados::cls::lock::break_lock(op, name, cookie, locker);
+ }
+
+ int Lock::break_lock(IoCtx& ioctx, string& oid, entity_name_t& locker)
+ {
+ return rados::cls::lock::break_lock(ioctx, oid, name, cookie, locker);
+ }
+ } // namespace lock
+ } // namespace cls
+} // namespace rados
+
--- /dev/null
+#ifndef CEPH_CLS_LOCK_CLIENT_H
+#define CEPH_CLS_LOCK_CLIENT_H
+
+
+#include "include/types.h"
+#include "include/rados/librados.hpp"
+
+#include "cls/lock/cls_lock_types.h"
+
+
+namespace rados {
+ namespace cls {
+ namespace lock {
+
+ extern void lock(ObjectWriteOperation& rados_op,
+ std::string& name, ClsLockType type,
+ std::string& cookie, std::string& tag,
+ std::string description, utime_t& duration, uint8_t flags);
+
+ extern int lock(IoCtx& ioctx,
+ std::string& oid,
+ std::string& name, ClsLockType type,
+ std::string& cookie, std::string& tag,
+ std::string description, utime_t& duration, uint8_t flags);
+
+ extern void unlock(ObjectWriteOperation& rados_op,
+ std::string& name, std::string& cookie);
+
+ extern int unlock(IoCtx& ioctx, std::string& oid,
+ std::string& name, std::string& cookie);
+
+ extern void break_lock(ObjectWriteOperation& op,
+ std::string& name, std::string& cookie,
+ entity_name_t& locker);
+
+ extern int break_lock(IoCtx& ioctx, std::string& oid,
+ std::string& name, std::string& cookie,
+ entity_name_t& locker);
+
+ extern int list_locks(librados::IoCtx& ioctx, std::string& oid, list<std::string> *locks);
+ extern int get_lock_info(librados::IoCtx& ioctx, std::string& oid, std::string& lock,
+ map<cls_lock_id_t, cls_lock_locker_info_t> *lockers,
+ ClsLockType *lock_type,
+ std::string *tag);
+
+ class Lock {
+ std::string name;
+ std::string cookie;
+ std::string tag;
+ std::string description;
+ utime_t duration;
+ uint8_t flags;
+
+ public:
+
+ Lock(const std::string& _n) : name(_n), flags(0) {}
+
+ void set_cookie(const std::string& c) { cookie = c; }
+ void set_tag(const std::string& t) { tag = t; }
+ void set_description(const std::string& desc) { description = desc; }
+ void set_duration(const utime_t& e) { duration = e; }
+ void set_renew(bool renew) {
+ if (renew) {
+ flags |= LOCK_FLAG_RENEW;
+ } else {
+ flags &= ~LOCK_FLAG_RENEW;
+ }
+ }
+
+ /* ObjectWriteOperation */
+ void lock_exclusive(ObjectWriteOperation& ioctx);
+ void lock_shared(ObjectWriteOperation& ioctx);
+ void unlock(ObjectWriteOperation& ioctx);
+ void break_lock(ObjectWriteOperation& ioctx, entity_name_t& locker);
+
+ /* IoCtx*/
+ int lock_exclusive(IoCtx& ioctx, std::string& oid);
+ int lock_shared(IoCtx& ioctx, std::string& oid);
+ int unlock(IoCtx& ioctx, std::string& oid);
+ int break_lock(IoCtx& ioctx, std::string& oid, entity_name_t& locker);
+ };
+
+ } // namespace lock
+ } // namespace cls
+} // namespace rados
+
+#endif
+
--- /dev/null
+// -*- 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/types.h"
+#include "msg/msg_types.h"
+#include "common/Formatter.h"
+
+#include "cls/lock/cls_lock_types.h"
+#include "cls/lock/cls_lock_ops.h"
+
+
+
+static void generate_lock_id(cls_lock_id_t& i, int n, const string& cookie)
+{
+ i.locker = entity_name_t(entity_name_t::CLIENT(n));
+ i.cookie = cookie;
+}
+
+void cls_lock_lock_op::dump(Formatter *f) const
+{
+ f->dump_string("name", name);
+ f->dump_string("type", cls_lock_type_str(type));
+ f->dump_string("cookie", cookie);
+ f->dump_string("tag", tag);
+ f->dump_string("description", description);
+ f->dump_stream("duration") << duration;
+ f->dump_int("flags", (int)flags);
+}
+
+void cls_lock_lock_op::generate_test_instances(list<cls_lock_lock_op*>& o)
+{
+ cls_lock_lock_op *i = new cls_lock_lock_op;
+ i->name = "name";
+ i->type = LOCK_SHARED;
+ i->cookie = "cookie";
+ i->tag = "tag";
+ i->description = "description";
+ i->duration = utime_t(5, 0);
+ i->flags = LOCK_FLAG_RENEW;
+ o.push_back(i);
+ o.push_back(new cls_lock_lock_op);
+}
+
+void cls_lock_unlock_op::dump(Formatter *f) const
+{
+ f->dump_string("name", name);
+ f->dump_string("cookie", cookie);
+}
+
+void cls_lock_unlock_op::generate_test_instances(list<cls_lock_unlock_op*>& o)
+{
+ cls_lock_unlock_op *i = new cls_lock_unlock_op;
+ i->name = "name";
+ i->cookie = "cookie";
+ o.push_back(i);
+ o.push_back(new cls_lock_unlock_op);
+}
+
+void cls_lock_break_op::dump(Formatter *f) const
+{
+ f->dump_string("name", name);
+ f->dump_string("cookie", cookie);
+ f->dump_stream("locker") << locker;
+}
+
+void cls_lock_break_op::generate_test_instances(list<cls_lock_break_op*>& o)
+{
+ cls_lock_break_op *i = new cls_lock_break_op;
+ i->name = "name";
+ i->cookie = "cookie";
+ i->locker = entity_name_t(entity_name_t::CLIENT(1));
+ o.push_back(i);
+ o.push_back(new cls_lock_break_op);
+}
+
+void cls_lock_get_info_op::dump(Formatter *f) const
+{
+ f->dump_string("name", name);
+}
+
+void cls_lock_get_info_op::generate_test_instances(list<cls_lock_get_info_op*>& o)
+{
+ cls_lock_get_info_op *i = new cls_lock_get_info_op;
+ i->name = "name";
+ o.push_back(i);
+ o.push_back(new cls_lock_get_info_op);
+}
+
+static void generate_test_addr(entity_addr_t& a, int nonce, int port)
+{
+ a.set_nonce(nonce);
+ a.set_family(AF_INET);
+ a.set_in4_quad(0, 127);
+ a.set_in4_quad(0, 0);
+ a.set_in4_quad(0, 1);
+ a.set_in4_quad(0, 2);
+ a.set_port(port);
+}
+
+void cls_lock_get_info_reply::dump(Formatter *f) const
+{
+ f->dump_string("lock_type", cls_lock_type_str(lock_type));
+ f->dump_string("tag", tag);
+ f->open_array_section("lockers");
+ map<cls_lock_id_t, cls_lock_locker_info_t>::const_iterator iter;
+ for (iter = lockers.begin(); iter != lockers.end(); ++iter) {
+ const cls_lock_id_t& id = iter->first;
+ const cls_lock_locker_info_t& info = iter->second;
+ f->open_object_section("object");
+ f->dump_stream("locker") << id.locker;
+ f->dump_string("description", info.description);
+ f->dump_string("cookie", id.cookie);
+ f->dump_stream("duration") << info.duration;
+ f->dump_stream("addr") << info.addr;
+ f->close_section();
+ }
+ f->close_section();
+}
+
+void cls_lock_get_info_reply::generate_test_instances(list<cls_lock_get_info_reply*>& o)
+{
+ cls_lock_get_info_reply *i = new cls_lock_get_info_reply;
+ i->lock_type = LOCK_SHARED;
+ i->tag = "tag";
+ cls_lock_id_t id1, id2;
+ entity_addr_t addr1, addr2;
+ generate_lock_id(id1, 1, "cookie1");
+ generate_test_addr(addr1, 10, 20);
+ i->lockers[id1] = cls_lock_locker_info_t(utime_t(10, 0), addr1, "description1");
+ generate_lock_id(id2, 2, "cookie2");
+ generate_test_addr(addr2, 30, 40);
+ i->lockers[id2] = cls_lock_locker_info_t(utime_t(20, 0), addr2, "description2");
+
+ o.push_back(i);
+ o.push_back(new cls_lock_get_info_reply);
+}
+
+void cls_lock_list_locks_reply::dump(Formatter *f) const
+{
+ list<string>::const_iterator iter;
+ f->open_array_section("locks");
+ for (iter = locks.begin(); iter != locks.end(); ++iter) {
+ f->open_array_section("object");
+ f->dump_string("lock", *iter);
+ f->close_section();
+ }
+ f->close_section();
+}
+
+void cls_lock_list_locks_reply::generate_test_instances(list<cls_lock_list_locks_reply*>& o)
+{
+ cls_lock_list_locks_reply *i = new cls_lock_list_locks_reply;
+ i->locks.push_back("lock1");
+ i->locks.push_back("lock2");
+ i->locks.push_back("lock3");
+
+ o.push_back(i);
+ o.push_back(new cls_lock_list_locks_reply);
+}
+
+
--- /dev/null
+#ifndef CEPH_CLS_LOCK_OPS_H
+#define CEPH_CLS_LOCK_OPS_H
+
+#include "include/types.h"
+#include "include/utime.h"
+#include "cls/lock/cls_lock_types.h"
+
+struct cls_lock_lock_op
+{
+ string name;
+ ClsLockType type;
+ string cookie;
+ string tag;
+ string description;
+ utime_t duration;
+ uint8_t flags;
+
+ cls_lock_lock_op() : type(LOCK_NONE), flags(0) {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ uint8_t t = (uint8_t)type;
+ ::encode(t, bl);
+ ::encode(cookie, bl);
+ ::encode(tag, bl);
+ ::encode(description, bl);
+ ::encode(duration, bl);
+ ::encode(flags, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(name, bl);
+ uint8_t t;
+ ::decode(t, bl);
+ type = (ClsLockType)t;
+ ::decode(cookie, bl);
+ ::decode(tag, bl);
+ ::decode(description, bl);
+ ::decode(duration, bl);
+ ::decode(flags, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_lock_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_lock_op)
+
+struct cls_lock_unlock_op
+{
+ string name;
+ string cookie;
+
+ cls_lock_unlock_op() {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ ::encode(cookie, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(name, bl);
+ ::decode(cookie, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_unlock_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_unlock_op)
+
+struct cls_lock_break_op
+{
+ string name;
+ entity_name_t locker;
+ string cookie;
+
+ cls_lock_break_op() {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ ::encode(locker, bl);
+ ::encode(cookie, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(name, bl);
+ ::decode(locker, bl);
+ ::decode(cookie, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_break_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_break_op)
+
+struct cls_lock_get_info_op
+{
+ string name;
+
+ cls_lock_get_info_op() {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(name, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(name, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_get_info_op*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_get_info_op)
+
+struct cls_lock_get_info_reply
+{
+ map<cls_lock_id_t, cls_lock_locker_info_t> lockers;
+ ClsLockType lock_type;
+ string tag;
+
+ cls_lock_get_info_reply() : lock_type(LOCK_NONE) {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(lockers, bl);
+ uint8_t t = (uint8_t)lock_type;
+ ::encode(t, bl);
+ ::encode(tag, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(lockers, bl);
+ uint8_t t;
+ ::decode(t, bl);
+ lock_type = (ClsLockType)t;
+ ::decode(tag, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_get_info_reply*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_get_info_reply)
+
+struct cls_lock_list_locks_reply
+{
+ list<string> locks;
+
+ cls_lock_list_locks_reply() {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(locks, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(locks, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_list_locks_reply*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_list_locks_reply)
+
+#endif
--- /dev/null
+// -*- 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/types.h"
+#include "msg/msg_types.h"
+#include "common/Formatter.h"
+
+#include "cls/lock/cls_lock_types.h"
+
+
+
+static void generate_lock_id(cls_lock_id_t& i, int n, const string& cookie)
+{
+ i.locker = entity_name_t(entity_name_t::CLIENT(n));
+ i.cookie = cookie;
+}
+
+void cls_lock_id_t::dump(Formatter *f) const
+{
+ f->dump_stream("locker") << locker;
+ f->dump_string("cookie", cookie);
+}
+
+void cls_lock_id_t::generate_test_instances(list<cls_lock_id_t*>& o)
+{
+ cls_lock_id_t *i = new cls_lock_id_t;
+ generate_lock_id(*i, 1, "cookie");
+ o.push_back(i);
+ o.push_back(new cls_lock_id_t);
+}
+
+void cls_lock_locker_info_t::dump(Formatter *f) const
+{
+ f->dump_stream("duration") << duration;
+ f->dump_stream("addr") << addr;
+ f->dump_string("description", description);
+}
+
+static void generate_test_addr(entity_addr_t& a, int nonce, int port)
+{
+ a.set_nonce(nonce);
+ a.set_family(AF_INET);
+ a.set_in4_quad(0, 127);
+ a.set_in4_quad(0, 0);
+ a.set_in4_quad(0, 1);
+ a.set_in4_quad(0, 2);
+ a.set_port(port);
+}
+
+void cls_lock_locker_info_t::generate_test_instances(list<cls_lock_locker_info_t*>& o)
+{
+ cls_lock_locker_info_t *i = new cls_lock_locker_info_t;
+ i->duration = utime_t(5, 0);
+ generate_test_addr(i->addr, 1, 2);
+ i->description = "description";
+ o.push_back(i);
+ o.push_back(new cls_lock_locker_info_t);
+}
+
--- /dev/null
+#ifndef CEPH_CLS_LOCK_TYPES_H
+#define CEPH_CLS_LOCK_TYPES_H
+
+#include "include/types.h"
+#include "include/utime.h"
+
+/* lock flags */
+#define LOCK_FLAG_RENEW 0x1 /* idempotent lock acquire */
+
+enum ClsLockType {
+ LOCK_NONE = 0,
+ LOCK_EXCLUSIVE = 1,
+ LOCK_SHARED = 2,
+};
+
+static inline const char *cls_lock_type_str(ClsLockType type)
+{
+ switch (type) {
+ case LOCK_NONE:
+ return "none";
+ case LOCK_EXCLUSIVE:
+ return "exclusive";
+ case LOCK_SHARED:
+ return "shared";
+ default:
+ return "<unknown>";
+ }
+}
+
+struct cls_lock_id_t {
+ entity_name_t locker;
+ string cookie;
+
+ cls_lock_id_t() {}
+ cls_lock_id_t(entity_name_t& _n, const string& _c) : locker(_n), cookie(_c) {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(locker, bl);
+ ::encode(cookie, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(locker, bl);
+ ::decode(cookie, bl);
+ DECODE_FINISH(bl);
+ }
+
+ bool operator<(const cls_lock_id_t& rhs) const {
+ if (locker == rhs.locker)
+ return cookie.compare(rhs.cookie) < 0;
+ if (locker < rhs.locker)
+ return true;
+ return false;
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_id_t*>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_id_t)
+
+struct cls_lock_locker_info_t
+{
+ utime_t duration;
+ entity_addr_t addr;
+ string description;
+
+ cls_lock_locker_info_t() {}
+ cls_lock_locker_info_t(const utime_t& _e, const entity_addr_t& _a,
+ const string& _d) : duration(_e), addr(_a), description(_d) {}
+
+ void encode(bufferlist &bl) const {
+ ENCODE_START(1, 1, bl);
+ ::encode(duration, bl);
+ ::encode(addr, bl);
+ ::encode(description, bl);
+ ENCODE_FINISH(bl);
+ }
+ void decode(bufferlist::iterator &bl) {
+ DECODE_START_LEGACY_COMPAT_LEN(1, 1, 1, bl);
+ ::decode(duration, bl);
+ ::decode(addr, bl);
+ ::decode(description, bl);
+ DECODE_FINISH(bl);
+ }
+ void dump(Formatter *f) const;
+ static void generate_test_instances(list<cls_lock_locker_info_t *>& o);
+};
+WRITE_CLASS_ENCODER(cls_lock_locker_info_t)
+
+#endif
#endif
+#include "cls/lock/cls_lock_types.h"
+TYPE(cls_lock_id_t)
+TYPE(cls_lock_locker_info_t)
+#include "cls/lock/cls_lock_ops.h"
+TYPE(cls_lock_lock_op)
+TYPE(cls_lock_unlock_op)
+TYPE(cls_lock_break_op)
+TYPE(cls_lock_get_info_op)
+TYPE(cls_lock_get_info_reply)
+TYPE(cls_lock_list_locks_reply)
+
+
// --- messages ---
#include "messages/MAuth.h"
MESSAGE(MAuth)
--- /dev/null
+// -*- 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 <iostream>
+#include <errno.h>
+
+#include "include/types.h"
+#include "msg/msg_types.h"
+#include "include/rados/librados.hpp"
+
+#include "test/rados-api/test.h"
+#include "gtest/gtest.h"
+
+using namespace librados;
+
+#include "cls/lock/cls_lock_client.h"
+#include "cls/lock/cls_lock_ops.h"
+
+using namespace rados::cls::lock;
+
+void lock_info(IoCtx& ioctx, string& oid, string& name, map<cls_lock_id_t, cls_lock_locker_info_t>& lockers,
+ ClsLockType *assert_type, string *assert_tag)
+{
+ ClsLockType lock_type = LOCK_NONE;
+ string tag;
+ lockers.clear();
+ ASSERT_EQ(0, get_lock_info(ioctx, oid, name, &lockers, &lock_type, &tag));
+ cout << "lock: " << name << std::endl;
+ cout << " lock_type: " << cls_lock_type_str(lock_type) << std::endl;
+ cout << " tag: " << tag << std::endl;
+ cout << " lockers:" << std::endl;
+
+ if (assert_type)
+ ASSERT_EQ(*assert_type, lock_type);
+
+ if (assert_tag)
+ ASSERT_EQ(*assert_tag, tag);
+
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator liter;
+ for (liter = lockers.begin(); liter != lockers.end(); ++liter) {
+ const cls_lock_id_t& locker = liter->first;
+ cout << " " << locker.locker << " duration=" << liter->second.duration
+ << " addr=" << liter->second.addr << " cookie=" << locker.cookie << std::endl;
+ }
+}
+
+void lock_info(IoCtx& ioctx, string& oid, string& name, map<cls_lock_id_t, cls_lock_locker_info_t>& lockers)
+{
+ lock_info(ioctx, oid, name, lockers, NULL, NULL);
+}
+
+TEST(ClsLock, TestMultiLocking) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+ ClsLockType lock_type_shared = LOCK_SHARED;
+ ClsLockType lock_type_exclusive = LOCK_EXCLUSIVE;
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+
+ /* test lock object */
+
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ /* test exclusive lock */
+ ASSERT_EQ(-EEXIST, l.lock_exclusive(ioctx, oid));
+
+ /* test idempotency */
+ l.set_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ l.set_renew(false);
+
+ /* test second client */
+ Lock l2(lock_name);
+ ASSERT_EQ(-EBUSY, l2.lock_exclusive(ioctx2, oid));
+ ASSERT_EQ(-EBUSY, l2.lock_shared(ioctx2, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(ioctx, oid, &locks));
+
+ ASSERT_EQ(1, (int)locks.size());
+ list<string>::iterator iter = locks.begin();
+ map<cls_lock_id_t, cls_lock_locker_info_t> lockers;
+ lock_info(ioctx, oid, *iter, lockers, &lock_type_exclusive, NULL);
+
+ ASSERT_EQ(1, (int)lockers.size());
+
+ /* test unlock */
+ ASSERT_EQ(0, l.unlock(ioctx, oid));
+ locks.clear();
+ ASSERT_EQ(0, list_locks(ioctx, oid, &locks));
+
+ /* test shared lock */
+ ASSERT_EQ(0, l2.lock_shared(ioctx2, oid));
+ ASSERT_EQ(0, l.lock_shared(ioctx, oid));
+
+ locks.clear();
+ ASSERT_EQ(0, list_locks(ioctx, oid, &locks));
+ ASSERT_EQ(1, (int)locks.size());
+ iter = locks.begin();
+ lock_info(ioctx, oid, *iter, lockers, &lock_type_shared, NULL);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ /* test break locks */
+ entity_name_t name = entity_name_t::CLIENT(cluster.get_instance_id());
+ entity_name_t name2 = entity_name_t::CLIENT(cluster2.get_instance_id());
+
+ l2.break_lock(ioctx2, oid, name);
+ lock_info(ioctx, oid, *iter, lockers);
+ ASSERT_EQ(1, (int)lockers.size());
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator liter = lockers.begin();
+ const cls_lock_id_t& id = liter->first;
+ ASSERT_EQ(name2, id.locker);
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(ioctx, oid));
+
+
+ /* test modify description */
+ string description = "new description";
+ l.set_description(description);
+ ASSERT_EQ(0, l.lock_shared(ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMeta) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+
+ Rados cluster2;
+ IoCtx ioctx2;
+ ASSERT_EQ("", connect_cluster_pp(cluster2));
+ cluster2.ioctx_create(pool_name.c_str(), ioctx2);
+
+ string oid = "foo";
+ bufferlist bl;
+ string lock_name = "mylock";
+
+ ASSERT_EQ(0, ioctx.write(oid, bl, bl.length(), 0));
+
+ Lock l(lock_name);
+ ASSERT_EQ(0, l.lock_shared(ioctx, oid));
+
+ /* test lock tag */
+ Lock l_tag(lock_name);
+ l_tag.set_tag("non-default tag");
+ ASSERT_EQ(-EBUSY, l_tag.lock_shared(ioctx2, oid));
+
+
+ ASSERT_EQ(0, l.unlock(ioctx, oid));
+
+ /* test description */
+ Lock l2(lock_name);
+ string description = "new description";
+ l2.set_description(description);
+ ASSERT_EQ(0, l2.lock_shared(ioctx2, oid));
+
+ map<cls_lock_id_t, cls_lock_locker_info_t> lockers;
+ lock_info(ioctx, oid, lock_name, lockers, NULL, NULL);
+ ASSERT_EQ(1, (int)lockers.size());
+
+ map<cls_lock_id_t, cls_lock_locker_info_t>::iterator iter = lockers.begin();
+ cls_lock_locker_info_t locker = iter->second;
+ ASSERT_EQ("new description", locker.description);
+
+ ASSERT_EQ(0, l2.unlock(ioctx2, oid));
+
+ /* check new tag */
+ string new_tag = "new_tag";
+ l.set_tag(new_tag);
+ l.set_renew(true);
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+ lock_info(ioctx, oid, lock_name, lockers, NULL, &new_tag);
+ ASSERT_EQ(1, (int)lockers.size());
+ l.set_tag("");
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(ioctx, oid));
+ l.set_tag(new_tag);
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestCookie) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ string lock_name = "mylock";
+ Lock l(lock_name);
+
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ /* new cookie */
+ string cookie = "new cookie";
+ l.set_cookie(cookie);
+ ASSERT_EQ(-EBUSY, l.lock_exclusive(ioctx, oid));
+ ASSERT_EQ(-ENOENT, l.unlock(ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.unlock(ioctx, oid));
+
+ map<cls_lock_id_t, cls_lock_locker_info_t> lockers;
+ lock_info(ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(0, (int)lockers.size());
+
+ l.set_cookie(cookie);
+ ASSERT_EQ(0, l.lock_shared(ioctx, oid));
+ l.set_cookie("");
+ ASSERT_EQ(0, l.lock_shared(ioctx, oid));
+
+ lock_info(ioctx, oid, lock_name, lockers);
+ ASSERT_EQ(2, (int)lockers.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestMultipleLocks) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock1");
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ Lock l2("lock2");
+ ASSERT_EQ(0, l2.lock_exclusive(ioctx, oid));
+
+ list<string> locks;
+ ASSERT_EQ(0, list_locks(ioctx, oid, &locks));
+
+ ASSERT_EQ(2, (int)locks.size());
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}
+
+TEST(ClsLock, TestLockDuration) {
+ Rados cluster;
+ std::string pool_name = get_temp_pool_name();
+ ASSERT_EQ("", create_one_pool_pp(pool_name, cluster));
+ IoCtx ioctx;
+ cluster.ioctx_create(pool_name.c_str(), ioctx);
+
+ string oid = "foo";
+ Lock l("lock");
+ l.set_duration(utime_t(5, 0));
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+ ASSERT_EQ(-EEXIST, l.lock_exclusive(ioctx, oid));
+
+ sleep(5);
+ ASSERT_EQ(0, l.lock_exclusive(ioctx, oid));
+
+ ASSERT_EQ(0, destroy_one_pool_pp(pool_name, cluster));
+}