
The current system_info plugin logs the system info for the last host in host_dirs rather than that of the active controller. It also does not capture the system info for all the nodes into its plugin output file. This update improves the system_info plugin as well implements the following improvements to rendering and substring handling improvements. 1. Improve system_info plugin capture and render. 2. Adds which controller was active at the time of the collect to the system info rendering output. 3. Improve report analysis rendering by displaying the full path to plugin and correlation files. 4. Adds string exclude support to the substring algorithm. This allows the generic string rearches like ERROR to be searched for and gathered while also allowing specific noise logs what are considered noise logs to be filtered out. 5. Create a separate SM errors substring plugin using the new exclude option. 6. Adds support for commented and empty lines in the plugins This allows for properly commented and formatted plugins. 7. Adds plugin label name error checking This allows esier debug of improperly coded plugins. 8. Fixed additional pep8 warnings. Test Plan: PASS: Verify on-system collect with --report option PASS: Verify on-system report generation PASS: Verify off-system report generation from git PASS: Verify system_info plugin collects info from all hosts PASS: Verify report displays system_info from active controller PASS: Verify handling when no active controller is detected PASS: Verify new sm_errors substring plugin with excludes PASS: Verify plugins can have empty or # commented lines PASS: Verify report tool plugins output include path to each plugin file PASS: Verify report tool correlations include path to each correlation file PASS: Verify report tool plugin label parsing error handling PASS: Verify all files pass pep8 without warning or error Story: 2010533 Task: 48072 Change-Id: I6d0253a4c3d8804a5e45b970d766e578ea69368f Signed-off-by: Eric MacDonald <eric.macdonald@windriver.com>
187 lines
6.4 KiB
Python
187 lines
6.4 KiB
Python
########################################################################
|
|
#
|
|
# Copyright (c) 2022 - 2023 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
########################################################################
|
|
#
|
|
# This file contains the functions for the substring plugin algorithm.
|
|
#
|
|
# The substring plugin algorithm looks for a set of substrings within
|
|
# a list of log files and extracts those log messages.
|
|
#
|
|
########################################################################
|
|
|
|
from datetime import datetime
|
|
import gzip
|
|
import logging
|
|
import os
|
|
import re
|
|
import subprocess
|
|
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
def substring(start, end, substr, files, exclude_list=None):
|
|
"""Substring algorithm
|
|
Looks for all substrings in substr within files
|
|
|
|
Parameters:
|
|
start (string): Start time for analysis
|
|
end (string): End time for analysis
|
|
substr (string list): List of substrings to look for
|
|
files (string list): List of absolute filepaths to search in
|
|
exclude_list (string list): list of strings to exclude from report
|
|
Errors:
|
|
FileNotFoundError
|
|
"""
|
|
# don't analyze older files, continue with current file
|
|
CONTINUE_CURRENT = 0
|
|
# analyze older files, continue with current file
|
|
CONTINUE_CURRENT_OLD = 1
|
|
|
|
data = []
|
|
for file in files:
|
|
try:
|
|
if not os.path.exists(file):
|
|
if (re.search("controller-1_(.+)/var/log/mtcAgent.log",
|
|
file)):
|
|
continue
|
|
else:
|
|
data.append("File not found: " + file)
|
|
continue
|
|
cont = True
|
|
# Searching through file
|
|
command = (f"""grep -Ea "{'|'.join(s for s in substr)}" """
|
|
f"""{file} 2>/dev/null""")
|
|
status = _continue(start, end, file)
|
|
|
|
if (status == CONTINUE_CURRENT or
|
|
status == CONTINUE_CURRENT_OLD):
|
|
# continue with current file
|
|
if status == CONTINUE_CURRENT:
|
|
cont = False
|
|
_evaluate_substring(start, end, data, command)
|
|
|
|
# Searching through rotated log files that aren't compressed
|
|
n = 1
|
|
while os.path.exists(f"{file}.{n}") and cont:
|
|
command = (f"""grep -Ea "{'|'.join(s for s in substr)}" """
|
|
f"""{file}.{n} 2>/dev/null""")
|
|
status = _continue(start, end, f"{file}.{n}")
|
|
|
|
if (status == CONTINUE_CURRENT or
|
|
status == CONTINUE_CURRENT_OLD):
|
|
if status == CONTINUE_CURRENT:
|
|
cont = False
|
|
_evaluate_substring(start, end, data, command)
|
|
|
|
n += 1
|
|
|
|
# Searching through rotated log files
|
|
while os.path.exists(f"{file}.{n}.gz") and cont:
|
|
command = (f"""zgrep -E "{'|'.join(s for s in substr)}" """
|
|
f"""{file}.{n}.gz 2>/dev/null""")
|
|
status = _continue(start, end, f"{file}.{n}.gz",
|
|
compressed=True)
|
|
|
|
if (status == CONTINUE_CURRENT or
|
|
status == CONTINUE_CURRENT_OLD):
|
|
if status == CONTINUE_CURRENT:
|
|
cont = False
|
|
_evaluate_substring(start, end, data, command)
|
|
|
|
n += 1
|
|
|
|
except FileNotFoundError as e:
|
|
logger.error(e)
|
|
continue
|
|
|
|
# now remove any logs that contain substrings in the exclude_list
|
|
if exclude_list:
|
|
filtered_data = []
|
|
for e in data:
|
|
found = False
|
|
for exclude in exclude_list:
|
|
if e.find(exclude) != -1:
|
|
found = True
|
|
if found is False:
|
|
filtered_data.append(e)
|
|
return sorted(filtered_data)
|
|
return sorted(data)
|
|
|
|
|
|
def _continue(start, end, file, compressed=False):
|
|
"""Determines if substring algorithm should analyze the current file
|
|
and older files based on if the time of the first log message in file
|
|
is less than or greater than end and less than or greater than start
|
|
|
|
Parameters:
|
|
start (string): Start time for analysis
|
|
end (string): End time for analysis
|
|
file (string): Current file
|
|
compressed (boolean): Indicates if current file is compressed or not
|
|
"""
|
|
# don't analyze older files, continue with current file
|
|
CONTINUE_CURRENT = 0
|
|
# analyze older files, continue with current file
|
|
CONTINUE_CURRENT_OLD = 1
|
|
# don't analyze current file, continue to older files
|
|
CONTINUE_OLD = 2
|
|
|
|
# check date of first log event and compare with provided
|
|
# start, end dates
|
|
first = ""
|
|
|
|
if not compressed:
|
|
with open(file) as f:
|
|
line = f.readline()
|
|
first = line[0:19]
|
|
else:
|
|
with gzip.open(file, "rb") as f:
|
|
line = f.readline().decode("utf-8")
|
|
first = line[0:19]
|
|
try:
|
|
datetime.strptime(line[0:19], "%Y-%m-%dT%H:%M:%S")
|
|
first = line[0:19]
|
|
except ValueError:
|
|
return CONTINUE_CURRENT_OLD
|
|
|
|
if first < start:
|
|
return CONTINUE_CURRENT
|
|
elif first < end and first > start:
|
|
return CONTINUE_CURRENT_OLD
|
|
elif first > end:
|
|
return CONTINUE_OLD
|
|
|
|
|
|
def _evaluate_substring(start, end, data, command):
|
|
"""Adds log messages from output from running command to data if the
|
|
timestamp of log message in greater than start and less than end
|
|
|
|
Parameters:
|
|
start (string): Start time for analysis
|
|
end (string): End time for analysis
|
|
data (string list): Log messages extracted so far
|
|
command (string): Shell command to run
|
|
"""
|
|
p = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
|
|
for line in p.stdout:
|
|
line = line.decode("utf-8")
|
|
# different date locations for log events
|
|
dates = [line[0:19], line[2:21]]
|
|
for date in dates:
|
|
try:
|
|
datetime.strptime(date, "%Y-%m-%dT%H:%M:%S")
|
|
if date > start and date < end:
|
|
if line[0] == "|": # sm-customer.log edge case
|
|
line = line[1:].strip()
|
|
line = re.sub("\\s+", " ", line)
|
|
data.append(line)
|
|
break
|
|
except ValueError:
|
|
if date == dates[-1]:
|
|
data.append(line)
|