]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/test/pybind/test_rbd.py
import 15.2.0 Octopus source
[ceph.git] / ceph / src / test / pybind / test_rbd.py
index 214266515fe4c0c906e3f0682fcc08a23f4bd411..1bd84e81f84ad45403d2308a08888c4cd2840883 100644 (file)
@@ -1,5 +1,6 @@
 # vim: expandtab smarttab shiftwidth=4 softtabstop=4
 import base64
+import copy
 import errno
 import functools
 import json
@@ -22,16 +23,22 @@ from rbd import (RBD, Group, Image, ImageNotFound, InvalidArgument, ImageExists,
                  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
@@ -407,6 +414,20 @@ def test_config_list():
     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()
 
@@ -462,6 +483,35 @@ def check_stat(info, size, order):
     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):
@@ -726,6 +776,11 @@ class TestImage(object):
         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')
@@ -747,6 +802,26 @@ class TestImage(object):
         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')
@@ -769,6 +844,12 @@ class TestImage(object):
         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()])
@@ -915,6 +996,38 @@ class TestImage(object):
         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)
@@ -1140,6 +1253,20 @@ class TestImage(object):
             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)
@@ -1778,6 +1905,10 @@ class TestMirroring(object):
         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)
@@ -1805,13 +1936,17 @@ class TestMirroring(object):
 
     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)))
@@ -1821,7 +1956,10 @@ class TestMirroring(object):
         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)))
@@ -1862,12 +2000,20 @@ class TestMirroring(object):
         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()
@@ -1894,6 +2040,7 @@ class TestMirroring(object):
         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)
 
@@ -1922,6 +2069,70 @@ class TestMirroring(object):
         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):