Skip to content
This repository was archived by the owner on Feb 8, 2024. It is now read-only.

Commit c3f69f2

Browse files
CORTX-33037: apply support bundle filter limit_size (#114)
* CORTX-33037: apply support bundle filter limit_size Signed-off-by: Shriya Deshmukh <[email protected]> Signed-off-by: rohit-k-dwivedi <[email protected]> Co-authored-by: Shriya Deshmukh <[email protected]>
1 parent fd95102 commit c3f69f2

File tree

2 files changed

+213
-42
lines changed

2 files changed

+213
-42
lines changed

src/rgw/const.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -178,6 +178,9 @@
178178
SVC_THREAD_POOL_SIZE_KEY = f'{COMPONENT_NAME} thread pool size'
179179
SVC_CONCURRENT_MAX_REQ_KEY = f'{COMPONENT_NAME} max concurrent request'
180180

181+
# Support Bundle
182+
SVC_FILE_PERCENTAGE = 95
183+
181184
class RgwEndpoint(Enum):
182185
"""Enum class to define rgw endpoints provided by hare."""
183186

src/rgw/support/rgw_support_bundle

Lines changed: 210 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -29,8 +29,10 @@ from cortx.utils.log import Log
2929
from cortx.utils.errors import BaseError
3030
from cortx.utils.process import SimpleProcess
3131
from cortx.utils.conf_store.conf_store import Conf, MappedConf
32+
from cortx.utils.support_framework.log_filters import FilterLog
3233
from cortx.rgw.const import (
33-
CONFIG_PATH_KEY, LOG_PATH_KEY, COMPONENT_NAME, RGW_CONF_FILE, RGW_CORE_FILE_DIR_NAME)
34+
CONFIG_PATH_KEY, LOG_PATH_KEY, COMPONENT_NAME, RGW_CONF_FILE,
35+
SVC_FILE_PERCENTAGE)
3436

3537

