rbd: initial python APIs to support mirroring

Fixes: http://tracker.ceph.com/issues/15656
Signed-off-by: Mykola Golub <mgolub@mirantis.com>
This commit is contained in:
Mykola Golub 2016-05-11 14:29:17 +03:00
parent 2c32c413ff
commit ef0ea8ee3c

View File

@ -18,8 +18,10 @@ from cpython cimport PyObject, ref, exc
from libc cimport errno from libc cimport errno
from libc.stdint cimport * from libc.stdint cimport *
from libc.stdlib cimport realloc, free from libc.stdlib cimport realloc, free
from libc.string cimport strdup
from collections import Iterable from collections import Iterable
from datetime import datetime
cimport rados cimport rados
@ -33,6 +35,9 @@ cdef extern from "Python.h":
char* PyBytes_AsString(PyObject *string) except NULL char* PyBytes_AsString(PyObject *string) except NULL
int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1 int _PyBytes_Resize(PyObject **string, Py_ssize_t newsize) except -1
cdef extern from "time.h":
ctypedef long int time_t
ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr) ctypedef int (*librbd_progress_fn_t)(uint64_t offset, uint64_t total, void* ptr)
cdef extern from "rbd/librbd.h" nogil: cdef extern from "rbd/librbd.h" nogil:
@ -81,6 +86,43 @@ cdef extern from "rbd/librbd.h" nogil:
uint64_t size uint64_t size
char *name char *name
ctypedef enum rbd_mirror_mode_t:
_RBD_MIRROR_MODE_DISABLED "RBD_MIRROR_MODE_DISABLED"
_RBD_MIRROR_MODE_IMAGE "RBD_MIRROR_MODE_IMAGE"
_RBD_MIRROR_MODE_POOL "RBD_MIRROR_MODE_POOL"
ctypedef struct rbd_mirror_peer_t:
char *uuid
char *cluster_name
char *client_name
ctypedef enum rbd_mirror_image_state_t:
_RBD_MIRROR_IMAGE_DISABLING "RBD_MIRROR_IMAGE_DISABLING"
_RBD_MIRROR_IMAGE_ENABLED "RBD_MIRROR_IMAGE_ENABLED"
_RBD_MIRROR_IMAGE_DISABLED "RBD_MIRROR_IMAGE_DISABLED"
ctypedef struct rbd_mirror_image_info_t:
char *global_id
rbd_mirror_image_state_t state
bint primary
ctypedef enum rbd_mirror_image_status_state_t:
_MIRROR_IMAGE_STATUS_STATE_UNKNOWN "MIRROR_IMAGE_STATUS_STATE_UNKNOWN"
_MIRROR_IMAGE_STATUS_STATE_ERROR "MIRROR_IMAGE_STATUS_STATE_ERROR"
_MIRROR_IMAGE_STATUS_STATE_SYNCING "MIRROR_IMAGE_STATUS_STATE_SYNCING"
_MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY"
_MIRROR_IMAGE_STATUS_STATE_REPLAYING "MIRROR_IMAGE_STATUS_STATE_REPLAYING"
_MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY "MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY"
_MIRROR_IMAGE_STATUS_STATE_STOPPED "MIRROR_IMAGE_STATUS_STATE_STOPPED"
ctypedef struct rbd_mirror_image_status_t:
char *name
rbd_mirror_image_info_t info
rbd_mirror_image_status_state_t state
char *description
time_t last_update
bint up
void rbd_version(int *major, int *minor, int *extra) void rbd_version(int *major, int *minor, int *extra)
void rbd_image_options_create(rbd_image_options_t* opts) void rbd_image_options_create(rbd_image_options_t* opts)
@ -108,6 +150,31 @@ cdef extern from "rbd/librbd.h" nogil:
int rbd_remove(rados_ioctx_t io, const char *name) int rbd_remove(rados_ioctx_t io, const char *name)
int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname, int rbd_rename(rados_ioctx_t src_io_ctx, const char *srcname,
const char *destname) const char *destname)
int rbd_mirror_mode_get(rados_ioctx_t io, rbd_mirror_mode_t *mirror_mode)
int rbd_mirror_mode_set(rados_ioctx_t io, rbd_mirror_mode_t mirror_mode)
int rbd_mirror_peer_add(rados_ioctx_t io, char *uuid,
size_t uuid_max_length, const char *cluster_name,
const char *client_name)
int rbd_mirror_peer_remove(rados_ioctx_t io, const char *uuid)
int rbd_mirror_peer_list(rados_ioctx_t io_ctx, rbd_mirror_peer_t *peers,
int *max_peers)
void rbd_mirror_peer_list_cleanup(rbd_mirror_peer_t *peers, int max_peers)
int rbd_mirror_peer_set_client(rados_ioctx_t io, const char *uuid,
const char *client_name)
int rbd_mirror_peer_set_cluster(rados_ioctx_t io_ctx, const char *uuid,
const char *cluster_name)
int rbd_mirror_image_status_list(rados_ioctx_t io, const char *start_id,
size_t max, char **image_ids,
rbd_mirror_image_status_t *images,
size_t *len)
void rbd_mirror_image_status_list_cleanup(char **image_ids,
rbd_mirror_image_status_t *images,
size_t len)
int rbd_mirror_image_status_summary(rados_ioctx_t io,
rbd_mirror_image_status_state_t *states,
int *counts, size_t *maxlen)
int rbd_open(rados_ioctx_t io, const char *name, int rbd_open(rados_ioctx_t io, const char *name,
rbd_image_t *image, const char *snap_name) rbd_image_t *image, const char *snap_name)
int rbd_open_read_only(rados_ioctx_t io, const char *name, int rbd_open_read_only(rados_ioctx_t io, const char *name,
@ -179,6 +246,17 @@ cdef extern from "rbd/librbd.h" nogil:
int rbd_flush(rbd_image_t image) int rbd_flush(rbd_image_t image)
int rbd_invalidate_cache(rbd_image_t image) int rbd_invalidate_cache(rbd_image_t image)
int rbd_mirror_image_enable(rbd_image_t image)
int rbd_mirror_image_disable(rbd_image_t image, bint force)
int rbd_mirror_image_promote(rbd_image_t image, bint force)
int rbd_mirror_image_demote(rbd_image_t image)
int rbd_mirror_image_resync(rbd_image_t image)
int rbd_mirror_image_get_info(rbd_image_t image,
rbd_mirror_image_info_t *mirror_image_info,
size_t info_size)
int rbd_mirror_image_get_status(rbd_image_t image,
rbd_mirror_image_status_t *mirror_image_status,
size_t status_size)
RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING RBD_FEATURE_LAYERING = _RBD_FEATURE_LAYERING
RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2 RBD_FEATURE_STRIPINGV2 = _RBD_FEATURE_STRIPINGV2
@ -196,6 +274,22 @@ RBD_FEATURES_ALL = _RBD_FEATURES_ALL
RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID RBD_FLAG_OBJECT_MAP_INVALID = _RBD_FLAG_OBJECT_MAP_INVALID
RBD_MIRROR_MODE_DISABLED = _RBD_MIRROR_MODE_DISABLED
RBD_MIRROR_MODE_IMAGE = _RBD_MIRROR_MODE_IMAGE
RBD_MIRROR_MODE_POOL = _RBD_MIRROR_MODE_POOL
RBD_MIRROR_IMAGE_DISABLING = _RBD_MIRROR_IMAGE_DISABLING
RBD_MIRROR_IMAGE_ENABLED = _RBD_MIRROR_IMAGE_ENABLED
RBD_MIRROR_IMAGE_DISABLED = _RBD_MIRROR_IMAGE_DISABLED
MIRROR_IMAGE_STATUS_STATE_UNKNOWN = _MIRROR_IMAGE_STATUS_STATE_UNKNOWN
MIRROR_IMAGE_STATUS_STATE_ERROR = _MIRROR_IMAGE_STATUS_STATE_ERROR
MIRROR_IMAGE_STATUS_STATE_SYNCING = _MIRROR_IMAGE_STATUS_STATE_SYNCING
MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STARTING_REPLAY
MIRROR_IMAGE_STATUS_STATE_REPLAYING = _MIRROR_IMAGE_STATUS_STATE_REPLAYING
MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY = _MIRROR_IMAGE_STATUS_STATE_STOPPING_REPLAY
MIRROR_IMAGE_STATUS_STATE_STOPPED = _MIRROR_IMAGE_STATUS_STATE_STOPPED
RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT RBD_IMAGE_OPTION_FORMAT = _RBD_IMAGE_OPTION_FORMAT
RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES RBD_IMAGE_OPTION_FEATURES = _RBD_IMAGE_OPTION_FEATURES
RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER RBD_IMAGE_OPTION_ORDER = _RBD_IMAGE_OPTION_ORDER
@ -566,6 +660,314 @@ class RBD(object):
if ret != 0: if ret != 0:
raise make_ex(ret, 'error renaming image') raise make_ex(ret, 'error renaming image')
def mirror_mode_get(self, ioctx):
"""
Get pool mirror mode.
:param ioctx: determines which RADOS pool is read
:type ioctx: :class:`rados.Ioctx`
:returns: int - pool mirror mode
"""
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
rbd_mirror_mode_t mirror_mode
with nogil:
ret = rbd_mirror_mode_get(_ioctx, &mirror_mode)
if ret != 0:
raise make_ex(ret, 'error getting mirror mode')
return mirror_mode
def mirror_mode_set(self, ioctx, mirror_mode):
"""
Set pool mirror mode.
:param ioctx: determines which RADOS pool is written
:type ioctx: :class:`rados.Ioctx`
:param mirror_mode: mirror mode to set
:type mirror_mode: int
"""
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
rbd_mirror_mode_t _mirror_mode = mirror_mode
with nogil:
ret = rbd_mirror_mode_set(_ioctx, _mirror_mode)
if ret != 0:
raise make_ex(ret, 'error setting mirror mode')
def mirror_peer_add(self, ioctx, cluster_name, client_name):
"""
Add mirror peer.
:param ioctx: determines which RADOS pool is used
:type ioctx: :class:`rados.Ioctx`
:param cluster_name: mirror peer cluster name
:type cluster_name: str
:param client_name: mirror peer client name
:type client_name: str
:returns: str - peer uuid
"""
cluster_name = cstr(cluster_name, 'cluster_name')
client_name = cstr(client_name, 'client_name')
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
char *_uuid = NULL
size_t _uuid_max_length = 512
char *_cluster_name = cluster_name
char *_client_name = client_name
try:
_uuid = <char *>realloc_chk(_uuid, _uuid_max_length)
ret = rbd_mirror_peer_add(_ioctx, _uuid, _uuid_max_length,
_cluster_name, _client_name)
if ret != 0:
raise make_ex(ret, 'error adding mirror peer')
return decode_cstr(_uuid)
finally:
free(_uuid)
def mirror_peer_remove(self, ioctx, uuid):
"""
Remove mirror peer.
:param ioctx: determines which RADOS pool is used
:type ioctx: :class:`rados.Ioctx`
:param uuid: peer uuid
:type uuid: str
"""
uuid = cstr(uuid, 'uuid')
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
char *_uuid = uuid
with nogil:
ret = rbd_mirror_peer_remove(_ioctx, _uuid)
if ret != 0:
raise make_ex(ret, 'error removing mirror peer')
def mirror_peer_list(self, ioctx):
"""
Iterate over the peers of a pool.
:param ioctx: determines which RADOS pool is read
:type ioctx: :class:`rados.Ioctx`
:returns: :class:`MirrorPeerIterator`
"""
return MirrorPeerIterator(ioctx)
def mirror_peer_set_client(self, ioctx, uuid, client_name):
"""
Set mirror peer client name
:param ioctx: determines which RADOS pool is written
:type ioctx: :class:`rados.Ioctx`
:param uuid: uuid of the mirror peer
:type uuid: str
:param client_name: client name of the mirror peer to set
:type client_name: str
"""
uuid = cstr(uuid, 'uuid')
client_name = cstr(client_name, 'client_name')
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
char *_uuid = uuid
char *_client_name = client_name
with nogil:
ret = rbd_mirror_peer_set_client(_ioctx, _uuid, _client_name)
if ret != 0:
raise make_ex(ret, 'error setting mirror peer client')
def mirror_peer_set_cluster(self, ioctx, uuid, cluster_name):
"""
Set mirror peer cluster name
:param ioctx: determines which RADOS pool is written
:type ioctx: :class:`rados.Ioctx`
:param uuid: uuid of the mirror peer
:type uuid: str
:param cluster_name: cluster name of the mirror peer to set
:type cluster_name: str
"""
uuid = cstr(uuid, 'uuid')
cluster_name = cstr(cluster_name, 'cluster_name')
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
char *_uuid = uuid
char *_cluster_name = cluster_name
with nogil:
ret = rbd_mirror_peer_set_cluster(_ioctx, _uuid, _cluster_name)
if ret != 0:
raise make_ex(ret, 'error setting mirror peer cluster')
def mirror_image_status_list(self, ioctx):
"""
Iterate over the mirror image statuses of a pool.
:param ioctx: determines which RADOS pool is read
:type ioctx: :class:`rados.Ioctx`
:returns: :class:`MirrorImageStatus`
"""
return MirrorImageStatusIterator(ioctx)
def mirror_image_status_summary(self, ioctx):
"""
Get mirror image status summary of a pool.
:param ioctx: determines which RADOS pool is read
:type ioctx: :class:`rados.Ioctx`
:returns: list - a list of (state, count) tuples
"""
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
rbd_mirror_image_status_state_t *states = NULL
int *counts = NULL
size_t maxlen = 32
try:
states = <rbd_mirror_image_status_state_t *>realloc_chk(states,
sizeof(rbd_mirror_image_status_state_t) * maxlen)
counts = <int *>realloc_chk(counts, sizeof(int) * maxlen)
with nogil:
ret = rbd_mirror_image_status_summary(_ioctx, states, counts,
&maxlen)
if ret < 0:
raise make_ex(ret, 'error getting mirror image status summary')
return [(states[i], counts[i]) for i in range(maxlen)]
finally:
free(states)
free(counts)
cdef class MirrorPeerIterator(object):
"""
Iterator over mirror peer info for a pool.
Yields a dictionary containing information about a peer.
Keys are:
* ``uuid`` (str) - uuid of the peer
* ``cluster_name`` (str) - cluster name of the peer
* ``client_name`` (str) - client name of the peer
"""
cdef:
rbd_mirror_peer_t *peers
int num_peers
def __init__(self, ioctx):
cdef:
rados_ioctx_t _ioctx = convert_ioctx(ioctx)
self.peers = NULL
self.num_peers = 10
while True:
self.peers = <rbd_mirror_peer_t *>realloc_chk(
self.peers, self.num_peers * sizeof(rbd_mirror_peer_t))
with nogil:
ret = rbd_mirror_peer_list(_ioctx, self.peers, &self.num_peers)
if ret < 0:
if ret == -errno.ERANGE:
continue
self.num_peers = 0
raise make_ex(ret, 'error listing peers')
break
def __iter__(self):
for i in range(self.num_peers):
yield {
'uuid' : decode_cstr(self.peers[i].uuid),
'cluster_name' : decode_cstr(self.peers[i].cluster_name),
'client_name' : decode_cstr(self.peers[i].client_name),
}
def __dealloc__(self):
if self.peers:
rbd_mirror_peer_list_cleanup(self.peers, self.num_peers)
free(self.peers)
cdef class MirrorImageStatusIterator(object):
"""
Iterator over mirror image status for a pool.
Yields a dictionary containing mirror status of an image.
Keys are:
* ``name`` (str) - mirror image name
* `info` (dict) - mirror image info
* `state` (int) - mirror state
* `description` (str) - status description
* `last_update` (datetime) - last status update time
* ``up`` (bool) - is mirroring agent up
"""
cdef:
rados_ioctx_t ioctx
size_t max_read
char *last_read
char **image_ids
rbd_mirror_image_status_t *images
size_t size
def __init__(self, ioctx):
self.ioctx = convert_ioctx(ioctx)
self.max_read = 1024
self.last_read = strdup("")
self.image_ids = <char **>realloc_chk(NULL,
sizeof(char *) * self.max_read)
self.images = <rbd_mirror_image_status_t *>realloc_chk(NULL,
sizeof(rbd_mirror_image_status_t) * self.max_read)
self.size = 0
self.get_next_chunk()
def __iter__(self):
while self.size > 0:
for i in range(self.size):
yield {
'name' : decode_cstr(self.images[i].name),
'info' : {
'global_id' : decode_cstr(self.images[i].info.global_id),
'state' : self.images[i].info.state,
},
'state' : self.images[i].state,
'description' : decode_cstr(self.images[i].description),
'last_update' : datetime.fromtimestamp(self.images[i].last_update),
'up' : self.images[i].up,
}
if self.size < self.max_read:
break
self.get_next_chunk()
def __dealloc__(self):
rbd_mirror_image_status_list_cleanup(self.image_ids, self.images,
self.size)
if self.last_read:
free(self.last_read)
if self.image_ids:
free(self.image_ids)
if self.images:
free(self.images)
def get_next_chunk(self):
if self.size > 0:
rbd_mirror_image_status_list_cleanup(self.image_ids, self.images,
self.size)
self.size = 0
with nogil:
ret = rbd_mirror_image_status_list(self.ioctx, self.last_read,
self.max_read, self.image_ids,
self.images, &self.size)
if ret < 0:
raise make_ex(ret, 'error listing mirror images status')
if self.size > 0:
free(self.last_read)
last_read = decode_cstr(self.image_ids[self.size - 1])
self.last_read = strdup(last_read)
else:
free(self.last_read)
self.last_read = strdup("")
cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \ cdef int diff_iterate_cb(uint64_t offset, size_t length, int write, void *cb) \
except? -9000 with gil: except? -9000 with gil:
@ -1398,6 +1800,130 @@ written." % (self.name, ret, length))
if ret < 0: if ret < 0:
raise make_ex(ret, 'error unlocking image') raise make_ex(ret, 'error unlocking image')
def mirror_image_enable(self):
"""
Enable mirroring for the image.
"""
with nogil:
ret = rbd_mirror_image_enable(self.image)
if ret < 0:
raise make_ex(ret, 'error enabling mirroring for image %s'
% (self.name,))
def mirror_image_disable(self, force):
"""
Disable mirroring for the image.
:param force: force disabling
:type force: bool
"""
cdef bint c_force = force
with nogil:
ret = rbd_mirror_image_disable(self.image, c_force)
if ret < 0:
raise make_ex(ret, 'error disabling mirroring for image %s' %
(self.name,))
def mirror_image_promote(self, force):
"""
Promote the image to primary for mirroring.
:param force: force promoting
:type force: bool
"""
cdef bint c_force = force
with nogil:
ret = rbd_mirror_image_promote(self.image, c_force)
if ret < 0:
raise make_ex(ret, 'error promoting image %s to primary' %
(self.name,))
def mirror_image_demote(self):
"""
Demote the image to secondary for mirroring.
"""
with nogil:
ret = rbd_mirror_image_demote(self.image)
if ret < 0:
raise make_ex(ret, 'error demoting image %s to secondary' %
(self.name,))
def mirror_image_resync(self):
"""
Flag the image to resync.
"""
with nogil:
ret = rbd_mirror_image_resync(self.image)
if ret < 0:
raise make_ex(ret, 'error to resync image %s' % (self.name,))
def mirror_image_get_info(self):
"""
Get mirror info for the image.
:returns: dict - contains the following keys:
* ``global_id`` (str) - image global id
* ``state`` (int) - mirror state
* ``primary`` (bool) - is image primary
"""
cdef rbd_mirror_image_info_t c_info
with nogil:
ret = rbd_mirror_image_get_info(self.image, &c_info, sizeof(c_info))
if ret != 0:
raise make_ex(ret, 'error getting mirror info for image %s' %
(self.name,))
info = {
'global_id' : decode_cstr(c_info.global_id),
'state' : int(c_info.state),
'primary' : c_info.primary,
}
free(c_info.global_id)
return info
def mirror_image_get_status(self):
"""
Get mirror status for the image.
:returns: dict - contains the following keys:
* ``name`` (str) - mirror image name
* `info` (dict) - mirror image info
* ``state`` (int) - status mirror state
* ``description`` (str) - status description
* ``last_update`` (datetime) - last status update time
* ``up`` (bool) - is mirroring agent up
"""
cdef rbd_mirror_image_status_t c_status
with nogil:
ret = rbd_mirror_image_get_status(self.image, &c_status,
sizeof(c_status))
if ret != 0:
raise make_ex(ret, 'error getting mirror status for image %s' %
(self.name,))
status = {
'name' : decode_cstr(c_status.name),
'info' : {
'global_id' : decode_cstr(c_status.info.global_id),
'state' : int(c_status.info.state),
'primary' : c_status.info.primary,
},
'state' : c_status.state,
'description' : decode_cstr(c_status.description),
'last_update' : datetime.fromtimestamp(c_status.last_update),
'up' : c_status.up,
}
free(c_status.name)
free(c_status.info.global_id)
free(c_status.description)
return status
cdef class SnapIterator(object): cdef class SnapIterator(object):
""" """