]> git.proxmox.com Git - ceph.git/blobdiff - ceph/src/pybind/mgr/dashboard/services/rbd.py
update ceph source to reef 18.2.1
[ceph.git] / ceph / src / pybind / mgr / dashboard / services / rbd.py
index 98fbba832573e8383aa01f62d944e2e12ee31e29..bb769ce194653fa0c327272346a5ea7ccebbd500 100644 (file)
@@ -2,6 +2,7 @@
 # pylint: disable=unused-argument
 import errno
 import json
+import math
 from enum import IntEnum
 
 import cherrypy
@@ -10,7 +11,7 @@ import rbd
 
 from .. import mgr
 from ..exceptions import DashboardException
-from ..plugins.ttl_cache import ttl_cache
+from ..plugins.ttl_cache import ttl_cache, ttl_cache_invalidator
 from ._paginate import ListPaginator
 from .ceph_service import CephService
 
@@ -32,6 +33,10 @@ RBD_FEATURES_NAME_MAPPING = {
     rbd.RBD_FEATURE_OPERATIONS: "operations",
 }
 
+RBD_IMAGE_REFS_CACHE_REFERENCE = 'rbd_image_refs'
+GET_IOCTX_CACHE = 'get_ioctx'
+POOL_NAMESPACES_CACHE = 'pool_namespaces'
+
 
 class MIRROR_IMAGE_MODE(IntEnum):
     journal = rbd.RBD_MIRROR_IMAGE_MODE_JOURNAL
@@ -86,6 +91,25 @@ def format_features(features):
     return res
 
 
+def _sort_features(features, enable=True):
+    """
+    Sorts image features according to feature dependencies:
+
+    object-map depends on exclusive-lock
+    journaling depends on exclusive-lock
+    fast-diff depends on object-map
+    """
+    ORDER = ['exclusive-lock', 'journaling', 'object-map', 'fast-diff']  # noqa: N806
+
+    def key_func(feat):
+        try:
+            return ORDER.index(feat)
+        except ValueError:
+            return id(feat)
+
+    features.sort(key=key_func, reverse=not enable)
+
+
 def get_image_spec(pool_name, namespace, rbd_name):
     namespace = '{}/'.format(namespace) if namespace else ''
     return '{}/{}{}'.format(pool_name, namespace, rbd_name)
@@ -244,6 +268,13 @@ class RbdConfiguration(object):
 class RbdService(object):
     _rbd_inst = rbd.RBD()
 
+    # set of image features that can be enable on existing images
+    ALLOW_ENABLE_FEATURES = {"exclusive-lock", "object-map", "fast-diff", "journaling"}
+
+    # set of image features that can be disabled on existing images
+    ALLOW_DISABLE_FEATURES = {"exclusive-lock", "object-map", "fast-diff", "deep-flatten",
+                              "journaling"}
+
     @classmethod
     def _rbd_disk_usage(cls, image, snaps, whole_object=True):
         class DUCallback(object):
@@ -393,14 +424,14 @@ class RbdService(object):
         return stat_parent
 
     @classmethod
-    @ttl_cache(10)
+    @ttl_cache(10, label=GET_IOCTX_CACHE)
     def get_ioctx(cls, pool_name, namespace=''):
         ioctx = mgr.rados.open_ioctx(pool_name)
         ioctx.set_namespace(namespace)
         return ioctx
 
     @classmethod
-    @ttl_cache(30)
+    @ttl_cache(30, label=RBD_IMAGE_REFS_CACHE_REFERENCE)
     def _rbd_image_refs(cls, pool_name, namespace=''):
         # We add and set the namespace here so that we cache by ioctx and namespace.
         images = []
@@ -409,7 +440,7 @@ class RbdService(object):
         return images
 
     @classmethod
-    @ttl_cache(30)
+    @ttl_cache(30, label=POOL_NAMESPACES_CACHE)
     def _pool_namespaces(cls, pool_name, namespace=None):
         namespaces = []
         if namespace:
@@ -491,6 +522,167 @@ class RbdService(object):
         except rbd.ImageNotFound:
             raise cherrypy.HTTPError(404, 'Image not found')
 
