From d60d5c42692c547608c9a45f879d67e9c79e10ca Mon Sep 17 00:00:00 2001 From: Bala Konda Reddy M Date: Fri, 31 Jan 2020 18:23:58 +0530 Subject: [Lib] Library for multi volume creation Earlier, brick creation is carried out based on the difference of used and unused bricks. This is a bottleneck for implementing brick multiplexing testcases. Moreover we can't create more than 10 volumes. With this library, implementing a way to create bricks on top of the existing servers in a cyclic way to have equal number of bricks on each brick partition on each server Added paramter in setup_volume function, if multi_vol flag is set it will fetch bricks using cyclic manner using (form_bricks_for_multi_vol) otherwise it will fetch using old mechanism. Added bulk_volume_creation function, to create multiple volumes the user has specified. Change-Id: I2103ec6ce2be4e091e0a96b18220d5e3502284a0 Signed-off-by: Bala Konda Reddy M --- .../glustolibs/gluster/brickmux_libs.py | 146 +++++++++++++++++++++ .../glustolibs/gluster/volume_libs.py | 79 ++++++++++- 2 files changed, 220 insertions(+), 5 deletions(-) create mode 100644 glustolibs-gluster/glustolibs/gluster/brickmux_libs.py (limited to 'glustolibs-gluster/glustolibs/gluster') diff --git a/glustolibs-gluster/glustolibs/gluster/brickmux_libs.py b/glustolibs-gluster/glustolibs/gluster/brickmux_libs.py new file mode 100644 index 000000000..1206b4682 --- /dev/null +++ b/glustolibs-gluster/glustolibs/gluster/brickmux_libs.py @@ -0,0 +1,146 @@ +# Copyright (C) 2020 Red Hat, Inc. +# +# 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 +# the Free Software Foundation; either version 2 of the License, or +# any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# 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 Brick multiplexing realted helper functions. +""" + +from itertools import cycle +try: + from itertools import zip_longest +except ImportError: + from itertools import izip_longest as zip_longest + +from glusto.core import Glusto as g +from glustolibs.gluster.volume_ops import get_volume_list +from glustolibs.gluster.lib_utils import get_servers_bricks_dict + + +def get_all_bricks_from_servers_multivol(servers, servers_info): + """ + Form list of all the bricks to create/add-brick from the given + servers and servers_info + + Args: + servers (list): List of servers in the storage pool. + servers_info (dict): Information about all servers. + + Returns: + brickCount (int): Number of bricks available from the servers. + bricks_list (list): List of all bricks from the servers provided. + + example : + 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'] + } + } + """ + if not isinstance(servers, list): + servers = [servers] + + brickCount, bricks_list = 0, [] + + servers_bricks = get_servers_bricks_dict(servers, servers_info) + server_ip = cycle(servers_bricks.keys()) + + for item in list(zip_longest(*list(servers_bricks.values()))): + for brick in item: + server = server_ip.next() + if brick: + bricks_list.append(server + ":" + brick) + brickCount += 1 + return brickCount, bricks_list + + +def get_current_brick_index(mnode): + """ + Get the brick current index from the node of the cluster. + + Args: + mnode (str): Node on which commands has to be executed. + + Returns: + NoneType: If there are any errors + int: Count of the bricks in the cluster. + """ + ret, brick_index, err = g.run(mnode, "gluster volume info | egrep " + "\"^Brick[0-9]+\" | grep -v \"ss_brick\"") + if ret: + g.log.error("Error in getting bricklist using gluster v info %s" % err) + return None + + g.log.info("brick_index is ", brick_index) + return len(brick_index.splitlines()) + + +def form_bricks_for_multivol(mnode, volname, number_of_bricks, servers, + servers_info): + """ + Forms brics list for volume create/add-brick given the number_of_bricks + servers, servers_info, for multiple volume cluster and for brick multiplex + enabled cluster. + + Args: + mnode (str): Node on which commands has to be executed. + volname (str): Volume name for which we require brick-list + number_of_bricks (int): The number of bricks for which brick list + has to be created. + servers (str|list): A server|List of servers from which the bricks + needs to be selected for creating the brick list. + servers_info (dict): Dict of server info of each servers. + + Returns: + list: List of bricks to use with volume create. + Nonetype: If unable to fetch the brick list + + """ + if not isinstance(servers, list): + servers = [servers] + + brick_index, brick_list_for_volume = 0, [] + + # Importing get_all_bricks() from bricks_libs to avoid cyclic imports + from glustolibs.gluster.brick_libs import get_all_bricks + + # Get all volume list present in the cluster from mnode + current_vol_list = get_volume_list(mnode) + for volume in current_vol_list: + brick_index = brick_index + len(get_all_bricks(mnode, volume)) + g.log.info("current brick_index %s" % brick_index) + + # Get all bricks_count and bricks_list + all_brick_count, bricks_list = get_all_bricks_from_servers_multivol( + servers, servers_info) + if not (all_brick_count > 1): + g.log.error("Unable to get the bricks present in the specified" + "servers") + return None + + for num in range(number_of_bricks): + brick = brick_index % all_brick_count + brick_list_for_volume.append("%s/%s_brick%d" % (bricks_list[brick], + volname, brick_index)) + brick_index += 1 + + return brick_list_for_volume diff --git a/glustolibs-gluster/glustolibs/gluster/volume_libs.py b/glustolibs-gluster/glustolibs/gluster/volume_libs.py index b530f80a1..a5e54101e 100644 --- a/glustolibs-gluster/glustolibs/gluster/volume_libs.py +++ b/glustolibs-gluster/glustolibs/gluster/volume_libs.py @@ -24,6 +24,7 @@ except ImportError: import xml.etree.ElementTree as etree from glusto.core import Glusto as g from glustolibs.gluster.lib_utils import form_bricks_list +from glustolibs.gluster.brickmux_libs import form_bricks_for_multivol from glustolibs.gluster.volume_ops import (volume_create, volume_start, set_volume_options, get_volume_info, volume_stop, volume_delete, @@ -65,7 +66,8 @@ def volume_exists(mnode, volname): return False -def setup_volume(mnode, all_servers_info, volume_config, force=False): +def setup_volume(mnode, all_servers_info, volume_config, multi_vol=False, + force=False): """Setup Volume with the configuration defined in volume_config Args: @@ -106,6 +108,14 @@ def setup_volume(mnode, all_servers_info, volume_config, force=False): 'transport': 'tcp'}}, 'options': {'performance.readdir-ahead': True} } + Kwargs: + multi_vol (bool): True, If bricks need to created for multiple + volumes(more than 5) + False, Otherwise. By default, value is set to False. + force (bool): If this option is set to True, then volume creation + command is executed with force option. + False, without force option. + By default, value is set to False Returns: bool : True on successful setup. False Otherwise @@ -261,10 +271,15 @@ def setup_volume(mnode, all_servers_info, volume_config, force=False): return False # get bricks_list - bricks_list = form_bricks_list(mnode=mnode, volname=volname, - number_of_bricks=number_of_bricks, - servers=servers, - servers_info=all_servers_info) + if multi_vol: + bricks_list = form_bricks_for_multivol( + mnode=mnode, volname=volname, number_of_bricks=number_of_bricks, + servers=servers, servers_info=all_servers_info) + else: + bricks_list = form_bricks_list(mnode=mnode, volname=volname, + number_of_bricks=number_of_bricks, + 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") @@ -407,6 +422,60 @@ def setup_volume(mnode, all_servers_info, volume_config, force=False): return True +def bulk_volume_creation(mnode, number_of_volumes, servers_info, + volume_config, vol_prefix="mult_vol_", + is_force=False): + """ + Creates the number of volumes user has specified + + Args: + mnode (str): Node on which commands has to be executed. + number_of_volumes (int): Specify the number of volumes + to be created. + servers_info (dict): Information about all servers. + volume_config (dict): Dict containing the volume information + + Kwargs: + vol_prefix (str): Prefix to be added to the volume name. + is_force (bool): True, If volume create command need to be executed + with force, False Otherwise. Defaults to False + Returns: + bool: True on successful bulk volume creation, False Otherwise. + + example: + volume_config = { + 'name': 'testvol', + 'servers': ['server-vm1', 'server-vm2', 'server-vm3', + 'server-vm4'], + 'voltype': {'type': 'distributed', + 'dist_count': 4, + 'transport': 'tcp'}, + 'extra_servers': ['server-vm9', 'server-vm10', + 'server-vm11', 'server-vm12'], + 'quota': {'limit_usage': {'path': '/', 'percent': None, + 'size': '100GB'}, + 'enable': False}, + 'uss': {'enable': False}, + 'options': {'performance.readdir-ahead': True} + } + """ + + if not (number_of_volumes > 1): + g.log.error("Provide number of volume greater than 1") + return False + + volume_name = volume_config['name'] + for volume in range(number_of_volumes): + volume_config['name'] = vol_prefix + volume_name + str(volume) + ret = setup_volume(mnode, servers_info, volume_config, multi_vol=True, + force=is_force) + if not ret: + g.log.error("Volume creation failed for the volume %s" + % volume_config['name']) + return False + return True + + def cleanup_volume(mnode, volname): """deletes snapshots in the volume, stops and deletes the gluster volume if given volume exists in gluster and deletes the -- cgit