From b4a5b92745ba0f3fb0e272444248e222ae42bc4a Mon Sep 17 00:00:00 2001 From: Sandy Kaur Date: Fri, 1 Oct 2021 12:09:42 -0500 Subject: [PATCH] pybind: add wrapper for rados_write_op_omap_cmp Signed-off-by: Sandy Kaur (cherry picked from commit c5a45d38d42de896fc989e51a260ba214b67b5e0) --- src/pybind/rados/c_rados.pxd | 9 +++++++ src/pybind/rados/mock_rados.pxi | 11 ++++++++ src/pybind/rados/rados.pyx | 25 ++++++++++++++++++ src/test/pybind/test_rados.py | 45 +++++++++++++++++++++++++++++++++ 4 files changed, 90 insertions(+) diff --git a/src/pybind/rados/c_rados.pxd b/src/pybind/rados/c_rados.pxd index 97ee5d0b9063..51564c03c662 100644 --- a/src/pybind/rados/c_rados.pxd +++ b/src/pybind/rados/c_rados.pxd @@ -31,6 +31,14 @@ cdef extern from "rados/librados.h" nogil: _LIBRADOS_OP_FLAG_FADVISE_NOCACHE "LIBRADOS_OP_FLAG_FADVISE_NOCACHE" + enum: + _LIBRADOS_CMPXATTR_OP_EQ "LIBRADOS_CMPXATTR_OP_EQ" + _LIBRADOS_CMPXATTR_OP_NE "LIBRADOS_CMPXATTR_OP_NE" + _LIBRADOS_CMPXATTR_OP_GT "LIBRADOS_CMPXATTR_OP_GT" + _LIBRADOS_CMPXATTR_OP_GTE "LIBRADOS_CMPXATTR_OP_GTE" + _LIBRADOS_CMPXATTR_OP_LT "LIBRADOS_CMPXATTR_OP_LT" + _LIBRADOS_CMPXATTR_OP_LTE "LIBRADOS_CMPXATTR_OP_LTE" + enum: _LIBRADOS_OPERATION_NOFLAG "LIBRADOS_OPERATION_NOFLAG" _LIBRADOS_OPERATION_BALANCE_READS "LIBRADOS_OPERATION_BALANCE_READS" @@ -259,6 +267,7 @@ cdef extern from "rados/librados.h" nogil: int rados_write_op_operate(rados_write_op_t write_op, rados_ioctx_t io, const char * oid, time_t * mtime, int flags) int rados_aio_write_op_operate(rados_write_op_t write_op, rados_ioctx_t io, rados_completion_t completion, const char *oid, time_t *mtime, int flags) void rados_write_op_cmpext(rados_write_op_t write_op, const char *cmp_buf, size_t cmp_len, uint64_t off, int *prval) + void rados_write_op_omap_cmp(rados_write_op_t write_op, const char *key, uint8_t comparison_operator, const char *val, size_t val_len, int *prval) void rados_write_op_omap_set(rados_write_op_t write_op, const char * const* keys, const char * const* vals, const size_t * lens, size_t num) void rados_write_op_omap_rm_keys(rados_write_op_t write_op, const char * const* keys, size_t keys_len) void rados_write_op_omap_clear(rados_write_op_t write_op) diff --git a/src/pybind/rados/mock_rados.pxi b/src/pybind/rados/mock_rados.pxi index 38053af8020e..4c1fe858b1a5 100644 --- a/src/pybind/rados/mock_rados.pxi +++ b/src/pybind/rados/mock_rados.pxi @@ -34,6 +34,15 @@ cdef nogil: _LIBRADOS_OP_FLAG_FADVISE_NOCACHE "LIBRADOS_OP_FLAG_FADVISE_NOCACHE" + enum: + _LIBRADOS_CMPXATTR_OP_EQ "LIBRADOS_CMPXATTR_OP_EQ" + _LIBRADOS_CMPXATTR_OP_NE "LIBRADOS_CMPXATTR_OP_NE" + _LIBRADOS_CMPXATTR_OP_GT "LIBRADOS_CMPXATTR_OP_GT" + _LIBRADOS_CMPXATTR_OP_GTE "LIBRADOS_CMPXATTR_OP_GTE" + _LIBRADOS_CMPXATTR_OP_LT "LIBRADOS_CMPXATTR_OP_LT" + _LIBRADOS_CMPXATTR_OP_LTE "LIBRADOS_CMPXATTR_OP_LTE" + + enum: _LIBRADOS_OPERATION_NOFLAG "LIBRADOS_OPERATION_NOFLAG" _LIBRADOS_OPERATION_BALANCE_READS "LIBRADOS_OPERATION_BALANCE_READS" @@ -380,6 +389,8 @@ cdef nogil: pass void rados_write_op_cmpext(rados_write_op_t write_op, const char *cmp_buf, size_t cmp_len, uint64_t off, int *prval): pass + void rados_write_op_omap_cmp(rados_write_op_t write_op, const char *key, uint8_t comparison_operator, const char *val, size_t val_len, int *prval): + pass void rados_write_op_omap_set(rados_write_op_t write_op, const char * const* keys, const char * const* vals, const size_t * lens, size_t num): pass void rados_write_op_omap_rm_keys(rados_write_op_t write_op, const char * const* keys, size_t keys_len): diff --git a/src/pybind/rados/rados.pyx b/src/pybind/rados/rados.pyx index 0c300ca40477..67d8a1e2c7e3 100644 --- a/src/pybind/rados/rados.pyx +++ b/src/pybind/rados/rados.pyx @@ -49,6 +49,13 @@ LIBRADOS_OP_FLAG_FADVISE_WILLNEED = _LIBRADOS_OP_FLAG_FADVISE_WILLNEED LIBRADOS_OP_FLAG_FADVISE_DONTNEED = _LIBRADOS_OP_FLAG_FADVISE_DONTNEED LIBRADOS_OP_FLAG_FADVISE_NOCACHE = _LIBRADOS_OP_FLAG_FADVISE_NOCACHE +LIBRADOS_CMPXATTR_OP_EQ = _LIBRADOS_CMPXATTR_OP_EQ +LIBRADOS_CMPXATTR_OP_NE = _LIBRADOS_CMPXATTR_OP_NE +LIBRADOS_CMPXATTR_OP_GT = _LIBRADOS_CMPXATTR_OP_GT +LIBRADOS_CMPXATTR_OP_GTE = _LIBRADOS_CMPXATTR_OP_GTE +LIBRADOS_CMPXATTR_OP_LT = _LIBRADOS_CMPXATTR_OP_LT +LIBRADOS_CMPXATTR_OP_LTE = _LIBRADOS_CMPXATTR_OP_LTE + LIBRADOS_SNAP_HEAD = _LIBRADOS_SNAP_HEAD LIBRADOS_OPERATION_NOFLAG = _LIBRADOS_OPERATION_NOFLAG @@ -1910,6 +1917,24 @@ cdef class WriteOp(object): with nogil: rados_write_op_cmpext(self.write_op, _cmp_buf, _cmp_buf_len, _offset, NULL) + def omap_cmp(self, key: str, val: str, cmp_op: int = LIBRADOS_CMPXATTR_OP_EQ): + """ + Ensure that an omap key value satisfies comparison + :param key: omap key whose associated value is evaluated for comparison + :param val: value to compare with + :param cmp_op: comparison operator, one of LIBRADOS_CMPXATTR_OP_EQ (1), + LIBRADOS_CMPXATTR_OP_GT (3), or LIBRADOS_CMPXATTR_OP_LT (5). + """ + key_raw = cstr(key, 'key') + val_raw = cstr(val, 'val') + cdef: + char *_key = key_raw + char *_val = val_raw + size_t _val_len = len(val) + uint8_t _comparison_operator = cmp_op + with nogil: + rados_write_op_omap_cmp(self.write_op, _key, _comparison_operator, _val, _val_len, NULL) + class WriteOpCtx(WriteOp, OpCtx): """write operation context manager""" diff --git a/src/test/pybind/test_rados.py b/src/test/pybind/test_rados.py index 099691490565..508cbe5ae9e4 100644 --- a/src/test/pybind/test_rados.py +++ b/src/test/pybind/test_rados.py @@ -5,6 +5,7 @@ from nose.tools import eq_ as eq, ok_ as ok, assert_raises from rados import (Rados, Error, RadosStateError, Object, ObjectExists, ObjectNotFound, ObjectBusy, NotConnected, LIBRADOS_ALL_NSPACES, WriteOpCtx, ReadOpCtx, LIBRADOS_CREATE_EXCLUSIVE, + LIBRADOS_CMPXATTR_OP_EQ, LIBRADOS_CMPXATTR_OP_GT, LIBRADOS_CMPXATTR_OP_LT, OSError, LIBRADOS_SNAP_HEAD, LIBRADOS_OPERATION_BALANCE_READS, LIBRADOS_OPERATION_SKIPRWLOCKS, MonitorLog, MAX_ERRNO, NoData, ExtendMismatch) from datetime import timedelta import time @@ -598,6 +599,50 @@ class TestIoctx(object): self.ioctx.operate_read_op(read_op, "test_obj") eq(list(iter), [("4", b"dddd")]) + def test_omap_cmp(self): + object_id = 'test' + self.ioctx.write(object_id, b'omap_cmp') + with WriteOpCtx() as write_op: + self.ioctx.set_omap(write_op, ('key1',), ('1',)) + self.ioctx.operate_write_op(write_op, object_id) + with WriteOpCtx() as write_op: + write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_EQ) + self.ioctx.set_omap(write_op, ('key1',), ('2',)) + self.ioctx.operate_write_op(write_op, object_id) + with ReadOpCtx() as read_op: + iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',)) + eq(ret, 0) + self.ioctx.operate_read_op(read_op, object_id) + eq(list(iter), [('key1', b'2')]) + with WriteOpCtx() as write_op: + write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_GT) + self.ioctx.set_omap(write_op, ('key1',), ('3',)) + self.ioctx.operate_write_op(write_op, object_id) + with ReadOpCtx() as read_op: + iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',)) + eq(ret, 0) + self.ioctx.operate_read_op(read_op, object_id) + eq(list(iter), [('key1', b'3')]) + with WriteOpCtx() as write_op: + write_op.omap_cmp('key1', '4', LIBRADOS_CMPXATTR_OP_LT) + self.ioctx.set_omap(write_op, ('key1',), ('4',)) + self.ioctx.operate_write_op(write_op, object_id) + with ReadOpCtx() as read_op: + iter, ret = self.ioctx.get_omap_vals_by_keys(read_op, ('key1',)) + eq(ret, 0) + self.ioctx.operate_read_op(read_op, object_id) + eq(list(iter), [('key1', b'4')]) + with WriteOpCtx() as write_op: + write_op.omap_cmp('key1', '1', LIBRADOS_CMPXATTR_OP_EQ) + self.ioctx.set_omap(write_op, ('key1',), ('5',)) + try: + self.ioctx.operate_write_op(write_op, object_id) + except (OSError, ExtendMismatch) as e: + eq(e.errno, 125) + else: + message = "omap_cmp did not raise Exception when omap content does not match" + raise AssertionError(message) + def test_cmpext_op(self): object_id = 'test' with WriteOpCtx() as write_op: -- 2.47.3