diff options
Diffstat (limited to 'glustolibs-gluster/glustolibs/gluster/glusterfile.py')
-rwxr-xr-x | glustolibs-gluster/glustolibs/gluster/glusterfile.py | 252 |
1 files changed, 217 insertions, 35 deletions
diff --git a/glustolibs-gluster/glustolibs/gluster/glusterfile.py b/glustolibs-gluster/glustolibs/gluster/glusterfile.py index 413a4f9a7..ee9b6040d 100755 --- a/glustolibs-gluster/glustolibs/gluster/glusterfile.py +++ b/glustolibs-gluster/glustolibs/gluster/glusterfile.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (C) 2018 Red Hat, Inc. <http://www.redhat.com> +# Copyright (C) 2018-2020 Red Hat, Inc. <http://www.redhat.com> # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License as published by @@ -27,8 +27,8 @@ import os import re from glusto.core import Glusto as g - from glustolibs.gluster.layout import Layout +from glustolibs.misc.misc_libs import upload_scripts def calculate_hash(host, filename): @@ -39,26 +39,43 @@ def calculate_hash(host, filename): Returns: An integer representation of the hash + + TODO: For testcases specifically testing hashing routine + consider using a baseline external Davies-Meyer hash_value.c + Creating comparison hash from same library we are testing + may not be best practice here. (Holloway) """ - # TODO: For testcases specifically testing hashing routine - # consider using a baseline external Davies-Meyer hash_value.c - # Creating comparison hash from same library we are testing - # may not be best practice here. (Holloway) try: # Check if libglusterfs.so.0 is available locally glusterfs = ctypes.cdll.LoadLibrary("libglusterfs.so.0") g.log.debug("Library libglusterfs.so.0 loaded locally") + computed_hash = ( + ctypes.c_uint32(glusterfs.gf_dm_hashfn(filename, + len(filename)))) + hash_value = int(computed_hash.value) except OSError: - conn = g.rpyc_get_connection(host) - glusterfs = \ - conn.modules.ctypes.cdll.LoadLibrary("libglusterfs.so.0") - g.log.debug("Library libglusterfs.so.0 loaded via rpyc") - - computed_hash = \ - ctypes.c_uint32(glusterfs.gf_dm_hashfn(filename, len(filename))) - # conn.close() - - return int(computed_hash.value) + script_path = ("/usr/share/glustolibs/scripts/" + "compute_hash.py") + if not file_exists(host, script_path): + if upload_scripts(host, script_path, + '/usr/share/glustolibs/scripts/'): + g.log.info("Successfully uploaded script " + "compute_hash.py!") + else: + g.log.error('Unable to upload the script to node {0}' + .format(host)) + return 0 + else: + g.log.info("compute_hash.py already present!") + cmd = ("/usr/bin/env python {0} {1}".format(script_path, + filename)) + ret, out, _ = g.run(host, cmd) + if ret: + g.log.error('Unable to run the script on node {0}' + .format(host)) + return 0 + hash_value = int(out.split('\n')[0]) + return hash_value def get_mountpoint(host, fqpath): @@ -80,40 +97,50 @@ def get_mountpoint(host, fqpath): return None -def get_fattr(host, fqpath, fattr): +def get_fattr(host, fqpath, fattr, encode="hex"): """getfattr for filepath on remote system Args: host (str): The hostname/ip of the remote system. fqpath (str): The fully-qualified path to the file. fattr (str): name of the fattr to retrieve - + Kwargs: + encode(str): The supported types of encoding are + [hex|text|base64] + Defaults to hex type of encoding Returns: getfattr result on success. None on fail. """ - command = ("getfattr --absolute-names --only-values -n '%s' %s" % - (fattr, fqpath)) + command = ("getfattr --absolute-names -e '%s' " + "-n '%s' %s" % + (encode, fattr, fqpath)) rcode, rout, rerr = g.run(host, command) - - if rcode == 0: - return rout.strip() + if not rcode: + return rout.strip().split('=')[1].replace('"', '') g.log.error('getfattr failed: %s' % rerr) return None -def get_fattr_list(host, fqpath): +def get_fattr_list(host, fqpath, encode_hex=False): """List of xattr for filepath on remote system. Args: host (str): The hostname/ip of the remote system. fqpath (str): The fully-qualified path to the file. + Kwargs: + encode_hex(bool): Fetch xattr in hex if True + (Default:False) + Returns: Dictionary of xattrs on success. None on fail. """ - command = "getfattr --absolute-names -d -m - %s" % fqpath - rcode, rout, rerr = g.run(host, command) + cmd = "getfattr --absolute-names -d -m - {}".format(fqpath) + if encode_hex: + cmd = ("getfattr --absolute-names -d -m - -e hex {}" + .format(fqpath)) + rcode, rout, rerr = g.run(host, cmd) if rcode == 0: xattr_list = {} @@ -220,7 +247,7 @@ def get_file_stat(host, fqpath): Returns: A dictionary of file stat data. None on fail. """ - statformat = '%F:%n:%i:%a:%s:%h:%u:%g:%U:%G' + statformat = '%F$%n$%i$%a$%s$%h$%u$%g$%U$%G$%x$%y$%z$%X$%Y$%Z' command = "stat -c '%s' %s" % (statformat, fqpath) rcode, rout, rerr = g.run(host, command) if rcode == 0: @@ -228,7 +255,9 @@ def get_file_stat(host, fqpath): stat_string = rout.strip() (filetype, filename, inode, access, size, links, - uid, gid, username, groupname) = stat_string.split(":") + uid, gid, username, groupname, + atime, mtime, ctime, epoch_atime, + epoch_mtime, epoch_ctime) = stat_string.split("$") stat_data['filetype'] = filetype stat_data['filename'] = filename @@ -240,6 +269,12 @@ def get_file_stat(host, fqpath): stat_data["groupname"] = groupname stat_data["uid"] = uid stat_data["gid"] = gid + stat_data["atime"] = atime + stat_data["mtime"] = mtime + stat_data["ctime"] = ctime + stat_data["epoch_atime"] = epoch_atime + stat_data["epoch_mtime"] = epoch_mtime + stat_data["epoch_ctime"] = epoch_ctime return stat_data @@ -365,7 +400,8 @@ def get_pathinfo(host, fqpath): A dictionary of pathinfo data for a remote file. None on fail. """ pathinfo = {} - pathinfo['raw'] = get_fattr(host, fqpath, 'trusted.glusterfs.pathinfo') + pathinfo['raw'] = get_fattr(host, fqpath, 'trusted.glusterfs.pathinfo', + encode="text") pathinfo['brickdir_paths'] = re.findall(r".*?POSIX.*?:(\S+)\>", pathinfo['raw']) @@ -388,17 +424,14 @@ def is_linkto_file(host, fqpath): """ command = 'file %s' % fqpath rcode, rout, _ = g.run(host, command) - if rcode == 0: - if 'sticky empty' in rout.strip(): + # An additional ',' is there for newer platforms + if 'sticky empty' or 'sticky, empty' in rout.strip(): stat = get_file_stat(host, fqpath) if int(stat['size']) == 0: - # xattr = get_fattr(host, fqpath, - # 'trusted.glusterfs.dht.linkto') xattr = get_dht_linkto_xattr(host, fqpath) if xattr is not None: return True - return False @@ -412,7 +445,8 @@ def get_dht_linkto_xattr(host, fqpath): Returns: Return value of get_fattr trusted.glusterfs.dht.linkto call. """ - linkto_xattr = get_fattr(host, fqpath, 'trusted.glusterfs.dht.linkto') + linkto_xattr = get_fattr(host, fqpath, 'trusted.glusterfs.dht.linkto', + encode="text") return linkto_xattr @@ -463,6 +497,154 @@ def check_if_pattern_in_file(host, pattern, fqpath): return 0 +def occurences_of_pattern_in_file(node, search_pattern, filename): + """ + Get the number of occurences of pattern in the file + + Args: + node (str): Host on which the command is executed. + search_pattern (str): Pattern to be found in the file. + filename (str): File in which the pattern is to be validated + + Returns: + (int): (-1), When the file doesn't exists. + (0), When pattern doesn't exists in the file. + (number), When pattern is found and the number of + occurences of pattern in the file. + + Example: + occurences_of_pattern_in_file(node, search_pattern, filename) + """ + + ret = file_exists(node, filename) + if not ret: + g.log.error("File %s is not present on the node " % filename) + return -1 + + cmd = ("grep -c '%s' %s" % (search_pattern, filename)) + ret, out, _ = g.run(node, cmd) + if ret: + g.log.error("No occurence of the pattern found in the file %s" % + filename) + return 0 + return int(out.strip('\n')) + + +def create_link_file(node, file, link, soft=False): + """ + Create hard or soft link for an exisiting file + + Args: + node(str): Host on which the command is executed. + file(str): Path to the source file. + link(str): Path to the link file. + + Kawrgs: + soft(bool): Create soft link if True else create + hard link. + + Returns: + (bool): True if command successful else False. + + Example: + >>> create_link_file('10.20.30.40', '/mnt/mp/file.txt', + '/mnt/mp/link') + True + """ + cmd = "ln {} {}".format(file, link) + if soft: + cmd = "ln -s {} {}".format(file, link) + + ret, _, err = g.run(node, cmd) + if ret: + if soft: + g.log.error('Failed to create soft link on {} ' + 'for file {} with error {}' + .format(node, file, err)) + else: + g.log.error('Failed to create hard link on {} ' + 'for file {} with error {}' + .format(node, file, err)) + return False + return True + + +def set_acl(client, rule, fqpath): + """Set acl rule on a specific file + + Args: + client(str): Host on which the command is executed. + rule(str): The acl rule to be set on the file. + fqpath (str): The fully-qualified path to the file. + + Returns: + (bool): True if command successful else False. + """ + cmd = "setfacl -m {} {}".format(rule, fqpath) + ret, _, _ = g.run(client, cmd) + if ret: + g.log.error('Failed to set rule {} on file {}'.format(rule, fqpath)) + return False + return True + + +def get_acl(client, path, filename): + """Get all acl rules set to a file + + Args: + client(str): Host on which the command is executed. + path (str): The fully-qualified path to the dir where file is present. + filename(str): Name of the file for which rules have to be fetched. + + Returns: + (dict): A dictionary with the formatted output of the command. + (None): In case of failures + + Example: + >>> get_acl('dhcp35-4.lab.eng.blr.redhat.com', '/root/', 'file') + {'owner': 'root', 'rules': ['user::rw-', 'user:root:rwx', 'group::r--', + 'mask::rwx', 'other::r--'], 'group': 'root', 'file': 'file'} + """ + cmd = "cd {};getfacl {}".format(path, filename) + ret, out, _ = g.run(client, cmd) + if ret: + return None + + # Generate a dict out of the output + output_dict = {} + data = out.strip().split('\n') + for key, index in (('file', 0), ('owner', 1), ('group', 2)): + output_dict[key] = data[index].split(' ')[2] + output_dict['rules'] = data[3:] + + return output_dict + + +def delete_acl(client, fqpath, rule=None): + """Delete a specific or all acl rules set on a file + + Args: + client(str): Host on which the command is executed. + fqpath (str): The fully-qualified path to the file. + + Kwargs: + rule(str): The acl rule to be removed from the file. + + Returns: + (bool): True if command successful else False. + """ + # Remove all acls set on a file + cmd = "setfacl -b {}".format(fqpath) + # Remove a specific acl of the file + if rule: + cmd = "setfacl -x {} {}".format(rule, fqpath) + + ret, _, _ = g.run(client, cmd) + if ret: + return False + return True + + class GlusterFile(object): """Class to handle files specific to Gluster (client and backend)""" def __init__(self, host, fqpath): |