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 <muagarwa@redhat.com>
This commit is contained in:
Mudit Agarwal 2020-05-29 07:06:55 +05:30 committed by mergify[bot]
parent 78963737d3
commit e9082768b6
2 changed files with 204 additions and 82 deletions

View File

@ -1 +1,2 @@
prettytable>=0.7.2 prettytable>=0.7.2
PTable>=0.9.2

View File

@ -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 Note: For the script to work properly python>=3.x is required
sample input: sample input:
python -c oc -k /home/.kube/config -n default -rn rook-ceph -id admin -key python -c oc -k /home/.kube/config -n default -rn rook-ceph -id admin -key
adminkey adminkey -cm ceph-csi-config
Sample output: Sample output:
+------------------------------------------------------------------------------------------------------------------------------------------------------------+
| RBD |
+----------+------------------------------------------+----------------------------------------------+-----------------+--------------+------------------+ +----------+------------------------------------------+----------------------------------------------+-----------------+--------------+------------------+
| PVC Name | PV Name | Image | PVC Name | PV Name | Image
Name | PV name in omap | Image ID in omap | Image in cluster | 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-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 | | 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 import argparse
@ -52,16 +61,24 @@ PARSER.add_argument("-id", "--userid",
default="admin", help="user ID to connect to ceph cluster") default="admin", help="user ID to connect to ceph cluster")
PARSER.add_argument("-key", "--userkey", PARSER.add_argument("-key", "--userkey",
default="", help="user password to connect to ceph cluster") 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): def list_pvc_vol_name_mapping(arg):
""" """
list pvc and volume name mapping list pvc and volume name mapping
""" """
table = prettytable.PrettyTable( table_rbd = prettytable.PrettyTable()
["PVC Name", "PV Name", "Image Name", "PV name in omap", 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"] "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] cmd = [arg.command]
if arg.kubeconfig != "": if arg.kubeconfig != "":
@ -86,16 +103,31 @@ def list_pvc_vol_name_mapping(arg):
except ValueError as err: except ValueError as err:
print(err, stdout) print(err, stdout)
sys.exit() 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 != "": 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: else:
for pvc in pvcs['items']: for pvc in pvcs['items']:
format_table(arg, pvc, table) pvname = pvc['spec']['volumeName']
print(table) 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 # pvc name
pvcname = pvc_data['metadata']['name'] pvcname = pvc_data['metadata']['name']
@ -108,7 +140,7 @@ def format_table(arg, pvc_data, table):
table.add_row([pvcname, "", "", False, table.add_row([pvcname, "", "", False,
False, False]) False, False])
return return
pool_name = get_pool_name(arg, volume_name) pool_name = get_pool_name(arg, volume_name, is_rbd)
if pool_name == "": if pool_name == "":
table.add_row([pvcname, pvname, "", False, table.add_row([pvcname, pvname, "", False,
False, False]) False, False])
@ -119,27 +151,28 @@ def format_table(arg, pvc_data, table):
table.add_row([pvcname, pvname, "", False, table.add_row([pvcname, pvname, "", False,
False, False]) False, False])
return return
# check image details present rados omap # check image/subvolume details present rados omap
pv_present, uuid_present = validate_volume_in_rados( pv_present, uuid_present = validate_volume_in_rados(arg, image_id, pvname, pool_name, is_rbd)
arg, image_id, pvname, pool_name) present_in_cluster = False
image_in_cluster = check_image_in_cluster(arg, image_id, pool_name) 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 image_name = "csi-vol-%s" % image_id
table.add_row([pvcname, pvname, image_name, pv_present, 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 validate volume information in rados
""" """
pv_present = check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd)
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, is_rbd)
uuid_present = check_image_uuid_in_rados(
arg, image_id, pvc_name, pool_name)
return pv_present, uuid_present return pv_present, uuid_present
def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name, is_rbd):
def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name):
""" """
validate pvc information in rados 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] omapkey, "--pool", pool_name]
if not arg.userkey: if not arg.userkey:
cmd += ["--id", arg.userid, "--key", arg.userkey] cmd += ["--id", arg.userid, "--key", arg.userkey]
if not is_rbd:
cmd += ["--namespace", "csi"]
if arg.toolboxdeployed is True: if arg.toolboxdeployed is True:
tool_box_name = get_tool_box_pod_name(arg) kube = get_cmd_prefix(arg)
kube = [arg.command] cmd = kube + cmd
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
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
@ -182,7 +209,6 @@ def check_pv_name_in_rados(arg, image_id, pvc_name, pool_name):
return False return False
return True return True
def check_image_in_cluster(arg, image_uuid, pool_name): def check_image_in_cluster(arg, image_uuid, pool_name):
""" """
validate pvc information in ceph backend validate pvc information in ceph backend
@ -192,16 +218,8 @@ def check_image_in_cluster(arg, image_uuid, pool_name):
if not arg.userkey: if not arg.userkey:
cmd += ["--id", arg.userid, "--key", arg.userkey] cmd += ["--id", arg.userid, "--key", arg.userkey]
if arg.toolboxdeployed is True: if arg.toolboxdeployed is True:
tool_box_name = get_tool_box_pod_name(arg) kube = get_cmd_prefix(arg)
kube = [arg.command] cmd = kube + cmd
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
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
@ -217,8 +235,7 @@ def check_image_in_cluster(arg, image_uuid, pool_name):
return False return False
return True return True
#pylint: disable=too-many-branches def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name, is_rbd):
def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name):
""" """
validate image uuid in rados 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] cmd = ['rados', 'getomapval', omapkey, "csi.volname", "--pool", pool_name]
if not arg.userkey: if not arg.userkey:
cmd += ["--id", arg.userid, "--key", arg.userkey] cmd += ["--id", arg.userid, "--key", arg.userkey]
if not is_rbd:
cmd += ["--namespace", "csi"]
if arg.toolboxdeployed is True: if arg.toolboxdeployed is True:
kube = [arg.command] kube = get_cmd_prefix(arg)
if arg.kubeconfig != "": cmd = kube + cmd
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
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
@ -265,6 +275,20 @@ def check_image_uuid_in_rados(arg, image_id, pvc_name, pool_name):
return True 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): def get_image_uuid(volume_handler):
""" """
fetch image uuid from volume handler fetch image uuid from volume handler
@ -332,43 +356,37 @@ def get_tool_box_pod_name(arg):
return "" return ""
#pylint: disable=too-many-branches #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 get pool name from ceph backend
""" """
if is_rbd:
cmd = ['ceph', 'osd', 'lspools', '--format=json'] cmd = ['ceph', 'osd', 'lspools', '--format=json']
else:
cmd = ['ceph', 'fs', 'ls', '--format=json']
if not arg.userkey: if not arg.userkey:
cmd += ["--id", arg.userid, "--key", arg.userkey] cmd += ["--id", arg.userid, "--key", arg.userkey]
if arg.toolboxdeployed is True: if arg.toolboxdeployed is True:
kube = [arg.command] kube = get_cmd_prefix(arg)
if arg.kubeconfig != "": cmd = kube + cmd
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
out = subprocess.Popen(cmd, stdout=subprocess.PIPE, out = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.STDOUT) stderr=subprocess.STDOUT)
stdout, stderr = out.communicate() stdout, stderr = out.communicate()
if stderr is not None: if stderr is not None:
if arg.debug: if arg.debug:
print("failed to pool name %s", stderr) print("failed to get the pool name %s", stderr)
return "" return ""
try: try:
pools = json.loads(stdout) pools = json.loads(stdout)
except ValueError as err: except ValueError as err:
if arg.debug: if arg.debug:
print("failed to pool name %s", err) print("failed to get the pool name %s", err)
return "" return ""
if is_rbd:
pool_id = vol_id.split('-') pool_id = vol_id.split('-')
if len(pool_id) < 4: if len(pool_id) < 4:
if arg.debug: raise Exception("pool id not in the proper format")
print("pood id not in proper format", pool_id)
return ""
if pool_id[3] in arg.rooknamespace: if pool_id[3] in arg.rooknamespace:
pool_id = pool_id[4] pool_id = pool_id[4]
else: else:
@ -376,8 +394,111 @@ def get_pool_name(arg, vol_id):
for pool in pools: for pool in pools:
if int(pool_id) is int(pool['poolnum']): if int(pool_id) is int(pool['poolnum']):
return pool['poolname'] return pool['poolname']
else:
for pool in pools:
return pool['metadata_pool']
return "" 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__": if __name__ == "__main__":
ARGS = PARSER.parse_args() ARGS = PARSER.parse_args()