From 8144fb8695a00cac1a97aaf7b9fcc2291211f86c Mon Sep 17 00:00:00 2001 From: Jesse S Date: Fri, 10 Nov 2023 10:54:18 -0800 Subject: [PATCH] feat: TOOLS-2995 a `generate config` output to collectinfo archive (#223) * feat: TOOLS-2995 a `generate config` output to collectinfo archive --- lib/live_cluster/client/cluster.py | 14 ++++++++-- lib/live_cluster/collectinfo_controller.py | 32 ++++++++++++++++++++++ test/e2e/util.py | 5 ++-- 3 files changed, 46 insertions(+), 5 deletions(-) diff --git a/lib/live_cluster/client/cluster.py b/lib/live_cluster/client/cluster.py index 71596ff5..4c6c1174 100644 --- a/lib/live_cluster/client/cluster.py +++ b/lib/live_cluster/client/cluster.py @@ -26,6 +26,7 @@ from lib.utils.lookup_dict import LookupDict from lib.utils import util, constants +from lib.utils.types import NodeDict from . import client_util from .node import Node @@ -179,7 +180,16 @@ def get_node_names(self, nodes=None): return node_names - def get_node_ids(self, nodes=None): + def get_node_ids(self, nodes: list[str] | None = None) -> NodeDict[str]: + """Gets node key to node id mapping + + Keyword Arguments: + nodes {list[str] | None} -- A subset of all nodes that should be returned. + If None all nodes are returned (default: {None}) + + Returns: + NodeDict[str] -- A dictionary mapping node keys (ip:port) to node ids. + """ selected_nodes = set() node_ids = {} @@ -459,7 +469,7 @@ def get_nodes( constants.NodeSelection.PRINCIPAL, constants.NodeSelection.RANDOM, ] - | list[str], + | list[str] = constants.NodeSelection.ALL, ) -> list[Node]: use_nodes = [] diff --git a/lib/live_cluster/collectinfo_controller.py b/lib/live_cluster/collectinfo_controller.py index 5b809d2f..f0380f96 100644 --- a/lib/live_cluster/collectinfo_controller.py +++ b/lib/live_cluster/collectinfo_controller.py @@ -21,6 +21,8 @@ import sys import traceback from typing import Any, Callable, Optional +from lib.live_cluster.client.node import Node +from lib.live_cluster.generate_config_controller import GenerateConfigController from lib.utils.types import NodeDict from lib.view.sheet.render import get_style_json, set_style_json @@ -799,6 +801,9 @@ async def _dump_collectinfo_sysinfo(self, as_logfile_prefix: str, fileHeader: st async def _dump_collectinfo_aerospike_conf( self, as_logfile_prefix: str, conf_path: Optional[str] = None ): + """ + Gets the static aerospike.conf if available. + """ complete_filename = as_logfile_prefix + "aerospike.conf" if not conf_path: @@ -812,6 +817,32 @@ async def _dump_collectinfo_aerospike_conf( self.logger.warning(str(e)) util.write_to_file(complete_filename, str(e)) + async def _dump_collectinfo_dynamic_aerospike_conf(self, as_logfile_prefix): + """ + Used the GenerateConfigController to get the active runtime config of the + cluster. This will include changes that have not yet been save to the static + aerospike.conf file. + """ + nodes: list[Node] = self.cluster.get_nodes() + + async def _get_aerospike_conf(self, key, id): + complete_filename = as_logfile_prefix + id + "_aerospike.conf" + line = f"-o {complete_filename} with {key}" + await GenerateConfigController().execute(line.split()) + + level = self.logger.getEffectiveLevel() + self.logger.setLevel(logging.ERROR) + results = await asyncio.gather( + *[_get_aerospike_conf(self, node.key, node.node_id) for node in nodes], + return_exceptions=True, + ) + self.logger.setLevel(level) + + for result in results: + if isinstance(result, Exception): + self.logger.error(str(result)) + continue + ########################################################################### # Collectinfo caller functions @@ -903,6 +934,7 @@ async def _run_collectinfo( self._dump_collectinfo_health(as_logfile_prefix, file_header), self._dump_collectinfo_sysinfo(as_logfile_prefix, file_header), self._dump_collectinfo_aerospike_conf(as_logfile_prefix, config_path), + self._dump_collectinfo_dynamic_aerospike_conf(as_logfile_prefix), ] for c in coroutines: diff --git a/test/e2e/util.py b/test/e2e/util.py index 7e5cf773..b49f3d22 100644 --- a/test/e2e/util.py +++ b/test/e2e/util.py @@ -138,13 +138,12 @@ async def capture_separate_and_parse_output(rc, commands): def get_collectinfo_path(cp: CompletedProcess, collectinfo_prefix: str): - collectinfo_path = None for line in reversed(cp.stderr.splitlines()): if collectinfo_prefix in line and line.startswith("INFO:"): words = line.split() for word in words: - if collectinfo_prefix in word: - print("Found collectinfo_prefix", collectinfo_path) + if collectinfo_prefix in word and ".tgz" in word: + print("Found collectinfo_prefix", word) return word raise Exception("Unable to find collectinfo path in output")