@@ -29,8 +29,10 @@ from cortx.utils.log import Log
2929from cortx .utils .errors import BaseError
3030from cortx .utils .process import SimpleProcess
3131from cortx .utils .conf_store .conf_store import Conf , MappedConf
32+ from cortx .utils .support_framework .log_filters import FilterLog
3233from 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
3638class 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