From a6ac5d251f446af5ffa9cc8af836be26f39532e4 Mon Sep 17 00:00:00 2001 From: Shwetha Panduranga Date: Fri, 3 Mar 2017 18:54:54 +0530 Subject: Adding a test for Gluster Basic Component Verification Sanity Suite. - expanding the volume i.e test add-brick is successful on the volume. Change-Id: I8110eea97cf46e3ccc24156d6c67cae0cbf5a7c1 Signed-off-by: Shwetha Panduranga --- .../glustolibs/gluster/brick_libs.py | 131 +++++++------ glustolibs-gluster/glustolibs/gluster/brick_ops.py | 47 +++-- .../glustolibs/gluster/volume_libs.py | 168 +++++++++++++++- tests/functional/bvt/test_cvt.py | 214 ++++++++++++++++----- 4 files changed, 429 insertions(+), 131 deletions(-) diff --git a/glustolibs-gluster/glustolibs/gluster/brick_libs.py b/glustolibs-gluster/glustolibs/gluster/brick_libs.py index 508eae6a9..a67ef1d7e 100644 --- a/glustolibs-gluster/glustolibs/gluster/brick_libs.py +++ b/glustolibs-gluster/glustolibs/gluster/brick_libs.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2015-2016 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify @@ -15,9 +14,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" - Description: Module for gluster brick related helper functions. -""" +""" Description: Module for gluster brick related helper functions. """ import random from glusto.core import Glusto as g @@ -39,7 +36,7 @@ def get_all_bricks(mnode, volname): """ volinfo = get_volume_info(mnode, volname) if volinfo is None: - g.log.error("Unable to get the volinfo of %s." % volname) + g.log.error("Unable to get the volinfo of %s.", volname) return None if 'Tier' in volinfo[volname]['typeStr']: @@ -63,15 +60,15 @@ def get_all_bricks(mnode, volname): all_bricks.append(brick['name']) else: g.log.error("brick %s doesn't have the key 'name' " - "for the volume: %s" % (brick, volname)) + "for the volume: %s", brick, volname) return None return all_bricks else: g.log.error("Bricks not found in Bricks section of volume " - "info for the volume %s" % volname) + "info for the volume %s", volname) return None else: - g.log.error("Bricks not found for the volume %s" % volname) + g.log.error("Bricks not found for the volume %s", volname) return None @@ -88,11 +85,11 @@ def get_hot_tier_bricks(mnode, volname): """ volinfo = get_volume_info(mnode, volname) if volinfo is None: - g.log.error("Unable to get the volinfo of %s." % volname) + g.log.error("Unable to get the volinfo of %s.", volname) return None if 'Tier' not in volinfo[volname]['typeStr']: - g.log.error("Volume %s is not a tiered volume" % volname) + g.log.error("Volume %s is not a tiered volume", volname) return None hot_tier_bricks = [] @@ -104,15 +101,15 @@ def get_hot_tier_bricks(mnode, volname): hot_tier_bricks.append(brick['name']) else: g.log.error("brick %s doesn't have the key 'name' " - "for the volume: %s" % (brick, volname)) + "for the volume: %s", brick, volname) return None else: g.log.error("Bricks not found in hotBricks section of volume " - "info for the volume %s" % volname) + "info for the volume %s", volname) return None return hot_tier_bricks else: - g.log.error("Bricks not found for the volume %s" % volname) + g.log.error("Bricks not found for the volume %s", volname) return None @@ -129,11 +126,11 @@ def get_cold_tier_bricks(mnode, volname): """ volinfo = get_volume_info(mnode, volname) if volinfo is None: - g.log.error("Unable to get the volinfo of %s." % volname) + g.log.error("Unable to get the volinfo of %s.", volname) return None if 'Tier' not in volinfo[volname]['typeStr']: - g.log.error("Volume %s is not a tiered volume" % volname) + g.log.error("Volume %s is not a tiered volume", volname) return None cold_tier_bricks = [] @@ -145,20 +142,20 @@ def get_cold_tier_bricks(mnode, volname): cold_tier_bricks.append(brick['name']) else: g.log.error("brick %s doesn't have the key 'name' " - "for the volume: %s" % (brick, volname)) + "for the volume: %s", brick, volname) return None else: g.log.error("Bricks not found in coldBricks section of volume " - "info for the volume %s" % volname) + "info for the volume %s", volname) return None return cold_tier_bricks else: - g.log.error("Bricks not found for the volume %s" % volname) + g.log.error("Bricks not found for the volume %s", volname) return None def bring_bricks_offline(volname, bricks_list, - bring_bricks_offline_methods=['service_kill']): + bring_bricks_offline_methods=None): """Bring the bricks specified in the bricks_list offline. Args: @@ -176,7 +173,12 @@ def bring_bricks_offline(volname, bricks_list, bool : True on successfully bringing all bricks offline. False otherwise """ - rc = True + if bring_bricks_offline_methods is None: + bring_bricks_offline_methods = ['service_kill'] + elif isinstance(bring_bricks_offline_methods, str): + bring_bricks_offline_methods = [bring_bricks_offline_methods] + + _rc = True failed_to_bring_offline_list = [] for brick in bricks_list: bring_brick_offline_method = (random.choice @@ -189,26 +191,25 @@ def bring_bricks_offline(volname, bricks_list, (volname, brick_node, brick_path)) ret, _, _ = g.run(brick_node, kill_cmd) if ret != 0: - g.log.error("Unable to kill the brick %s" % brick) + g.log.error("Unable to kill the brick %s", brick) failed_to_bring_offline_list.append(brick) - rc = False + _rc = False else: - g.log.error("Invalid method '%s' to bring brick offline" % + g.log.error("Invalid method '%s' to bring brick offline", bring_brick_offline_method) return False - if not rc: - g.log.error("Unable to bring some of the bricks %s offline" % + if not _rc: + g.log.error("Unable to bring some of the bricks %s offline", failed_to_bring_offline_list) return False - g.log.info("All the bricks : %s are brought offline" % bricks_list) + g.log.info("All the bricks : %s are brought offline", bricks_list) return True def bring_bricks_online(mnode, volname, bricks_list, - bring_bricks_online_methods=['glusterd_restart', - 'volume_start_force']): + bring_bricks_online_methods=None): """Bring the bricks specified in the bricks_list online. Args: @@ -230,39 +231,45 @@ def bring_bricks_online(mnode, volname, bricks_list, bool : True on successfully bringing all bricks online. False otherwise """ - rc = True + if bring_bricks_online_methods is None: + bring_bricks_online_methods = ['glusterd_restart', + 'volume_start_force'] + elif isinstance(bring_bricks_online_methods, str): + bring_bricks_online_methods = [bring_bricks_online_methods] + + _rc = True failed_to_bring_online_list = [] for brick in bricks_list: bring_brick_online_method = random.choice(bring_bricks_online_methods) if bring_brick_online_method == 'glusterd_restart': bring_brick_online_command = "service glusterd restart" - brick_node, brick_path = brick.split(":") + brick_node, _ = brick.split(":") ret, _, _ = g.run(brick_node, bring_brick_online_command) if ret != 0: - g.log.error("Unable to restart glusterd on node %s" % - (brick_node)) - rc = False + g.log.error("Unable to restart glusterd on node %s", + brick_node) + _rc = False failed_to_bring_online_list.append(brick) elif bring_brick_online_method == 'volume_start_force': bring_brick_online_command = ("gluster volume start %s force" % volname) ret, _, _ = g.run(mnode, bring_brick_online_command) if ret != 0: - g.log.error("Unable to start the volume %s with force option" % - (volname)) - rc = False + g.log.error("Unable to start the volume %s with force option", + volname) + _rc = False else: break else: - g.log.error("Invalid method '%s' to bring brick online" % + g.log.error("Invalid method '%s' to bring brick online", bring_brick_online_method) return False - if not rc: - g.log.error("Unable to bring some of the bricks %s online" % + if not _rc: + g.log.error("Unable to bring some of the bricks %s online", failed_to_bring_online_list) return False - g.log.info("All the bricks : %s are brought online" % bricks_list) + g.log.info("All the bricks : %s are brought online", bricks_list) return True @@ -278,26 +285,26 @@ def are_bricks_offline(mnode, volname, bricks_list): bool : True if all bricks offline. False otherwise. NoneType: None on failure in getting volume status """ - rc = True + _rc = True online_bricks_list = [] volume_status = get_volume_status(mnode, volname) if not volume_status: - g.log.error("Unable to check if bricks are offline for the volume %s" % + g.log.error("Unable to check if bricks are offline for the volume %s", volname) return None for brick in bricks_list: brick_node, brick_path = brick.split(":") status = int(volume_status[volname][brick_node][brick_path]['status']) if status != 0: - g.log.error("BRICK : %s is not offline" % (brick)) + g.log.error("BRICK : %s is not offline", brick) online_bricks_list.append(brick) - rc = False - if not rc: - g.log.error("Some of the bricks %s are not offline" % + _rc = False + if not _rc: + g.log.error("Some of the bricks %s are not offline", online_bricks_list) return False - g.log.info("All the bricks in %s are offline" % bricks_list) + g.log.info("All the bricks in %s are offline", bricks_list) return True @@ -313,27 +320,27 @@ def are_bricks_online(mnode, volname, bricks_list): bool : True if all bricks online. False otherwise. NoneType: None on failure in getting volume status """ - rc = True + _rc = True offline_bricks_list = [] volume_status = get_volume_status(mnode, volname) if not volume_status: - g.log.error("Unable to check if bricks are online for the volume %s" % + g.log.error("Unable to check if bricks are online for the volume %s", volname) return None for brick in bricks_list: brick_node, brick_path = brick.split(":") status = int(volume_status[volname][brick_node][brick_path]['status']) if status != 1: - g.log.error("BRICK : %s is not online" % (brick)) + g.log.error("BRICK : %s is not online", brick) offline_bricks_list.append(brick) - rc = False + _rc = False - if not rc: - g.log.error("Some of the bricks %s are not online" % + if not _rc: + g.log.error("Some of the bricks %s are not online", offline_bricks_list) return False - g.log.info("All the bricks %s are online" % bricks_list) + g.log.info("All the bricks %s are online", bricks_list) return True @@ -351,7 +358,7 @@ def get_offline_bricks_list(mnode, volname): offline_bricks_list = [] volume_status = get_volume_status(mnode, volname) if not volume_status: - g.log.error("Unable to get offline bricks_list for the volume %s" % + g.log.error("Unable to get offline bricks_list for the volume %s", volname) return None @@ -379,7 +386,7 @@ def get_online_bricks_list(mnode, volname): online_bricks_list = [] volume_status = get_volume_status(mnode, volname) if not volume_status: - g.log.error("Unable to get online bricks_list for the volume %s" % + g.log.error("Unable to get online bricks_list for the volume %s", volname) return None @@ -402,13 +409,13 @@ def delete_bricks(bricks_list): Returns: bool : True if all the bricks are deleted. False otherwise. """ - rc = True + _rc = True for brick in bricks_list: brick_node, brick_path = brick.split(":") _, _, _ = g.run(brick_node, "rm -rf %s" % brick_path) - ret, out, err = g.run(brick_node, "ls %s" % brick_path) + ret, _, _ = g.run(brick_node, "ls %s" % brick_path) if ret == 0: - g.log.error("Unable to delete brick %s on node %s" % - (brick_path, brick_node)) - rc = False - return rc + g.log.error("Unable to delete brick %s on node %s", + brick_path, brick_node) + _rc = False + return _rc diff --git a/glustolibs-gluster/glustolibs/gluster/brick_ops.py b/glustolibs-gluster/glustolibs/gluster/brick_ops.py index 61c86fa75..016569f15 100644 --- a/glustolibs-gluster/glustolibs/gluster/brick_ops.py +++ b/glustolibs-gluster/glustolibs/gluster/brick_ops.py @@ -1,4 +1,3 @@ -#!/usr/bin/env python # Copyright (C) 2015-2016 Red Hat, Inc. # # This program is free software; you can redistribute it and/or modify @@ -15,14 +14,12 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" - Description: Module for gluster brick operations -""" +""" Description: Module for gluster brick operations """ from glusto.core import Glusto as g -def add_brick(mnode, volname, bricks_list, replica=None): +def add_brick(mnode, volname, bricks_list, force=False, **kwargs): """Add Bricks specified in the bricks_list to the volume. Args: @@ -31,8 +28,14 @@ def add_brick(mnode, volname, bricks_list, replica=None): bricks_list (list): List of bricks to be added Kwargs: - replica (int): Replica count to increase the replica count of - the volume. + force (bool): If this option is set to True, then add brick command + will get executed with force option. If it is set to False, + then add brick command will get executed without force option + + **kwargs + The keys, values in kwargs are: + - replica_count : (int)|None + - arbiter_count : (int)|None Returns: tuple: Tuple containing three elements (ret, out, err). @@ -45,17 +48,32 @@ def add_brick(mnode, volname, bricks_list, replica=None): The third element 'err' is of type 'str' and is the stderr value of the command execution. """ - if replica is None: - cmd = ("gluster volume add-brick %s %s" % - (volname, ' '.join(bricks_list))) - else: - cmd = ("gluster volume add-brick %s replica %d %s" % - (volname, int(replica), ' '.join(bricks_list))) + replica_count = arbiter_count = None + + if 'replica_count' in kwargs: + replica_count = int(kwargs['replica_count']) + + if 'arbiter_count' in kwargs: + arbiter_count = int(kwargs['arbiter_count']) + + replica = arbiter = '' + + if replica_count is not None: + replica = "replica %d" % replica_count + + if arbiter_count is not None: + arbiter = "arbiter %d" % arbiter_count + + force_value = '' + if force: + force_value = "force" + + cmd = ("gluster volume add-brick %s %s %s %s %s" % + (volname, replica, arbiter, ' '.join(bricks_list), force_value)) return g.run(mnode, cmd) -# remove_brick def remove_brick(mnode, volname, bricks_list, option, replica=None): """Remove bricks specified in the bricks_list from the volume. @@ -94,7 +112,6 @@ def remove_brick(mnode, volname, bricks_list, option, replica=None): return g.run(mnode, cmd) -# replace_brick def replace_brick(mnode, volname, src_brick, dst_brick): """Replace src brick with dst brick from the volume. diff --git a/glustolibs-gluster/glustolibs/gluster/volume_libs.py b/glustolibs-gluster/glustolibs/gluster/volume_libs.py index 6a3f80ac3..22ac7e375 100644 --- a/glustolibs-gluster/glustolibs/gluster/volume_libs.py +++ b/glustolibs-gluster/glustolibs/gluster/volume_libs.py @@ -14,9 +14,7 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" - Description: Module for gluster volume related helper functions. -""" +""" Description: Module for gluster volume related helper functions. """ from glusto.core import Glusto as g @@ -26,7 +24,8 @@ from glustolibs.gluster.volume_ops import (volume_create, volume_start, set_volume_options, get_volume_info, volume_stop, volume_delete, volume_info, volume_status, - get_volume_options) + get_volume_options, + get_volume_list) from glustolibs.gluster.tiering_ops import (add_extra_servers_to_cluster, tier_attach, is_tier_process_running) @@ -36,6 +35,29 @@ from glustolibs.gluster.uss_ops import enable_uss, is_uss_enabled from glustolibs.gluster.snap_ops import snap_delete_by_volumename from glustolibs.gluster.brick_libs import are_bricks_online, get_all_bricks from glustolibs.gluster.heal_libs import are_all_self_heal_daemons_are_online +from glustolibs.gluster.brick_ops import add_brick + + +def volume_exists(mnode, volname): + """Check if volume already exists + + Args: + mnode (str): Node on which commands has to be executed + volname (str): Name of the volume. + + Returns: + NoneType: If there are errors + bool : True if volume exists. False Otherwise + """ + volume_list = get_volume_list(mnode) + if volume_list is None: + g.log.error("'gluster volume list' on node %s Failed", mnode) + return None + + if volname in volume_list: + return True + else: + return False def setup_volume(mnode, all_servers_info, volume_config, force=False): @@ -482,6 +504,7 @@ def get_subvols(mnode, volname): """ subvols = { + 'is_tier': False, 'hot_tier_subvols': [], 'cold_tier_subvols': [], 'volume_subvols': [] @@ -490,6 +513,9 @@ def get_subvols(mnode, volname): if volinfo is not None: voltype = volinfo[volname]['typeStr'] if voltype == 'Tier': + # Set is_tier to True + subvols['is_tier'] = True + # Get hot tier subvols hot_tier_type = (volinfo[volname]["bricks"] ['hotBricks']['hotBrickType']) @@ -1093,3 +1119,137 @@ def enable_and_validate_volume_options(mnode, volname, volume_options_list, time.sleep(time_delay) return True + + +def expand_volume(mnode, volname, servers, all_servers_info, force=False, + add_to_hot_tier=False, **kwargs): + """Forms list of bricks to add and adds those bricks to the volume. + + Args: + mnode (str): Node on which commands has to be executed + volname (str): volume name + servers (list): List of servers in the storage pool. + all_servers_info (dict): Information about all servers. + example : + all_servers_info = { + 'abc.lab.eng.xyz.com': { + 'host': 'abc.lab.eng.xyz.com', + 'brick_root': '/bricks', + 'devices': ['/dev/vdb', '/dev/vdc', '/dev/vdd', '/dev/vde'] + }, + 'def.lab.eng.xyz.com':{ + 'host': 'def.lab.eng.xyz.com', + 'brick_root': '/bricks', + 'devices': ['/dev/vdb', '/dev/vdc', '/dev/vdd', '/dev/vde'] + } + } + Kwargs: + force (bool): If this option is set to True, then add-brick command + will get executed with force option. If it is set to False, + then add-brick command will get executed without force option + + add_to_hot_tier (bool): True If bricks are to be added to hot_tier. + False otherwise. Defaults to False. + + **kwargs + The keys, values in kwargs are: + - replica_count : (int)|None + - arbiter_count : (int)|None + - distribute_count: (int)|None + + Returns: + bool: True of expanding volumes is successful. + False otherwise. + """ + # Check whether we need to increase the replica count of the volume + if 'replica_count' in kwargs: + new_replica_count = int(kwargs['replica_count']) + + # Get replica count info. + replica_count_info = get_replica_count(mnode, volname) + + # Get Subvols + subvols_info = get_subvols(mnode, volname) + + # Calculate number of bricks to add + if subvols_info['is_tier']: + if add_to_hot_tier: + num_of_subvols = len(subvols_info['hot_tier_subvols']) + current_replica_count = ( + int(replica_count_info['hot_tier_replica_count'])) + else: + num_of_subvols = len(subvols_info['cold_tier_subvols']) + current_replica_count = ( + int(replica_count_info['cold_tier_replica_count'])) + else: + num_of_subvols = len(subvols_info['volume_subvols']) + current_replica_count = ( + int(replica_count_info['volume_replica_count'])) + + if num_of_subvols == 0: + g.log.error("No Sub-Volumes available for the volume %s." + "Hence cannot proceed with add-brick", volname) + return False + + if new_replica_count <= current_replica_count: + g.log.error("Provided replica count '%d' is less than or equal to " + "the Existing replica count '%d' of the volume %s. " + "Hence cannot proceed with add-brick", + new_replica_count, current_replica_count, volname) + return False + + num_of_bricks_to_add = ( + (new_replica_count - current_replica_count) * num_of_subvols) + + else: + # Check if the volume has to be expanded by n distribute count. + if 'distribute_count' in kwargs: + distribute_count_to_add = int(kwargs['distribute_count']) + else: + distribute_count_to_add = 1 + + # Get Number of bricks per subvolume. + bricks_per_subvol_dict = get_num_of_bricks_per_subvol(mnode, volname) + + # Get number of bricks to add. + if bricks_per_subvol_dict['is_tier']: + if add_to_hot_tier: + num_of_bricks_per_subvol = ( + bricks_per_subvol_dict['hot_tier_num_of_bricks_per_subvol'] + ) + else: + num_of_bricks_per_subvol = ( + bricks_per_subvol_dict + ['cold_tier_num_of_bricks_per_subvol'] + ) + else: + num_of_bricks_per_subvol = ( + bricks_per_subvol_dict['volume_num_of_bricks_per_subvol']) + + if num_of_bricks_per_subvol is None: + g.log.error("Number of bricks per subvol is None. " + "Something majorly went wrong on the voluem %s", + volname) + return False + + num_of_bricks_to_add = ( + num_of_bricks_per_subvol * distribute_count_to_add) + + # Form bricks list to add bricks to the volume. + bricks_list = form_bricks_list(mnode=mnode, volname=volname, + number_of_bricks=num_of_bricks_to_add, + servers=servers, + servers_info=all_servers_info) + if not bricks_list: + g.log.error("Number of bricks is greater than the unused bricks on " + "servers. Hence failed to perform add-brick operation") + return False + + # Add bricks to the volume + ret, out, err = add_brick(mnode, volname, bricks_list, force=force, + **kwargs) + if ret != 0: + g.log.error("Failed to add bricks to the volume: %s", err) + return False + g.log.info("Successfully added bricks to the volume: %s", out) + return True diff --git a/tests/functional/bvt/test_cvt.py b/tests/functional/bvt/test_cvt.py index d2d88f44b..bc6634bb2 100644 --- a/tests/functional/bvt/test_cvt.py +++ b/tests/functional/bvt/test_cvt.py @@ -14,7 +14,23 @@ # with this program; if not, write to the Free Software Foundation, Inc., # 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. -""" Description: BVT-Component Verification Tests (CVT) """ +""" Build Verification Tests (BVT) : Component Verification Tests (CVT) + Test Cases in this module tests the basic gluster operations sanity + while IO is in progress. These tests verifies basic gluster features + which should not be broken at all. + + Basic Gluster Operations tested: + - add-brick + - rebalance + - set volume options which changes the client graphs + TODO: + - remove-brick + - n/w failure followed by heal + - replace-brick + - enable quota + - collecting snapshot + - attach-tier, detach-tier +""" import pytest import time @@ -22,6 +38,13 @@ from glusto.core import Glusto as g from glustolibs.gluster.gluster_base_class import (GlusterVolumeBaseClass, runs_on) from glustolibs.gluster.volume_libs import enable_and_validate_volume_options +from glustolibs.gluster.volume_libs import ( + verify_all_process_of_volume_are_online) +from glustolibs.gluster.volume_libs import (log_volume_info_and_status, + expand_volume) +from glustolibs.gluster.rebalance_ops import (rebalance_start, + wait_for_rebalance_to_complete, + rebalance_status) from glustolibs.misc.misc_libs import upload_scripts from glustolibs.io.utils import (validate_io_procs, log_mounts_info, list_all_files_and_dirs_mounts, @@ -29,24 +52,8 @@ from glustolibs.io.utils import (validate_io_procs, log_mounts_info, from glustolibs.gluster.exceptions import ExecutionError -@runs_on([['replicated', 'distributed', 'distributed-replicated', - 'dispersed', 'distributed-dispersed'], - ['glusterfs', 'nfs', 'cifs']]) -class TestGlusterBasicFeaturesSanity(GlusterVolumeBaseClass): - """ TestGlusterBasicFeaturesSanity contains tests which verifies basic - gluster features which should not be broken at all. - This covers testing gluster operations while IO is in progress. - - set volume options which changes the client graphs - TODO: - - add-brick - - rebalance - - remove-brick - - n/w failure followed by heal - - replace-brick - - enable quota - - collecting snapshot - - attach-tier, detach-tier - """ +class GlusterBasicFeaturesSanityBaseClass(GlusterVolumeBaseClass): + """ BaseClass for all the gluster basic features sanity tests. """ @classmethod def setUpClass(cls): """Setup Volume, Create Mounts and upload the necessary scripts to run @@ -93,7 +100,7 @@ class TestGlusterBasicFeaturesSanity(GlusterVolumeBaseClass): cmd = ("python %s create_deep_dirs_with_files " "--dirname-start-num %d " "--dir-depth 2 " - "--dir-length 10 " + "--dir-length 15 " "--max-num-of-dirs 5 " "--num-of-files 5 %s" % (self.script_upload_path, self.counter, @@ -108,12 +115,144 @@ class TestGlusterBasicFeaturesSanity(GlusterVolumeBaseClass): # is to ensure IO's are in progress and giving some time to fill data time.sleep(15) + def tearDown(self): + """If test method failed before validating IO, tearDown waits for the + IO's to complete and checks for the IO exit status + """ + # Wait for IO to complete if io validation is not executed in the + # test method + if not self.io_validation_complete: + g.log.info("Wait for IO to complete as IO validation did not " + "succeed in test method") + ret = wait_for_io_to_complete(self.all_mounts_procs, self.mounts) + if not ret: + raise ExecutionError("IO failed on some of the clients") + g.log.info("IO is successful on all mounts") + GlusterVolumeBaseClass.tearDown.im_func(self) + + @classmethod + def tearDownClass(cls): + """Cleanup data from mount and cleanup volume. + """ + # Log Mounts info + g.log.info("Log mounts info") + log_mounts_info(cls.mounts) + + GlusterVolumeBaseClass.tearDownClass.im_func(cls) + + +@runs_on([['replicated', 'distributed', 'distributed-replicated', + 'dispersed', 'distributed-dispersed'], + ['glusterfs', 'nfs', 'cifs']]) +class TestGlusterExpandVolumeSanity(GlusterBasicFeaturesSanityBaseClass): + """Sanity tests for Expanding Volume""" + @pytest.mark.bvt_cvt + def test_expanding_volume_when_io_in_progress(self): + """Test expanding volume (Increase distribution) using existing + servers bricks when IO is in progress. + + Description: + - add bricks + - starts rebalance + - wait for rebalance to complete + - validate IO + """ + # Log Volume Info and Status before expanding the volume. + g.log.info("Logging volume info and Status before expanding volume") + ret = log_volume_info_and_status(self.mnode, self.volname) + self.assertTrue(ret, ("Logging volume info and status failed on " + "volume %s", self.volname)) + g.log.info("Successful in logging volume info and status of volume %s", + self.volname) + + # Expanding volume by adding bricks to the volume when IO in progress + g.log.info("Start adding bricks to volume when IO in progress") + ret = expand_volume(self.mnode, self.volname, self.servers, + self.all_servers_info) + self.assertTrue(ret, ("Failed to expand the volume when IO in " + "progress on volume %s", self.volname)) + g.log.info("Expanding volume when IO in progress is successful on " + "volume %s", self.volname) + + # Wait for gluster processes to come online + time.sleep(30) + + # Log Volume Info and Status after expanding the volume + g.log.info("Logging volume info and Status after expanding volume") + ret = log_volume_info_and_status(self.mnode, self.volname) + self.assertTrue(ret, ("Logging volume info and status failed on " + "volume %s", self.volname)) + g.log.info("Successful in logging volume info and status of volume %s", + self.volname) + + # Verify volume's all process are online + g.log.info("Verifying volume's all process are online") + ret = verify_all_process_of_volume_are_online(self.mnode, self.volname) + self.assertTrue(ret, ("Volume %s : All process are not online", + self.volname)) + g.log.info("Volume %s : All process are online", self.volname) + + # Start Rebalance + g.log.info("Starting Rebalance on the volume") + ret, _, _ = rebalance_start(self.mnode, self.volname) + self.assertEqual(ret, 0, ("Failed to start rebalance on the volume " + "%s", self.volname)) + g.log.info("Successfully started rebalance on the volume %s", + self.volname) + + # Check Rebalance status + g.log.info("Checking Rebalance status") + ret, _, _ = rebalance_status(self.mnode, self.volname) + self.assertEqual(ret, 0, ("Failed to get rebalance status for the " + "volume %s", self.volname)) + g.log.info("Successfully got rebalance status of the volume %s", + self.volname) + + # Wait for rebalance to complete + g.log.info("Waiting for rebalance to complete") + ret = wait_for_rebalance_to_complete(self.mnode, self.volname) + self.assertTrue(ret, ("Rebalance is not yet complete on the volume " + "%s", self.volname)) + g.log.info("Rebalance is successfully complete on the volume %s", + self.volname) + + # Check Rebalance status after rebalance is complete + g.log.info("Checking Rebalance status") + ret, _, _ = rebalance_status(self.mnode, self.volname) + self.assertEqual(ret, 0, ("Failed to get rebalance status for the " + "volume %s", self.volname)) + g.log.info("Successfully got rebalance status of the volume %s", + self.volname) + + # Validate IO + g.log.info("Wait for IO to complete and validate IO ...") + ret = validate_io_procs(self.all_mounts_procs, self.mounts) + self.io_validation_complete = True + self.assertTrue(ret, "IO failed on some of the clients") + g.log.info("IO is successful on all mounts") + + # List all files and dirs created + g.log.info("List all files and directories:") + ret = list_all_files_and_dirs_mounts(self.mounts) + self.assertTrue(ret, "Failed to list all files and dirs") + g.log.info("Listing all files and directories is successful") + + +@runs_on([['replicated', 'distributed', 'distributed-replicated', + 'dispersed', 'distributed-dispersed'], + ['glusterfs', 'nfs', 'cifs']]) +class TestGlusterVolumeSetSanity(GlusterBasicFeaturesSanityBaseClass): + """ Sanity tests for Volume Set operation + """ @pytest.mark.bvt_cvt def test_volume_set_when_io_in_progress(self): - """Set Volume options while IO is in progress. - Volume Options: - - uss - - shard + """Set Volume options which changes the client graphs while IO is + in progress. + + Description: + - set volume option uss, shard to 'enable' and + validate it is successful + - validate IO to be successful """ # List of volume options to set volume_options_list = ["features.uss", "features.shard"] @@ -122,7 +261,7 @@ class TestGlusterBasicFeaturesSanity(GlusterVolumeBaseClass): g.log.info("Setting the volume options: %s", volume_options_list) ret = enable_and_validate_volume_options(self.mnode, self.volname, volume_options_list, - time_delay=10) + time_delay=30) self.assertTrue(ret, ("Unable to enable the volume options: %s", volume_options_list)) g.log.info("Successfully enabled all the volume options: %s", @@ -140,28 +279,3 @@ class TestGlusterBasicFeaturesSanity(GlusterVolumeBaseClass): ret = list_all_files_and_dirs_mounts(self.mounts) self.assertTrue(ret, "Failed to list all files and dirs") g.log.info("Listing all files and directories is successful") - - def tearDown(self): - """If test method failed before validating IO, tearDown waits for the - IO's to complete and checks for the IO exit status - """ - # Wait for IO to complete if io validation is not executed in the - # test method - if not self.io_validation_complete: - g.log.info("Wait for IO to complete as IO validation did not " - "succeed in test method") - ret = wait_for_io_to_complete(self.all_mounts_procs, self.mounts) - if not ret: - raise ExecutionError("IO failed on some of the clients") - g.log.info("IO is successful on all mounts") - GlusterVolumeBaseClass.tearDown.im_func(self) - - @classmethod - def tearDownClass(cls): - """Cleanup data from mount and cleanup volume. - """ - # Log Mounts info - g.log.info("Log mounts info") - log_mounts_info(cls.mounts) - - GlusterVolumeBaseClass.tearDownClass.im_func(cls) -- cgit