Eric MacDonald b6343a9e55 Improve report tool system_info plugin behavior
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>
2023-05-25 13:11:45 +00:00

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)