Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from 2 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions src/rgw/const.py
Original file line number Diff line number Diff line change
Expand Up @@ -178,6 +178,9 @@
SVC_THREAD_POOL_SIZE_KEY = f'{COMPONENT_NAME} thread pool size'
SVC_CONCURRENT_MAX_REQ_KEY = f'{COMPONENT_NAME} max concurrent request'

# Support Bundle
SVC_FILE_PERCENTAGE = 95

class RgwEndpoint(Enum):
"""Enum class to define rgw endpoints provided by hare."""

Expand Down
231 changes: 195 additions & 36 deletions src/rgw/support/rgw_support_bundle
Original file line number Diff line number Diff line change
Expand Up @@ -29,8 +29,10 @@ from cortx.utils.log import Log
from cortx.utils.errors import BaseError
from cortx.utils.process import SimpleProcess
from cortx.utils.conf_store.conf_store import Conf, MappedConf
from cortx.utils.support_framework.log_filters import FilterLog
from cortx.rgw.const import (
CONFIG_PATH_KEY, LOG_PATH_KEY, COMPONENT_NAME, RGW_CONF_FILE, RGW_CORE_FILE_DIR_NAME)
CONFIG_PATH_KEY, LOG_PATH_KEY, COMPONENT_NAME, RGW_CONF_FILE,
SVC_FILE_PERCENTAGE)


