From a84034a5f7f547790c095950cd8a697c8c7b31ca Mon Sep 17 00:00:00 2001 From: Arthy Loganathan Date: Mon, 9 May 2016 13:03:52 +0530 Subject: distaf: Added library functions for gluster rebalance operations Change-Id: I8df9199571dbf9e649d93e50f15cfbf3cea7f2bd BUG: 1334208 Signed-off-by: Arthy Loganathan Reviewed-on: http://review.gluster.org/14255 Smoke: Gluster Build System CentOS-regression: Gluster Build System Tested-by: Gluster Build System NetBSD-regression: NetBSD Build System Reviewed-by: M S Vishwanath Bhat --- .../distaflibs/gluster/rebalance_ops.py | 332 +++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 tests/distaf/distaf_libs/distaflibs-gluster/distaflibs/gluster/rebalance_ops.py (limited to 'tests') diff --git a/tests/distaf/distaf_libs/distaflibs-gluster/distaflibs/gluster/rebalance_ops.py b/tests/distaf/distaf_libs/distaflibs-gluster/distaflibs/gluster/rebalance_ops.py new file mode 100644 index 00000000000..4e4ea6bb6db --- /dev/null +++ b/tests/distaf/distaf_libs/distaflibs-gluster/distaflibs/gluster/rebalance_ops.py @@ -0,0 +1,332 @@ +#!/usr/bin/env python +# This file is part of DiSTAF +# Copyright (C) 2015-2016 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: Library for gluster rebalance operations. +""" + +from distaf.util import tc +import re +import time + +try: + import xml.etree.cElementTree as etree +except ImportError: + import xml.etree.ElementTree as etree + + +def rebalance_start(volname, mnode=None, fix_layout=False, force=False): + """Starts rebalance on the given volume. + + Example: + rebalance_start(testvol) + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which cmd has to be executed. + If None, defaults to nodes[0]. + fix_layout (bool) : If this option is set to True, then rebalance + start will get execute with fix-layout option. If set to False, + then rebalance start will get executed without fix-layout option + force (bool): If this option is set to True, then rebalance + start will get execute with force option. If it is set to False, + then rebalance start will get executed without force option + + Returns: + tuple: Tuple containing three elements (ret, out, err). + The first element 'ret' is of type 'int' and is the return value + of command execution. + + The second element 'out' is of type 'str' and is the stdout value + of the command execution. + + The third element 'err' is of type 'str' and is the stderr value + of the command execution. + """ + if mnode is None: + mnode = tc.nodes[0] + + flayout = '' + if fix_layout: + flayout = "fix-layout" + + frce = '' + if force: + frce = 'force' + + if fix_layout and force: + tc.logger.warning("Both fix-layout and force option is specified." + "Ignoring force option") + frce = '' + + cmd = "gluster volume rebalance %s %s start %s" % (volname, flayout, frce) + ret = tc.run(mnode, cmd) + return ret + + +def rebalance_stop(volname, mnode=None): + """Stops rebalance on the given volume. + + Example: + rebalance_stop(testvol) + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which cmd has to be executed. + If None, defaults to nodes[0]. + + Returns: + tuple: Tuple containing three elements (ret, out, err). + The first element 'ret' is of type 'int' and is the return value + of command execution. + + The second element 'out' is of type 'str' and is the stdout value + of the command execution. + + The third element 'err' is of type 'str' and is the stderr value + of the command execution. + """ + if mnode is None: + mnode = tc.nodes[0] + + cmd = "gluster volume rebalance %s stop" % volname + ret = tc.run(mnode, cmd) + return ret + + +def rebalance_status(volname, mnode=None): + """Executes rebalance status on the given volume. + + Example: + rebalance_status(testvol) + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which cmd has to be executed. + If None, defaults to nodes[0]. + + Returns: + tuple: Tuple containing three elements (ret, out, err). + The first element 'ret' is of type 'int' and is the return value + of command execution. + + The second element 'out' is of type 'str' and is the stdout value + of the command execution. + + The third element 'err' is of type 'str' and is the stderr value + of the command execution. + """ + if mnode is None: + mnode = tc.nodes[0] + + cmd = "gluster volume rebalance %s status" % volname + ret = tc.run(mnode, cmd) + return ret + + +def get_rebalance_status(volname, mnode=None): + """Parse the output of 'gluster vol rebalance status' command + for the given volume + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which command has to be executed. + If None, defaults to nodes[0]. + + Returns: + NoneType: None if command execution fails, parse errors. + dict: dict on success. rebalance status will be + in dict format + + Examples: + >>> get_rebalance_status(testvol, mnode = 'abc.lab.eng.xyz.com') + {'node': [{'files': '0', 'status': '3', 'lookups': '0', 'skipped': '0', + 'nodeName': 'localhost', 'failures': '0', 'runtime': '0.00', 'id': + '11336017-9561-4e88-9ac3-a94d4b403340', 'statusStr': 'completed', + 'size': '0'}, {'files': '0', 'status': '1', 'lookups': '0', 'skipped': + '0', 'nodeName': '10.70.47.16', 'failures': '0', 'runtime': '0.00', + 'id': 'a2b88b10-eba2-4f97-add2-8dc37df08b27', 'statusStr': + 'in progress', 'size': '0'}, {'files': '0', 'status': '3', + 'lookups': '0', 'skipped': '0', 'nodeName': '10.70.47.152', + 'failures': '0', 'runtime': '0.00', 'id': + 'b15b8337-9f8e-4ec3-8bdb-200d6a67ae12', 'statusStr': 'completed', + 'size': '0'}, {'files': '0', 'status': '3', 'lookups': '0', 'skipped': + '0', 'nodeName': '10.70.46.52', 'failures': '0', 'runtime': '0.00', + 'id': '77dc299a-32f7-43d8-9977-7345a344c398', 'statusStr': 'completed', + 'size': '0'}], 'task-id': 'a16f99d1-e165-40e7-9960-30508506529b', + 'aggregate': {'files': '0', 'status': '1', 'lookups': '0', 'skipped': + '0', 'failures': '0', 'runtime': '0.00', 'statusStr': 'in progress', + 'size': '0'}, 'nodeCount': '4', 'op': '3'} + """ + + if mnode is None: + mnode = tc.nodes[0] + + cmd = "gluster volume rebalance %s status --xml" % volname + ret, out, _ = tc.run(mnode, cmd, verbose=False) + if ret != 0: + tc.logger.error("Failed to execute 'rebalance status' on node %s. " + "Hence failed to get the rebalance status.", mnode) + return None + + try: + root = etree.XML(out) + except etree.ParseError: + tc.logger.error("Failed to parse the gluster rebalance status " + "xml output.") + return None + + rebal_status = {} + rebal_status["node"] = [] + for info in root.findall("volRebalance"): + for element in info.getchildren(): + if element.tag == "node": + status_info = {} + for elmt in element.getchildren(): + status_info[elmt.tag] = elmt.text + rebal_status[element.tag].append(status_info) + elif element.tag == "aggregate": + status_info = {} + for elmt in element.getchildren(): + status_info[elmt.tag] = elmt.text + rebal_status[element.tag] = status_info + else: + rebal_status[element.tag] = element.text + return rebal_status + + +def rebalance_stop_and_get_status(volname, mnode=None): + """Parse the output of 'gluster vol rebalance stop' command + for the given volume + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which command has to be executed. + If None, defaults to nodes[0]. + + Returns: + NoneType: None if command execution fails, parse errors. + dict: dict on success. rebalance status will be + in dict format + + Examples: + >>> rebalance_stop_and_get_status(testvol, mnode = 'abc.xyz.com') + {'node': [{'files': '0', 'status': '3', 'lookups': '0', 'skipped': '0', + 'nodeName': 'localhost', 'failures': '0', 'runtime': '0.00', 'id': + '11336017-9561-4e88-9ac3-a94d4b403340', 'statusStr': 'completed', + 'size': '0'}, {'files': '0', 'status': '1', 'lookups': '0', 'skipped': + '0', 'nodeName': '10.70.47.16', 'failures': '0', 'runtime': '0.00', + 'id': 'a2b88b10-eba2-4f97-add2-8dc37df08b27', 'statusStr': + 'in progress', 'size': '0'}, {'files': '0', 'status': '3', + 'lookups': '0', 'skipped': '0', 'nodeName': '10.70.47.152', + 'failures': '0', 'runtime': '0.00', 'id': + 'b15b8337-9f8e-4ec3-8bdb-200d6a67ae12', 'statusStr': 'completed', + 'size': '0'}, {'files': '0', 'status': '3', 'lookups': '0', 'skipped': + '0', 'nodeName': '10.70.46.52', 'failures': '0', 'runtime': '0.00', + 'id': '77dc299a-32f7-43d8-9977-7345a344c398', 'statusStr': 'completed', + 'size': '0'}], 'task-id': 'a16f99d1-e165-40e7-9960-30508506529b', + 'aggregate': {'files': '0', 'status': '1', 'lookups': '0', 'skipped': + '0', 'failures': '0', 'runtime': '0.00', 'statusStr': 'in progress', + 'size': '0'}, 'nodeCount': '4', 'op': '3'} + """ + + if mnode is None: + mnode = tc.nodes[0] + + cmd = "gluster volume rebalance %s stop --xml" % volname + ret, out, _ = tc.run(mnode, cmd, verbose=False) + if ret != 0: + tc.logger.error("Failed to execute 'rebalance stop' on node %s. " + "Hence failed to parse the rebalance status.", mnode) + return None + + try: + root = etree.XML(out) + except etree.ParseError: + tc.logger.error("Failed to parse gluster rebalance stop xml output.") + return None + + rebal_status = {} + rebal_status["node"] = [] + for info in root.findall("volRebalance"): + for element in info.getchildren(): + if element.tag == "node": + status_info = {} + for elmt in element.getchildren(): + status_info[elmt.tag] = elmt.text + rebal_status[element.tag].append(status_info) + elif element.tag == "aggregate": + status_info = {} + for elmt in element.getchildren(): + status_info[elmt.tag] = elmt.text + rebal_status[element.tag] = status_info + else: + rebal_status[element.tag] = element.text + return rebal_status + + +def wait_for_rebalance_to_complete(volname, mnode=None): + """Waits for the rebalance to complete + + Args: + volname (str): volume name + + Kwargs: + mnode (str): Node on which command has to be executed. + If None, defaults to nodes[0]. + + Returns: + True on success, False otherwise + + Examples: + >>> wait_for_rebalance_to_complete("testvol") + """ + + if mnode is None: + mnode = tc.nodes[0] + + count = 0 + flag = 0 + while (count < 300): + status_info = get_rebalance_status(volname, mnode=mnode) + if status_info is None: + return False + + status = status_info['aggregate']['statusStr'] + if status == 'completed': + flag = 1 + break + + time.sleep(10) + count = count + 5 + if not flag: + tc.logger.error("rebalance is not completed") + return False + else: + tc.logger.info("rebalance is successfully completed") + return True -- cgit