3638
class SupportBundleError(BaseError):
@@ -50,7 +52,8 @@ class RGWSupportBundle:
5052
Log.info(f"{COMPONENT_NAME} support bundle generation started!!")
5153
coredumps = filters.get('coredumps', False)
5254
stacktrace = filters.get('stacktrace', False)
53-
duration = filters.get('duration',False)
55+
duration = filters.get('duration', False)
56+
size_limit = filters.get('size_limit')
5457
machine_id = Conf.machine_id
5558
cortx_config_store = MappedConf(cluster_conf)
5659
log_base = cortx_config_store.get(LOG_PATH_KEY)
@@ -63,11 +66,20 @@ class RGWSupportBundle:
6366
os.makedirs(RGWSupportBundle._tmp_src, exist_ok=True)
6467
# copy configuration files
6568
if os.path.exists(config_file):
66-
shutil.copyfile(config_file,\
69+
shutil.copyfile(config_file,
6770
os.path.join(RGWSupportBundle._tmp_src, RGW_CONF_FILE))
6871
else:
69-
Log.error(f"{config_file} is not present."
70-
f"Skipping the config file collection for {COMPONENT_NAME}")
72+
Log.error(f"{config_file} is not present. Skipping the config file"
73+
f" collection for {COMPONENT_NAME}")
74+
75+
# add cortx components rpm version
76+
cmd = "rpm -qa | grep cortx"
77+
output, _, rc = SimpleProcess(cmd).run()
78+
if rc == 0:
79+
infile = os.path.join(
80+
RGWSupportBundle._tmp_src, 'installed-cortx-rpms.txt')
81+
with open(infile, 'w') as fin:
82+
fin.write(output.decode("utf-8"))
7183

7284
# copy rgw client log files
7385
log_dir = os.path.join(log_base, f'rgw/{machine_id}')
@@ -86,7 +98,7 @@ class RGWSupportBundle:
8698
# files in delim_T use timestamp syntax as
8799
# 2022-07-10T05:28:35.570+0000
88100
from cortx.utils.support_framework.log_filters import FilterLog
89-
from cortx.rgw.const import ( RGW_STARTUP_LOG, RGW_SETUP_LOG,
101+
from cortx.rgw.const import (RGW_STARTUP_LOG, RGW_SETUP_LOG,
90102
RGW_SUPPORT_BUNDLE_LOG, RADOSGW_ADMIN_LOG, RGW_1_LOG,
91103
LOG_DATE_REGEX, LOG_TIME_REGEX, DATETIME_DATE_REGEX,
92104
DATETIME_TIME_REGEX)
@@ -117,32 +129,60 @@ class RGWSupportBundle:
117129
else:
118130
Log.error("RGW log file does not exists hence skipping log file collection.")
119131

120-
# copy addb logs
121-
addb_log_path = os.path.join(log_dir, 'addb_files-*')
122-
addb_dirs = glob.glob(addb_log_path)
123-
for addb_dir in addb_dirs:
124-
shutil.copytree(addb_dir,
125-
os.path.join(RGWSupportBundle._tmp_src, addb_dir.split('/')[-1]))
132+
# If size_limit filter is applied then,
133+
# 1. Check total log size of all textual log files.
134+
# 2. Calculate remaining size of src_dir after copying RPMs and conf file.
135+
# if remaining_size of src_dir is greater than the textual log size
136+
# then copy all files. otherwise allocate 95% of remaining_size to
137+
# rgw service logs(rgw-*) and then add other files in SB if it fits
138+
# withing the size_limit.
139+
if size_limit:
140+
from cortx.utils.support_framework.log_filters import FilterLog
141+
# Move all files to temp dir and apply size filter on it.
142+
tmp_client_log_dir = os.path.join('/tmp', 'rgw_log')
143+
os.makedirs(tmp_client_log_dir, exist_ok=True)
144+
for file in os.listdir(RGWSupportBundle._tmp_src):
145+
shutil.move(os.path.join(RGWSupportBundle._tmp_src, file),
146+
os.path.join(tmp_client_log_dir, file))
147+
Log.info(f'Size_limit allocated to {COMPONENT_NAME} is {size_limit}')
148+
size_limit_in_byte = FilterLog._get_size_in_bytes(size_limit)
149+
dest_dir_size = RGWSupportBundle._get_dir_size(
150+
RGWSupportBundle._tmp_src)
151+
client_log_dir_size = RGWSupportBundle._get_dir_size(tmp_client_log_dir)
152+
Log.info(f'Total size of textual log files: {client_log_dir_size}')
153+
remaining_size_limit = size_limit_in_byte - dest_dir_size
154+
Log.info(f'Remaining size_limit for textual log : {remaining_size_limit}')
155+
if (remaining_size_limit >= client_log_dir_size):
156+
for file_regex in ['rgw*', 'radosgw*']:
157+
RGWSupportBundle._apply_size_filter(
158+
tmp_client_log_dir, size_limit, file_regex)
159+
else:
160+
tmp_svc_files_dir = os.path.join('/tmp', 'rgw_svc_log')
161+
os.makedirs(tmp_svc_files_dir, exist_ok=True)
162+
for file in os.listdir(tmp_client_log_dir):
163+
if file.startswith('rgw-') and file.endswith('.log'):
164+
shutil.move(os.path.join(tmp_client_log_dir, file),
165+
os.path.join(tmp_svc_files_dir, file))
126166

127-
# copy motr trace log files
128-
motr_trace_dir = os.path.join(log_dir, 'motr_trace_files')
129-
if os.path.exists(motr_trace_dir):
130-
# include the latest 5 log files of motr traces in support bundle
131-
list_of_files = filter(lambda f: os.path.isfile(os.path.join(motr_trace_dir, f)),
132-
os.listdir(motr_trace_dir))
133-
# sort the files based on last modification time
134-
list_of_files = sorted(list_of_files,
135-
key = lambda f: os.path.getmtime(os.path.join(motr_trace_dir, f)),
136-
reverse=True)
137-
list_of_files = list_of_files[0:5]
138-
for file in list_of_files:
139-
infile = os.path.join(motr_trace_dir, file)
140-
tmp_motr_trace_dir = os.path.join(RGWSupportBundle._tmp_src, 'motr_trace_files')
141-
os.makedirs(tmp_motr_trace_dir, exist_ok=True)
142-
outfile = os.path.join(tmp_motr_trace_dir, file)
143-
# Convert m0trace file to human readable yaml format
144-
SimpleProcess(f'm0trace -i {infile} -Y -o {outfile}.yaml').run()
145-
SimpleProcess(f'xz {outfile}.yaml').run() # compress the output file
167+
# Allocate 95% of size from total size_limit to
168+
# rgw svc log(rgw-1.log) files
169+
allocated_size = str(((
170+
size_limit_in_byte * SVC_FILE_PERCENTAGE)/100)) + 'B'
171+
Log.info(f'Allocated size for {COMPONENT_NAME} svc files:{allocated_size}')
172+
RGWSupportBundle._apply_size_filter(
173+
tmp_svc_files_dir, allocated_size, 'rgw-*',
174+
check_remaining_size=False)
175+
RGWSupportBundle._copy_compressed_files(
176+
tmp_client_log_dir, allocated_size)
177+
RGWSupportBundle._apply_size_filter(
178+
tmp_client_log_dir, size_limit, 'rgw_*')
179+
RGWSupportBundle._apply_size_filter(
180+
tmp_client_log_dir, size_limit, 'radosgw*')
181+
shutil.rmtree(tmp_svc_files_dir)
182+
shutil.rmtree(tmp_client_log_dir)
183+
184+
RGWSupportBundle._collect_motr_trace_files(size_limit, log_dir)
185+
RGWSupportBundle._collect_addb_files(size_limit, log_dir)
146186

147187
if stacktrace:
148188
from subprocess import check_output
@@ -164,21 +204,148 @@ class RGWSupportBundle:
164204

165205
# copy ceph crash-dump files
166206
if coredumps:
167-
crash_dump_dir = os.path.join(log_dir, RGW_CORE_FILE_DIR_NAME)
207+
crash_dump_dir = os.path.join(log_dir, 'rgw_debug')
208+
dest_dump_dir = os.path.join(RGWSupportBundle._tmp_src, 'rgw_debug')
168209
if os.path.exists(crash_dump_dir):
169-
shutil.copytree(crash_dump_dir,\
170-
os.path.join(RGWSupportBundle._tmp_src, RGW_CORE_FILE_DIR_NAME))
210+
if size_limit:
211+
# get latest 2 core dumps
212+
latest_cores = RGWSupportBundle._get_latest_files(
213+
directory=crash_dump_dir, number_of_files=2)
214+
if len(latest_cores) == 2:
215+
remaining_size = RGWSupportBundle._get_remaining_folder_size(
216+
RGWSupportBundle._tmp_src, size_limit)
217+
# check if sum of both cores is less than remaining size else collect only one
218+
sum_of_core_size = os.path.getsize(
219+
os.path.join(crash_dump_dir, latest_cores[0])) + \
220+
os.path.getsize(
221+
os.path.join(crash_dump_dir, latest_cores[1]))
222+
# select only one core if both cores exceed the size limit
223+
if sum_of_core_size > int(remaining_size[:-1]):
224+
latest_cores.pop()
225+
if not os.path.exists(dest_dump_dir):
226+
os.mkdir(dest_dump_dir)
227+
for file_name in latest_cores:
228+
shutil.copyfile(os.path.join(crash_dump_dir, file_name),
229+
os.path.join(dest_dump_dir, file_name))
230+
else:
231+
shutil.copytree(crash_dump_dir, dest_dump_dir)
171232

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

236+
@staticmethod
237+
def _get_dir_size(dir_path: str):
238+
"""Calculate total directory size."""
239+
dir_size = 0
240+
for path, _, files in os.walk(dir_path):
241+
for file in files:
242+
file_path = os.path.join(path, file)
243+
dir_size += os.path.getsize(file_path)
244+
return dir_size
245+
246+
@staticmethod
247+
def _apply_size_filter(src_dir: str, size_limit: str, file_name_reg_ex: str,
248+
check_remaining_size: bool = True):
249+
"""Apply limit_size filter on given files."""
250+
Log.info(f'Bundle {file_name_reg_ex} files.')
251+
remaining_size_limit = size_limit
252+
if check_remaining_size:
253+
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
254+
RGWSupportBundle._tmp_src, size_limit)
255+
if remaining_size_limit == '0B':
256+
Log.warn('Exhausted support bundle size limit while '
257+
f'collecting {file_name_reg_ex} files.')
258+
return
259+
FilterLog.limit_size(src_dir=src_dir, dest_dir=RGWSupportBundle._tmp_src,
260+
size=remaining_size_limit, file_name_reg_ex=file_name_reg_ex)
261+
262+
@staticmethod
263+
def _copy_compressed_files(src_dir: str, size_limit: str):
264+
"""Copy compressed log files in support bundle."""
265+
for file_path in glob.glob(os.path.join(src_dir, 'rgw-*.gz')):
266+
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
267+
RGWSupportBundle._tmp_src, size_limit)
268+
file_size = os.path.getsize(file_path)
269+
if int(remaining_size_limit[:-1]) < file_size:
270+
Log.warn('Exhausted support bundle size limit while '
271+
'collecting compressed log files.')
272+
break
273+
shutil.move(file_path, os.path.join(
274+
RGWSupportBundle._tmp_src, os.path.basename(file_path)))
275+
276+
@staticmethod
277+
def _collect_addb_files(size_limit: str, log_dir: str):
278+
"""Collect addb-files in support bundle."""
279+
# copy addb logs
280+
addb_log_path = os.path.join(log_dir, 'addb_files-*')
281+
addb_dirs = glob.glob(addb_log_path)
282+
remaining_size_limit = 0
283+
for addb_dir in addb_dirs:
284+
Log.info('Bundle addb files.')
285+
if size_limit:
286+
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
287+
RGWSupportBundle._tmp_src, size_limit)
288+
if (os.path.getsize(addb_dir) > int(remaining_size_limit[:-1])):
289+
Log.warn('Exhausted support bundle size limit while '
290+
'collecting addb files.')
291+
break
292+
shutil.copytree(addb_dir, os.path.join(
293+
RGWSupportBundle._tmp_src, addb_dir.split('/')[-1]))
294+
295+
@staticmethod
296+
def _collect_motr_trace_files(size_limit: str, log_dir: str):
297+
"""Collect motr trace files in support bundle."""
298+
# copy motr trace log files
299+
motr_trace_dir = os.path.join(log_dir, 'motr_trace_files')
300+
if not os.path.exists(motr_trace_dir):
301+
return
302+
tmp_motr_trace_dir = os.path.join(RGWSupportBundle._tmp_src, 'motr_trace_files')
303+
remaining_size_limit = 0
304+
# include the latest 5 log files of motr traces in support bundle
305+
latest_files = RGWSupportBundle._get_latest_files(directory=motr_trace_dir)
306+
Log.info('Bundle motr trace files')
307+
for file in latest_files:
308+
infile = os.path.join(motr_trace_dir, file)
309+
outfile = os.path.join(tmp_motr_trace_dir, file)
310+
if size_limit:
311+
remaining_size_limit = RGWSupportBundle._get_remaining_folder_size(
312+
RGWSupportBundle._tmp_src, size_limit)
313+
if (os.path.getsize(infile) > int(remaining_size_limit[:-1])):
314+
Log.warn('Exhausted support bundle size limit while '
315+
'collecting motr-trace files.')
316+
break
317+
os.makedirs(tmp_motr_trace_dir, exist_ok=True)
318+
# Convert m0trace file to human readable yaml format
319+
SimpleProcess(f'm0trace -i {infile} -Y -o {outfile}.yaml').run()
320+
SimpleProcess(f'xz {outfile}.yaml').run() # compress the output file
321+
322+
@staticmethod
323+
def _get_remaining_folder_size(directory: str, size_limit: str) -> str:
324+
"""Returns remaining size of log directory in bytes. eg '3B', '100B', '0B'."""
325+
current_size = 0
326+
for path, _, files in os.walk(directory):
327+
for file in files:
328+
file_path = os.path.join(path, file)
329+
current_size += os.path.getsize(file_path)
330+
size_limit_in_byte = FilterLog._get_size_in_bytes(size_limit)
331+
remaining_size_in_byte = size_limit_in_byte - current_size
332+
if remaining_size_in_byte < 0:
333+
return '0B'
334+
else:
335+
return str(remaining_size_in_byte) + 'B'
336+
337+
@staticmethod
338+
def _get_latest_files(directory: str, number_of_files: int = 5) -> list:
339+
all_files = filter(
340+
lambda f: os.path.isfile(os.path.join(directory, f)),
341+
os.listdir(directory))
342+
# sort the files based on last modification time
343+
sorted_files = sorted(
344+
all_files,
345+
key=lambda f: os.path.getmtime(os.path.join(directory, f)),
346+
reverse=True)
347+
return sorted_files[0:number_of_files]
348+
182349
@staticmethod
183350
def _generate_tar(bundle_id: str, target_path: str):
184351
""" Generate tar.gz file at given path """
@@ -188,7 +355,8 @@ class RGWSupportBundle:
188355
if not os.path.exists(target_path):
189356
os.makedirs(target_path)
190357
with tarfile.open(tar_file_name, 'w:gz') as tar:
191-
tar.add(RGWSupportBundle._tmp_src,
358+
tar.add(
359+
RGWSupportBundle._tmp_src,
192360
arcname=os.path.basename(RGWSupportBundle._tmp_src))
193361

194362
@staticmethod

0 commit comments

Comments
 (0)