class SupportBundleError(BaseError):
Expand All @@ -50,7 +52,8 @@ class RGWSupportBundle:
Log.info(f"{COMPONENT_NAME} support bundle generation started!!")
coredumps = filters.get('coredumps', False)
stacktrace = filters.get('stacktrace', False)
duration = filters.get('duration',False)
duration = filters.get('duration', False)
size_limit = filters.get('size_limit')
machine_id = Conf.machine_id
cortx_config_store = MappedConf(cluster_conf)
log_base = cortx_config_store.get(LOG_PATH_KEY)
Expand All @@ -63,11 +66,19 @@ class RGWSupportBundle:
os.makedirs(RGWSupportBundle._tmp_src, exist_ok=True)
# copy configuration files
if os.path.exists(config_file):
shutil.copyfile(config_file,\
shutil.copyfile(config_file,
os.path.join(RGWSupportBundle._tmp_src, RGW_CONF_FILE))
else:
Log.error(f"{config_file} is not present."
f"Skipping the config file collection for {COMPONENT_NAME}")
Log.error(f"{config_file} is not present. Skipping the config file"
f" collection for {COMPONENT_NAME}")

# add cortx components rpm version
cmd = "rpm -qa | grep cortx"
output, _, rc = SimpleProcess(cmd).run()
if rc == 0:
infile = os.path.join(RGWSupportBundle._tmp_src, 'cortx-rpms')
with open(infile, 'w') as fin:
fin.write(output.decode("utf-8"))

# copy rgw client log files
log_dir = os.path.join(log_base, f'rgw/{machine_id}')
Expand All @@ -79,14 +90,19 @@ class RGWSupportBundle:
outfile = os.path.join(RGWSupportBundle._tmp_src, file)
if os.path.isfile(infile):
shutil.copyfile(infile, outfile)
list_of_files = filter(lambda f: os.path.isfile(os.path.join(
RGWSupportBundle._tmp_src, f)), os.listdir(RGWSupportBundle._tmp_src))
list_of_files = sorted(list_of_files,
key=lambda f: os.path.getmtime(os.path.join(RGWSupportBundle._tmp_src, f)),
reverse=True)

if duration:
# files in delim_space use timestamp syntax as
# [2022-07-10 05:26:57,579] & 2022-07-10 05:26:34
# files in delim_T use timestamp syntax as
# 2022-07-10T05:28:35.570+0000
from cortx.utils.support_framework.log_filters import FilterLog
from cortx.rgw.const import ( RGW_STARTUP_LOG, RGW_SETUP_LOG,
from cortx.rgw.const import (RGW_STARTUP_LOG, RGW_SETUP_LOG,
RGW_SUPPORT_BUNDLE_LOG, RADOSGW_ADMIN_LOG, RGW_1_LOG,
LOG_DATE_REGEX, LOG_TIME_REGEX, DATETIME_DATE_REGEX,
DATETIME_TIME_REGEX)
Expand Down Expand Up @@ -117,32 +133,95 @@ class RGWSupportBundle:
else:
Log.error("RGW log file does not exists hence skipping log file collection.")

# copy addb logs
addb_log_path = os.path.join(log_dir, 'addb_files-*')
addb_dirs = glob.glob(addb_log_path)
for addb_dir in addb_dirs:
shutil.copytree(addb_dir,
os.path.join(RGWSupportBundle._tmp_src, addb_dir.split('/')[-1]))
# apply truncate logic on all textual logs
if size_limit:
from cortx.utils.support_framework.log_filters import FilterLog
# Move all files to temp dir and apply size filter on it.
tmp_client_log_dir = os.path.join('/tmp', 'rgw_log')
os.makedirs(tmp_client_log_dir, exist_ok=True)
for file in os.listdir(RGWSupportBundle._tmp_src):
shutil.move(os.path.join(RGWSupportBundle._tmp_src, file),
os.path.join(tmp_client_log_dir, file))
Log.info(f'Size_limit allocated to {COMPONENT_NAME} is {size_limit}')
size_limit_in_byte = FilterLog._get_size_in_bytes(size_limit)
dest_dir_size = RGWSupportBundle._get_dir_size(
RGWSupportBundle._tmp_src)
client_log_dir_size = RGWSupportBundle._get_dir_size(tmp_client_log_dir)
Log.info(f'Total size of textual log files: {client_log_dir_size}')
remaining_size_limit = size_limit_in_byte - dest_dir_size
Log.info(f'Remaining size_limit for textual log : {remaining_size_limit}')
if (remaining_size_limit >= client_log_dir_size):
for file_regex in ['rgw*', 'radosgw*']:
RGWSupportBundle._apply_size_filter(
tmp_client_log_dir, size_limit, file_regex)
else:
tmp_svc_files_dir = os.path.join('/tmp', 'rgw_svc_log')
os.makedirs(tmp_svc_files_dir, exist_ok=True)
for file in os.listdir(tmp_client_log_dir):
if file.startswith('rgw-') and file.endswith('.log'):
shutil.move(os.path.join(tmp_client_log_dir, file),
os.path.join(tmp_svc_files_dir, file))
# Allocate 95% of size from total size_limit to
# rgw svc log(rgw-1.log) files
allocated_size = str(((
size_limit_in_byte * SVC_FILE_PERCENTAGE)/100)) + 'B'
Log.info(f'Allocated size for {COMPONENT_NAME} svc files:{allocated_size}')
RGWSupportBundle._apply_size_filter(
tmp_svc_files_dir, allocated_size, 'rgw-*',
check_remaining_size=False)
RGWSupportBundle._copy_compressed_files(tmp_client_log_dir,
allocated_size)
RGWSupportBundle._apply_size_filter(
tmp_client_log_dir, size_limit, 'rgw_*')
RGWSupportBundle._apply_size_filter(
tmp_client_log_dir, size_limit, 'radosgw*')
shutil.rmtree(tmp_svc_files_dir)
shutil.rmtree(tmp_client_log_dir)

# copy motr trace log files
motr_trace_dir = os.path.join(log_dir, 'motr_trace_files')
if os.path.exists(motr_trace_dir):
tmp_motr_trace_dir = os.path.join(RGWSupportBundle._tmp_src, 'motr_trace_files')
# include the latest 5 log files of motr traces in support bundle
list_of_files = filter(lambda f: os.path.isfile(os.path.join(motr_trace_dir, f)),
os.listdir(motr_trace_dir))
# sort the files based on last modification time
list_of_files = sorted(list_of_files,
key = lambda f: os.path.getmtime(os.path.join(motr_trace_dir, f)),
reverse=True)
list_of_files = list_of_files[0:5]
for file in list_of_files:
latest_files = RGWSupportBundle._get_latest_files(directory=motr_trace_dir)
Log.info('Bundle motr trace files')
for file in latest_files:
infile = os.path.join(motr_trace_dir, file)
tmp_motr_trace_dir = os.path.join(RGWSupportBundle._tmp_src, 'motr_trace_files')
os.makedirs(tmp_motr_trace_dir, exist_ok=True)
outfile = os.path.join(tmp_motr_trace_dir, file)
# Convert m0trace file to human readable yaml format
SimpleProcess(f'm0trace -i {infile} -Y -o {outfile}.yaml').run()
SimpleProcess(f'xz {outfile}.yaml').run() # compress the output file
if size_limit:
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
RGWSupportBundle._tmp_src, size_limit)
if (os.path.getsize(infile) >= FilterLog._get_size_in_bytes(
remaining_size_limit)):
Log.info('size_limit is full.')
break
os.makedirs(tmp_motr_trace_dir, exist_ok=True)
# Convert m0trace file to human readable yaml format
SimpleProcess(f'm0trace -i {infile} -Y -o {outfile}.yaml').run()
SimpleProcess(f'xz {outfile}.yaml').run() # compress the output file
else:
os.makedirs(tmp_motr_trace_dir, exist_ok=True)
# Convert m0trace file to human readable yaml format
SimpleProcess(f'm0trace -i {infile} -Y -o {outfile}.yaml').run()
SimpleProcess(f'xz {outfile}.yaml').run() # compress the output file

# copy addb logs
addb_log_path = os.path.join(log_dir, 'addb_files-*')
addb_dirs = glob.glob(addb_log_path)
for addb_dir in addb_dirs:
Log.info('Bundle addb files.')
if size_limit:
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
RGWSupportBundle._tmp_src, size_limit)
if (os.path.getsize(addb_dir) >= FilterLog._get_size_in_bytes(
remaining_size_limit)):
Log.info('size_limit is full.')
break
shutil.copytree(addb_dir, os.path.join(
RGWSupportBundle._tmp_src, addb_dir.split('/')[-1]))
else:
shutil.copytree(addb_dir, os.path.join(
RGWSupportBundle._tmp_src, addb_dir.split('/')[-1]))

