summaryrefslogtreecommitdiffstats
path: root/glustolibs-io
diff options
context:
space:
mode:
Diffstat (limited to 'glustolibs-io')
-rw-r--r--glustolibs-io/glustolibs/io/memory_and_cpu_utils.py924
-rwxr-xr-xglustolibs-io/glustolibs/io/utils.py389
-rw-r--r--glustolibs-io/setup.py38
-rwxr-xr-xglustolibs-io/shared_files/scripts/fd_writes.py68
-rwxr-xr-xglustolibs-io/shared_files/scripts/file_dir_ops.py473
-rw-r--r--glustolibs-io/shared_files/scripts/file_lock.py51
-rwxr-xr-xglustolibs-io/shared_files/scripts/generate_io.py246
-rw-r--r--glustolibs-io/shared_files/scripts/memory_and_cpu_logger.py108
-rw-r--r--glustolibs-io/shared_files/tools/fio/run_fio.py18
9 files changed, 1912 insertions, 403 deletions
diff --git a/glustolibs-io/glustolibs/io/memory_and_cpu_utils.py b/glustolibs-io/glustolibs/io/memory_and_cpu_utils.py
new file mode 100644
index 000000000..4e1dadbd7
--- /dev/null
+++ b/glustolibs-io/glustolibs/io/memory_and_cpu_utils.py
@@ -0,0 +1,924 @@
+# Copyright (C) 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
+# 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.
+
+from glusto.core import Glusto as g
+
+from glustolibs.gluster.volume_ops import get_volume_status
+from glustolibs.gluster.glusterfile import file_exists
+from glustolibs.misc.misc_libs import upload_scripts, kill_process
+
+import numpy as np
+import pandas as pd
+from statistics import mean, median
+
+
+def check_upload_memory_and_cpu_logger_script(servers):
+ """Check and upload memory_and_cpu_logger.py to servers if not present
+
+ Args:
+ servers(list): List of all servers where script has to be uploaded
+
+ Returns:
+ bool: True if script is uploaded successfully else false
+ """
+ script = "/usr/share/glustolibs/io/scripts/memory_and_cpu_logger.py"
+ is_present = []
+ for server in servers:
+ if not file_exists(server, script):
+ if not upload_scripts(server, script):
+ g.log.error("Unable to upload memory_and_cpu_logger.py on %s",
+ server)
+ is_present.append(False)
+ else:
+ is_present.append(True)
+ return all(is_present)
+
+
+def _start_logging_processes(process, servers, test_name, interval, count):
+ """Start logging processes on all nodes for a given process
+
+ Args:
+ servers(list): Servers on which CPU and memory usage has to be logged
+ test_name(str): Name of testcase for which logs are to be collected
+ interval(int): Time interval after which logs are to be collected
+ count(int): Number of samples to be captured
+
+ Returns:
+ list: A list of logging processes
+ """
+ cmd = ("/usr/bin/env python "
+ "/usr/share/glustolibs/io/scripts/memory_and_cpu_logger.py"
+ " -p %s -t %s -i %d -c %d" % (process, test_name,
+ interval, count))
+ logging_process = []
+ for server in servers:
+ proc = g.run_async(server, cmd)
+ logging_process.append(proc)
+ return logging_process
+
+
+def log_memory_and_cpu_usage_on_servers(servers, test_name, interval=60,
+ count=100):
+ """Log memory and CPU usage of gluster server processes
+
+ Args:
+ servers(list): Servers on which CPU and memory usage has to be logged
+ test_name(str): Name of the testcase for which logs are to be collected
+
+ Kwargs:
+ interval(int): Time interval after which logs are to be collected
+ (Default:60)
+ count(int): Number of samples to be captured (Default:100)
+
+ Returns:
+ dict: Logging processes dict for all gluster server processes
+ """
+ logging_process_dict = {}
+ for proc_name in ('glusterd', 'glusterfs', 'glusterfsd'):
+ logging_procs = _start_logging_processes(
+ proc_name, servers, test_name, interval, count)
+ logging_process_dict[proc_name] = logging_procs
+ return logging_process_dict
+
+
+def log_memory_and_cpu_usage_on_clients(servers, test_name, interval=60,
+ count=100):
+ """Log memory and CPU usage of gluster client processes
+
+ Args:
+ servers(list): Clients on which CPU and memory usage has to be logged
+ test_name(str): Name of testcase for which logs are to be collected
+
+ Kwargs:
+ interval(int): Time interval after which logs are to be collected
+ (Defaults:60)
+ count(int): Number of samples to be captured (Default:100)
+
+ Returns:
+ dict: Logging processes dict for all gluster client processes
+ """
+ logging_process_dict = {}
+ logging_procs = _start_logging_processes(
+ 'glusterfs', servers, test_name, interval, count)
+ logging_process_dict['glusterfs'] = logging_procs
+ return logging_process_dict
+
+
+def log_memory_and_cpu_usage_on_cluster(servers, clients, test_name,
+ interval=60, count=100):
+ """Log memory and CPU usage on gluster cluster
+
+ Args:
+ servers(list): Servers on which memory and CPU usage is to be logged
+ clients(list): Clients on which memory and CPU usage is to be logged
+ test_name(str): Name of testcase for which logs are to be collected
+
+ Kwargs:
+ interval(int): Time interval after which logs are to be collected
+ (Default:60)
+ count(int): Number of samples to be captured (Default:100)
+
+ Returns:
+ dict: Logging processes dict for all servers and clients
+ """
+ # Start logging on all servers
+ server_logging_processes = log_memory_and_cpu_usage_on_servers(
+ servers, test_name, interval, count)
+ if not server_logging_processes:
+ return {}
+
+ # Starting logging on all clients
+ client_logging_processes = log_memory_and_cpu_usage_on_clients(
+ clients, test_name, interval, count)
+ if not client_logging_processes:
+ return {}
+
+ # Combining dicts
+ logging_process_dict = {}
+ for node_type, proc_dict in (('server', server_logging_processes),
+ ('client', client_logging_processes)):
+ logging_process_dict[node_type] = {}
+ for proc in proc_dict:
+ logging_process_dict[node_type][proc] = (
+ proc_dict[proc])
+ return logging_process_dict
+
+
+def _process_wait_flag_append(proc, flag):
+ """Run async communicate and adds true to flag list"""
+ # If the process is already completed async_communicate()
+ # throws a ValueError
+ try:
+ proc.async_communicate()
+ flag.append(True)
+ except ValueError:
+ flag.append(True)
+
+
+def wait_for_logging_processes_to_stop(proc_dict, cluster=False):
+ """Wait for all given logging processes to stop
+
+ Args:
+ proc_dict(dict): Dictionary of all the active logging processes
+
+ Kwargs:
+ cluster(bool): True if proc_dict is for the entire cluster else False
+ (Default:False)
+
+ Retruns:
+ bool: True if processes are completed else False
+ """
+ flag = []
+ if cluster:
+ for sub_dict in proc_dict:
+ for proc_name in proc_dict[sub_dict]:
+ for proc in proc_dict[sub_dict][proc_name]:
+ _process_wait_flag_append(proc, flag)
+ else:
+ for proc_name in proc_dict:
+ for proc in proc_dict[proc_name]:
+ _process_wait_flag_append(proc, flag)
+ return all(flag)
+
+
+def kill_all_logging_processes(proc_dict, nodes, cluster=False):
+ """Kill logging processes on all given nodes
+
+ Args:
+ proc_dict(dict): Dictonary of all active logging processes
+ nodes(list): List of nodes where logging has to be stopped
+
+ Kwargs:
+ cluster(bool): True if proc_dict is for a full cluster else False
+ (Default:False)
+
+ Retruns:
+ bool: True if processes are completed else False
+ """
+ # Kill all logging processes
+ for server in nodes:
+ if not kill_process(server, process_names='memory_and_cpu_logger.py'):
+ g.log.error("Unable to kill some of the processes at %s.", server)
+
+ # This will stop the async threads created by run_aysnc() as the proc is
+ # already killed.
+ ret = wait_for_logging_processes_to_stop(proc_dict, cluster)
+ if ret:
+ return True
+ return False
+
+
+def create_dataframe_from_csv(node, proc_name, test_name):
+ """Creates a dataframe from a given process.
+
+ Args:
+ node(str): Node from which csv is to be picked
+ proc_name(str): Name of process for which csv is to picked
+ test_name(str): Name of the testcase for which CSV
+
+ Returns:
+ dataframe: Pandas dataframe if CSV file exits else None
+ """
+ # Read the csv file generated by memory_and_cpu_logger.py
+ ret, raw_data, _ = g.run(node, "cat /root/{}.csv"
+ .format(proc_name))
+ if ret:
+ return None
+
+ # Split the complete dump to individual lines
+ data = raw_data.split("\r\n")
+ rows, flag = [], False
+ for line in data:
+ values = line.split(',')
+ if test_name == values[0]:
+ # Reset rows if it's the second instance
+ if flag:
+ rows = []
+ flag = True
+ continue
+
+ # Pick and append values which have complete entry
+ if flag and len(values) == 4:
+ rows.append(values)
+
+ # Create a panda dataframe and set the type for columns
+ dataframe = pd.DataFrame(rows[1:], columns=rows[0])
+ conversion_dict = {'Process ID': int,
+ 'CPU Usage': float,
+ 'Memory Usage': float}
+ dataframe = dataframe.astype(conversion_dict)
+ return dataframe
+
+
+def _get_min_max_mean_median(entrylist):
+ """Get the mix, max. mean and median of a list
+
+ Args:
+ entrylist(list): List of values to be used
+
+ Returns:
+ dict:Result dict generate from list
+ """
+ result = {}
+ result['Min'] = min(entrylist)
+ result['Max'] = max(entrylist)
+ result['Mean'] = mean(entrylist)
+ result['Median'] = median(entrylist)
+ return result
+
+
+def _compute_min_max_mean_median(dataframe, data_dict, process, node,
+ volume=None, brick=None):
+ """Compute min, max, mean and median for a given process
+
+ Args:
+ dataframe(panda dataframe): Panda data frame of the csv file
+ data_dict(dict): data dict to which info is to be added
+ process(str): Name of process for which data is to be computed
+ node(str): Node for which min, max, mean and median has to be computed
+
+ Kwargs:
+ volume(str): Volume name of the volume for which data is to be computed
+ brick(str): Brick path of the brick for which data is to be computed
+ """
+ if volume and process == 'glusterfs':
+ # Create subdict inside dict
+ data_dict[node][process][volume] = {}
+ for usage in ('CPU Usage', 'Memory Usage'):
+ # Create usage subdict
+ data_dict[node][process][volume][usage] = {}
+
+ # Clean data and compute values
+ cleaned_usage = list(dataframe[usage].dropna())
+ out = _get_min_max_mean_median(cleaned_usage)
+
+ # Add values to data_dict
+ for key in ('Min', 'Max', 'Mean', 'Median'):
+ data_dict[node][process][volume][usage][key] = out[key]
+
+ if volume and brick and process == 'glusterfsd':
+ # Create subdict inside dict
+ data_dict[node][process][volume] = {}
+ data_dict[node][process][volume][brick] = {}
+ for usage in ('CPU Usage', 'Memory Usage'):
+ # Create usage subdict
+ data_dict[node][process][volume][brick][usage] = {}
+
+ # Clean data and compute values
+ cleaned_usage = list(dataframe[usage].dropna())
+ out = _get_min_max_mean_median(cleaned_usage)
+
+ # Add values to data_dict
+ for key in ('Min', 'Max', 'Mean', 'Median'):
+ data_dict[node][process][volume][brick][usage][key] = out[key]
+
+ # Compute CPU Uage and Memory Usage for glusterd
+ else:
+ for usage in ('CPU Usage', 'Memory Usage'):
+ # Create uage subdict
+ data_dict[node][process][usage] = {}
+
+ # Clean data and compute value
+ cleaned_usage = list(dataframe[usage].dropna())
+ out = _get_min_max_mean_median(cleaned_usage)
+
+ # Add values to data_dict
+ for key in ('Min', 'Max', 'Mean', 'Median'):
+ data_dict[node][process][usage][key] = out[key]
+
+
+def compute_data_usage_stats_on_servers(nodes, test_name):
+ """Compute min, max, mean and median for servers
+
+ Args:
+ nodes(list): Servers from which data is to be used to compute min, max
+ , mean, mode and median
+ test_name(str): Name of testcase for which data has to be processed
+
+ Returns:
+ dict: dict of min, max, mean and median for a given process
+
+ NOTE:
+ This function has to be always run before cleanup.
+ """
+ data_dict = {}
+ for node in nodes:
+ # Get the volume status on the node
+ volume_status = get_volume_status(node)
+ data_dict[node] = {}
+ for process in ('glusterd', 'glusterfs', 'glusterfsd'):
+
+ # Generate a dataframe from the csv file
+ dataframe = create_dataframe_from_csv(node, process, test_name)
+ if dataframe.empty:
+ return {}
+
+ data_dict[node][process] = {}
+ if process == 'glusterd':
+ # Checking if glusterd is restarted.
+ if len(set(dataframe['Process ID'])) > 1:
+ data_dict[node][process]['is_restarted'] = True
+ else:
+ data_dict[node][process]['is_restarted'] = False
+
+ # Call function to compute min, max, mean and median
+ _compute_min_max_mean_median(dataframe, data_dict, process,
+ node)
+ continue
+
+ # Map volumes to volume process
+ for volume in volume_status.keys():
+ for proc in volume_status[volume][node].keys():
+ if (proc == 'Self-heal Daemon' and process == 'glusterfs'):
+ # Fetching pid from volume status output and create a
+ # dataframe with the entries of only that pid
+ pid = volume_status[volume][node][proc]['pid']
+ proc_dataframe = dataframe[
+ dataframe['Process ID'] == pid]
+
+ # Call function to compute min, max, mean
+ # and median
+ _compute_min_max_mean_median(
+ proc_dataframe, data_dict, process, node, volume)
+
+ if (proc.count('/') >= 2 and process == 'glusterfsd'):
+ # Fetching pid from volume status output and create a
+ # dataframe with the entries of only that pid
+ pid = volume_status[volume][node][proc]['pid']
+ proc_dataframe = dataframe[
+ dataframe['Process ID'] == pid]
+
+ # Call function to compute min, max, mean and median
+ _compute_min_max_mean_median(
+ proc_dataframe, data_dict, process, node, volume,
+ proc)
+
+ return data_dict
+
+
+def compute_data_usage_stats_on_clients(nodes, test_name):
+ """Compute min, max, mean and median for clients
+
+ Args:
+ nodes(list): Clients from which data is to be used to compute min, max
+ , mean, mode and median
+ test_name(str): Name of the testcase for which data has to be processed
+
+ Returns:
+ dict: dict of min, max, mean and median for a given process
+ """
+ data_dict = {}
+ for node in nodes:
+ data_dict[node] = {}
+ dataframe = create_dataframe_from_csv(node, 'glusterfs', test_name)
+ if dataframe.empty:
+ return {}
+
+ data_dict[node]['glusterfs'] = {}
+ # Call function to compute min, max, mean and median
+ _compute_min_max_mean_median(dataframe, data_dict, 'glusterfs', node)
+
+ return data_dict
+
+
+def _perform_three_point_check_for_memory_leak(dataframe, node, process, gain,
+ volume_status=None,
+ volume=None,
+ vol_name=None):
+ """Perform three point check
+
+ Args:
+ dataframe(panda dataframe): Panda dataframe of a given process
+ node(str): Node on which memory leak has to be checked
+ process(str): Name of process for which check has to be done
+ gain(float): Accepted amount of leak for a given testcase in MB
+
+ kwargs:
+ volume_status(dict): Volume status output on the give name
+ volumne(str):Name of volume for which 3 point check has to be done
+ vol_name(str): Name of volume process according to volume status
+
+ Returns:
+ bool: True if memory leak instances are observed else False
+ """
+ # Filter dataframe to be process wise if it's volume specific process
+ if process in ('glusterfs', 'glusterfsd'):
+ if process == 'glusterfs' and vol_name:
+ pid = int(volume_status[volume][node][vol_name]['pid'])
+ dataframe = dataframe[dataframe['Process ID'] == pid]
+
+ # Compute usage gain throught the data frame
+ memory_increments = list(dataframe['Memory Usage'].diff().dropna())
+
+ # Check if usage is more than accepted amount of leak
+ memory_leak_decision_array = np.where(
+ dataframe['Memory Usage'].diff().dropna() > gain, True, False)
+ instances_of_leak = np.where(memory_leak_decision_array)[0]
+
+ # If memory leak instances are present check if it's reduced
+ count_of_leak_instances = len(instances_of_leak)
+ if count_of_leak_instances > 0:
+ g.log.error('There are %s instances of memory leaks on node %s',
+ count_of_leak_instances, node)
+ for instance in instances_of_leak:
+ # In cases of last log file entry the below op could throw
+ # IndexError which is handled as below.
+ try:
+ # Check if memory gain had decrease in the consecutive
+ # entries, after 2 entry and betwen current and last entry
+ if all([memory_increments[instance+1] >
+ memory_increments[instance],
+ memory_increments[instance+2] >
+ memory_increments[instance],
+ (memory_increments[len(memory_increments)-1] >
+ memory_increments[instance])]):
+ return True
+
+ except IndexError:
+ # In case of last log file entry rerun the command
+ # and check for difference
+ g.log.info('Instance at last log entry.')
+ if process in ('glusterfs', 'glusterfsd'):
+ cmd = ("ps u -p %s | awk 'NR>1 && $11~/%s$/{print "
+ " $6/1024}'" % (pid, process))
+ else:
+ cmd = ("ps u -p `pgrep glusterd` | awk 'NR>1 && $11~/"
+ "glusterd$/{print $6/1024}'")
+ ret, out, _ = g.run(node, cmd)
+ if ret:
+ g.log.error('Unable to run the command to fetch current '
+ 'memory utilization.')
+ continue
+ usage_now = float(out.replace('\n', '')[2])
+ last_entry = dataframe['Memory Usage'].iloc[-1]
+
+ # Check if current memory usage is higher than last entry
+ fresh_diff = last_entry - usage_now
+ if fresh_diff > gain and last_entry > fresh_diff:
+ return True
+ return False
+
+
+def check_for_memory_leaks_in_glusterd(nodes, test_name, gain=30.0):
+ """Check for memory leaks in glusterd
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ gain(float): Accepted amount of leak for a given testcase in MB
+ (Default:30)
+
+ Returns:
+ bool: True if memory leak was obsevred else False
+ """
+ is_there_a_leak = []
+ for node in nodes:
+ dataframe = create_dataframe_from_csv(node, 'glusterd', test_name)
+ if dataframe.empty:
+ return False
+
+ # Call 3 point check function
+ three_point_check = _perform_three_point_check_for_memory_leak(
+ dataframe, node, 'glusterd', gain)
+ if three_point_check:
+ g.log.error("Memory leak observed on node %s in glusterd",
+ node)
+ is_there_a_leak.append(three_point_check)
+
+ return any(is_there_a_leak)
+
+
+def check_for_memory_leaks_in_glusterfs(nodes, test_name, gain=30.0):
+ """Check for memory leaks in glusterfs
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ gain(float): Accepted amount of leak for a given testcase in MB
+ (Default:30)
+
+ Returns:
+ bool: True if memory leak was obsevred else False
+
+ NOTE:
+ This function should be executed with the volumes present on the cluster
+ """
+ is_there_a_leak = []
+ for node in nodes:
+ # Get the volume status on the node
+ volume_status = get_volume_status(node)
+ dataframe = create_dataframe_from_csv(node, 'glusterfs', test_name)
+ if dataframe.empty:
+ return False
+
+ for volume in volume_status.keys():
+ for process in volume_status[volume][node].keys():
+ # Skiping if process isn't Self-heal Deamon
+ if process != 'Self-heal Daemon':
+ continue
+
+ # Call 3 point check function
+ three_point_check = _perform_three_point_check_for_memory_leak(
+ dataframe, node, 'glusterfs', gain, volume_status, volume,
+ 'Self-heal Daemon')
+ if three_point_check:
+ g.log.error("Memory leak observed on node %s in shd "
+ "on volume %s", node, volume)
+ is_there_a_leak.append(three_point_check)
+
+ return any(is_there_a_leak)
+
+
+def check_for_memory_leaks_in_glusterfsd(nodes, test_name, gain=30.0):
+ """Check for memory leaks in glusterfsd
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ gain(float): Accepted amount of leak for a given testcase in MB
+ (Default:30)
+
+ Returns:
+ bool: True if memory leak was obsevred else False
+
+ NOTE:
+ This function should be executed with the volumes present on the cluster.
+ """
+ is_there_a_leak = []
+ for node in nodes:
+ # Get the volume status on the node
+ volume_status = get_volume_status(node)
+ dataframe = create_dataframe_from_csv(node, 'glusterfsd', test_name)
+ if dataframe.empty:
+ return False
+
+ for volume in volume_status.keys():
+ for process in volume_status[volume][node].keys():
+ # Skiping if process isn't brick process
+ if not process.count('/'):
+ continue
+
+ # Call 3 point check function
+ three_point_check = _perform_three_point_check_for_memory_leak(
+ dataframe, node, 'glusterfsd', gain, volume_status, volume,
+ process)
+ if three_point_check:
+ g.log.error("Memory leak observed on node %s in brick "
+ " process for brick %s on volume %s", node,
+ process, volume)
+ is_there_a_leak.append(three_point_check)
+
+ return any(is_there_a_leak)
+
+
+def check_for_memory_leaks_in_glusterfs_fuse(nodes, test_name, gain=30.0):
+ """Check for memory leaks in glusterfs fuse
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ gain(float): Accepted amount of leak for a given testcase in MB
+ (Default:30)
+
+ Returns:
+ bool: True if memory leak was observed else False
+
+ NOTE:
+ This function should be executed when the volume is still mounted.
+ """
+ is_there_a_leak = []
+ for node in nodes:
+ # Get the volume status on the node
+ dataframe = create_dataframe_from_csv(node, 'glusterfs', test_name)
+ if dataframe.empty:
+ return False
+
+ # Call 3 point check function
+ three_point_check = _perform_three_point_check_for_memory_leak(
+ dataframe, node, 'glusterfs', gain)
+ if three_point_check:
+ g.log.error("Memory leak observed on node %s for client",
+ node)
+
+ # If I/O is constantly running on Clients the memory
+ # usage spikes up and stays at a point for long.
+ last_entry = dataframe['Memory Usage'].iloc[-1]
+ cmd = ("ps u -p `pidof glusterfs` | "
+ "awk 'NR>1 && $11~/glusterfs$/{print"
+ " $6/1024}'")
+ ret, out, _ = g.run(node, cmd)
+ if ret:
+ g.log.error('Unable to run the command to fetch current '
+ 'memory utilization.')
+ continue
+
+ if float(out) != last_entry:
+ if float(out) > last_entry:
+ is_there_a_leak.append(True)
+ continue
+
+ is_there_a_leak.append(False)
+
+ return any(is_there_a_leak)
+
+
+def _check_for_oom_killers(nodes, process, oom_killer_list):
+ """Checks for OOM killers for a specific process
+
+ Args:
+ nodes(list): Nodes on which OOM killers have to be checked
+ process(str): Process for which OOM killers have to be checked
+ oom_killer_list(list): A list in which the presence of
+ OOM killer has to be noted
+ """
+ cmd = ("grep -i 'killed process' /var/log/messages* "
+ "| grep -w '{}'".format(process))
+ ret_codes = g.run_parallel(nodes, cmd)
+ for key in ret_codes.keys():
+ ret, out, _ = ret_codes[key]
+ if not ret:
+ g.log.error('OOM killer observed on %s for %s', key, process)
+ g.log.error(out)
+ oom_killer_list.append(True)
+ else:
+ oom_killer_list.append(False)
+
+
+def check_for_oom_killers_on_servers(nodes):
+ """Check for OOM killers on servers
+
+ Args:
+ nodes(list): Servers on which OOM kills have to be checked
+
+ Returns:
+ bool: True if OOM killers are present on any server else False
+ """
+ oom_killer_list = []
+ for process in ('glusterfs', 'glusterfsd', 'glusterd'):
+ _check_for_oom_killers(nodes, process, oom_killer_list)
+ return any(oom_killer_list)
+
+
+def check_for_oom_killers_on_clients(nodes):
+ """Check for OOM killers on clients
+
+ Args:
+ nodes(list): Clients on which OOM kills have to be checked
+
+ Returns:
+ bool: True if OOM killers are present on any client else false
+ """
+ oom_killer_list = []
+ _check_for_oom_killers(nodes, 'glusterfs', oom_killer_list)
+ return any(oom_killer_list)
+
+
+def _check_for_cpu_usage_spikes(dataframe, node, process, threshold,
+ volume_status=None, volume=None,
+ vol_name=None):
+ """Check for cpu spikes for a given process
+
+ Args:
+ dataframe(panda dataframe): Panda dataframe of a given process
+ node(str): Node on which cpu spikes has to be checked
+ process(str): Name of process for which check has to be done
+ threshold(int): Accepted amount of 100% CPU usage instances
+
+ kwargs:
+ volume_status(dict): Volume status output on the give name
+ volume(str):Name of volume for which check has to be done
+ vol_name(str): Name of volume process according to volume status
+
+ Returns:
+ bool: True if number of instances more than threshold else False
+ """
+ # Filter dataframe to be process wise if it's volume specific process
+ if process in ('glusterfs', 'glusterfsd'):
+ pid = int(volume_status[volume][node][vol_name]['pid'])
+ dataframe = dataframe[dataframe['Process ID'] == pid]
+
+ # Check if usage is more than accepted amount of leak
+ cpu_spike_decision_array = np.where(
+ dataframe['CPU Usage'].dropna() == 100.0, True, False)
+ instances_of_spikes = np.where(cpu_spike_decision_array)[0]
+
+ return bool(len(instances_of_spikes) > threshold)
+
+
+def check_for_cpu_usage_spikes_on_glusterd(nodes, test_name, threshold=3):
+ """Check for CPU usage spikes on glusterd
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ threshold(int): Accepted amount of instances of 100% CPU usage
+ (Default:3)
+
+ Returns:
+ bool: True if CPU spikes are more than threshold else False
+ """
+ is_there_a_spike = []
+ for node in nodes:
+ dataframe = create_dataframe_from_csv(node, 'glusterd', test_name)
+ if dataframe.empty:
+ return False
+
+ # Call function to check for cpu spikes
+ cpu_spikes = _check_for_cpu_usage_spikes(
+ dataframe, node, 'glusterd', threshold)
+ if cpu_spikes:
+ g.log.error("CPU usage spikes observed more than "
+ "threshold %d on node %s for glusterd",
+ threshold, node)
+ is_there_a_spike.append(cpu_spikes)
+
+ return any(is_there_a_spike)
+
+
+def check_for_cpu_usage_spikes_on_glusterfs(nodes, test_name, threshold=3):
+ """Check for CPU usage spikes on glusterfs
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ threshold(int): Accepted amount of instances of 100% CPU usage
+ (Default:3)
+
+ Returns:
+ bool: True if CPU spikes are more than threshold else False
+
+ NOTE:
+ This function should be exuected with the volumes present on the cluster.
+ """
+ is_there_a_spike = []
+ for node in nodes:
+ # Get the volume status on the node
+ volume_status = get_volume_status(node)
+ dataframe = create_dataframe_from_csv(node, 'glusterfs', test_name)
+ if dataframe.empty:
+ return False
+
+ for volume in volume_status.keys():
+ for process in volume_status[volume][node].keys():
+ # Skiping if process isn't Self-heal Deamon
+ if process != 'Self-heal Daemon':
+ continue
+
+ # Call function to check for cpu spikes
+ cpu_spikes = _check_for_cpu_usage_spikes(
+ dataframe, node, 'glusterfs', threshold, volume_status,
+ volume, 'Self-heal Daemon')
+ if cpu_spikes:
+ g.log.error("CPU usage spikes observed more than "
+ "threshold %d on node %s on volume %s for shd",
+ threshold, node, volume)
+ is_there_a_spike.append(cpu_spikes)
+
+ return any(is_there_a_spike)
+
+
+def check_for_cpu_usage_spikes_on_glusterfsd(nodes, test_name, threshold=3):
+ """Check for CPU usage spikes in glusterfsd
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ threshold(int): Accepted amount of instances of 100% CPU usage
+ (Default:3)
+
+ Returns:
+ bool: True if CPU spikes are more than threshold else False
+
+ NOTE:
+ This function should be exuected with the volumes present on the cluster.
+ """
+ is_there_a_spike = []
+ for node in nodes:
+ # Get the volume status on the node
+ volume_status = get_volume_status(node)
+ dataframe = create_dataframe_from_csv(node, 'glusterfsd', test_name)
+ if dataframe.empty:
+ return False
+
+ for volume in volume_status.keys():
+ for process in volume_status[volume][node].keys():
+ # Skiping if process isn't brick process
+ if process in ('Self-heal Daemon', 'Quota Daemon'):
+ continue
+
+ # Call function to check for cpu spikes
+ cpu_spikes = _check_for_cpu_usage_spikes(
+ dataframe, node, 'glusterfsd', threshold, volume_status,
+ volume, process)
+ if cpu_spikes:
+ g.log.error("CPU usage spikes observed more than "
+ "threshold %d on node %s on volume %s for "
+ "brick process %s",
+ threshold, node, volume, process)
+ is_there_a_spike.append(cpu_spikes)
+
+ return any(is_there_a_spike)
+
+
+def check_for_cpu_usage_spikes_on_glusterfs_fuse(nodes, test_name,
+ threshold=3):
+ """Check for CPU usage spikes on glusterfs fuse
+
+ Args:
+ nodes(list): Servers on which memory leaks have to be checked
+ test_name(str): Name of testcase for which memory leaks has to be checked
+
+ Kwargs:
+ threshold(int): Accepted amount of instances of 100% CPU usage
+ (Default:3)
+
+ Returns:
+ bool: True if CPU spikes are more than threshold else False
+
+ NOTE:
+ This function should be executed when the volume is still mounted.
+ """
+ is_there_a_spike = []
+ for node in nodes:
+ # Get the volume status on the node
+ dataframe = create_dataframe_from_csv(node, 'glusterfs', test_name)
+ if dataframe.empty:
+ return False
+
+ # Call function to check for cpu spikes
+ cpu_spikes = _check_for_cpu_usage_spikes(
+ dataframe, node, 'glusterfs', threshold)
+ if cpu_spikes:
+ g.log.error("CPU usage spikes observed more than "
+ "threshold %d on node %s for client",
+ threshold, node)
+ is_there_a_spike.append(cpu_spikes)
+
+ return any(is_there_a_spike)
diff --git a/glustolibs-io/glustolibs/io/utils.py b/glustolibs-io/glustolibs/io/utils.py
index 52c2c7df0..16ee93f21 100755
--- a/glustolibs-io/glustolibs/io/utils.py
+++ b/glustolibs-io/glustolibs/io/utils.py
@@ -1,4 +1,4 @@
-# Copyright (C) 2015-2016 Red Hat, Inc. <http://www.redhat.com>
+# Copyright (C) 2015-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
@@ -17,20 +17,26 @@
"""
Description: Helper library for io modules.
"""
+from multiprocessing import Pool
import os
import subprocess
+
from glusto.core import Glusto as g
+from glustolibs.gluster.glusterfile import file_exists
from glustolibs.gluster.mount_ops import GlusterMount
-from multiprocessing import Pool
from glustolibs.gluster.volume_libs import get_subvols
+from glustolibs.misc.misc_libs import upload_scripts
-def collect_mounts_arequal(mounts):
+def collect_mounts_arequal(mounts, path=''):
"""Collects arequal from all the mounts
Args:
mounts (list): List of all GlusterMount objs.
+ Kwargs:
+ path (str): Path whose arequal is to be calculated.
+ Defaults to root of mountpoint
Returns:
tuple(bool, list):
On success returns (True, list of arequal-checksums of each mount)
@@ -45,11 +51,11 @@ def collect_mounts_arequal(mounts):
g.log.info("Start collecting arequal-checksum from all mounts")
all_mounts_procs = []
for mount_obj in mounts:
+ total_path = os.path.join(mount_obj.mountpoint, path)
g.log.info("arequal-checksum of mount %s:%s", mount_obj.client_system,
- mount_obj.mountpoint)
- cmd = "arequal-checksum -p %s -i .trashcan" % mount_obj.mountpoint
- proc = g.run_async(mount_obj.client_system, cmd,
- user=mount_obj.user)
+ total_path)
+ cmd = "arequal-checksum -p %s -i .trashcan" % total_path
+ proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
all_mounts_arequal_checksums = []
_rc = True
@@ -68,7 +74,7 @@ def collect_mounts_arequal(mounts):
def log_mounts_info(mounts):
- """Logs mount information like df, stat, ls
+ """Log mount information like df, stat, ls
Args:
mounts (list): List of all GlusterMount objs.
@@ -83,22 +89,22 @@ def log_mounts_info(mounts):
# Mount Info
g.log.info("Look For Mountpoint:\n")
cmd = "mount | grep %s" % mount_obj.mountpoint
- _, _, _ = g.run(mount_obj.client_system, cmd)
+ g.run(mount_obj.client_system, cmd)
# Disk Space Usage
g.log.info("Disk Space Usage Of Mountpoint:\n")
cmd = "df -h %s" % mount_obj.mountpoint
- _, _, _ = g.run(mount_obj.client_system, cmd)
+ g.run(mount_obj.client_system, cmd)
# Long list the mountpoint
g.log.info("List Mountpoint Entries:\n")
cmd = "ls -ld %s" % mount_obj.mountpoint
- _, _, _ = g.run(mount_obj.client_system, cmd)
+ g.run(mount_obj.client_system, cmd)
# Stat mountpoint
g.log.info("Mountpoint Status:\n")
cmd = "stat %s" % mount_obj.mountpoint
- _, _, _ = g.run(mount_obj.client_system, cmd)
+ g.run(mount_obj.client_system, cmd)
def get_mounts_stat(mounts):
@@ -119,9 +125,8 @@ def get_mounts_stat(mounts):
for mount_obj in mounts:
g.log.info("Stat of mount %s:%s", mount_obj.client_system,
mount_obj.mountpoint)
- cmd = ("find %s | xargs stat" % (mount_obj.mountpoint))
- proc = g.run_async(mount_obj.client_system, cmd,
- user=mount_obj.user)
+ cmd = "find %s | xargs stat" % (mount_obj.mountpoint)
+ proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
_rc = True
for i, proc in enumerate(all_mounts_procs):
@@ -157,7 +162,7 @@ def list_all_files_and_dirs_mounts(mounts):
for mount_obj in mounts:
g.log.info("Listing files and dirs on %s:%s", mount_obj.client_system,
mount_obj.mountpoint)
- cmd = ("find %s | grep -ve '%s'" % (mount_obj.mountpoint, ignore_dirs))
+ cmd = "find %s | grep -ve '%s'" % (mount_obj.mountpoint, ignore_dirs)
proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
_rc = True
@@ -194,7 +199,7 @@ def view_snaps_from_mount(mounts, snaps):
for mount_obj in mounts:
g.log.info("Viewing '.snaps' on %s:%s", mount_obj.client_system,
mount_obj.mountpoint)
- cmd = ("ls -1 %s/.snaps" % mount_obj.mountpoint)
+ cmd = "ls -1 %s/.snaps" % mount_obj.mountpoint
proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
@@ -229,7 +234,7 @@ def view_snaps_from_mount(mounts, snaps):
def validate_io_procs(all_mounts_procs, mounts):
- """Validates whether IO was successful or not
+ """Validate whether IO was successful or not.
Args:
all_mounts_procs (list): List of open connection descriptor as
@@ -316,19 +321,16 @@ def cleanup_mounts(mounts):
for mount_obj in mounts:
g.log.info("Cleaning up data from %s:%s", mount_obj.client_system,
mount_obj.mountpoint)
- if (not mount_obj.mountpoint or
- (os.path.realpath(os.path.abspath(mount_obj.mountpoint))
- == '/')):
+ if (not mount_obj.mountpoint or (os.path.realpath(os.path.abspath(
+ mount_obj.mountpoint)) == '/')):
g.log.error("%s on %s is not a valid mount point",
mount_obj.mountpoint, mount_obj.client_system)
continue
cmd = "rm -rf %s/*" % (mount_obj.mountpoint)
- proc = g.run_async(mount_obj.client_system, cmd,
- user=mount_obj.user)
+ proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
valid_mounts.append(mount_obj)
- g.log.info("rm -rf on all clients is complete. Validating "
- "deletion now...")
+ g.log.info("rm -rf on all clients is complete. Validating deletion now...")
# Get cleanup status
_rc_rmdir = True
@@ -355,8 +357,7 @@ def cleanup_mounts(mounts):
for mount_obj in mounts:
cmd = ("find %s -mindepth 1 | grep -ve '%s'" %
(mount_obj.mountpoint, ignore_dirs))
- proc = g.run_async(mount_obj.client_system, cmd,
- user=mount_obj.user)
+ proc = g.run_async(mount_obj.client_system, cmd, user=mount_obj.user)
all_mounts_procs.append(proc)
# Get cleanup status
@@ -383,8 +384,7 @@ def cleanup_mounts(mounts):
def run_bonnie(servers, directory_to_run, username="root"):
- """
- Module to run bonnie test suite on the given servers.
+ """Run bonnie test suite on the given servers.
Args:
servers (list): servers in which tests to be run.
@@ -459,8 +459,7 @@ def run_bonnie(servers, directory_to_run, username="root"):
def run_fio(servers, directory_to_run):
- """
- Module to run fio test suite on the given servers.
+ """Run fio test suite on the given servers.
Args:
servers (list): servers in which tests to be run.
@@ -536,15 +535,14 @@ def run_fio(servers, directory_to_run):
def run_mixed_io(servers, io_tools, directory_to_run):
- """
- Module to run different io patterns on each given servers.
+ """Run different io patterns on each given servers.
Args:
servers (list): servers in which tests to be run.
io_tools (list): different io tools. Currently fio, bonnie are
- supported.
+ supported.
directory_to_run (list): directory path where tests will run for
- each server.
+ each server.
Returns:
bool: True, if test passes in all servers, False otherwise
@@ -565,8 +563,7 @@ def run_mixed_io(servers, io_tools, directory_to_run):
for items in zip(servers, io_tools):
server_io_dict[items[0]] = items[1]
- io_dict = {'fio': run_fio,
- 'bonnie': run_bonnie}
+ io_dict = {'fio': run_fio, 'bonnie': run_bonnie}
func_list = []
for index, server in enumerate(servers):
@@ -586,8 +583,7 @@ def run_mixed_io(servers, io_tools, directory_to_run):
def is_io_procs_fail_with_rofs(self, all_mounts_procs, mounts):
- """
- Checks whether IO failed with Read-only file system error
+ """Check whether IO failed with Read-only file system error.
Args:
all_mounts_procs (list): List of open connection descriptor as
@@ -619,8 +615,8 @@ def is_io_procs_fail_with_rofs(self, all_mounts_procs, mounts):
g.log.info("EXPECTED : IO Failed on %s:%s",
self.mounts[i].client_system,
self.mounts[i].mountpoint)
- if ("Read-only file system" in err or
- "Read-only file system" in out):
+ if ("Read-only file system" in err
+ or "Read-only file system" in out):
g.log.info("EXPECTED : Read-only file system in output")
io_results[proc] = True
else:
@@ -637,8 +633,7 @@ def is_io_procs_fail_with_rofs(self, all_mounts_procs, mounts):
def is_io_procs_fail_with_error(self, all_mounts_procs, mounts, mount_type):
- """
- Checks whether IO failed with connection error
+ """Check whether IO failed with connection error.
Args:
all_mounts_procs (list): List of open connection descriptor as
@@ -672,8 +667,8 @@ def is_io_procs_fail_with_error(self, all_mounts_procs, mounts, mount_type):
self.mounts[i].client_system,
self.mounts[i].mountpoint)
if mount_type == "glusterfs":
- if ("Transport endpoint is not connected" in err or
- "Transport endpoint is not connected" in out):
+ if ("Transport endpoint is not connected" in err
+ or "Transport endpoint is not connected" in out):
g.log.info("EXPECTED : Transport endpoint is not connected"
" in output")
io_results[proc] = True
@@ -683,8 +678,7 @@ def is_io_procs_fail_with_error(self, all_mounts_procs, mounts, mount_type):
"not found in output")
io_results[proc] = False
if mount_type == "nfs":
- if ("Input/output error" in err or
- "Input/output error" in out):
+ if "Input/output error" in err or "Input/output error" in out:
g.log.info("EXPECTED : Input/output error in output")
io_results[proc] = True
else:
@@ -702,8 +696,7 @@ def is_io_procs_fail_with_error(self, all_mounts_procs, mounts, mount_type):
def compare_dir_structure_mount_with_brick(mnthost, mntloc, brick_list, type):
- """ Compare directory structure from mount point with brick path along
- with stat parameter
+ """Compare mount point dir structure with brick path along with stat param..
Args:
mnthost (str): hostname or ip of mnt system
@@ -725,8 +718,8 @@ def compare_dir_structure_mount_with_brick(mnthost, mntloc, brick_list, type):
if type == 2:
statformat = '%A'
- command = ("find %s -mindepth 1 -type d | xargs -r stat -c '%s'"
- % (mntloc, statformat))
+ command = "find %s -mindepth 1 -type d | xargs -r stat -c '%s'" % (
+ mntloc, statformat)
rcode, rout, _ = g.run(mnthost, command)
all_dir_mnt_perm = rout.strip().split('\n')
@@ -736,7 +729,8 @@ def compare_dir_structure_mount_with_brick(mnthost, mntloc, brick_list, type):
"xargs -r stat -c '%s'" % (brick_path, statformat))
rcode, rout, _ = g.run(brick_node, command)
all_brick_dir_perm = rout.strip().split('\n')
- retval = cmp(all_dir_mnt_perm, all_brick_dir_perm)
+ retval = (all_dir_mnt_perm > all_brick_dir_perm) - (
+ all_dir_mnt_perm < all_brick_dir_perm)
if retval != 0:
return False
@@ -769,8 +763,7 @@ def check_arequal_bricks_replicated(mnode, volname):
subvol_brick_list = subvols_dict['volume_subvols'][i]
node, brick_path = subvol_brick_list[0].split(':')
command = ('arequal-checksum -p %s '
- '-i .glusterfs -i .landfill -i .trashcan'
- % brick_path)
+ '-i .glusterfs -i .landfill -i .trashcan' % brick_path)
ret, arequal, _ = g.run(node, command)
if ret != 0:
g.log.error("Failed to calculate arequal for first brick"
@@ -782,21 +775,297 @@ def check_arequal_bricks_replicated(mnode, volname):
for brick in subvol_brick_list[1:]:
node, brick_path = brick.split(':')
command = ('arequal-checksum -p %s '
- '-i .glusterfs -i .landfill -i .trashcan'
- % brick_path)
+ '-i .glusterfs -i .landfill -i .trashcan' % brick_path)
ret, brick_arequal, _ = g.run(node, command)
if ret != 0:
- g.log.error('Failed to get arequal on brick %s'
- % brick)
+ g.log.error('Failed to get arequal on brick %s' % brick)
return False
g.log.info('Getting arequal for %s is successful', brick)
brick_total = brick_arequal.splitlines()[-1].split(':')[-1]
# compare arequal of first brick of subvol with all brick other
# bricks in subvol
if first_brick_total != brick_total:
- g.log.error('Arequals for subvol and %s are not equal'
- % brick)
+ g.log.error('Arequals for subvol and %s are not equal' % brick)
return False
g.log.info('Arequals for subvol and %s are equal', brick)
g.log.info('All arequals are equal for volume %s', volname)
return True
+
+
+def run_crefi(client, mountpoint, number, breadth, depth, thread=5,
+ random_size=False, fop='create', filetype='text',
+ minfs=10, maxfs=500, single=False, multi=False, size=100,
+ interval=100, nameBytes=10, random_filename=True):
+ """Run crefi on a given mount point and generate I/O.
+
+ Args:
+ client(str): Client on which I/O has to be performed.
+ mountpoint(str): Mount point where the client is mounted.
+ number(int): Number of files to be created.
+ breadth(int): Number of directories in one level.
+ depth(int): Number of levels of directories.
+
+ Kwargs:
+ thread(int): Number of threads used to generate fop.
+ random_size(bool): Random size of the file between min and max.
+ fop(str): fop can be [create|rename|chmod|chown|chgrp|symlink|hardlink|
+ truncate|setxattr] this specifies the type of fop to be
+ executed by default it is create.
+ filetype(str): filetype can be [text|sparse|binary|tar] this specifies
+ the type of file by default it is text.
+ minfs(int): If random is set to true then this value has to be altered
+ to change minimum file size. (Value is in KB)
+ maxfs(int): If random is set to true then this value has to be altered
+ to change maximum file size. (Value is in KB)
+ single(bool): Create files in a single directory.
+ multi(bool): Create files in sub-dir and sub-sub dir.
+ size(int): Size of the files to be created. (Value is in KB)
+ interval(int): Print number files created of interval.
+ nameBytes(int): Number of bytes for filename. (Value is in Bytes)
+ random_filename(bool): It creates files with random names, if set to
+ False it creates files with file name file1,
+ file2 and so on.
+
+ Returns:
+ bool: True if I/O was sucessfully otherwise False.
+
+ NOTE:
+ To use this function it is a prerequisite to have crefi installed
+ on all the clients. Please use the below command to install it:
+ $ pip install crefi
+ $ pip install pyxattr
+ """
+
+ # Checking value of fop.
+ list_of_fops = ["create", "rename", "chmod", "chown", "chgrp", "symlink",
+ "hardlink", "truncate", "setxattr"]
+ if fop not in list_of_fops:
+ g.log.error("fop value is not valid.")
+ return False
+
+ # Checking value of filetype.
+ list_of_filetypes = ["text", "sparse", "binary", "tar"]
+ if filetype not in list_of_filetypes:
+ g.log.error("filetype is not a valid file type.")
+ return False
+
+ # Checking if single and multi both are set to true.
+ if single and multi:
+ g.log.error("single and mutli both can't be true.")
+ return False
+
+ # Checking if file size and random size arguments are given together.
+ if (size > 100 or size < 100) and random_size:
+ g.log.error("Size and Random size can't be used together.")
+ return False
+
+ # Checking if minfs is greater than or equal to maxfs.
+ if random_size and (minfs >= maxfs):
+ g.log.error("minfs shouldn't be greater than or equal to maxfs.")
+ return False
+
+ # Creating basic command.
+ command = "crefi %s -n %s -b %s -d %s " % (
+ mountpoint, number, breadth, depth)
+
+ # Checking thread value and adding it, If it is greater or smaller than 5.
+ if thread > 5 or thread < 5:
+ command = command + ("-T %s " % thread)
+
+ # Checking if random size is true or false.
+ if random_size:
+ command = command + "--random "
+ if minfs > 10 or minfs < 10:
+ command = command + ("--min %s " % minfs)
+ if maxfs > 500 or maxfs < 500:
+ command = command + ("--max %s " % maxfs)
+
+ # Checking fop and adding it if not create.
+ if fop != "create":
+ command = command + ("--fop %s " % fop)
+
+ # Checking if size if greater than or less than 100.
+ if size > 100 or size < 100:
+ command = command + ("--size %s " % size)
+
+ # Checking if single or mutli is true.
+ if single:
+ command = command + "--single "
+ if multi:
+ command = command + "--multi "
+
+ # Checking if random_filename is false.
+ if not random_filename:
+ command = command + "-R "
+
+ # Checking if print interval is greater than or less than 100.
+ if interval > 100 or interval < 100:
+ command = command + ("-I %s " % interval)
+
+ # Checking if name Bytes is greater than or less than 10.
+ if nameBytes > 10 or nameBytes < 10:
+ command = command + ("-l %s " % nameBytes)
+
+ # Checking filetype and setting it if not
+ # text.
+ if filetype != "text":
+ command = command + ("-t %s " % filetype)
+
+ # Running the command on the client node.
+ ret, _, _ = g.run(client, command)
+ if ret:
+ g.log.error("Failed to run crefi on %s." % client)
+ return False
+ return True
+
+
+def run_cthon(mnode, volname, clients, dir_name):
+ """This function runs the cthon test suite.
+
+ Args:
+ mnode (str) : IP of the server exporting the gluster volume.
+ volname (str) : The volume name.
+ clients (list) : List of client machines where
+ the test needs to be run.
+ dir_name (str) : Directory where the repo
+ is cloned.
+
+ Returns:
+ bool : True if the cthon test passes successfully
+ False otherwise.
+ """
+ param_list = ['-b', '-g', '-s', '-l']
+ vers_list = ['4.0', '4.1']
+
+ for client in clients:
+ g.log.info("Running tests on client %s" % client)
+ for vers in vers_list:
+ g.log.info("Running tests on client version %s" % vers)
+ for param in param_list:
+ # Initialising the test_type that will be running
+ if param == '-b':
+ test_type = "Basic"
+ elif param == '-g':
+ test_type = "General"
+ elif param == '-s':
+ test_type = "Special"
+ else:
+ test_type = "Lock"
+ g.log.info("Running %s test" % test_type)
+ cmd = "cd /root/%s; ./server %s -o vers=%s -p %s -N 1 %s;" % (
+ dir_name, param, vers, volname, mnode)
+ ret, _, _ = g.run(client, cmd)
+ if ret:
+ g.log.error("Error with %s test" % test_type)
+ return False
+ else:
+ g.log.info("%s test successfully passed" % test_type)
+ return True
+
+
+def upload_file_dir_ops(clients):
+ """Upload file_dir_ops.py to all the clients.
+
+ Args:
+ clients(list): List of client machines where we need to upload
+ the script.
+
+ Returns:
+ bool: True if script is uploaded successfully
+ False otherwise.
+ """
+
+ g.log.info("Upload io scripts to clients %s for running IO on "
+ "mounts", clients)
+ file_dir_ops_path = ("/usr/share/glustolibs/io/scripts/"
+ "file_dir_ops.py")
+
+ if not upload_scripts(clients, file_dir_ops_path):
+ g.log.error("Failed to upload IO scripts to clients %s" %
+ clients)
+ return False
+
+ g.log.info("Successfully uploaded IO scripts to clients %s",
+ clients)
+ return True
+
+
+def open_file_fd(mountpoint, time, client, start_range=0,
+ end_range=0):
+ """Open FD for a file and write to file.
+
+ Args:
+ mountpoint(str): The mount point where the FD of file is to
+ be opened.
+ time(int): The time to wait after opening an FD.
+ client(str): The client from which FD is to be opened.
+
+ Kwargs:
+ start_range(int): The start range of the open FD.
+ (Default: 0)
+ end_range(int): The end range of the open FD.
+ (Default: 0)
+
+ Returns:
+ proc(object): Returns a process object
+
+ NOTE:
+ Before opening FD, check the currently used fds on the
+ system as only a limited number of fds can be opened on
+ a system at a given time for each process.
+ """
+ if not (start_range and end_range):
+ cmd = ("cd {}; exec 30<> file_openfd ; sleep {};"
+ "echo 'xyz' >&30".format(mountpoint, time))
+ else:
+ cmd = ('cd {}; for i in `seq {} {}`;'
+ ' do eval "exec $i<>file_openfd$i"; sleep {};'
+ ' echo "Write to open FD" >&$i; done'.format(
+ mountpoint, start_range, end_range, time))
+ proc = g.run_async(client, cmd)
+ return proc
+
+
+def run_linux_untar(clients, mountpoint, dirs=('.')):
+ """Run linux kernal untar on a given mount point
+
+ Args:
+ clients(str|list): Client nodes on which I/O
+ has to be started.
+ mountpoint(str): Mount point where the volume is
+ mounted.
+ Kwagrs:
+ dirs(tuple): A tuple of dirs where untar has to
+ started. (Default:('.'))
+ Returns:
+ list: Returns a list of process object else None
+ """
+ # Checking and convering clients to list.
+ if not isinstance(clients, list):
+ clients = [clients]
+
+ list_of_procs = []
+ for client in clients:
+ # Download linux untar to root, so that it can be
+ # utilized in subsequent run_linux_untar() calls.
+ cmd = ("wget https://cdn.kernel.org/pub/linux/kernel/"
+ "v5.x/linux-5.4.54.tar.xz")
+ if not file_exists(client, '/root/linux-5.4.54.tar.xz'):
+ ret, _, _ = g.run(client, cmd)
+ if ret:
+ return None
+
+ for directory in dirs:
+ # copy linux tar to dir
+ cmd = ("cp /root/linux-5.4.54.tar.xz {}/{}"
+ .format(mountpoint, directory))
+ ret, _, _ = g.run(client, cmd)
+ if ret:
+ return None
+ # Start linux untar
+ cmd = ("cd {}/{};tar -xvf linux-5.4.54.tar.xz"
+ .format(mountpoint, directory))
+ proc = g.run_async(client, cmd)
+ list_of_procs.append(proc)
+
+ return list_of_procs
diff --git a/glustolibs-io/setup.py b/glustolibs-io/setup.py
index 41655dad6..9ee90b15b 100644
--- a/glustolibs-io/setup.py
+++ b/glustolibs-io/setup.py
@@ -1,5 +1,5 @@
-#!/usr/bin/python
-# Copyright (c) 2016 Red Hat, Inc.
+#!/usr/bin/env python
+# Copyright (c) 2016-2019 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
@@ -15,9 +15,11 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
#
+
+from distutils import dir_util
import os
+
from setuptools import setup, find_packages
-from distutils import dir_util
version = '0.1.2'
name = 'glustolibs-io'
@@ -26,29 +28,33 @@ setup(
name=name,
version=version,
description='Glusto - Red Hat I/O Libraries',
- license='GPLv2+',
+ license='GPLv3+',
author='Red Hat, Inc.',
author_email='gluster-devel@gluster.org',
url='http://www.gluster.org',
packages=find_packages(),
classifiers=[
- 'Development Status :: 4 - Beta'
- 'Environment :: Console'
- 'Intended Audience :: Developers'
- 'License :: OSI Approved :: GNU General Public License v2 or later (GPLv2+)'
- 'Operating System :: POSIX :: Linux'
- 'Programming Language :: Python'
- 'Programming Language :: Python :: 2'
- 'Programming Language :: Python :: 2.6'
- 'Programming Language :: Python :: 2.7'
- 'Topic :: Software Development :: Testing'
+ 'Development Status :: 4 - Beta',
+ 'Environment :: Console',
+ 'Intended Audience :: Developers',
+ 'License :: OSI Approved :: GNU General Public License v3 or '
+ 'later (GPLv3+)',
+ 'Operating System :: POSIX :: Linux',
+ 'Programming Language :: Python',
+ 'Programming Language :: Python :: 2',
+ 'Programming Language :: Python :: 2.6',
+ 'Programming Language :: Python :: 2.7',
+ 'Programming Language :: Python :: 3',
+ 'Topic :: Software Development :: Testing',
],
install_requires=['glusto'],
- dependency_links=['http://github.com/loadtheaccumulator/glusto/tarball/master#egg=glusto'],
+ dependency_links=[
+ 'http://github.com/loadtheaccumulator/glusto/tarball/master#egg=glusto'
+ ],
namespace_packages = ['glustolibs']
)
try:
dir_util.copy_tree('./shared_files', '/usr/share/glustolibs/io')
-except:
+except Exception:
pass
diff --git a/glustolibs-io/shared_files/scripts/fd_writes.py b/glustolibs-io/shared_files/scripts/fd_writes.py
index 87358f45a..e3ebccb63 100755
--- a/glustolibs-io/shared_files/scripts/fd_writes.py
+++ b/glustolibs-io/shared_files/scripts/fd_writes.py
@@ -15,14 +15,15 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
+from __future__ import print_function
import argparse
-import random
-import os
-import time
-import string
import datetime
from multiprocessing import Process
+import os
+import random
+import string
import sys
+import time
def is_root(path):
@@ -35,9 +36,9 @@ def is_root(path):
True if path is '/' , False otherwise
"""
if os.path.realpath(os.path.abspath(path)) == '/':
- print ("Directory '%s' is the root of filesystem. "
- "Not performing any operations on the root of filesystem" %
- os.path.abspath(path))
+ print("Directory '%s' is the root of filesystem. "
+ "Not performing any operations on the root of filesystem" % (
+ os.path.abspath(path)))
return True
else:
return False
@@ -72,26 +73,25 @@ def create_dir(dir_path):
try:
os.makedirs(dir_abs_path)
except (OSError, IOError):
- print "Unable to create dir: %s" % dir_abs_path
+ print("Unable to create dir: %s" % dir_abs_path)
return 1
return 0
def fd_write_file(filename, file_size, chunk_sizes_list, write_time,
delay_between_writes=10, log_level='INFO'):
- """Write random data to the file until write_time
- """
+ """Write random data to the file until write_time."""
rc = 0
time_counter = 0
try:
fd = open(filename, "w+b")
- fd.seek(file_size-1)
- fd.write("0")
+ fd.seek(file_size - 1)
+ fd.write(bytes(str("0").encode("utf-8")))
fd.flush()
except IOError as e:
- print ("Unable to open file %s for writing : %s" % (filename,
- e.strerror))
+ print("Unable to open file %s for writing : %s" % (
+ filename, e.strerror))
return 1
while time_counter < write_time:
@@ -102,18 +102,18 @@ def fd_write_file(filename, file_size, chunk_sizes_list, write_time,
range(current_chunk_size)))
offset = random.randint(0, (actual_file_size - current_chunk_size))
if log_level.upper() == 'DEBUG':
- print ("\tFileName: %s, File Size: %s, "
- "Writing to offset: %s, "
- "Data Length: %d, Time Counter: %d" %
- (filename, actual_file_size, offset, len(write_data),
- time_counter))
+ print("\tFileName: %s, File Size: %s, "
+ "Writing to offset: %s, "
+ "Data Length: %d, Time Counter: %d" % (
+ filename, actual_file_size, offset, len(write_data),
+ time_counter))
fd.seek(offset)
- fd.write(write_data)
+ fd.write(bytes(str(write_data).encode("utf-8")))
fd.seek(0)
fd.flush()
except IOError as e:
- print ("Unable to write to file '%s' : %s at time count: %dS" %
- (filename, e.strerror, time_counter))
+ print("Unable to write to file '%s' : %s at time count: %dS" % (
+ filename, e.strerror, time_counter))
rc = 1
time.sleep(delay_between_writes)
@@ -129,11 +129,11 @@ def fd_writes(args):
base_file_name = args.base_file_name
file_sizes_list = args.file_sizes_list
if file_sizes_list:
- file_sizes_list = filter(None, args.file_sizes_list.split(","))
+ file_sizes_list = list(filter(None, args.file_sizes_list.split(",")))
chunk_sizes_list = args.chunk_sizes_list
if chunk_sizes_list:
- chunk_sizes_list = map(int, filter(None,
- args.chunk_sizes_list.split(",")))
+ chunk_sizes_list = list(
+ map(int, filter(None, args.chunk_sizes_list.split(","))))
write_time = int(args.write_time)
delay_between_writes = int(args.delay_between_writes)
log_level = args.log_level
@@ -150,11 +150,11 @@ def fd_writes(args):
file_sizes_dict = {
'k': 1024,
'K': 1024,
- 'm': 1024*1024,
- 'M': 1024*1024,
- 'g': 1024*1024*1024,
- 'G': 1024*1024*1024
- }
+ 'm': 1024 ** 2,
+ 'M': 1024 ** 2,
+ 'g': 1024 ** 3,
+ 'G': 1024 ** 3,
+ }
file_sizes_expanded_list = []
for size in file_sizes_list:
@@ -240,15 +240,15 @@ if __name__ == "__main__":
parser.set_defaults(func=fd_writes)
- print "Starting Script: %s" % ' '.join(sys.argv)
- print "StarTime :'%s' " % (datetime.datetime.now())
+ print("Starting Script: %s" % ' '.join(sys.argv))
+ print("StarTime :'%s' " % datetime.datetime.now())
test_start_time = datetime.datetime.now().replace(microsecond=0)
args = parser.parse_args()
rc = args.func(args)
test_end_time = datetime.datetime.now().replace(microsecond=0)
- print "Execution time: %s" % (test_end_time - test_start_time)
- print "EndTime :'%s' " % (datetime.datetime.now())
+ print("Execution time: %s" % (test_end_time - test_start_time))
+ print("EndTime :'%s' " % datetime.datetime.now())
sys.exit(rc)
diff --git a/glustolibs-io/shared_files/scripts/file_dir_ops.py b/glustolibs-io/shared_files/scripts/file_dir_ops.py
index 96e53262d..908a48c8e 100755
--- a/glustolibs-io/shared_files/scripts/file_dir_ops.py
+++ b/glustolibs-io/shared_files/scripts/file_dir_ops.py
@@ -1,5 +1,5 @@
#!/usr/bin/env python
-# Copyright (C) 2015-2018 Red Hat, Inc. <http://www.redhat.com>
+# Copyright (C) 2015-2019 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
@@ -20,18 +20,22 @@
"""
from __future__ import print_function
-import os
import argparse
-import sys
-import random
-import string
+import contextlib
import datetime
from multiprocessing import Process
-import subprocess
-from docx import Document
-import contextlib
+from multiprocessing.pool import ThreadPool
+import os
import platform
+import random
import shutil
+import string
+import subprocess
+import sys
+
+from docx import Document
+import numpy as np
+from sh import rsync as sh_rsync
if platform.system() == "Windows":
path_sep = "\\"
@@ -49,9 +53,9 @@ def is_root(path):
True if path is '/' , False otherwise
"""
if os.path.realpath(os.path.abspath(path)) == '/':
- print ("Directory '%s' is the root of filesystem. "
- "Not performing any operations on the root of filesystem" %
- os.path.abspath(path))
+ print("Directory '%s' is the root of filesystem. "
+ "Not performing any operations on the root of filesystem" % (
+ os.path.abspath(path)))
return True
else:
return False
@@ -106,7 +110,7 @@ def create_dir(dir_path):
try:
os.makedirs(dir_abs_path)
except (OSError, IOError):
- print ("Unable to create dir: %s" % dir_abs_path)
+ print("Unable to create dir: %s" % dir_abs_path)
return 1
return 0
@@ -138,16 +142,16 @@ def create_dirs(dir_path, depth, num_of_dirs, num_of_files=0,
base_file_name, file_types)
except (OSError, IOError) as e:
if 'File exists' not in e.strerror:
- print ("Unable to create dir '%s' : %s"
- % (dir_path, e.strerror))
+ print("Unable to create dir '%s' : %s" % (
+ dir_path, e.strerror))
with open("/tmp/file_dir_ops_create_dirs_rc", "w") as fd:
try:
fd.write("1")
fd.flush()
fd.close()
- except IOError as e:
- print ("Unable to write the rc to the "
- "/tmp/file_dir_ops_create_dirs_rc file")
+ except IOError:
+ print("Unable to write the rc to the "
+ "/tmp/file_dir_ops_create_dirs_rc file")
if depth == 0:
return 0
for i in range(num_of_dirs):
@@ -183,9 +187,10 @@ def create_deep_dirs(args):
for i in range(dirname_start_num, (dirname_start_num + dir_length)):
num_of_dirs = random.choice(range(1, max_num_of_dirs + 1))
process_dir_path = os.path.join(dir_path, "user%d" % i)
- process_list.append(Process(target=create_dirs,
- args=(process_dir_path, dir_depth,
- num_of_dirs)))
+ process_list.append(Process(
+ target=create_dirs,
+ args=(process_dir_path, dir_depth, num_of_dirs)
+ ))
for each_process in process_list:
each_process.start()
@@ -237,11 +242,11 @@ def create_deep_dirs_with_files(args):
for i in range(dirname_start_num, (dirname_start_num + dir_length)):
num_of_dirs = random.choice(range(1, max_num_of_dirs + 1))
process_dir_path = os.path.join(dir_path, "user%d" % i)
- process_list.append(Process(target=create_dirs,
- args=(process_dir_path, dir_depth,
- num_of_dirs, num_of_files,
- fixed_file_size, base_file_name,
- file_types)))
+ process_list.append(Process(
+ target=create_dirs,
+ args=(process_dir_path, dir_depth, num_of_dirs, num_of_files,
+ fixed_file_size, base_file_name, file_types)
+ ))
for each_process in process_list:
each_process.start()
@@ -256,6 +261,48 @@ def create_deep_dirs_with_files(args):
return int(rc)
+def _create_file(file_abs_path, file_type, file_size):
+ rc = 0
+
+ if file_type == 'txt':
+ file_abs_path += ".txt"
+
+ with open(file_abs_path, "w+") as new_file:
+ try:
+ new_file.write(''.join(
+ np.random.choice(list(string.printable), file_size)))
+ new_file.flush()
+ new_file.close()
+ except IOError as err:
+ print("Unable to write to file '%s' : %s" % (
+ file_abs_path, err.strerror))
+ rc = 1
+
+ elif file_type == 'docx':
+ file_abs_path += ".docx"
+ try:
+ document = Document()
+ str_to_write = list(string.ascii_letters + string.digits)
+ file_str = ''.join(np.random.choice(str_to_write, file_size))
+ document.add_paragraph(file_str)
+ document.save(file_abs_path)
+ except Exception as err:
+ print("Unable to write to file '%s' : %s" % (
+ file_abs_path, err.strerror))
+ rc = 1
+
+ elif file_type == 'empty_file':
+ try:
+ with open(file_abs_path, "w+") as new_file:
+ new_file.close()
+ except IOError as err:
+ print("Unable to write to file '%s' : %s" % (
+ file_abs_path, err.strerror))
+ rc = 1
+
+ return rc
+
+
def _create_files(dir_path, num_of_files, fixed_file_size=None,
base_file_name='testfile', file_types='txt'):
rc = 0
@@ -264,62 +311,38 @@ def _create_files(dir_path, num_of_files, fixed_file_size=None,
'1k': 1024,
'10k': 10240,
'512k': 524288,
- '1M': 1048576
- }
+ '1M': 1048576,
+ }
# Create dir_path
rc = create_dir(dir_path)
if rc != 0:
return rc
- for count in range(num_of_files):
- fname = base_file_name + str(count)
- fname_abs_path = os.path.join(dir_path, fname)
- if fixed_file_size is None:
- file_size = (
- file_sizes_dict[random.choice(list(file_sizes_dict.keys()))])
- else:
- try:
- file_size = file_sizes_dict[fixed_file_size]
- except KeyError as e:
- print ("File sizes can be [1k, 10k, 512k, 1M]")
- return 1
+ fname_abs_path = os.path.join(dir_path, base_file_name)
+ if fixed_file_size is None:
+ # this generator yields file tuples: (file name, file type, file size)
+ files = ((fname_abs_path + str(num),
+ random.choice(file_types_list),
+ random.choice(list(file_sizes_dict.values())))
+ for num in range(num_of_files))
+ else:
+ try:
+ files = ((fname_abs_path + str(num),
+ random.choice(file_types_list),
+ file_sizes_dict[fixed_file_size])
+ for num in range(num_of_files))
+ except KeyError:
+ print("File sizes can be [1k, 10k, 512k, 1M]")
+ return 1
- type = random.choice(file_types_list)
- if type == 'txt':
- fname_abs_path = fname_abs_path + ".txt"
+ # Thread per filetype (for now)
+ pool = ThreadPool(len(file_types_list))
+ ret = pool.map(lambda file_tuple: _create_file(*file_tuple), files)
+ pool.close()
+ pool.join()
+ rc = 1 if any(ret) else 0
- with open(fname_abs_path, "w+") as fd:
- try:
- fd.write(''.join(random.choice(string.printable) for x in
- range(file_size)))
- fd.flush()
- fd.close()
- except IOError as e:
- print ("Unable to write to file '%s' : %s" %
- (fname_abs_path, e.strerror))
- rc = 1
- elif type == 'docx':
- fname_abs_path = fname_abs_path + ".docx"
- try:
- document = Document()
- str_to_write = string.ascii_letters + string.digits
- file_str = (''.join(random.choice(str_to_write)
- for x in range(file_size)))
- document.add_paragraph(file_str)
- document.save(fname_abs_path)
- except Exception as e:
- print ("Unable to write to file '%s' : %s" %
- (fname_abs_path, e.strerror))
- rc = 1
- elif type == 'empty_file':
- try:
- with open(fname_abs_path, "w+") as fd:
- fd.close()
- except IOError as e:
- print ("Unable to write to file '%s' : %s" %
- (fname_abs_path, e.strerror))
- rc = 1
return rc
@@ -367,7 +390,7 @@ def rename(args):
# Check if dir_path exists
if not path_exists(dir_path):
- print ("Directory '%s' does not exist" % dir_path)
+ print("Directory '%s' does not exist" % dir_path)
return 1
rc = 0
@@ -381,7 +404,7 @@ def rename(args):
os.rename(old, new)
except OSError:
rc = 1
- print ("Unable to rename %s -> %s" % (old, new))
+ print("Unable to rename %s -> %s" % (old, new))
# rename dirs
if dirName != dir_path:
@@ -391,19 +414,18 @@ def rename(args):
os.rename(old, new)
except OSError:
rc = 1
- print ("Unable to rename %s -> %s" % (old, new))
+ print("Unable to rename %s -> %s" % (old, new))
return rc
def ls(args):
- """Recursively list all the files/dirs under 'dir'
- """
+ """Recursively list all the files/dirs under 'dir'."""
dir_path = os.path.abspath(args.dir)
log_file_name = args.log_file_name
# Check if dir_path exists
if not path_exists(dir_path):
- print ("Directory '%s' does not exist" % dir_path)
+ print("Directory '%s' does not exist" % dir_path)
return 1
with open_file_to_write(log_file_name) as file_handle:
@@ -423,12 +445,10 @@ def ls(args):
def _get_path_stats(path):
- """Get the stat of a specified path.
- """
+ """Get the stat of a specified path."""
rc = 0
path = os.path.abspath(args.path)
file_stats = {}
- file_stats = {}
if platform.system() == "Linux":
cmd = "stat -c " + "'%A %U %G' " + path
@@ -455,8 +475,8 @@ def _get_path_stats(path):
'mtime': stat.st_mtime,
'ctime': stat.st_ctime,
'inode': stat.st_ino,
- 'stat': stat
- })
+ 'stat': stat,
+ })
except Exception:
rc = 1
err = "Unable to get the stat of path %s" % path
@@ -465,41 +485,39 @@ def _get_path_stats(path):
def get_path_stats(args):
- """Get file/dir Stat
- """
+ """Get file/dir Stat."""
path = os.path.abspath(args.path)
recursive = args.recursive
log_file_name = args.log_file_name
# Check if dir_path exists
if not path_exists(path):
- print ("PATH '%s' does not exist" % path)
+ print("PATH '%s' does not exist" % path)
return 1
file_stats = {}
if os.path.isfile(path):
- file_stats[path] = (_get_path_stats(path))
+ file_stats[path] = _get_path_stats(path)
if os.path.isdir(path):
if recursive:
for dirName, subdirList, fileList in os.walk(path, topdown=False):
- file_stats[dirName] = (_get_path_stats(dirName))
+ file_stats[dirName] = _get_path_stats(dirName)
for fname in fileList:
fname_abs_path = os.path.join(dirName, fname)
- file_stats[fname_abs_path] = (_get_path_stats(
- fname_abs_path))
+ file_stats[fname_abs_path] = _get_path_stats(
+ fname_abs_path)
else:
- file_stats[path] = (_get_path_stats(path))
+ file_stats[path] = _get_path_stats(path)
rc = 0
with open_file_to_write(log_file_name) as file_handle:
if log_file_name:
time_str = _get_current_time()
- file_handle.write("Starting 'stat %s' : %s" % (
- path, time_str))
+ file_handle.write("Starting 'stat %s' : %s" % (path, time_str))
for key in file_stats.keys():
file_handle.write("\nFile: %s" % key)
ret, file_stat, err = file_stats[key]
@@ -510,8 +528,7 @@ def get_path_stats(args):
file_handle.write("\t%s\n" % file_stat)
if log_file_name:
time_str = _get_current_time()
- file_handle.write("Ending 'stat %s' : %s" % (
- path, time_str))
+ file_handle.write("Ending 'stat %s' : %s" % (path, time_str))
file_handle.write("\n")
return rc
@@ -531,7 +548,7 @@ def compress(args):
# Check if dir_path exists
if not path_exists(dir_path):
- print ("Directory '%s' does not exist" % dir_path)
+ print("Directory '%s' does not exist" % dir_path)
return 1
# Create dir_path
@@ -546,16 +563,16 @@ def compress(args):
proc_list = []
for each_dir in dirs:
if compress_type == '7z':
- file_name = (dest_dir + path_sep +
- os.path.basename(each_dir) + "_7z.7z")
+ file_name = dest_dir + path_sep + os.path.basename(
+ each_dir) + "_7z.7z"
cmd = "7z a -t7z " + file_name + " " + each_dir
elif compress_type == 'gzip':
- tmp_file_name = (dir_path + path_sep +
- os.path.basename(each_dir) + "_tar.tar")
- file_name = (dest_dir + path_sep +
- os.path.basename(each_dir) + "_tgz.tgz")
- cmd = ("7z a -ttar -so " + tmp_file_name + " " +
- each_dir + " | 7z a -si " + file_name)
+ tmp_file_name = dir_path + path_sep + os.path.basename(
+ each_dir) + "_tar.tar"
+ file_name = dest_dir + path_sep + os.path.basename(
+ each_dir) + "_tgz.tgz"
+ cmd = ("7z a -ttar -so " + tmp_file_name + " "
+ + each_dir + " | 7z a -si " + file_name)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
proc_list.append(proc)
@@ -570,12 +587,12 @@ def compress(args):
file_name = dest_dir + path_sep + os.path.basename(dir_path) + "_7z.7z"
cmd = "7z a -t7z " + file_name + " " + dir_path
elif compress_type == 'gzip':
- tmp_file_name = (dest_dir + path_sep + os.path.basename(dir_path) +
- "_tar.tar")
- file_name = (dest_dir + path_sep + os.path.basename(dir_path) +
- "_tgz.tgz")
- cmd = ("7z a -ttar -so " + tmp_file_name + " " + dir_path +
- " | 7z a -si " + file_name)
+ tmp_file_name = (dest_dir + path_sep + os.path.basename(dir_path)
+ + "_tar.tar")
+ file_name = dest_dir + path_sep + os.path.basename(
+ dir_path) + "_tgz.tgz"
+ cmd = ("7z a -ttar -so " + tmp_file_name + " " + dir_path
+ + " | 7z a -si " + file_name)
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
proc.communicate()
@@ -587,13 +604,12 @@ def compress(args):
def uncompress(args):
- """UnCompress the given compressed file
- """
+ """UnCompress the given compressed file."""
compressed_file = os.path.abspath(args.compressed_file)
dest_dir = args.dest_dir
date_time = datetime.datetime.now().strftime("%I_%M%p_%B_%d_%Y")
- cmd = ("7z x " + compressed_file + " -o" + dest_dir + path_sep +
- "uncompress_" + date_time + " -y")
+ cmd = ("7z x " + compressed_file + " -o" + dest_dir + path_sep
+ + "uncompress_" + date_time + " -y")
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
proc.communicate()
@@ -605,13 +621,12 @@ def uncompress(args):
def uncompress_dir(args):
- """UnCompress all compressed files in destination directory
- """
+ """UnCompress all compressed files in destination directory."""
dir_path = os.path.abspath(args.dir)
dest_dir = args.dest_dir
date_time = datetime.datetime.now().strftime("%I_%M%p_%B_%d_%Y")
- cmd = ("7z x " + dir_path + " -o" + dest_dir + path_sep +
- "uncompress_" + date_time + " -y")
+ cmd = ("7z x " + dir_path + " -o" + dest_dir + path_sep
+ + "uncompress_" + date_time + " -y")
proc = subprocess.Popen(cmd, stdout=subprocess.PIPE,
stderr=subprocess.PIPE, shell=True)
proc.communicate()
@@ -623,7 +638,7 @@ def uncompress_dir(args):
def create_hard_links(args):
- """Creates hard link"""
+ """Create hard link."""
src_dir = os.path.abspath(args.src_dir)
dest_dir = args.dest_dir
@@ -633,7 +648,7 @@ def create_hard_links(args):
# Check if src_dir exists
if not path_exists(src_dir):
- print ("Directory '%s' does not exist" % src_dir)
+ print("Directory '%s' does not exist" % src_dir)
return 1
# Create dir_path
@@ -650,8 +665,8 @@ def create_hard_links(args):
rc = create_dir(dest_dir + path_sep + tmp_dir)
if rc != 0:
rc = 1
- link_file = (dest_dir + path_sep + tmp_dir + path_sep +
- new_fname + "_h")
+ link_file = (dest_dir + path_sep + tmp_dir + path_sep
+ + new_fname + "_h")
target_file = os.path.join(dir_name, fname)
if platform.system() == "Windows":
cmd = "mklink /H " + link_file + " " + target_file
@@ -702,9 +717,7 @@ def read(args):
def copy(args):
- """
- Copies files/dirs under 'dir' to destination directory
- """
+ """Copy files/dirs under 'dir' to destination directory."""
src_dir = os.path.abspath(args.src_dir)
dest_dir = args.dest_dir
@@ -714,7 +727,7 @@ def copy(args):
# Check if src_dir exists
if not path_exists(src_dir):
- print ("Directory '%s' does not exist" % src_dir)
+ print("Directory '%s' does not exist" % src_dir)
return 1
# Create dest_dir
@@ -735,8 +748,8 @@ def copy(args):
if dir_name != src_dir:
try:
src = dir_name
- dst = (dest_dir + path_sep +
- os.path.basename(os.path.normpath(src)))
+ dst = (dest_dir + path_sep
+ + os.path.basename(os.path.normpath(src)))
shutil.copytree(src, dst)
except OSError:
rc = 1
@@ -744,9 +757,7 @@ def copy(args):
def delete(args):
- """
- Deletes files/dirs under 'dir'
- """
+ """Delete files/dirs under 'dir'."""
dir_path = os.path.abspath(args.dir)
# Check if dir_path is '/'
@@ -755,7 +766,7 @@ def delete(args):
# Check if dir_path exists
if not path_exists(dir_path):
- print ("Directory '%s' does not exist" % dir_path)
+ print("Directory '%s' does not exist" % dir_path)
return 1
rc = 0
@@ -774,8 +785,137 @@ def delete(args):
return rc
+sizes_dict = {
+ '1k': 1024,
+ '10k': 10240,
+ '512k': 524288,
+ '1M': 1048576,
+ '0.5k': 513
+}
+
+
+def append(args):
+ """
+ Appends all files under 'dir' with randomly sized data.
+ """
+ dir_path = os.path.abspath(args.dir)
+ if not path_exists(args.dir):
+ return 1
+ rc = 0
+
+ for dir_name, subdir_list, file_list in os.walk(dir_path, topdown=False):
+ for fname in file_list:
+ append_size = sizes_dict[
+ random.choice(list(sizes_dict.keys()))]
+ try:
+ file = os.path.join(dir_name, fname)
+ with open(file, "a") as fd:
+ try:
+ fd.write(''.join(random.choice(string.printable)
+ for x in range(append_size)))
+ fd.flush()
+ except IOError as e:
+ print("Unable to append to file '%s' : %s" %
+ (file, e.strerror))
+ rc = 1
+
+ except OSError:
+ rc = 1
+
+ return rc
+
+
+def overwrite(args):
+ """
+ Truncates everything present and overwrites the file with new data.
+ """
+ dir_path = os.path.abspath(args.dir)
+ if not path_exists(args.dir):
+ return 1
+ rc = 0
+
+ for dir_name, subdir_list, file_list in os.walk(dir_path, topdown=False):
+ for fname in file_list:
+ new_size = sizes_dict[
+ random.choice(list(sizes_dict.keys()))]
+ try:
+ file = os.path.join(dir_name, fname)
+ with open(file, "w+") as fd:
+ try:
+ fd.write(''.join(random.choice(string.printable)
+ for x in range(new_size)))
+ fd.flush()
+ except IOError as e:
+ print("Unable to write to file '%s' : %s" %
+ (file, e.strerror))
+ rc = 1
+ except OSError:
+ rc = 1
+ return rc
+
+
+def truncate(args):
+ """
+ Truncates files to a certain size calculated randomly.
+ """
+ dir_path = os.path.abspath(args.dir)
+ if not path_exists(args.dir):
+ return 1
+ rc = 0
+
+ for dir_name, subdir_list, file_list in os.walk(dir_path, topdown=False):
+ for fname in file_list:
+ try:
+ file = os.path.join(dir_name, fname)
+ with open(file, "a+") as fd:
+ try:
+ fsize = os.path.getsize(file)
+ new_size = random.randrange(
+ 0, fsize//random.choice([2, 3, 4, 5]))
+ fd.truncate(new_size)
+
+ except IOError as e:
+ print("Unable to truncate file '%s' : %s" %
+ (file, e.strerror))
+ rc = 1
+ except OSError:
+ rc = 1
+ return rc
+
+
+def rsync(args):
+ """
+ rsync files from source to destination.
+ """
+ src_dir = os.path.abspath(args.src_dir)
+ remote_dir = args.remote_dir
+
+ if platform.system() == "Windows":
+ print("rsync not supported on Windows,Exiting!")
+ return 1
+
+ # Check if src_dir exists
+ if not path_exists(src_dir):
+ print("Directory '%s' does not exist" % src_dir)
+ return 1
+
+ # Create dest_dir
+ rc = create_dir(remote_dir)
+ if rc != 0:
+ return rc
+ rc = 0
+
+ try:
+ sh_rsync("-r", remote_dir, src_dir)
+
+ except Exception as e:
+ print("Can't rsync! : %s" % e.strerror)
+ rc = 1
+ return rc
+
+
if __name__ == "__main__":
- print ("Starting File/Dir Ops: %s" % _get_current_time())
+ print("Starting File/Dir Ops: %s" % _get_current_time())
test_start_time = datetime.datetime.now().replace(microsecond=0)
parser = argparse.ArgumentParser(
@@ -1019,7 +1159,66 @@ if __name__ == "__main__":
help="Directory on which operations has to be performed")
read_parser.set_defaults(func=read)
- # copy all files/directories under dir
+ # Appends files under dir
+ append_parser = subparsers.add_parser(
+ 'append',
+ help=("Appends data to already created files. "),
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ append_parser.add_argument(
+ '--log-file', help="Output log filename to log the "
+ "contents of file",
+ metavar=('log_file'), dest='log_file',
+ type=str, default=default_log_file)
+ append_parser.add_argument(
+ 'dir', metavar='DIR', type=str,
+ help="Directory on which operations has to be performed")
+ append_parser.set_defaults(func=append)
+
+ # Overwrites files under dir
+ overwrite_parser = subparsers.add_parser(
+ 'overwrite',
+ help=("Overwrites existing files with new data "),
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ overwrite_parser.add_argument(
+ '--log-file', help="Output log filename to log the "
+ "contents of file",
+ metavar=('log_file'), dest='log_file',
+ type=str, default=default_log_file)
+ overwrite_parser.add_argument(
+ 'dir', metavar='DIR', type=str,
+ help="Directory on which operations has to be performed")
+ overwrite_parser.set_defaults(func=overwrite)
+
+ # rsync dir to a remote directory
+ rsyncs_parser = subparsers.add_parser(
+ 'rsync',
+ help=("Rsync all dirs in a remote location to 'dir'. "),
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ rsyncs_parser.add_argument(
+ '--remote-dir', help="Remote location to rsync from)",
+ metavar=('remote_dir'), dest='remote_dir',
+ type=str)
+ rsyncs_parser.add_argument(
+ 'src_dir', metavar='src_dir', type=str,
+ help="Directory on which operations has to be performed")
+ rsyncs_parser.set_defaults(func=rsync)
+
+ # Truncates files under dir
+ truncate_parser = subparsers.add_parser(
+ 'truncate',
+ help=("Truncates existing files "),
+ formatter_class=argparse.ArgumentDefaultsHelpFormatter)
+ truncate_parser.add_argument(
+ '--log-file', help="Output log filename to log the "
+ "contents of file",
+ metavar=('log_file'), dest='log_file',
+ type=str, default=default_log_file)
+ truncate_parser.add_argument(
+ 'dir', metavar='DIR', type=str,
+ help="Directory on which operations has to be performed")
+ truncate_parser.set_defaults(func=truncate)
+
+ # Copy all files/directories under dir
copy_parser = subparsers.add_parser(
'copy',
help=("Copy all files/directories under 'dir'. "),
@@ -1047,6 +1246,6 @@ if __name__ == "__main__":
rc = args.func(args)
test_end_time = datetime.datetime.now().replace(microsecond=0)
- print ("Execution time: %s" % (test_end_time - test_start_time))
- print ("Ending File/Dir Ops %s" % _get_current_time())
+ print("Execution time: %s" % (test_end_time - test_start_time))
+ print("Ending File/Dir Ops %s" % _get_current_time())
sys.exit(rc)
diff --git a/glustolibs-io/shared_files/scripts/file_lock.py b/glustolibs-io/shared_files/scripts/file_lock.py
new file mode 100644
index 000000000..e29fd1b1d
--- /dev/null
+++ b/glustolibs-io/shared_files/scripts/file_lock.py
@@ -0,0 +1,51 @@
+#!/usr/bin/env python
+# Copyright (C) 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
+# 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.
+
+from fcntl import flock, LOCK_EX, LOCK_NB, LOCK_UN
+from time import sleep
+from argparse import ArgumentParser
+
+
+def get_file_lock(args):
+ """
+ Gets the lock to a file and releases it after timeout
+ """
+ file_name = args.f
+ timeout = args.t
+ f = open(file_name, 'w')
+ flock(f.fileno(), LOCK_EX | LOCK_NB)
+ sleep(int(timeout))
+ flock(f.fileno(), LOCK_UN)
+
+
+if __name__ == "__main__":
+ file_lock_parser = ArgumentParser(
+ prog="file_lock.py", description="Program to validate file lock ops")
+
+ file_lock_req_args = file_lock_parser.add_argument_group(
+ 'required named arguments')
+ file_lock_req_args.add_argument(
+ '-f', type=str, required=True,
+ help="File on which lock has to be applied")
+ file_lock_req_args.add_argument(
+ '-t', help="time for which lock has to be retained", type=int,
+ required=True)
+
+ file_lock_parser.set_defaults(func=get_file_lock)
+
+ args = file_lock_parser.parse_args()
+ rc = args.func(args)
diff --git a/glustolibs-io/shared_files/scripts/generate_io.py b/glustolibs-io/shared_files/scripts/generate_io.py
index d07bda7b0..d80389fd3 100755
--- a/glustolibs-io/shared_files/scripts/generate_io.py
+++ b/glustolibs-io/shared_files/scripts/generate_io.py
@@ -15,29 +15,29 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-import subprocess
-import re
-import time
+"""
+Script for generating IO on client
+"""
+
+from __future__ import print_function
+import argparse
+import datetime
import multiprocessing
-import tempfile
import os
+import re
import shutil
import signal
-import argparse
+import subprocess
import sys
+import tempfile
+import time
import yaml
-import datetime
-ONE_GB_BYTES = 1073741824.0
-
-"""
-Script for generating IO on client
-"""
+ONE_GB_BYTES = float(1024 ** 3)
def get_disk_usage(path):
- """
- This module gets disk usage of the given path
+ """Get disk usage of the given path.
Args:
path (str): path for which disk usage to be calculated
@@ -45,7 +45,6 @@ def get_disk_usage(path):
Returns:
dict: disk usage in dict format on success
None Type, on failure
-
"""
cmd = 'stat -f ' + path
@@ -73,30 +72,27 @@ def get_disk_usage(path):
print("Regex mismatch in get_disk_usage()")
return None
- usage_info = dict()
- keys = ['b_size', 'b_total', 'b_free', 'b_avail', 'i_total', 'i_free']
- val = list(match.groups())
- info = dict(zip(keys, val))
- usage_info['total'] = ((int(info['b_total']) * int(info['b_size'])) /
- ONE_GB_BYTES)
- usage_info['free'] = ((int(info['b_free']) * int(info['b_size'])) /
- ONE_GB_BYTES)
- usage_info['used_percent'] = (100 - (100.0 * usage_info['free'] /
- usage_info['total']))
- usage_info['total_inode'] = int(info['i_total'])
- usage_info['free_inode'] = int(info['i_free'])
- usage_info['used_percent_inode'] = ((100 -
- (100.0 * usage_info['free_inode']) /
- usage_info['total_inode']))
+ keys = ('b_size', 'b_total', 'b_free', 'b_avail', 'i_total', 'i_free')
+ values = list(match.groups())
+ data = dict(zip(keys, values))
+ usage_info = {'total': (
+ int(data['b_total']) * int(data['b_size']) // ONE_GB_BYTES)}
+ usage_info['free'] = (
+ int(data['b_free']) * int(data['b_size']) // ONE_GB_BYTES)
+ usage_info['used_percent'] = (
+ 100 - (100.0 * usage_info['free'] // usage_info['total']))
+ usage_info['total_inode'] = int(data['i_total'])
+ usage_info['free_inode'] = int(data['i_free'])
+ usage_info['used_percent_inode'] = (
+ 100 - (100.0 * usage_info['free_inode']) // usage_info['total_inode'])
usage_info['used'] = usage_info['total'] - usage_info['free']
- usage_info['used_inode'] = (usage_info['total_inode'] -
- usage_info['free_inode'])
+ usage_info['used_inode'] = (
+ usage_info['total_inode'] - usage_info['free_inode'])
return usage_info
def get_disk_used_percent(dirname):
- """
- Module to get disk used percent
+ """Get disk used percentage.
Args:
dirname (str): absolute path of directory
@@ -107,21 +103,18 @@ def get_disk_used_percent(dirname):
Example:
get_disk_used_percent("/mnt/glusterfs")
-
"""
output = get_disk_usage(dirname)
if output is None:
- print("Failed to get disk used percent for %s"
- % dirname)
+ print("Failed to get disk used percent for %s" % dirname)
return None
return output['used_percent']
def check_if_percent_to_fill_or_timeout_is_met(dirname, percent_to_fill,
timeout):
- """
- Module to check if percent to fill or timeout is met.
+ """Check if percent to fill or timeout is met.
Args:
dirname (str): absolute path of directory
@@ -133,8 +126,7 @@ def check_if_percent_to_fill_or_timeout_is_met(dirname, percent_to_fill,
is met, False otherwise
Example:
- check_if_percent_to_fill_or_timeout_is_met("/mnt/glusterfs",
- 10, 60)
+ check_if_percent_to_fill_or_timeout_is_met("/mnt/glusterfs", 10, 60)
"""
flag = 0
count = 0
@@ -145,11 +137,11 @@ def check_if_percent_to_fill_or_timeout_is_met(dirname, percent_to_fill,
if int(percent_to_fill) > int(used):
remaining_to_fill = int(percent_to_fill) - int(used)
- print("Remaining space left to fill data in directory %s is %s"
- % (dirname, str(remaining_to_fill)))
+ print("Remaining space left to fill data in directory %s is %s" % (
+ dirname, str(remaining_to_fill)))
time_str = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
- print("Directory %s used percent at time %s: %s"
- % (dirname, time_str, used))
+ print("Directory %s used percent at time %s: %s" % (
+ dirname, time_str, used))
if int(percent_to_fill) <= int(used):
flag = 1
break
@@ -157,8 +149,8 @@ def check_if_percent_to_fill_or_timeout_is_met(dirname, percent_to_fill,
count = count + 5
else:
print("Directory %s is filled with given percent already. "
- "Percentage filled: %s"
- % (dirname, str(percent_to_fill)))
+ "Percentage filled: %s" % (
+ dirname, str(percent_to_fill)))
flag = 1
break
@@ -169,19 +161,15 @@ def check_if_percent_to_fill_or_timeout_is_met(dirname, percent_to_fill,
else:
print("Timeout %s seconds reached before filling directory with "
"given percentage %s" % (str(timeout), str(percent_to_fill)))
- return True
- return False
+ return False
def run_check_if_percent_to_fill_or_timeout_is_met(dirname,
percent_to_fill,
timeout, event):
- """
- Helper Module to check if percent to fill or timeout is met.
- """
- ret = check_if_percent_to_fill_or_timeout_is_met(dirname,
- percent_to_fill,
- timeout)
+ """Check if percent to fill or timeout is met."""
+ ret = check_if_percent_to_fill_or_timeout_is_met(
+ dirname, percent_to_fill, timeout)
if ret:
event.set()
return True
@@ -189,10 +177,8 @@ def run_check_if_percent_to_fill_or_timeout_is_met(dirname,
return False
-def run_fio(proc_queue, script_path, dirname,
- job_files_list, log_file):
- """
- Module to invoke IOs using fio tool
+def run_fio(proc_queue, script_path, dirname, job_files_list, log_file):
+ """Invoke IOs using fio tool.
Args:
proc_queue (obj): multiprocessing queue object
@@ -204,7 +190,6 @@ def run_fio(proc_queue, script_path, dirname,
Returns:
bool: True, if fio starts to write data and stops when it
gets "STOP" string in queue, False otherwise
-
"""
tmpdir = tempfile.mkdtemp()
job_files_list_to_run = []
@@ -213,23 +198,17 @@ def run_fio(proc_queue, script_path, dirname,
shutil.copy(job_file, job_file_to_run)
job_files_list_to_run.append(job_file_to_run)
+ python_bin = "/usr/bin/env python%d" % sys.version_info.major
+ cmd = "%s %s --job-files '%s' %s" % (
+ python_bin, script_path, ' '.join(job_files_list_to_run), dirname)
if log_file is not None:
with open(log_file, "w") as fd:
time_str = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
- title = ("=========STARTING FIO-" + time_str +
- "=======\n")
+ title = ("=========STARTING FIO-" + time_str + "=======\n")
fd.write(title)
fd.close()
- cmd = ("python " + script_path +
- " --job-files '" + ' '.join(job_files_list_to_run) + "' " +
- dirname + " >> " + log_file + " 2>&1")
-
- else:
- cmd = ("python " + script_path +
- " --job-files '" + ' '.join(job_files_list_to_run) +
- "' " + dirname)
- p = subprocess.Popen(cmd, shell=True,
- preexec_fn=os.setsid)
+ cmd += " >> %s 2>&1" % log_file
+ p = subprocess.Popen(cmd, shell=True, preexec_fn=os.setsid)
time.sleep(10)
if p is None:
print("Unable to trigger IO using fio")
@@ -241,8 +220,7 @@ def run_fio(proc_queue, script_path, dirname,
with open(log_file, "a") as fd:
time_str = (datetime.datetime.now().
strftime('%Y_%m_%d_%H_%M_%S'))
- title = ("=========ENDING FIO-" + time_str +
- "=======\n")
+ title = ("=========ENDING FIO-" + time_str + "=======\n")
fd.write(title)
fd.close()
break
@@ -251,10 +229,8 @@ def run_fio(proc_queue, script_path, dirname,
return True
-def start_populate_data(mount_point, io_dict,
- percent_to_fill, timeout):
- """
- Starts populating data on the directory
+def start_populate_data(mount_point, io_dict, percent_to_fill, timeout):
+ """Start populating data on a directory.
Args:
mount_point(str): Directory name to fill data
@@ -264,29 +240,23 @@ def start_populate_data(mount_point, io_dict,
Returns:
bool: returns True, if IO succeeds. False, otherwise
-
"""
dirname = mount_point
m = multiprocessing.Manager()
event = m.Event()
- proc_list = []
- proc_queue = []
-
+ proc_list, proc_queue = [], []
for each_io in io_dict.keys():
q = multiprocessing.Queue()
proc_queue.append(q)
workload_type = io_dict[each_io]['workload_type']
- proc = multiprocessing.Process(target=(io_dict[each_io]
- ['function_addr']),
- args=(q,
- (io_dict[each_io]
- ['script_path']),
- dirname,
- (io_dict[each_io]['job_files']
- [workload_type]),
- io_dict[each_io]['log_file']))
+ proc = multiprocessing.Process(
+ target=io_dict[each_io]['function_addr'],
+ args=(q, io_dict[each_io]['script_path'], dirname,
+ io_dict[each_io]['job_files'][workload_type],
+ io_dict[each_io]['log_file'])
+ )
proc_list.append(proc)
time.sleep(5)
proc.start()
@@ -304,8 +274,7 @@ def start_populate_data(mount_point, io_dict,
def stop_populate_data(proc_list, proc_queue, mevent=None):
- """
- Stops populating data on the directory
+ """Stop populating data on a directory.
Args:
proc_list (list): List of processes to kill
@@ -338,27 +307,22 @@ def stop_populate_data(proc_list, proc_queue, mevent=None):
proc.terminate()
return True
except Exception as e:
- print("Exception occurred in stop_populate_data(): %s"
- % e)
+ print("Exception occurred in stop_populate_data(): %s" % e)
return False
def call_get_disk_usage(args):
- """
- Main method for getting disk usage
- """
+ """Main method for getting disk usage."""
disk_usage = get_disk_usage(args.dir)
if disk_usage is None:
return 1
- print disk_usage
+ print(disk_usage)
return 0
def call_start_populate_data(args):
- """
- Main method for populating data
- """
+ """Main method for populating data."""
dirname = args.dir
config_file_list = args.c.split()
@@ -386,24 +350,18 @@ def call_start_populate_data(args):
# case4: If -i | -w | -i and -w is not specified , run all the tools
# specified in the config file
- if args.i is not None:
- io_list = args.i.split()
- else:
- io_list = []
-
+ io_list = [] if args.i is None else args.i.split()
workload_type = ""
if workload is not None:
- if (('workload' in config_data['io'] and
- config_data['io']['workload'] and
- workload in config_data['io']['workload'])):
+ if workload in (config_data['io'].get('workload', []) or []):
if not io_list:
io_list = config_data['io']['workload'][workload]
else:
io_list_from_user = io_list
- io_list_for_given_workload = (config_data['io']
- ['workload'][workload])
- io_list = (list(set(io_list_from_user).
- intersection(io_list_for_given_workload)))
+ io_list_for_given_workload = (
+ config_data['io']['workload'][workload])
+ io_list = (list(set(io_list_from_user).intersection(
+ io_list_for_given_workload)))
workload_type = workload
else:
if not io_list:
@@ -425,41 +383,41 @@ def call_start_populate_data(args):
time_str = datetime.datetime.now().strftime('%Y_%m_%d_%H_%M_%S')
log_file = filename + "_" + time_str + file_ext
- print "GENERATE IO Log file: %s" % log_file
+ print("GENERATE IO Log file: %s" % log_file)
- if('io' in config_data and 'tools' in config_data['io']):
+ if 'io' in config_data and 'tools' in config_data['io']:
config_data_io = dict(config_data['io']['tools'])
else:
- print "io tools info is not given in config file"
+ print("io tools info is not given in config file")
return 1
- if('io' in config_data and 'scripts' in config_data['io']):
+ if 'io' in config_data and 'scripts' in config_data['io']:
config_data_io.update(config_data['io']['scripts'])
else:
- print "io scripts info is not given in config file"
+ print("io scripts info is not given in config file")
return 1
io_details = {}
for io in io_list:
if io in config_data_io.keys():
config_data_io[io]['function_addr'] = eval("run_" + io)
- config_data_io[io]['log_file'] = (log_file_dir + "/" +
- io + "_log.log")
+ config_data_io[io]['log_file'] = (
+ log_file_dir + "/" + io + "_log.log")
config_data_io[io]['workload_type'] = workload_type
io_details[io] = config_data_io[io]
else:
- print ("The IO tool/script - '%s' details not present in config "
- "file. Skipping the IO - '%s'" % (io, io))
+ print("The IO tool/script - '%s' details not present in config "
+ "file. Skipping the IO - '%s'" % (io, io))
if not io_details:
- print "Config file doesn't have IO details for %s" % ','.join(io_list)
+ print("Config file doesn't have IO details for %s" % ','.join(io_list))
return 1
# Starts generating IO
# If -t and -p bot are passed as options, runs all the io's as specified
# until '-t' or '-p' is reached. i.e which ever reaches first.
ret = start_populate_data(dirname, io_details, percent, timeout)
- print "Disk Usage Details of %s: %s" % (dirname, get_disk_usage(dirname))
+ print("Disk Usage Details of %s: %s" % (dirname, get_disk_usage(dirname)))
fd_list = []
for io in io_details.keys():
@@ -472,8 +430,8 @@ def call_start_populate_data(args):
for each_fh in fd_list:
fd.write(each_fh.read())
each_fh.close()
- fd.write("\nDisk Usage Details of %s: %s" % (dirname,
- get_disk_usage(dirname)))
+ fd.write("\nDisk Usage Details of %s: %s" % (
+ dirname, get_disk_usage(dirname)))
fd.close()
if ret:
@@ -483,39 +441,35 @@ def call_start_populate_data(args):
if __name__ == "__main__":
- print "Starting IO Generation..."
+ print("Starting IO Generation...")
test_start_time = datetime.datetime.now().replace(microsecond=0)
- write_data_parser = argparse.ArgumentParser(prog="generate_io.py",
- description=("Program for "
- "generating io"))
+ write_data_parser = argparse.ArgumentParser(
+ prog="generate_io.py", description="Program for generating io")
write_data_required_parser = write_data_parser.add_argument_group(
- 'required named arguments')
-
+ 'required named arguments')
write_data_required_parser.add_argument(
'dir', metavar='DIR', type=str,
help="Directory on which operations has to be performed")
- write_data_required_parser.add_argument('-c', help="space separated list "
- "of config files",
- required=True)
- write_data_parser.add_argument('-i', help="space separated list of "
- "io tools")
+ write_data_required_parser.add_argument(
+ '-c', help="space separated list of config files", required=True)
+ write_data_parser.add_argument(
+ '-i', help="space separated list of io tools")
write_data_parser.add_argument('-w', help="Workload type")
- write_data_parser.add_argument('-p', help="percentage to fill the"
- "directory",
- type=int, default=100)
- write_data_parser.add_argument('-t', help="timeout value in seconds.",
- type=int)
+ write_data_parser.add_argument(
+ '-p', help="percentage to fill the directory", type=int, default=100)
+ write_data_parser.add_argument(
+ '-t', help="timeout value in seconds.", type=int)
default_log_file = "/var/tmp/generate_io/generate_io.log"
- write_data_parser.add_argument('-l', help="log file name.",
- default=default_log_file)
+ write_data_parser.add_argument(
+ '-l', help="log file name.", default=default_log_file)
write_data_parser.set_defaults(func=call_start_populate_data)
args = write_data_parser.parse_args()
rc = args.func(args)
test_end_time = datetime.datetime.now().replace(microsecond=0)
- print "Execution time: %s" % (test_end_time - test_start_time)
- print "Ending IO Generation"
+ print("Execution time: %s" % (test_end_time - test_start_time))
+ print("Ending IO Generation")
sys.exit(rc)
diff --git a/glustolibs-io/shared_files/scripts/memory_and_cpu_logger.py b/glustolibs-io/shared_files/scripts/memory_and_cpu_logger.py
new file mode 100644
index 000000000..d2ee80d6c
--- /dev/null
+++ b/glustolibs-io/shared_files/scripts/memory_and_cpu_logger.py
@@ -0,0 +1,108 @@
+#!/usr/bin/env python
+# Copyright (C) 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
+# 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.
+
+"""
+A tool to monitor and log memory consumption processes.
+"""
+from __future__ import print_function
+
+import argparse
+import csv
+from time import sleep
+import subprocess
+
+
+def run_command(cmd):
+ """
+ Run command using Popen and return output
+ """
+ ret = subprocess.Popen(cmd, stdout=subprocess.PIPE,
+ stderr=subprocess.PIPE, shell=True)
+ output = ret.stdout.read().decode('utf8').split('\n')[:-1]
+ return output
+
+
+def get_memory_and_cpu_consumption(proc_name):
+ """
+ Get the memory and cpu consumed by a given process
+ """
+ # The command gives an output as shown below:
+ # [2020-08-07 09:34:48] 16422 0.0 9.99609
+ #
+ # Where,
+ # [2020-08-07 09:34:48] is UTC timestamp.
+ # 16422 is the process ID.
+ # 0.0 is the CPU usage.
+ # 9.99609 is memory consumption in MB.
+ cmd = ("ps u -p `pgrep " + proc_name + "` | "
+ "awk 'NR>1 && $11~/" + proc_name + "$/{print "
+ "strftime(\"[%Y-%d-%m %H:%M:%S]\", "
+ "systime(), 1), $2,$3,$6/1024}'")
+ memory_and_cpu_consumed = run_command(cmd)
+ return memory_and_cpu_consumed
+
+
+def main():
+ """
+ Main function of the tool.
+ """
+ # Setting up command line arguments
+ parser = argparse.ArgumentParser(
+ description="A tool to log memory usage of a given process"
+ )
+ parser.add_argument(
+ "-p", "--process_name", type=str, dest="process_name", required=True,
+ help="Name of process for which cpu and memory is to be logged")
+ parser.add_argument(
+ "-i", "--interval", type=int, dest="interval", default=60,
+ help="Time interval to wait between consecutive logs(Default:60)")
+ parser.add_argument(
+ "-c", "--count", type=int, dest="count", default=10,
+ help="Number of times memory and CPU has to be logged (Default:10)")
+ parser.add_argument(
+ '-t', '--testname', type=str, dest="testname", required=True,
+ help="Test name for which memory is logged")
+ args = parser.parse_args()
+
+ # Declare all three parameters
+ process_name = args.process_name
+ count = args.count
+ interval = args.interval
+
+ # Generating CSV file header
+ with open('{}.csv'.format(process_name), 'a') as file:
+ csv_writer_obj = csv.writer(file)
+ csv_writer_obj.writerow([args.testname, '', '', ''])
+ csv_writer_obj.writerow([
+ 'Time stamp', 'Process ID', 'CPU Usage', 'Memory Usage'])
+
+ # Taking memory output for a given
+ # number of times
+ for counter in range(0, count):
+ print("Iteration: {}".format(counter))
+ data = get_memory_and_cpu_consumption(process_name)
+
+ # Logging information to csv file
+ for line in data:
+ info = line.split(" ")
+ csv_writer_obj.writerow([" ".join(info[:2]), info[2],
+ info[3], info[4]])
+ sleep(interval)
+
+
+if __name__ == "__main__":
+ main()
diff --git a/glustolibs-io/shared_files/tools/fio/run_fio.py b/glustolibs-io/shared_files/tools/fio/run_fio.py
index ea20175cb..f65ad93d3 100644
--- a/glustolibs-io/shared_files/tools/fio/run_fio.py
+++ b/glustolibs-io/shared_files/tools/fio/run_fio.py
@@ -15,17 +15,16 @@
# with this program; if not, write to the Free Software Foundation, Inc.,
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-import os
import argparse
import fileinput
+import os
import re
import subprocess
import time
def generate_workload_using_fio(root_dirname, ini_file):
- """
- Populates data in the given directory using fio tool.
+ """Populate data in the given directory using fio tool.
Args:
root_dirname (str): Directory name
@@ -54,16 +53,15 @@ if __name__ == "__main__":
# http://git.kernel.dk/?p=fio.git;a=blob;f=README;
# h=5fa37f3eed33a15a15a38836cf0080edc81688fd;hb=HEAD
- parser = argparse.ArgumentParser(prog="test_fio.py",
- description=("Generate workload "
- "using fio"))
+ parser = argparse.ArgumentParser(
+ prog="test_fio.py",
+ description=("Generate workload using fio"))
parser.add_argument(
'dir', metavar='DIR', type=str,
help="Directory on which IO has to be performed")
- parser.add_argument('--job-files',
- metavar=('job_files'), dest='job_files',
- help="space separated absolute paths of "
- "ini job files", required=True)
+ parser.add_argument(
+ '--job-files', metavar=('job_files'), dest='job_files',
+ help="space separated absolute paths of ini job files", required=True)
args = parser.parse_args()
root_dirname = args.dir
ini_files_list = args.job_files.split()