X-Git-Url: https://git.proxmox.com/?a=blobdiff_plain;f=ceph%2Fsrc%2Fpybind%2Fmgr%2Fdashboard%2Fservices%2Frbd.py;h=bb769ce194653fa0c327272346a5ea7ccebbd500;hb=aee94f6923ba628a85d855d0c5316d0da78bfa2a;hp=98fbba832573e8383aa01f62d944e2e12ee31e29;hpb=27f45121cc74e31203777ad565f78d8aad9b92a2;p=ceph.git diff --git a/ceph/src/pybind/mgr/dashboard/services/rbd.py b/ceph/src/pybind/mgr/dashboard/services/rbd.py index 98fbba832..bb769ce19 100644 --- a/ceph/src/pybind/mgr/dashboard/services/rbd.py +++ b/ceph/src/pybind/mgr/dashboard/services/rbd.py @@ -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):