
A recent update introduced an empty file (__init__.py) in the plugins folder which was causing report traceback failures for off system runs. Also the current handling of the --plugin option is broken. The fix to that issue lead to a few additional more general plugin handling improvements. Test Plan: PASS: Verify ignore handling of empty plugin files. PASS: Verify all python file permissions set to executable on fresh pull in git and after on-system package install. PASS: Verify all plugin file permissions are not executable on fresh pull in git and after on-system package install. PASS: Verify general handling of the --plugin option with space delimited plugins that follow. PASS: Verify correlator is not run if there is no plugin data to correlate. PASS: Verify missing plugin output log files do not lead to a file not found error on the console. PASS: Verify refactored plugin search handling success and error paths. PASS: Verify refactored plugin search handling finds and adds built-in and localhost plugins with and without the --plugin option specified. PASS: Verify that previous plugin data is removed prior to a rerun of the tool. This is helpful for localhost plugin development. PASS: Verify handling of adding multiple plugins that span both built-in and localhost locations. PASS: Verify handling of missing plugin(s) when specified with the --plugin option. Regression: PASS: Verify collector package build and passes tox. PASS: Verify both on-system and off-system Report handling. PASS: Verify collect all using --report option PASS: Verify logging with and without --debug option. PASS: Verify no pep8 errors or warnings. Story: 2010533 Task: 48433 Task: 48432 Task: 48443 Change-Id: I42616daad2de6b0785f11736ef20b11e19f19869 Signed-off-by: Eric MacDonald <eric.macdonald@windriver.com>
210 lines
7.5 KiB
Python
Executable File
210 lines
7.5 KiB
Python
Executable File
########################################################################
|
|
#
|
|
# Copyright (c) 2022 - 2023 Wind River Systems, Inc.
|
|
#
|
|
# SPDX-License-Identifier: Apache-2.0
|
|
#
|
|
########################################################################
|
|
#
|
|
# This file contains the Plugin class.
|
|
# The Plugin class contains all the labels and information of a plugin.
|
|
#
|
|
# Plugins contain labels to instruct the execution engine what to search
|
|
# for and where to search.
|
|
#
|
|
########################################################################
|
|
|
|
from datetime import datetime
|
|
import json
|
|
import logging
|
|
import os
|
|
|
|
import algorithms
|
|
|
|
logger = logging.getLogger(__name__)
|
|
|
|
|
|
class Plugin:
|
|
def __init__(self, file="", opts=None):
|
|
"""Constructor for the Plugin class
|
|
|
|
Parameters:
|
|
file (string) : Absolute filepath of the plugin
|
|
opts (dictionary): Options from command line when running algorithm
|
|
"""
|
|
self.file = file
|
|
self.opts = opts
|
|
self.state = {
|
|
"algorithm": None,
|
|
"files": [],
|
|
"hosts": [],
|
|
"substring": [],
|
|
"exclude": [],
|
|
"alarm_exclude": [],
|
|
"entity_exclude": [],
|
|
"start": None,
|
|
"end": None,
|
|
}
|
|
logger.debug("plugin init: %s", file)
|
|
if file and os.path.isfile(file):
|
|
try:
|
|
logger.debug("calling _file_set_attributes file: %s", file)
|
|
self._file_set_attributes()
|
|
except KeyError as e:
|
|
raise e
|
|
elif opts:
|
|
logger.debug("calling _opts_set_attributes opts: %s", self.opts)
|
|
self._opts_set_attributes()
|
|
else:
|
|
logger.debug("no plugin opts specified")
|
|
|
|
try:
|
|
self.verify()
|
|
except ValueError as e:
|
|
raise e
|
|
|
|
def _file_set_attributes(self):
|
|
"""Sets plugin attributes from plugin files"""
|
|
with open(self.file) as f:
|
|
for line in f:
|
|
try:
|
|
self.extract(line)
|
|
except Exception as e:
|
|
raise e
|
|
|
|
def _opts_set_attributes(self):
|
|
"""Sets plugin attributes from command line options"""
|
|
for k, v in self.opts.items():
|
|
self.state[k] = v
|
|
|
|
def extract(self, line):
|
|
"""Extracts and sets attributes for this plugin
|
|
|
|
Parameters:
|
|
line (string): Line from plugin file to extract
|
|
"""
|
|
|
|
# allow plugins to have empty lines or comments starting with #
|
|
if len(line) <= 1 or line[0] == '#':
|
|
return
|
|
|
|
# split string from first '=', left side is label right side is value
|
|
data = line.strip().split("=", 1)
|
|
if len(data) <= 1:
|
|
raise ValueError("Value not specified for label")
|
|
label = data[0]
|
|
value = data[1]
|
|
label = label.replace(" ", "")
|
|
|
|
# ignore labels that don't start with an alphabetical char
|
|
if label[0].isalpha() is False:
|
|
raise ValueError("Invalid label value")
|
|
try:
|
|
if label == "algorithm":
|
|
self.state["algorithm"] = value.replace(" ", "")
|
|
elif label == "substring":
|
|
self.state["substring"].append(data[1])
|
|
elif label == "exclude":
|
|
self.state["exclude"].append(data[1])
|
|
elif label == "hosts":
|
|
self.state["hosts"] = value.replace(" ", "").split(",")
|
|
elif label == "alarm_exclude":
|
|
self.state["alarm_exclude"] = value.replace(" ", "").split(",")
|
|
elif label == "entity_exclude":
|
|
self.state["entity_exclude"] = value.replace(
|
|
" ", "").split(",")
|
|
elif label == "files":
|
|
self.state["files"] = value.replace(" ", "").split(",")
|
|
elif label == "start":
|
|
self.state["start"] = value
|
|
elif label == "end":
|
|
self.state["end"] = value
|
|
else:
|
|
logger.warning("unknown label: %s", label)
|
|
|
|
except KeyError:
|
|
logger.warning("unknown label: %s", label)
|
|
|
|
def verify(self):
|
|
"""Verify if this plugin's attributes are viable
|
|
|
|
Errors:
|
|
ValueError if a value is incorrectly set
|
|
"""
|
|
|
|
plugin_name = os.path.basename(self.file)
|
|
HOSTS_ERR = f"plugin: '{plugin_name}' shouldn't have 'hosts' label"
|
|
|
|
if self.state["algorithm"] == algorithms.SUBSTRING:
|
|
self.validate_state(plugin_name, "files")
|
|
self.validate_state(plugin_name, "hosts")
|
|
self.validate_state(plugin_name, "substring")
|
|
elif self.state["algorithm"] == algorithms.ALARM:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.SYSTEM_INFO:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.SWACT_ACTIVITY:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.PUPPET_ERRORS:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.PROCESS_FAILURES:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.HEARTBEAT_LOSS:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.MAINTENANCE_ERR:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.DAEMON_FAILURES:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.STATE_CHANGES:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
elif self.state["algorithm"] == algorithms.AUDIT:
|
|
if len(self.state["hosts"]) > 0:
|
|
raise ValueError(HOSTS_ERR)
|
|
|
|
try:
|
|
datetime.strptime(self.state["start"], "%Y-%m-%d %H:%M:%S")
|
|
except ValueError as e:
|
|
logger.error(
|
|
"plugin '%s' needs a valid start time in YYYY-MM-DD \
|
|
HH:MM:SS format", plugin_name)
|
|
|
|
try:
|
|
datetime.strptime(self.state["end"], "%Y-%m-%d %H:%M:%S")
|
|
except ValueError as e:
|
|
logger.error(
|
|
"plugin '%s' needs a valid end time in YYYY-MM-DD \
|
|
HH:MM:SS format", plugin_name)
|
|
|
|
else:
|
|
raise ValueError(
|
|
f"plugin: '{plugin_name}' algorithm label unsupported value: "
|
|
f"{self.state['algorithm']}"
|
|
)
|
|
|
|
for host in self.state["hosts"]:
|
|
if host not in ["controllers", "workers", "storages", "all"]:
|
|
raise ValueError(
|
|
f"hosts label has unsupported values: '{host}', "
|
|
f"accepted hosts are "
|
|
f"'controllers', 'workers', 'storages', 'all'"
|
|
)
|
|
|
|
def validate_state(self, plugin_name, key):
|
|
if len(self.state[key]) == 0:
|
|
raise ValueError(
|
|
f"plugin: {plugin_name} needs '{key}' label specified for "
|
|
f"substring algorithm"
|
|
)
|
|
|
|
def __str__(self) -> str:
|
|
return f"{json.dumps(self.state)} File: {self.file}"
|