From e9082768b6d607ac18a90f9fc91f69b48758e466 Mon Sep 17 00:00:00 2001 From: Mudit Agarwal Date: Fri, 29 May 2020 07:06:55 +0530 Subject: [PATCH] cephfs: enhance tracevol.py script to work with cephfs tracevol.py script traces a RBD PVC to its RADOS map, key and image. The script is enhanced to provide the same functionality for cephfs now. Signed-off-by: Mudit Agarwal --- troubleshooting/tools/requirement.txt | 1 + troubleshooting/tools/tracevol.py | 285 ++++++++++++++++++-------- 2 files changed, 204 insertions(+), 82 deletions(-) diff --git a/troubleshooting/tools/requirement.txt b/troubleshooting/tools/requirement.txt index c416e0b2e..26779af8c 100644 --- a/troubleshooting/tools/requirement.txt +++ b/troubleshooting/tools/requirement.txt @@ -1 +1,2 @@ prettytable>=0.7.2 +PTable>=0.9.2 diff --git a/troubleshooting/tools/tracevol.py b/troubleshooting/tools/tracevol.py index 6a6340c4e..4bbb4b66d 100755 --- a/troubleshooting/tools/tracevol.py +++ b/troubleshooting/tools/tracevol.py @@ -15,8 +15,10 @@ python tool to trace backend image name from pvc Note: For the script to work properly python>=3.x is required sample input: python -c oc -k /home/.kube/config -n default -rn rook-ceph -id admin -key -adminkey +adminkey -cm ceph-csi-config Sample output: ++------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| RBD | +----------+------------------------------------------+----------------------------------------------+-----------------+--------------+------------------+ | PVC Name | PV Name | Image Name | PV name in omap | Image ID in omap | Image in cluster | @@ -24,6 +26,13 @@ Name | PV name in omap | Image ID in omap | Image in cluster | | rbd-pvc | pvc-f1a501dd-03f6-45c9-89f4-85eed7a13ef2 | csi-vol-1b00f5f8-b1c1-11e9-8421-9243c1f659f0 | True | True | False | | rbd-pvcq | pvc-09a8bceb-0f60-4036-85b9-dc89912ae372 | csi-vol-b781b9b1-b1c5-11e9-8421-9243c1f659f0 | True | True | True | +----------+------------------------------------------+----------------------------------------------+-----------------+--------------+------------------+ ++--------------------------------------------------------------------------------------------------------------------------------------------------------------------------+ +| CephFS | ++----------------+------------------------------------------+----------------------------------------------+-----------------+----------------------+----------------------+ +| PVC Name | PV Name | Subvolume Name | PV name in omap | Subvolume ID in omap | Subvolume in cluster | ++----------------+------------------------------------------+----------------------------------------------+-----------------+----------------------+----------------------+ +| csi-cephfs-pvc | pvc-b3492186-73c0-4a4e-a810-0d0fa0daf709 | csi-vol-6f283b82-a09d-11ea-81a7-0242ac11000f | True | True | True | ++----------------+------------------------------------------+----------------------------------------------+-----------------+----------------------+----------------------+ """ import argparse @@ -52,16 +61,24 @@ PARSER.add_argument("-id", "--userid", default="admin", help="user ID to connect to ceph cluster") PARSER.add_argument("-key", "--userkey", default="", help="user password to connect to ceph cluster") +PARSER.add_argument("-cm", "--configmap", default="ceph-csi-config", + help="configmap name which holds the cephcsi configuration") def list_pvc_vol_name_mapping(arg): """ list pvc and volume name mapping """ - table = prettytable.PrettyTable( - ["PVC Name", "PV Name", "Image Name", "PV name in omap", - "Image ID in omap", "Image in cluster"] - ) + table_rbd = prettytable.PrettyTable() + table_rbd.title = "RBD" + table_rbd.field_names = ["PVC Name", "PV Name", "Image Name", "PV name in omap", + "Image ID in omap", "Image in cluster"] + + table_cephfs = prettytable.PrettyTable() + table_cephfs.title = "CephFS" + table_cephfs.field_names = ["PVC Name", "PV Name", "Subvolume Name", "PV name in omap", + "Subvolume ID in omap", "Subvolume in cluster"] + cmd = [arg.command] if arg.kubeconfig != "": @@ -86,16 +103,31 @@ def list_pvc_vol_name_mapping(arg): except ValueError as err: print(err, stdout) sys.exit() + format_and_print_tables(arg, pvcs, table_rbd, table_cephfs) + +def format_and_print_tables(arg, pvcs, table_rbd, table_cephfs): + """ + format and print tables with all relevant information. + """ if arg.pvcname != "": - format_table(arg, pvcs, table) + pvname = pvcs['spec']['volumeName'] + if is_rbd_pv(arg, pvname): + format_table(arg, pvcs, table_rbd, True) + else: + format_table(arg, pvcs, table_cephfs, False) else: for pvc in pvcs['items']: - format_table(arg, pvc, table) - print(table) + pvname = pvc['spec']['volumeName'] + if is_rbd_pv(arg, pvname): + format_table(arg, pvc, table_rbd, True) + else: + format_table(arg, pvc, table_cephfs, False) + print(table_rbd) + print(table_cephfs) -def format_table(arg, pvc_data, table): +def format_table(arg, pvc_data, table, is_rbd): """ - format table for pvc and image information + format tables for pvc and image information """ # pvc name pvcname = pvc_data['metadata']['name'] @@ -108,7 +140,7 @@ def format_table(arg, pvc_data, table): table.add_row([pvcname, "", "", False, False, False]) return - pool_name = get_pool_name(arg, volume_name) + pool_name = get_pool_name(arg, volume_name, is_rbd) if pool_name == "": table.add_row([pvcname, pvname, "", False, False, False]) @@ -119,27 +151,28 @@ def format_table(arg, pvc_data, table): table.add_row([pvcname, pvname, "", False, False, False]) return - # check image details present rados omap - pv_present, uuid_present = validate_volume_in_rados( - arg, image_id, pvname, pool_name) - image_in_cluster = check_image_in_cluster(arg, image_id, pool_name) + # check image/subvolume details present rados omap + pv_present, uuid_present = validate_volume_in_rados(arg, image_id, pvname, pool_name, is_rbd) + present_in_cluster = False + if is_rbd: + present_in_cluster = check_image_in_cluster(arg, image_id, pool_name) + else: + subvolname = "csi-vol-%s" % image_id + present_in_cluster = check_subvol_in_cluster(arg, subvolname) image_name = "csi-vol-%s" % image_id table.add_row([pvcname, pvname, image_name, pv_present, - uuid_present, image_in_cluster]) + uuid_present, present_in_cluster]) -def validate_volume_in_rados(arg, image_id, pvc_name, pool_name): +def validate_volume_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): """ validate volume information in rados """ - - pv_present = check_pv_name_in_rados(arg, image_id, pvc_name, pool_name) - uuid_present = check_image_uuid_in_rados( - arg, image_id, pvc_name, pool_name) + pv_present = check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd) + uuid_present = check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name, is_rbd) return pv_present, uuid_present - -def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name): +def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): """ validate pvc information in rados """ @@ -148,17 +181,11 @@ def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name): omapkey, "--pool", pool_name] if not arg.userkey: cmd += ["--id", arg.userid, "--key", arg.userkey] + if not is_rbd: + cmd += ["--namespace", "csi"] if arg.toolboxdeployed is True: - tool_box_name = get_tool_box_pod_name(arg) - kube = [arg.command] - if arg.kubeconfig != "": - if arg.command == "oc": - kube += ["--config", arg.kubeconfig] - else: - kube += ["--kubeconfig", arg.kubeconfig] - kube += ['exec', '-it', tool_box_name, '-n', - arg.rooknamespace, '--'] - cmd = kube+cmd + kube = get_cmd_prefix(arg) + cmd = kube + cmd out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -182,7 +209,6 @@ def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name): return False return True - def check_image_in_cluster(arg, image_uuid, pool_name): """ validate pvc information in ceph backend @@ -192,16 +218,8 @@ def check_image_in_cluster(arg, image_uuid, pool_name): if not arg.userkey: cmd += ["--id", arg.userid, "--key", arg.userkey] if arg.toolboxdeployed is True: - tool_box_name = get_tool_box_pod_name(arg) - kube = [arg.command] - if arg.kubeconfig != "": - if arg.command == "oc": - kube += ["--config", arg.kubeconfig] - else: - kube += ["--kubeconfig", arg.kubeconfig] - kube += ['exec', '-it', tool_box_name, '-n', - arg.rooknamespace, '--'] - cmd = kube+cmd + kube = get_cmd_prefix(arg) + cmd = kube + cmd out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -217,8 +235,7 @@ def check_image_in_cluster(arg, image_uuid, pool_name): return False return True -#pylint: disable=too-many-branches -def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name): +def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name, is_rbd): """ validate image uuid in rados """ @@ -226,18 +243,11 @@ def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name): cmd = ['rados', 'getomapval', omapkey, "csi.volname", "--pool", pool_name] if not arg.userkey: cmd += ["--id", arg.userid, "--key", arg.userkey] + if not is_rbd: + cmd += ["--namespace", "csi"] if arg.toolboxdeployed is True: - kube = [arg.command] - if arg.kubeconfig != "": - if arg.command == "oc": - kube += ["--config", arg.kubeconfig] - else: - kube += ["--kubeconfig", arg.kubeconfig] - tool_box_name = get_tool_box_pod_name(arg) - kube += ['exec', '-it', tool_box_name, '-n', - arg.rooknamespace, '--'] - cmd = kube+cmd - + kube = get_cmd_prefix(arg) + cmd = kube + cmd out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) @@ -265,6 +275,20 @@ def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name): return True +def get_cmd_prefix(arg): + """ + Returns command prefix + """ + kube = [arg.command] + if arg.kubeconfig != "": + if arg.command == "oc": + kube += ["--config", arg.kubeconfig] + else: + kube += ["--kubeconfig", arg.kubeconfig] + tool_box_name = get_tool_box_pod_name(arg) + kube += ['exec', '-it', tool_box_name, '-n', arg.rooknamespace, '--'] + return kube + def get_image_uuid(volume_handler): """ fetch image uuid from volume handler @@ -332,52 +356,149 @@ def get_tool_box_pod_name(arg): return "" #pylint: disable=too-many-branches -def get_pool_name(arg, vol_id): +def get_pool_name(arg, vol_id, is_rbd): """ get pool name from ceph backend """ - cmd = ['ceph', 'osd', 'lspools', '--format=json'] + if is_rbd: + cmd = ['ceph', 'osd', 'lspools', '--format=json'] + else: + cmd = ['ceph', 'fs', 'ls', '--format=json'] if not arg.userkey: cmd += ["--id", arg.userid, "--key", arg.userkey] if arg.toolboxdeployed is True: - kube = [arg.command] - if arg.kubeconfig != "": - if arg.command == "oc": - kube += ["--config", arg.kubeconfig] - else: - kube += ["--kubeconfig", arg.kubeconfig] - tool_box_name = get_tool_box_pod_name(arg) - kube += ['exec', '-it', tool_box_name, '-n', - arg.rooknamespace, '--'] - cmd = kube+cmd + kube = get_cmd_prefix(arg) + cmd = kube + cmd out = subprocess.Popen(cmd, stdout=subprocess.PIPE, stderr=subprocess.STDOUT) stdout, stderr = out.communicate() if stderr is not None: if arg.debug: - print("failed to pool name %s", stderr) + print("failed to get the pool name %s", stderr) return "" try: pools = json.loads(stdout) except ValueError as err: if arg.debug: - print("failed to pool name %s", err) + print("failed to get the pool name %s", err) return "" - pool_id = vol_id.split('-') - if len(pool_id) < 4: - if arg.debug: - print("pood id not in proper format", pool_id) - return "" - if pool_id[3] in arg.rooknamespace: - pool_id = pool_id[4] + if is_rbd: + pool_id = vol_id.split('-') + if len(pool_id) < 4: + raise Exception("pool id not in the proper format") + if pool_id[3] in arg.rooknamespace: + pool_id = pool_id[4] + else: + pool_id = pool_id[3] + for pool in pools: + if int(pool_id) is int(pool['poolnum']): + return pool['poolname'] else: - pool_id = pool_id[3] - for pool in pools: - if int(pool_id) is int(pool['poolnum']): - return pool['poolname'] + for pool in pools: + return pool['metadata_pool'] return "" +def check_subvol_in_cluster(arg, subvol_name): + """ + Checks if subvolume exists in cluster or not. + """ + # check if user has specified subvolumeGroup + subvol_group = get_subvol_group(arg) + if subvol_group == "": + # default subvolumeGroup + subvol_group = "csi" + return check_subvol_path(arg, subvol_name, subvol_group) + +def check_subvol_path(arg, subvol_name, subvol_group): + """ + Returns True if subvolume path exists in the cluster. + """ + cmd = ['ceph', 'fs', 'subvolume', 'getpath', + 'myfs', subvol_name, subvol_group] + if not arg.userkey: + cmd += ["--id", arg.userid, "--key", arg.userkey] + if arg.toolboxdeployed is True: + kube = get_cmd_prefix(arg) + cmd = kube + cmd + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = out.communicate() + if stderr is not None: + if arg.debug: + print("failed to get toolbox %s", stderr) + return False + if b"Error" in stdout: + if arg.debug: + print("subvolume not found in cluster", stdout) + return False + return True + +def get_subvol_group(arg): + """ + Returns sub volume group from configmap. + """ + cmd = [arg.command] + if arg.kubeconfig != "": + if arg.command == "oc": + cmd += ["--config", arg.kubeconfig] + else: + cmd += ["--kubeconfig", arg.kubeconfig] + cmd += ['get', 'cm', arg.configmap, '-o', 'json'] + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = out.communicate() + if stderr is not None: + if arg.debug: + print("failed to get configmap %s", stderr) + sys.exit() + try: + config_map = json.loads(stdout) + except ValueError as err: + print(err, stdout) + sys.exit() + cm_data = config_map['data']['config.json'] + subvol_group = "" + if "subvolumeGroup" in cm_data: + try: + cm_data_json = json.loads(cm_data) + except ValueError as err: + print(err, stdout) + sys.exit() + for data in cm_data_json: + subvol_group = data['cephFS']['subvolumeGroup'] + return subvol_group + +def is_rbd_pv(arg, pvname): + """ + Checks if volume attributes in a pv has an attribute named 'fsname'. + If it has, returns False else return True. + """ + cmd = [arg.command] + if arg.kubeconfig != "": + cmd += ["--config", arg.kubeconfig] + else: + cmd += ["--kubeconfig", arg.kubeconfig] + + cmd += ['get', 'pv', pvname, '-o', 'json'] + out = subprocess.Popen(cmd, stdout=subprocess.PIPE, + stderr=subprocess.STDOUT) + stdout, stderr = out.communicate() + if stderr is not None: + if arg.debug: + print("failed to get pv %s", stderr) + sys.exit() + try: + pvdata = json.loads(stdout) + volume_attr = pvdata['spec']['csi']['volumeAttributes'] + key = 'fsName' + if key in volume_attr.keys(): + return False + except ValueError as err: + if arg.degug: + print("failed to get pv %s", err) + sys.exit() + return True if __name__ == "__main__": ARGS = PARSER.parse_args()