diff options
Diffstat (limited to 'glustolibs-gluster/glustolibs/gluster')
8 files changed, 271 insertions, 22 deletions
diff --git a/glustolibs-gluster/glustolibs/gluster/dht_test_utils.py b/glustolibs-gluster/glustolibs/gluster/dht_test_utils.py index d205211bb..11f2eda62 100644 --- a/glustolibs-gluster/glustolibs/gluster/dht_test_utils.py +++ b/glustolibs-gluster/glustolibs/gluster/dht_test_utils.py @@ -456,3 +456,5 @@ def is_layout_complete(mnode, volname, dirpath): return False elif hash_difference < 1: g.log.error("Layout has overlaps") + + return True diff --git a/glustolibs-gluster/glustolibs/gluster/gluster_base_class.py b/glustolibs-gluster/glustolibs/gluster/gluster_base_class.py index 3ce38a304..65061cb13 100755 --- a/glustolibs-gluster/glustolibs/gluster/gluster_base_class.py +++ b/glustolibs-gluster/glustolibs/gluster/gluster_base_class.py @@ -1,4 +1,4 @@ -# Copyright (C) 2018-2020 Red Hat, Inc. <http://www.redhat.com> +# Copyright (C) 2018-2021 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 @@ -196,6 +196,11 @@ class GlusterBaseClass(TestCase): Returns (bool): True if all peers are in connected with other peers. False otherwise. """ + + # If the setup has single node server, by pass this validation. + if len(cls.servers) == 1: + return True + # Validate if peer is connected from all the servers g.log.info("Validating if servers %s are connected from other servers " "in the cluster", cls.servers) @@ -469,6 +474,9 @@ class GlusterBaseClass(TestCase): """ g.log.info("Starting to mount volume %s", cls.volname) for mount_obj in mounts: + # For nfs-ganesha, mount is done via vip + if cls.enable_nfs_ganesha: + mount_obj.server_system = cls.vips[0] g.log.info("Mounting volume '%s:%s' on '%s:%s'", mount_obj.server_system, mount_obj.volname, mount_obj.client_system, mount_obj.mountpoint) @@ -1052,6 +1060,7 @@ class GlusterBaseClass(TestCase): ret = setup_nfs_ganesha(cls) if not ret: raise ExecutionError("Failed to setup nfs ganesha") + g.log.info("Successful in setting up NFS Ganesha Cluster") msg = "Setupclass: %s : %s" % (cls.__name__, cls.glustotest_run_id) g.log.info(msg) diff --git a/glustolibs-gluster/glustolibs/gluster/gluster_init.py b/glustolibs-gluster/glustolibs/gluster/gluster_init.py index d6740b773..6a49ffc8b 100644 --- a/glustolibs-gluster/glustolibs/gluster/gluster_init.py +++ b/glustolibs-gluster/glustolibs/gluster/gluster_init.py @@ -23,13 +23,17 @@ from time import sleep from glusto.core import Glusto as g -def start_glusterd(servers): +def start_glusterd(servers, enable_retry=True): """Starts glusterd on specified servers if they are not running. Args: servers (str|list): A server|List of server hosts on which glusterd has to be started. + Kwargs: + enable_retry(Bool): If set to True then runs reset-failed else + do nothing. + Returns: bool : True if starting glusterd is successful on all servers. False otherwise. @@ -46,10 +50,13 @@ def start_glusterd(servers): if retcode != 0: g.log.error("Unable to start glusterd on server %s", server) _rc = False - if not _rc: - return False + if not _rc and enable_retry: + ret = reset_failed_glusterd(servers) + if ret: + ret = start_glusterd(servers) + return ret - return True + return _rc def stop_glusterd(servers): diff --git a/glustolibs-gluster/glustolibs/gluster/glusterfile.py b/glustolibs-gluster/glustolibs/gluster/glusterfile.py index 227ef6f8d..ee9b6040d 100755 --- a/glustolibs-gluster/glustolibs/gluster/glusterfile.py +++ b/glustolibs-gluster/glustolibs/gluster/glusterfile.py @@ -569,6 +569,82 @@ def create_link_file(node, file, link, soft=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): diff --git a/glustolibs-gluster/glustolibs/gluster/heal_libs.py b/glustolibs-gluster/glustolibs/gluster/heal_libs.py index 91d720e41..4a551cd48 100755 --- a/glustolibs-gluster/glustolibs/gluster/heal_libs.py +++ b/glustolibs-gluster/glustolibs/gluster/heal_libs.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (C) 2016-2020 Red Hat, Inc. <http://www.redhat.com> +# Copyright (C) 2016-2021 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 @@ -551,3 +551,41 @@ def is_shd_daemon_running(mnode, node, volname): return True except KeyError: return False + + +def enable_granular_heal(mnode, volname): + """Enable granular heal on a given volume + + Args: + mnode(str): Node on which command has to be exectued + volname(str): Name of the volume on which granular heal is to be enabled + + Returns: + bool: True if granular heal is enabled successfully else False + """ + cmd = "gluster volume heal {} granular-entry-heal enable".format(volname) + ret, _, _ = g.run(mnode, cmd) + if ret: + g.log.error('Unable to enable granular-entry-heal on volume %s', + volname) + return False + return True + + +def disable_granular_heal(mnode, volname): + """Diable granular heal on a given volume + + Args: + mnode(str): Node on which command will be exectued + volname(str): Name of the volume on which granular heal is to be disabled + + Returns: + bool: True if granular heal is disabled successfully else False + """ + cmd = "gluster volume heal {} granular-entry-heal disable".format(volname) + ret, _, _ = g.run(mnode, cmd) + if ret: + g.log.error('Unable to disable granular-entry-heal on volume %s', + volname) + return False + return True diff --git a/glustolibs-gluster/glustolibs/gluster/lib_utils.py b/glustolibs-gluster/glustolibs/gluster/lib_utils.py index 7299874d0..b04976b1c 100755 --- a/glustolibs-gluster/glustolibs/gluster/lib_utils.py +++ b/glustolibs-gluster/glustolibs/gluster/lib_utils.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (C) 2015-2020 Red Hat, Inc. <http://www.redhat.com> +# Copyright (C) 2015-2021 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 @@ -387,7 +387,8 @@ def get_servers_unused_bricks_dict(mnode, servers, servers_info): return servers_unused_bricks_dict -def form_bricks_list(mnode, volname, number_of_bricks, servers, servers_info): +def form_bricks_list(mnode, volname, number_of_bricks, servers, servers_info, + dirname=None): """Forms bricks list for create-volume/add-brick given the num_of_bricks servers and servers_info. @@ -400,6 +401,9 @@ def form_bricks_list(mnode, volname, number_of_bricks, servers, servers_info): needs to be selected for creating the brick list. servers_info (dict): dict of server info of each servers. + kwargs: + dirname (str): Name of the directory for glusterfs brick + Returns: list - List of bricks to use with volume-create/add-brick None - if number_of_bricks is greater than unused bricks. @@ -437,10 +441,18 @@ def form_bricks_list(mnode, volname, number_of_bricks, servers, servers_info): list(servers_unused_bricks_dict.values())[dict_index]) brick_path = '' if current_server_unused_bricks_list: - brick_path = ("%s:%s/%s_brick%s" % - (current_server, - current_server_unused_bricks_list[0], volname, num)) - bricks_list.append(brick_path) + if dirname and (" " not in dirname): + brick_path = ("%s:%s/%s_brick%s" % + (current_server, + current_server_unused_bricks_list[0], dirname, + num)) + bricks_list.append(brick_path) + else: + brick_path = ("%s:%s/%s_brick%s" % + (current_server, + current_server_unused_bricks_list[0], volname, + num)) + bricks_list.append(brick_path) # Remove the added brick from the current_server_unused_bricks_list list(servers_unused_bricks_dict.values())[dict_index].pop(0) @@ -1004,6 +1016,30 @@ def group_add(servers, groupname): return True +def group_del(servers, groupname): + """ + Deletes a group in all the servers. + + Args: + servers(list|str): Nodes on which cmd is to be executed. + groupname(str): Name of the group to be removed. + + Return always True + """ + if not isinstance(servers, list): + servers = [servers] + + cmd = "groupdel %s" % groupname + results = g.run_parallel(servers, cmd) + + for server, ret_value in list(results.items()): + retcode, _, err = ret_value + if retcode != 0 and "does not exist" in err: + g.log.error("Group %s on server %s already removed", + groupname, server) + return True + + def ssh_keygen(mnode): """ Creates a pair of ssh private and public key if not present @@ -1199,3 +1235,25 @@ def collect_bricks_arequal(bricks_list): arequal_list.append(arequal) return (return_code, arequal_list) + + +def get_usable_size_per_disk(brickpath, min_free_limit=10): + """Get the usable size per disk + + Args: + brickpath(str): Brick path to be used to calculate usable size + + Kwargs: + min_free_limit(int): Min free disk limit to be used + + Returns: + (int): Usable size in GB. None in case of errors. + """ + node, brick_path = brickpath.split(':') + size = get_size_of_mountpoint(node, brick_path) + if not size: + return None + size = int(size) + min_free_size = size * min_free_limit // 100 + usable_size = ((size - min_free_size) // 1048576) + 1 + return usable_size diff --git a/glustolibs-gluster/glustolibs/gluster/nfs_ganesha_ops.py b/glustolibs-gluster/glustolibs/gluster/nfs_ganesha_ops.py index ec4ca176b..d8486c7d2 100755 --- a/glustolibs-gluster/glustolibs/gluster/nfs_ganesha_ops.py +++ b/glustolibs-gluster/glustolibs/gluster/nfs_ganesha_ops.py @@ -1,5 +1,5 @@ #!/usr/bin/env python -# Copyright (C) 2016-2017 Red Hat, Inc. <http://www.redhat.com> +# Copyright (C) 2016-2021 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 @@ -50,17 +50,33 @@ def teardown_nfs_ganesha_cluster(servers, force=False): Example: teardown_nfs_ganesha_cluster(servers) """ + # Copy ganesha.conf before proceeding to clean up + for server in servers: + cmd = "cp /etc/ganesha/ganesha.conf ganesha.conf" + ret, _, _ = g.run(server, cmd) + if ret: + g.log.error("Failed to copy ganesha.conf") + if force: g.log.info("Executing force cleanup...") + cleanup_ops = ['--teardown', '--cleanup'] for server in servers: - cmd = ("/usr/libexec/ganesha/ganesha-ha.sh --teardown " - "/var/run/gluster/shared_storage/nfs-ganesha") - _, _, _ = g.run(server, cmd) - cmd = ("/usr/libexec/ganesha/ganesha-ha.sh --cleanup /var/run/" - "gluster/shared_storage/nfs-ganesha") - _, _, _ = g.run(server, cmd) + # Perform teardown and cleanup + for op in cleanup_ops: + cmd = ("/usr/libexec/ganesha/ganesha-ha.sh {} /var/run/" + "gluster/shared_storage/nfs-ganesha".format(op)) + _, _, _ = g.run(server, cmd) + + # Stop nfs ganesha service _, _, _ = stop_nfs_ganesha_service(server) + + # Clean shared storage, ganesha.conf, and replace with backup + for cmd in ("rm -rf /var/run/gluster/shared_storage/*", + "rm -rf /etc/ganesha/ganesha.conf", + "cp ganesha.conf /etc/ganesha/ganesha.conf"): + _, _, _ = g.run(server, cmd) return True + ret, _, _ = disable_nfs_ganesha(servers[0]) if ret != 0: g.log.error("Nfs-ganesha disable failed") @@ -755,6 +771,22 @@ def create_nfs_ganesha_cluster(servers, vips): # Create backup of ganesha-ha.conf file in ganesha_mnode g.upload(ganesha_mnode, tmp_ha_conf, '/etc/ganesha/') + # setsebool ganesha_use_fusefs on + cmd = "setsebool ganesha_use_fusefs on" + for server in servers: + ret, _, _ = g.run(server, cmd) + if ret: + g.log.error("Failed to 'setsebool ganesha_use_fusefs on' on %", + server) + return False + + # Verify ganesha_use_fusefs is on + _, out, _ = g.run(server, "getsebool ganesha_use_fusefs") + if "ganesha_use_fusefs --> on" not in out: + g.log.error("Failed to 'setsebool ganesha_use_fusefs on' on %", + server) + return False + # Enabling ganesha g.log.info("Enable nfs-ganesha") ret, _, _ = enable_nfs_ganesha(ganesha_mnode) @@ -768,6 +800,31 @@ def create_nfs_ganesha_cluster(servers, vips): # pcs status output _, _, _ = g.run(ganesha_mnode, "pcs status") + # pacemaker status output + _, _, _ = g.run(ganesha_mnode, "systemctl status pacemaker") + + return True + + +def enable_firewall(servers): + """Enables Firewall if not enabled already + Args: + servers(list): Hostname of ganesha nodes + Returns: + Status (bool) : True/False based on the status of firewall enable + """ + + cmd = "systemctl status firewalld | grep Active" + for server in servers: + ret, out, _ = g.run(server, cmd) + if 'inactive' in out: + g.log.info("Firewalld is not running. Enabling Firewalld") + for command in ("enable", "start"): + ret, out, _ = g.run(server, + "systemctl {} firewalld".format(command)) + if ret: + g.log.error("Failed to enable Firewalld on %s", server) + return False return True @@ -781,9 +838,11 @@ def ganesha_server_firewall_settings(servers): True(bool): If successfully set the firewall settings False(bool): If failed to do firewall settings """ + if not enable_firewall(servers): + return False + services = ['nfs', 'rpc-bind', 'high-availability', 'nlm', 'mountd', 'rquota'] - ret = add_services_to_firewall(servers, services, True) if not ret: g.log.error("Failed to set firewall zone permanently on ganesha nodes") diff --git a/glustolibs-gluster/glustolibs/gluster/volume_libs.py b/glustolibs-gluster/glustolibs/gluster/volume_libs.py index 376ac5089..87e70ca8c 100644 --- a/glustolibs-gluster/glustolibs/gluster/volume_libs.py +++ b/glustolibs-gluster/glustolibs/gluster/volume_libs.py @@ -124,8 +124,8 @@ def setup_volume(mnode, all_servers_info, volume_config, multi_vol=False, return False # Check if the volume already exists - volinfo = get_volume_info(mnode=mnode) - if volinfo is not None and volname in volinfo.keys(): + vollist = get_volume_list(mnode=mnode) + if vollist is not None and volname in vollist: g.log.info("volume %s already exists. Returning...", volname) return True |