# vim: expandtab smarttab shiftwidth=4 softtabstop=4
import base64
+import copy
import errno
import functools
import json
DiskQuotaExceeded, ConnectionShutdown, PermissionError,
RBD_FEATURE_LAYERING, RBD_FEATURE_STRIPINGV2,
RBD_FEATURE_EXCLUSIVE_LOCK, RBD_FEATURE_JOURNALING,
+ RBD_FEATURE_DEEP_FLATTEN, RBD_FEATURE_FAST_DIFF,
+ RBD_FEATURE_OBJECT_MAP,
RBD_MIRROR_MODE_DISABLED, RBD_MIRROR_MODE_IMAGE,
RBD_MIRROR_MODE_POOL, RBD_MIRROR_IMAGE_ENABLED,
RBD_MIRROR_IMAGE_DISABLED, MIRROR_IMAGE_STATUS_STATE_UNKNOWN,
+ RBD_MIRROR_IMAGE_MODE_JOURNAL, RBD_MIRROR_IMAGE_MODE_SNAPSHOT,
RBD_LOCK_MODE_EXCLUSIVE, RBD_OPERATION_FEATURE_GROUP,
RBD_SNAP_NAMESPACE_TYPE_TRASH,
+ RBD_SNAP_NAMESPACE_TYPE_MIRROR,
RBD_IMAGE_MIGRATION_STATE_PREPARED, RBD_CONFIG_SOURCE_CONFIG,
RBD_CONFIG_SOURCE_POOL, RBD_CONFIG_SOURCE_IMAGE,
RBD_MIRROR_PEER_ATTRIBUTE_NAME_MON_HOST,
RBD_MIRROR_PEER_ATTRIBUTE_NAME_KEY,
- RBD_MIRROR_PEER_DIRECTION_RX)
+ RBD_MIRROR_PEER_DIRECTION_RX, RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ RBD_SNAP_REMOVE_UNPROTECT, RBD_SNAP_MIRROR_STATE_PRIMARY,
+ RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED)
rados = None
ioctx = None
for option in rbd.config_list(ioctx):
eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+def test_pool_config_set_and_get_and_remove():
+ rbd = RBD()
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ rbd.config_set(ioctx, "rbd_request_timed_out_seconds", "100")
+ new_value = rbd.config_get(ioctx, "rbd_request_timed_out_seconds")
+ eq(new_value, "100")
+ rbd.config_remove(ioctx, "rbd_request_timed_out_seconds")
+
+ for option in rbd.config_list(ioctx):
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
def test_namespaces():
rbd = RBD()
eq(info['num_objs'], size // (1 << order))
eq(info['obj_size'], 1 << order)
+@require_new_format()
+def test_features_to_string():
+ rbd = RBD()
+ features = RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
+ | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
+ expected_features_string = "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
+ features_string = rbd.features_to_string(features)
+ eq(expected_features_string, features_string)
+
+ features = RBD_FEATURE_LAYERING
+ features_string = rbd.features_to_string(features)
+ eq(features_string, "layering")
+
+ features = 16777216
+ assert_raises(InvalidArgument, rbd.features_to_string, features)
+
+@require_new_format()
+def test_features_from_string():
+ rbd = RBD()
+ features_string = "deep-flatten,exclusive-lock,fast-diff,layering,object-map"
+ expected_features_bitmask = RBD_FEATURE_DEEP_FLATTEN | RBD_FEATURE_EXCLUSIVE_LOCK | RBD_FEATURE_FAST_DIFF \
+ | RBD_FEATURE_LAYERING | RBD_FEATURE_OBJECT_MAP
+ features = rbd.features_from_string(features_string)
+ eq(expected_features_bitmask, features)
+
+ features_string = "layering"
+ features = rbd.features_from_string(features_string)
+ eq(features, RBD_FEATURE_LAYERING)
+
class TestImage(object):
def setUp(self):
eq(snap_data, b'\0' * 256)
self.image.remove_snap('snap1')
+ def test_create_snap_exists(self):
+ self.image.create_snap('snap1')
+ assert_raises(ImageExists, self.image.create_snap, 'snap1')
+ self.image.remove_snap('snap1')
+
def test_list_snaps(self):
eq([], list(self.image.list_snaps()))
self.image.create_snap('snap1')
self.image.remove_snap('snap1')
eq([], list(self.image.list_snaps()))
+ def test_remove_snap_not_found(self):
+ assert_raises(ImageNotFound, self.image.remove_snap, 'snap1')
+
+ @require_features([RBD_FEATURE_LAYERING])
+ def test_remove_snap2(self):
+ self.image.create_snap('snap1')
+ self.image.protect_snap('snap1')
+ assert(self.image.is_protected_snap('snap1'))
+ self.image.remove_snap2('snap1', RBD_SNAP_REMOVE_UNPROTECT)
+ eq([], list(self.image.list_snaps()))
+
+ def test_remove_snap_by_id(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
+ for snap in self.image.list_snaps():
+ snap_id = snap["id"]
+ self.image.remove_snap_by_id(snap_id)
+ eq([], list(self.image.list_snaps()))
+
def test_rename_snap(self):
eq([], list(self.image.list_snaps()))
self.image.create_snap('snap1')
assert_raises(ImageNotFound, self.image.unprotect_snap, 'snap1')
assert_raises(ImageNotFound, self.image.is_protected_snap, 'snap1')
+ def test_snap_exists(self):
+ self.image.create_snap('snap1')
+ eq(self.image.snap_exists('snap1'), True)
+ self.image.remove_snap('snap1')
+ eq(self.image.snap_exists('snap1'), False)
+
def test_snap_timestamp(self):
self.image.create_snap('snap1')
eq(['snap1'], [snap['name'] for snap in self.image.list_snaps()])
eq(read, data)
self.image.remove_snap('snap1')
+ def test_snap_get_name(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ self.image.create_snap('snap2')
+ self.image.create_snap('snap3')
+
+ for snap in self.image.list_snaps():
+ expected_snap_name = self.image.snap_get_name(snap['id'])
+ eq(expected_snap_name, snap['name'])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+ self.image.remove_snap('snap3')
+ eq([], list(self.image.list_snaps()))
+
+ assert_raises(ImageNotFound, self.image.snap_get_name, 1)
+
+ def test_snap_get_id(self):
+ eq([], list(self.image.list_snaps()))
+ self.image.create_snap('snap1')
+ self.image.create_snap('snap2')
+ self.image.create_snap('snap3')
+
+ for snap in self.image.list_snaps():
+ expected_snap_id = self.image.snap_get_id(snap['name'])
+ eq(expected_snap_id, snap['id'])
+ self.image.remove_snap('snap1')
+ self.image.remove_snap('snap2')
+ self.image.remove_snap('snap3')
+ eq([], list(self.image.list_snaps()))
+
+ assert_raises(ImageNotFound, self.image.snap_get_id, 'snap1')
+
def test_set_snap_sparse(self):
self.image.create_snap('snap1')
read = self.image.read(0, 256)
for option in image.config_list():
eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+ def test_image_config_set_and_get_and_remove(self):
+ with Image(ioctx, image_name) as image:
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
+ image.config_set("rbd_request_timed_out_seconds", "100")
+ modify_value = image.config_get("rbd_request_timed_out_seconds")
+ eq(modify_value, '100')
+
+ image.config_remove("rbd_request_timed_out_seconds")
+
+ for option in image.config_list():
+ eq(option['source'], RBD_CONFIG_SOURCE_CONFIG)
+
def test_sparsify(self):
assert_raises(InvalidArgument, self.image.sparsify, 16)
self.image.sparsify(4096)
remove_image()
self.rbd.mirror_mode_set(ioctx, self.initial_mirror_mode)
+ def test_uuid(self):
+ mirror_uuid = self.rbd.mirror_uuid_get(ioctx)
+ assert(mirror_uuid)
+
def test_site_name(self):
site_name = "us-west-1"
self.rbd.mirror_site_name_set(rados, site_name)
def test_mirror_peer(self):
eq([], list(self.rbd.mirror_peer_list(ioctx)))
- cluster_name = "test_cluster"
+ site_name = "test_site"
client_name = "test_client"
- uuid = self.rbd.mirror_peer_add(ioctx, cluster_name, client_name)
+ uuid = self.rbd.mirror_peer_add(ioctx, site_name, client_name,
+ direction=RBD_MIRROR_PEER_DIRECTION_RX_TX)
assert(uuid)
peer = {
'uuid' : uuid,
- 'cluster_name' : cluster_name,
+ 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ 'site_name' : site_name,
+ 'cluster_name' : site_name,
+ 'mirror_uuid': '',
'client_name' : client_name,
}
eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
self.rbd.mirror_peer_set_client(ioctx, uuid, client_name)
peer = {
'uuid' : uuid,
+ 'direction': RBD_MIRROR_PEER_DIRECTION_RX_TX,
+ 'site_name' : cluster_name,
'cluster_name' : cluster_name,
+ 'mirror_uuid': '',
'client_name' : client_name,
}
eq([peer], list(self.rbd.mirror_peer_list(ioctx)))
info = self.image.mirror_image_get_info()
self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, False)
+ entries = dict(self.rbd.mirror_image_info_list(ioctx))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
+ eq(info, entries[self.image.id()])
+
self.image.mirror_image_resync()
self.image.mirror_image_promote(True)
info = self.image.mirror_image_get_info()
self.check_info(info, global_id, RBD_MIRROR_IMAGE_ENABLED, True)
+ entries = dict(self.rbd.mirror_image_info_list(ioctx))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_JOURNAL;
+ eq(info, entries[self.image.id()])
+
fail = False
try:
self.image.mirror_image_resync()
eq(image_name, status['name'])
eq(False, status['up'])
eq(MIRROR_IMAGE_STATUS_STATE_UNKNOWN, status['state'])
+ eq([], status['remote_statuses'])
info = status['info']
self.check_info(info, global_id, state, primary)
for i in range(N):
self.rbd.remove(ioctx, image_name + str(i))
+ def test_mirror_image_create_snapshot(self):
+ assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
+
+ peer1_uuid = self.rbd.mirror_peer_add(ioctx, "cluster1", "client")
+ peer2_uuid = self.rbd.mirror_peer_add(ioctx, "cluster2", "client")
+ self.rbd.mirror_mode_set(ioctx, RBD_MIRROR_MODE_IMAGE)
+ self.image.mirror_image_disable(False)
+ self.image.mirror_image_enable(RBD_MIRROR_IMAGE_MODE_SNAPSHOT)
+ mode = self.image.mirror_image_get_mode()
+ eq(RBD_MIRROR_IMAGE_MODE_SNAPSHOT, mode)
+
+ snaps = list(self.image.list_snaps())
+ eq(1, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+
+ info = self.image.mirror_image_get_info()
+ eq(True, info['primary'])
+ entries = dict(
+ self.rbd.mirror_image_info_list(ioctx,
+ RBD_MIRROR_IMAGE_MODE_SNAPSHOT))
+ info['mode'] = RBD_MIRROR_IMAGE_MODE_SNAPSHOT;
+ eq(info, entries[self.image.id()])
+
+ snap_id = self.image.mirror_image_create_snapshot()
+
+ snaps = list(self.image.list_snaps())
+ eq(2, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+ snap = snaps[1]
+ eq(snap['id'], snap_id)
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY, snap['mirror']['state'])
+ eq(sorted([peer1_uuid, peer2_uuid]),
+ sorted(snap['mirror']['mirror_peer_uuids']))
+
+ eq(RBD_SNAP_NAMESPACE_TYPE_MIRROR,
+ self.image.snap_get_namespace_type(snap_id))
+ mirror_snap = self.image.snap_get_mirror_namespace(snap_id)
+ eq(mirror_snap, snap['mirror'])
+
+ self.image.mirror_image_demote()
+
+ assert_raises(InvalidArgument, self.image.mirror_image_create_snapshot)
+
+ snaps = list(self.image.list_snaps())
+ eq(3, len(snaps))
+ snap = snaps[0]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ snap = snaps[1]
+ eq(snap['id'], snap_id)
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ snap = snaps[2]
+ eq(snap['namespace'], RBD_SNAP_NAMESPACE_TYPE_MIRROR)
+ eq(RBD_SNAP_MIRROR_STATE_PRIMARY_DEMOTED, snap['mirror']['state'])
+ eq(sorted([peer1_uuid, peer2_uuid]),
+ sorted(snap['mirror']['mirror_peer_uuids']))
+
+ self.rbd.mirror_peer_remove(ioctx, peer1_uuid)
+ self.rbd.mirror_peer_remove(ioctx, peer2_uuid)
+ self.image.mirror_image_promote(False)
class TestTrash(object):