+    @classmethod
+    @ttl_cache_invalidator(RBD_IMAGE_REFS_CACHE_REFERENCE)
+    def create(cls, name, pool_name, size, namespace=None,
+               obj_size=None, features=None, stripe_unit=None, stripe_count=None,
+               data_pool=None, configuration=None, metadata=None):
+        size = int(size)
+
+        def _create(ioctx):
+            rbd_inst = cls._rbd_inst
+
+            # Set order
+            l_order = None
+            if obj_size and obj_size > 0:
+                l_order = int(round(math.log(float(obj_size), 2)))
+
+            # Set features
+            feature_bitmask = format_features(features)
+
+            rbd_inst.create(ioctx, name, size, order=l_order, old_format=False,
+                            features=feature_bitmask, stripe_unit=stripe_unit,
+                            stripe_count=stripe_count, data_pool=data_pool)
+            RbdConfiguration(pool_ioctx=ioctx, namespace=namespace,
+                             image_name=name).set_configuration(configuration)
+            if metadata:
+                with rbd.Image(ioctx, name) as image:
+                    RbdImageMetadataService(image).set_metadata(metadata)
+        rbd_call(pool_name, namespace, _create)
+
+    @classmethod
+    @ttl_cache_invalidator(RBD_IMAGE_REFS_CACHE_REFERENCE)
+    def set(cls, image_spec, name=None, size=None, features=None,
+            configuration=None, metadata=None, enable_mirror=None, primary=None,
+            force=False, resync=False, mirror_mode=None, schedule_interval='',
+            remove_scheduling=False):
+        # pylint: disable=too-many-branches
+        pool_name, namespace, image_name = parse_image_spec(image_spec)
+
+        def _edit(ioctx, image):
+            rbd_inst = cls._rbd_inst
+            # check rename image
+            if name and name != image_name:
+                rbd_inst.rename(ioctx, image_name, name)
+
+            # check resize
+            if size and size != image.size():
+                image.resize(size)
+
+            mirror_image_info = image.mirror_image_get_info()
+            if enable_mirror and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_DISABLED:
+                RbdMirroringService.enable_image(
+                    image_name, pool_name, namespace,
+                    MIRROR_IMAGE_MODE[mirror_mode])
+            elif (enable_mirror is False
+                  and mirror_image_info['state'] == rbd.RBD_MIRROR_IMAGE_ENABLED):
+                RbdMirroringService.disable_image(
+                    image_name, pool_name, namespace)
+
+            # check enable/disable features
+            if features is not None:
+                curr_features = format_bitmask(image.features())
+                # check disabled features
+                _sort_features(curr_features, enable=False)
+                for feature in curr_features:
+                    if (feature not in features
+                       and feature in cls.ALLOW_DISABLE_FEATURES
+                       and feature in format_bitmask(image.features())):
+                        f_bitmask = format_features([feature])
+                        image.update_features(f_bitmask, False)
+                # check enabled features
+                _sort_features(features)
+                for feature in features:
+                    if (feature not in curr_features
+                       and feature in cls.ALLOW_ENABLE_FEATURES
+                       and feature not in format_bitmask(image.features())):
+                        f_bitmask = format_features([feature])
+                        image.update_features(f_bitmask, True)
+
+            RbdConfiguration(pool_ioctx=ioctx, image_name=image_name).set_configuration(
+                configuration)
+            if metadata:
+                RbdImageMetadataService(image).set_metadata(metadata)
+
+            if primary and not mirror_image_info['primary']:
+                RbdMirroringService.promote_image(
+                    image_name, pool_name, namespace, force)
+            elif primary is False and mirror_image_info['primary']:
+                RbdMirroringService.demote_image(
+                    image_name, pool_name, namespace)
+
+            if resync:
+                RbdMirroringService.resync_image(image_name, pool_name, namespace)
+
+            if schedule_interval:
+                RbdMirroringService.snapshot_schedule_add(image_spec, schedule_interval)
+
+            if remove_scheduling:
+                RbdMirroringService.snapshot_schedule_remove(image_spec)
+
+        return rbd_image_call(pool_name, namespace, image_name, _edit)
+
+    @classmethod
+    @ttl_cache_invalidator(RBD_IMAGE_REFS_CACHE_REFERENCE)
+    def delete(cls, image_spec):
+        pool_name, namespace, image_name = parse_image_spec(image_spec)
+
+        image = RbdService.get_image(image_spec)
+        snapshots = image['snapshots']
+        for snap in snapshots:
+            RbdSnapshotService.remove_snapshot(image_spec, snap['name'], snap['is_protected'])
+
+        rbd_inst = rbd.RBD()
+        return rbd_call(pool_name, namespace, rbd_inst.remove, image_name)
+
+    @classmethod
+    @ttl_cache_invalidator(RBD_IMAGE_REFS_CACHE_REFERENCE)
+    def copy(cls, image_spec, dest_pool_name, dest_namespace, dest_image_name,
+             snapshot_name=None, obj_size=None, features=None,
+             stripe_unit=None, stripe_count=None, data_pool=None,
+             configuration=None, metadata=None):
+        pool_name, namespace, image_name = parse_image_spec(image_spec)
+
+        def _src_copy(s_ioctx, s_img):
+            def _copy(d_ioctx):
+                # Set order
+                l_order = None
+                if obj_size and obj_size > 0:
+                    l_order = int(round(math.log(float(obj_size), 2)))
+
+                # Set features
+                feature_bitmask = format_features(features)
+
+                if snapshot_name:
+                    s_img.set_snap(snapshot_name)
+
+                s_img.copy(d_ioctx, dest_image_name, feature_bitmask, l_order,
+                           stripe_unit, stripe_count, data_pool)
+                RbdConfiguration(pool_ioctx=d_ioctx, image_name=dest_image_name).set_configuration(
+                    configuration)
+                if metadata:
+                    with rbd.Image(d_ioctx, dest_image_name) as image:
+                        RbdImageMetadataService(image).set_metadata(metadata)
+
+            return rbd_call(dest_pool_name, dest_namespace, _copy)
+
+        return rbd_image_call(pool_name, namespace, image_name, _src_copy)
+
+    @classmethod
+    @ttl_cache_invalidator(RBD_IMAGE_REFS_CACHE_REFERENCE)
+    def flatten(cls, image_spec):
+        def _flatten(ioctx, image):
+            image.flatten()
+
+        pool_name, namespace, image_name = parse_image_spec(image_spec)
+        return rbd_image_call(pool_name, namespace, image_name, _flatten)
+
+    @classmethod
+    def move_image_to_trash(cls, image_spec, delay):
+        pool_name, namespace, image_name = parse_image_spec(image_spec)
+        rbd_inst = cls._rbd_inst
+        return rbd_call(pool_name, namespace, rbd_inst.trash_move, image_name, delay)
+
 
 class RbdSnapshotService(object):