From 7543eb839de39ecc354725aeb4e7c0e2db322f7e Mon Sep 17 00:00:00 2001 From: "Joshua M. Boniface" Date: Tue, 3 Sep 2024 20:30:18 -0400 Subject: [PATCH] Add dedicated volume scan endpoint Allows an imported volume to be scanned for stats independently. Designed to be used as part of a snapshot import via API, to allow the "create" to happen before the real import (to check for available space, etc.) and then run this import after when the RBD volume actually exists. --- api-daemon/pvcapid/flaskapi.py | 35 ++++++++++++++ api-daemon/pvcapid/helper.py | 16 +++++++ daemon-common/ceph.py | 84 +++++++++++++++------------------- 3 files changed, 88 insertions(+), 47 deletions(-) diff --git a/api-daemon/pvcapid/flaskapi.py b/api-daemon/pvcapid/flaskapi.py index 8345fe74..94e7becc 100755 --- a/api-daemon/pvcapid/flaskapi.py +++ b/api-daemon/pvcapid/flaskapi.py @@ -6461,6 +6461,41 @@ api.add_resource( ) +# /storage/ceph/volume///scan +class API_Storage_Ceph_Volume_Element_Scan(Resource): + @Authenticator + def post(self, pool, volume): + """ + Scan a Ceph volume {volume} in pool {pool} for stats (after import) + --- + tags: + - storage / ceph + parameters: + responses: + 200: + description: OK + schema: + type: object + id: Message + 404: + description: Not found + schema: + type: object + id: Message + 400: + description: Bad request + schema: + type: object + id: Message + """ + return api_helper.ceph_volume_scan(pool, volume) + + +api.add_resource( + API_Storage_Ceph_Volume_Element_Scan, "/storage/ceph/volume///scan" +) + + # /storage/ceph/volume///clone class API_Storage_Ceph_Volume_Element_Clone(Resource): @RequestParser( diff --git a/api-daemon/pvcapid/helper.py b/api-daemon/pvcapid/helper.py index 98152df3..a9c205aa 100755 --- a/api-daemon/pvcapid/helper.py +++ b/api-daemon/pvcapid/helper.py @@ -1996,6 +1996,22 @@ def ceph_volume_list(zkhandler, pool=None, limit=None, is_fuzzy=True): return retdata, retcode +@ZKConnection(config) +def ceph_volume_scan(zkhandler, pool, name): + """ + (Re)scan a Ceph RBD volume for stats in the PVC Ceph storage cluster. + """ + retflag, retdata = pvc_ceph.scan_volume(zkhandler, pool, name) + + if retflag: + retcode = 200 + else: + retcode = 400 + + output = {"message": retdata.replace('"', "'")} + return output, retcode + + @ZKConnection(config) def ceph_volume_add(zkhandler, pool, name, size, force_flag=False): """ diff --git a/daemon-common/ceph.py b/daemon-common/ceph.py index 2f9479f1..37636d1c 100644 --- a/daemon-common/ceph.py +++ b/daemon-common/ceph.py @@ -560,7 +560,21 @@ def getVolumeInformation(zkhandler, pool, volume): return volume_information -def add_volume(zkhandler, pool, name, size, force_flag=False): +def scan_volume(zkhandler, pool, name): + retcode, stdout, stderr = common.run_os_command( + "rbd info --format json {}/{}".format(pool, name) + ) + volstats = stdout + + # 3. Add the new volume to Zookeeper + zkhandler.write( + [ + (("volume.stats", f"{pool}/{name}"), volstats), + ] + ) + + +def add_volume(zkhandler, pool, name, size, force_flag=False, zk_only=False): # 1. Verify the size of the volume pool_information = getPoolInformation(zkhandler, pool) size_bytes = format_bytes_fromhuman(size) @@ -592,27 +606,28 @@ def add_volume(zkhandler, pool, name, size, force_flag=False): ) # 2. Create the volume - retcode, stdout, stderr = common.run_os_command( - "rbd create --size {}B {}/{}".format(size_bytes, pool, name) - ) - if retcode: - return False, 'ERROR: Failed to create RBD volume "{}": {}'.format(name, stderr) - - # 2. Get volume stats - retcode, stdout, stderr = common.run_os_command( - "rbd info --format json {}/{}".format(pool, name) - ) - volstats = stdout + # zk_only flag skips actually creating the volume - this would be done by some other mechanism + if not zk_only: + retcode, stdout, stderr = common.run_os_command( + "rbd create --size {}B {}/{}".format(size_bytes, pool, name) + ) + if retcode: + return False, 'ERROR: Failed to create RBD volume "{}": {}'.format( + name, stderr + ) # 3. Add the new volume to Zookeeper zkhandler.write( [ (("volume", f"{pool}/{name}"), ""), - (("volume.stats", f"{pool}/{name}"), volstats), + (("volume.stats", f"{pool}/{name}"), ""), (("snapshot", f"{pool}/{name}"), ""), ] ) + # 4. Scan the volume stats + scan_volume(zkhandler, pool, name) + return True, 'Created RBD volume "{}" of size "{}" in pool "{}".'.format( name, format_bytes_tohuman(size_bytes), pool ) @@ -662,21 +677,18 @@ def clone_volume(zkhandler, pool, name_src, name_new, force_flag=False): ), ) - # 3. Get volume stats - retcode, stdout, stderr = common.run_os_command( - "rbd info --format json {}/{}".format(pool, name_new) - ) - volstats = stdout - - # 4. Add the new volume to Zookeeper + # 3. Add the new volume to Zookeeper zkhandler.write( [ (("volume", f"{pool}/{name_new}"), ""), - (("volume.stats", f"{pool}/{name_new}"), volstats), + (("volume.stats", f"{pool}/{name_new}"), ""), (("snapshot", f"{pool}/{name_new}"), ""), ] ) + # 4. Scan the volume stats + scan_volume(zkhandler, pool, name_new) + return True, 'Cloned RBD volume "{}" to "{}" in pool "{}"'.format( name_src, name_new, pool ) @@ -761,20 +773,8 @@ def resize_volume(zkhandler, pool, name, size, force_flag=False): except Exception: pass - # 4. Get volume stats - retcode, stdout, stderr = common.run_os_command( - "rbd info --format json {}/{}".format(pool, name) - ) - volstats = stdout - - # 5. Update the volume in Zookeeper - zkhandler.write( - [ - (("volume", f"{pool}/{name}"), ""), - (("volume.stats", f"{pool}/{name}"), volstats), - (("snapshot", f"{pool}/{name}"), ""), - ] - ) + # 4. Scan the volume stats + scan_volume(zkhandler, pool, name) return True, 'Resized RBD volume "{}" to size "{}" in pool "{}".'.format( name, format_bytes_tohuman(size_bytes), pool @@ -807,18 +807,8 @@ def rename_volume(zkhandler, pool, name, new_name): ] ) - # 3. Get volume stats - retcode, stdout, stderr = common.run_os_command( - "rbd info --format json {}/{}".format(pool, new_name) - ) - volstats = stdout - - # 4. Update the volume stats in Zookeeper - zkhandler.write( - [ - (("volume.stats", f"{pool}/{new_name}"), volstats), - ] - ) + # 3. Scan the volume stats + scan_volume(zkhandler, pool, new_name) return True, 'Renamed RBD volume "{}" to "{}" in pool "{}".'.format( name, new_name, pool