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