if stacktrace:
from subprocess import check_output
Expand All @@ -164,21 +243,100 @@ class RGWSupportBundle:

# copy ceph crash-dump files
if coredumps:
crash_dump_dir = os.path.join(log_dir, RGW_CORE_FILE_DIR_NAME)
crash_dump_dir = os.path.join(log_dir, 'rgw_debug')
dest_dump_dir = os.path.join(RGWSupportBundle._tmp_src, 'rgw_debug')
if os.path.exists(crash_dump_dir):
shutil.copytree(crash_dump_dir,\
os.path.join(RGWSupportBundle._tmp_src, RGW_CORE_FILE_DIR_NAME))
if size_limit:
# get latest 2 core dumps
latest_cores = RGWSupportBundle._get_latest_files(
directory=crash_dump_dir, number_of_files=2)
if len(latest_cores) == 2:
remaining_size = RGWSupportBundle._get_remaining_folder_size(
RGWSupportBundle._tmp_src, size_limit)
# check if sum of both cores is less than remaining size else collect only one
sum_of_core_size = os.path.getsize(
os.path.join(crash_dump_dir,latest_cores[0])) + \
os.path.getsize(
os.path.join(crash_dump_dir,latest_cores[1]))
# select only one core if both cores exceed the size limit
if sum_of_core_size > int(remaining_size[:-1]):
latest_cores.pop()
if not os.path.exists(dest_dump_dir):
os.mkdir(dest_dump_dir)
for file_name in latest_cores:
shutil.copyfile(os.path.join(crash_dump_dir, file_name),
os.path.join(dest_dump_dir, file_name))
else:
shutil.copytree(crash_dump_dir, dest_dump_dir)

# add cortx components rpm version
cmd = "rpm -qa | grep cortx"
output, _, rc = SimpleProcess(cmd).run()
if rc == 0:
infile = os.path.join(RGWSupportBundle._tmp_src, 'cortx-rpms')
with open(infile, 'w') as fin:
fin.write(output.decode("utf-8"))
RGWSupportBundle._generate_tar(bundle_id, target_path)
RGWSupportBundle._cleanup()

@staticmethod
def _get_dir_size(dir_path: str):
"""Calculate total directory size."""
dir_size = 0
for path, _, files in os.walk(dir_path):
for file in files:
file_path = os.path.join(path, file)
dir_size += os.path.getsize(file_path)
return dir_size

@staticmethod
def _apply_size_filter(src_dir: str, size_limit: str, file_name_reg_ex: str,
check_remaining_size: bool = True):
"""Apply limit_size filter on given files."""
Log.info(f'Bundle {file_name_reg_ex} files.')
remaining_size_limit = size_limit
if check_remaining_size:
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
RGWSupportBundle._tmp_src, size_limit)
if remaining_size_limit == '0B':
Log.info(f'size_limit is full for {file_name_reg_ex} files.')
return
FilterLog.limit_size(src_dir=src_dir, dest_dir=RGWSupportBundle._tmp_src,
size=remaining_size_limit, file_name_reg_ex=file_name_reg_ex)

@staticmethod
def _copy_compressed_files(src_dir: str, size_limit: str):
"""Copy compressed log files in support bundle."""
for file_path in glob.glob(os.path.join(src_dir, 'rgw-*.gz')):
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
RGWSupportBundle._tmp_src, size_limit)
file_size = os.path.getsize(file_path)
if FilterLog._get_size_in_bytes(remaining_size_limit) < file_size:
Log.info('size_limit is full.')
break
shutil.move(file_path, os.path.join(
RGWSupportBundle._tmp_src, os.path.basename(file_path)))

@staticmethod
def _get_remaining_folder_size(directory: str, size_limit: str) -> str:
"""Returns remaining size of log directory in bytes. eg '3B', '100B', '0B'."""
current_size = 0
for path, _, files in os.walk(directory):
for file in files:
file_path = os.path.join(path, file)
current_size += os.path.getsize(file_path)
size_limit_in_byte = FilterLog._get_size_in_bytes(size_limit)
remaining_size_in_byte = size_limit_in_byte - current_size
if remaining_size_in_byte < 0:
return '0B'
else:
return str(remaining_size_in_byte) + 'B'

@staticmethod
def _get_latest_files(directory: str, number_of_files: int = 5) -> list:
all_files = filter(
lambda f: os.path.isfile(os.path.join(directory, f)),
os.listdir(directory))
# sort the files based on last modification time
sorted_files = sorted(
all_files,
key=lambda f: os.path.getmtime(os.path.join(directory, f)),
reverse=True)
return sorted_files[0:number_of_files]

@staticmethod
def _generate_tar(bundle_id: str, target_path: str):
""" Generate tar.gz file at given path """
Expand All @@ -188,7 +346,8 @@ class RGWSupportBundle:
if not os.path.exists(target_path):
os.makedirs(target_path)
with tarfile.open(tar_file_name, 'w:gz') as tar:
tar.add(RGWSupportBundle._tmp_src,
tar.add(
RGWSupportBundle._tmp_src,
arcname=os.path.basename(RGWSupportBundle._tmp_src))

@staticmethod
Expand Down