diff --git a/pyproject.toml b/pyproject.toml index ea0e7ee..85665b3 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -28,6 +28,7 @@ system_tests_launcher = [ 'ansible>=4.10.0', 'pytest-testinfra>=6.7.0', 'pytest==7.3.1', + 'pywinrm>=0.2.2', 'ansible_runner>=2.3.2' ] unit_testing = [ diff --git a/src/wazuh_qa_framework/generic_modules/tools/configuration.py b/src/wazuh_qa_framework/generic_modules/tools/configuration.py new file mode 100644 index 0000000..e789a10 --- /dev/null +++ b/src/wazuh_qa_framework/generic_modules/tools/configuration.py @@ -0,0 +1,249 @@ +import xml.etree.ElementTree as ET +import yaml +from typing import List + + +unlimited_sections = ['localfile', 'command', 'active-response'] +xml_configuration_files = ['ossec.conf', 'agent.conf'] + + +# customize _serialize_xml to avoid lexicographical order in XML attributes +def _serialize_xml(write, elem, qnames, namespaces, + short_empty_elements, **kwargs): + tag = elem.tag + text = elem.text + if tag is ET.Comment: + write("" % text) + elif tag is ET.ProcessingInstruction: + write("" % text) + else: + tag = qnames[tag] + if tag is None: + if text: + write(ET._escape_cdata(text)) + for e in elem: + _serialize_xml(write, e, qnames, None, + short_empty_elements=short_empty_elements) + else: + write("<" + tag) + items = list(elem.items()) + if items or namespaces: + if namespaces: + for v, k in sorted(namespaces.items(), + key=lambda x: x[1]): # sort on prefix + if k: + k = ":" + k + write(" xmlns%s=\"%s\"" % ( + k, + ET._escape_attrib(v) + )) + for k, v in items: # avoid lexicographical order for XML attributes + if isinstance(k, ET.QName): + k = k.text + if isinstance(v, ET.QName): + v = qnames[v.text] + else: + v = ET._escape_attrib(v) + write(" %s=\"%s\"" % (qnames[k], v)) + if text or len(elem) or not short_empty_elements: + write(">") + if text: + write(ET._escape_cdata(text)) + for e in elem: + _serialize_xml(write, e, qnames, None, + short_empty_elements=short_empty_elements) + write("") + else: + write(" />") + if elem.tail: + write(ET._escape_cdata(elem.tail)) + + +def set_section_wazuh_conf(sections, template=None): + """ + Set a configuration in a section of Wazuh. It replaces the content if it exists. + + Args: + sections (list): List of dicts with section and new elements + section (str, optional): Section of Wazuh configuration to replace. Default `'syscheck'` + new_elements (list, optional) : List with dictionaries for settings elements in the section. Default `None` + template (list of string, optional): File content template + + Returns: + List of str: List of str with the custom Wazuh configuration. + """ + + def create_elements(section: ET.Element, elements: List): + """ + Insert new elements in a Wazuh configuration section. + + Args: + section (ET.Element): Section where the element will be inserted. + elements (list): List with the new elements to be inserted. + Returns: + ET.ElementTree: Modified Wazuh configuration. + """ + tag = None + for element in elements: + for tag_name, properties in element.items(): + tag = ET.SubElement(section, tag_name) + new_elements = properties.get('elements') + attributes = properties.get('attributes') + if attributes is not None: + for attribute in attributes: + if isinstance(attribute, dict): # noqa: E501 + for attr_name, attr_value in attribute.items(): + tag.attrib[attr_name] = str(attr_value) + if new_elements: + create_elements(tag, new_elements) + else: + tag.text = str(properties.get('value')) + attributes = properties.get('attributes') + if attributes: + for attribute in attributes: + if attribute is not None and isinstance(attribute, dict): # noqa: E501 + for attr_name, attr_value in attribute.items(): + tag.attrib[attr_name] = str(attr_value) + tag.tail = "\n " + tag.tail = "\n " + + def purge_multiple_root_elements(str_list: List[str], root_delimeter: str = "") -> List[str]: + """ + Remove from the list all the lines located after the root element ends. + + This operation is needed before attempting to convert the list to ElementTree because if the ossec.conf had more + than one `` element as root the conversion would fail. + + Args: + str_list (list or str): The content of the ossec.conf file in a list of str. + root_delimeter (str, optional: The expected string to identify when the first root element ends, + by default "" + + Returns: + list of str : The first N lines of the specified str_list until the root_delimeter is found. The rest of + the list will be ignored. + """ + line_counter = 0 + for line in str_list: + line_counter += 1 + if root_delimeter in line: + return str_list[0:line_counter] + else: + return str_list + + def to_element_tree(str_list: List[str], root_delimeter: str = "") -> ET.ElementTree: + """ + Turn a list of str into an ElementTree object. + + As ElementTree does not support xml with more than one root element this function will parse the list first with + `purge_multiple_root_elements` to ensure there is only one root element. + + Args: + str_list (list of str): A list of strings with every line of the ossec conf. + + Returns: + ElementTree: A ElementTree object with the data of the `str_list` + """ + str_list = purge_multiple_root_elements(str_list, root_delimeter) + return ET.ElementTree(ET.fromstringlist(str_list)) + + def to_str_list(elementTree: ET.ElementTree) -> List[str]: + """ + Turn an ElementTree object into a list of str. + + Args: + elementTree (ElementTree): A ElementTree object with all the data of the ossec.conf. + + Returns: + (list of str): A list of str containing all the lines of the ossec.conf. + """ + return ET.tostringlist(elementTree.getroot(), encoding="unicode") + + def find_module_config(wazuh_conf: ET.ElementTree, section: str, attributes: List[dict]) -> ET.ElementTree: + r""" + Check if a certain configuration section exists in ossec.conf and returns the corresponding block if exists. + (This extra function has been necessary to implement it to configure the wodle blocks, since they have the same + section but different attributes). + + Args: + wazuh_conf (ElementTree): An ElementTree object with all the data of the ossec.conf + section (str): Name of the tag or configuration section to search for. For example: vulnerability_detector + attributes (list of dict): List with section attributes. Needed to check if the section exists with all the + searched attributes and values. For example (wodle section) [{'name': 'syscollector'}] + Returns: + ElementTree: An ElementTree object with the section data found in ossec.conf. None if nothing was found. + """ + if attributes is None: + return wazuh_conf.find(section) + else: + attributes_query = ''.join([f"[@{attribute}='{value}']" for index, _ in enumerate(attributes) + for attribute, value in attributes[index].items()]) + query = f"{section}{attributes_query}" + + try: + return wazuh_conf.find(query) + except AttributeError: + return None + + # Generate a ElementTree representation of the previous list to work with its sections + root_delimeter = '' if '' in template else '' + wazuh_conf = to_element_tree(template, root_delimeter) + for section in sections: + attributes = section.get('attributes') + section_conf = find_module_config(wazuh_conf, section['section'], attributes) + # Create section if it does not exist, clean otherwise + if not section_conf: + section_conf = ET.SubElement(wazuh_conf.getroot(), section['section']) + section_conf.text = '\n ' + section_conf.tail = '\n\n ' + else: + # Add section if there is no limit + if section_conf.tag in unlimited_sections: + section_conf = ET.SubElement(wazuh_conf.getroot(), section['section']) + section_conf.text = '\n ' + section_conf.tail = '\n\n ' + else: + prev_text = section_conf.text + prev_tail = section_conf.tail + section_conf.clear() + section_conf.text = prev_text + section_conf.tail = prev_tail + + # Insert section attributes + if attributes: + for attribute in attributes: + if attribute is not None and isinstance(attribute, dict): # noqa: E501 + for attr_name, attr_value in attribute.items(): + section_conf.attrib[attr_name] = str(attr_value) + + # Insert elements + new_elements = section.get('elements', list()) + if new_elements: + create_elements(section_conf, new_elements) + + return to_str_list(wazuh_conf) + + +def configure_local_internal_options(new_conf): + local_internal_configuration_string = '' + for option_name, option_value in new_conf.items(): + local_internal_configuration_string += f"{str(option_name)}={str(option_value)}\n" + return local_internal_configuration_string + + +def configure_ossec_conf(new_conf, template): + new_configuration = ''.join(set_section_wazuh_conf(new_conf, template)) + return new_configuration + + +def configure_api_yaml(new_conf): + new_configuration = yaml.dump(new_conf) + return new_configuration + + +conf_functions = { + 'local_internal_options.conf': configure_local_internal_options, + 'ossec.conf': configure_ossec_conf, + 'agent.conf': configure_ossec_conf, + 'api.yaml': configure_api_yaml +} diff --git a/src/wazuh_qa_framework/system/wazuh_handler.py b/src/wazuh_qa_framework/system/wazuh_handler.py index 3fab9da..4cd620e 100644 --- a/src/wazuh_qa_framework/system/wazuh_handler.py +++ b/src/wazuh_qa_framework/system/wazuh_handler.py @@ -3,20 +3,26 @@ # This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 import os -import re +import yaml from multiprocessing.pool import ThreadPool from wazuh_qa_framework.generic_modules.logging.base_logger import BaseLogger +from wazuh_qa_framework.generic_modules.tools.configuration import conf_functions, xml_configuration_files from wazuh_qa_framework.global_variables.daemons import WAZUH_ANGENT_WINDOWS_SERVICE_NAME from wazuh_qa_framework.system.host_manager import HostManager DEFAULT_INSTALL_PATH = { 'linux': '/var/ossec', - 'windows': 'C:\\Program Files\\ossec-agent', + 'windows': 'C:/Program Files (x86)/ossec-agent', 'darwin': '/Library/Ossec' } +DEFAULT_TEMPORAL_DIRECTORY = { + 'linux': '/tmp', + 'windows': 'C:/Users/qa/AppData/Local/Temp' +} + def get_configuration_directory_path(custom_installation_path=None, os_host='linux'): installation_path = custom_installation_path if custom_installation_path else DEFAULT_INSTALL_PATH[os_host] @@ -40,7 +46,7 @@ def get_api_directory(custom_installation_path=None): def get_api_configuration_directory(custom_installation_path=None): installation_path = custom_installation_path if custom_installation_path else DEFAULT_INSTALL_PATH['linux'] - return os.path.join(get_api_directory(custom_installation_path), 'configuration') + return os.path.join(get_api_directory(installation_path), 'configuration') def get_alert_directory_path(custom_installation_path=None): @@ -68,10 +74,10 @@ def get_group_configuration_directory(custom_installation_path=None, os_host='li installation_path = custom_installation_path if custom_installation_path else DEFAULT_INSTALL_PATH[os_host] group_configuration_path = None if component == 'manager': - group_configuration_path = os.path.join(get_shared_directory_path(custom_installation_path, os_host), + group_configuration_path = os.path.join(get_shared_directory_path(installation_path, os_host), group) else: - group_configuration_path = os.path.join(get_shared_directory_path(custom_installation_path, os_host)) + group_configuration_path = os.path.join(get_shared_directory_path(installation_path, os_host)) return group_configuration_path @@ -132,7 +138,7 @@ def get_wazuh_file_path(custom_installation_path=None, os_host='linux', file_nam 'files': ['agent.conf'], 'path_calculator': lambda filename: os.path.join(get_group_configuration_directory(installation_path, os_host, - group_name=group, + group=group, component=component), filename) } @@ -340,108 +346,212 @@ def get_ruleset_directory_path(self, host): return ruleset_directory_path - def configure_host(self, host, configuration_host): + def configure_host(self, host, configuration_file, configuration_values): """Configure ossec.conf, agent.conf, api.conf and local_internal_options of specified host of the environment Configuration should fit the format expected for each configuration file: - ossec and agent.conf configuration should be provided as a list of configuration sections section. - local_internal_options configuration should be provided as a map - api.yaml should be provided as a map - Example: - local_internal_options: - remoted.debug: 2 - wazuh_modulesd.debug: 2 - ossec.conf: - - 'section': 'client', - 'elements': - - 'server': - 'elements': - - 'address': - 'value': 121.1.3.1 - agent.conf: - - 'group': 'default', - - configuration: - - 'section': 'client', - 'elements': - - 'server': - 'elements': - - 'address': - 'value': 121.1.3.1 + Examples: + - [('manager1', 'local_internal_options.conf', {'remoted.debug': '2'})] + - [('manager1', 'ossec.conf', [{'section': 'client', 'elements': [{'server': {'elements': [{'address': + {'value': '121.1.3.1'}}]}}]}])] + - [('manager1', 'agent.conf', {'group': 'default', 'configuration': + [{'section': 'client', 'elements': [{'server': {'elements': [{'address': {'value': '121.1.3.1'}}]}}]}]})] + - [('manager1', 'api.yaml', {'logs': {'level': 'debug'}})] Args: host (str): Hostname - configuration_host (Map): Map with new hosts configuration + configuration_file (str): File name to be configured + configuration_values (dict): Dictionary with the new configuration """ - pass + self.logger.debug(f"Configuring {configuration_file} in {host}") + + if configuration_file not in conf_functions: + raise Exception(f"Invalid operation for {configuration_file} configuration file. Please select one \ + of the following: {conf_functions.keys()}") + + # Get group folder and new configuration for agent.conf + group = configuration_values.get('group', 'default') if configuration_file == 'agent.conf' else None + configuration_values = (configuration_values['configuration'] if configuration_file == 'agent.conf' + else configuration_values) + + # Get configuration file path + host_configuration_file_path = self.get_file_fullpath(host, configuration_file, group) + + parameters = {'new_conf': configuration_values} + + # Get template for ossec.conf and agent.conf + if configuration_file in xml_configuration_files: + current_configuration = self.get_file_content(host, host_configuration_file_path, become=True) + parameters.update({'template': current_configuration}) + + # Set new configuration + new_configuration = conf_functions[configuration_file](**parameters) + self.modify_file_content(host, host_configuration_file_path, new_configuration, + not self.is_windows(host), self.is_windows(host)) + + self.logger.debug(f"{configuration_file} in {host} configured successfully") def configure_environment(self, configuration_hosts, parallel=True): """Configure multiple hosts at the same time. Example: - wazuh-agent1: - local_internal_options: - remoted.debug: 2 - wazuh_modulesd.debug: 2 + wazuh-manager1: + local_internal_options.conf: + remoted.debug: '2' ossec.conf: - - 'section': 'client', - 'elements': - - 'server': - 'elements': - - 'address': - 'value': 121.1.3.1 - api.yml: - .... - wazuh-agent2: + - section: client + elements: + - server: + elements: + - address: + value: 121.1.3.1 + agent.conf: + group: default + configuration: + - section: client + elements: + - server: + elements: + - address: + value: 121.1.3.1 + api.yaml: + logs: + level: debug + wazuh-agent1: ossec.conf: ... Args: configuration_host (Map): Map with new hosts configuration parallel(Boolean): Enable parallel tasks """ - pass + self.logger.info('Configuring environment') + if parallel: + host_configuration_map = [] + for host, configuration in configuration_hosts.items(): + for configuration_file, configuration_values in configuration.items(): + host_configuration_map.append((host, configuration_file, configuration_values)) + self.pool.starmap(self.configure_host, host_configuration_map) + else: + for host, configurations in configuration_hosts.items(): + for configuration_file, configuration_values in configurations.items(): + self.configure_host(host, configuration_file, configuration_values) + self.logger.info('Environment configured successfully') - def change_agents_configure_manager(self, agent_list, manager, use_manager_name=True): + def change_agents_configured_manager(self, agent_list, manager, use_manager_name=True): """Change configured manager of specified agent Args: - agent (str): Agent name. + agent_list (list): List of agents that configuration will be changed. manager (str): Manager name in the environment/Manager or IP. use_manager_name (Boolean): Replace manager name with manager IP. Default True """ - pass + self.logger.debug('Changing configured manager') + if type(agent_list) != list: + raise TypeError('Expected a list of agents') + + new_configuration = {} + new_manager = manager if use_manager_name else self.get_host_ansible_ip(manager) + + server_block = {'server': {'elements': [{'address': {'value': new_manager}}]}} + configuration = [{'section': 'client', 'elements': [server_block]}] + + for agent in agent_list: + new_configuration[agent] = { + 'ossec.conf': configuration + } + + self.configure_environment(new_configuration) + self.logger.debug('Changed configured manager successfully') - def backup_host_configuration(self, configuration_list): - """Backup specified files in + def backup_host_configuration(self, host, file, group=None): + """Backup specified files in host Args: - configuration_list (dict): Host configuration files to backup + host (str): Hostname to backup + file (str): File to backup Returns: dict: Host backup filepaths """ + self.logger.debug(f"Creating {file} backup on {host}") + backup_paths = {host: {}} + host_configuration_file_path = self.get_file_fullpath(host, file, group) + temporal_folder = DEFAULT_TEMPORAL_DIRECTORY[self.get_ansible_host_os(host)] + backup_file = os.path.join(temporal_folder, file + '.backup') + backup_paths[host][host_configuration_file_path] = backup_file - def backup_environment_configuration(self, configuration_list, parallel=True): + self.copy_file(host, host_configuration_file_path, backup_file, remote_src=True, + become=not self.is_windows(host)) + + self.logger.debug(f"Created {file} backup on {host} successfully") + return backup_paths + + def backup_environment_configuration(self, configuration_hosts, parallel=True): """Backup specified files in all hosts Args: - configuration_list (dict): Host configuration files to backup + configuration_hosts(dict): Host configuration files to backup Returns: dict: Host backup filepaths """ - pass + self.logger.info('Creating backup') + backup_configuration = [] + if parallel: + host_configuration_map = [] + for host, configuration in configuration_hosts.items(): + for file in configuration['files']: + group = configuration['group'] if file == 'agent.conf' else None + host_configuration_map.append((host, file, group)) + backup_configuration = self.pool.starmap(self.backup_host_configuration, host_configuration_map) - def restore_host_backup_configuration(self, backup_configuration): + else: + for host, configuration in configuration_hosts.items(): + for file in configuration['files']: + group = configuration['group'] if file == 'agent.conf' else None + backup_map = (self.backup_host_configuration(host, file, group)) + backup_configuration.append(backup_map) + + final_backup_configuration = {} + for backup_conf_host in backup_configuration: + for host, file in backup_conf_host.items(): + if host in final_backup_configuration: + final_backup_configuration[host].update(file) + else: + final_backup_configuration[host] = file + + self.logger.info('Created backup successfully') + return final_backup_configuration + + def restore_host_backup_configuration(self, host, dest_file, backup_file): """Restore backup configuration Args: - backup_configuration (dict): Backup configuration filepaths + host (str): Hostname to restore + dest_file (str): File to restore """ - pass + self.logger.debug(f"Restoring {dest_file} backup on {host}") + self.copy_file(host=host, dest_path=dest_file, + src_path=backup_file, remote_src=True, become=not self.is_windows(host)) + self.logger.debug(f"Restored {dest_file} backup on {host} succesfully") - def restore_environment_backup_configuration(self, backup_configuration, parallel=True): + def restore_environment_backup_configuration(self, backup_configurations, parallel=True): """Restore environment backup configuration Args: - backup_configuration (dict): Backup configuration filepaths + backup_configurations (dict): Backup configuration filepaths """ - pass + self.logger.info('Restoring backup') + if parallel: + host_configuration_map = [] + for host, files in backup_configurations.items(): + for dest_file, backup_file in files.items(): + host_configuration_map.append((host, dest_file, backup_file)) + self.pool.starmap(self.restore_host_backup_configuration, host_configuration_map) + else: + for host, files in backup_configurations.items(): + for dest_file, backup_file in files.items(): + self.restore_host_backup_configuration(host, dest_file, backup_file) + self.logger.info('Restored backup successfully') def log_search(self, host, pattern, timeout, file, escape=False, output_file='log_search_output.json'): """Search log in specified host file @@ -517,13 +627,13 @@ def restart_agent(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Restarting agent {host}') + self.logger.debug(f"Restarting agent {host}") service_name = WAZUH_ANGENT_WINDOWS_SERVICE_NAME if self.is_windows(host) else 'wazuh-agent' if self.is_agent(host): self.control_service(host, service_name, 'restarted') - self.logger.debug(f'Agent {host} restarted successfully') + self.logger.debug(f"Agent {host} restarted successfully") else: - raise ValueError(f'Host {host} is not an agent') + raise ValueError(f"Host {host} is not an agent") def restart_agents(self, agent_list=None, parallel=True): """Restart list of agents @@ -532,13 +642,13 @@ def restart_agents(self, agent_list=None, parallel=True): agent_list (list, optional): Agent list. Defaults to None. parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Restarting agents: {agent_list}') + self.logger.info(f"Restarting agents: {agent_list}") if parallel: - agent_restart_tasks = self.pool.map(self.restart_agent, agent_list) + self.pool.map(self.restart_agent, agent_list) else: for agent in agent_list: self.restart_agent(agent) - self.logger.info(f'Agents restarted successfully: {agent_list}') + self.logger.info(f"Agents restarted successfully: {agent_list}") def restart_manager(self, host): """Restart manager @@ -546,12 +656,12 @@ def restart_manager(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Restarting manager {host}') + self.logger.debug(f"Restarting manager {host}") if self.is_manager(host): self.control_service(host, 'wazuh-manager', 'restarted', become=True) - self.logger.debug(f'Manager {host} restarted successfully') + self.logger.debug(f"Manager {host} restarted successfully") else: - ValueError(f'Host {host} is not a manager') + ValueError(f"Host {host} is not a manager") def restart_managers(self, manager_list, parallel=True): """Restart managers @@ -560,13 +670,13 @@ def restart_managers(self, manager_list, parallel=True): manager_list (list): Managers list parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Restarting managers: {manager_list}') + self.logger.info(f"Restarting managers: {manager_list}") if parallel: self.pool.map(self.restart_manager, manager_list) else: for manager in manager_list: self.restart_manager(manager) - self.logger.info(f'Managers restarted successfully: {manager_list}') + self.logger.info(f"Managers restarted successfully: {manager_list}") def stop_agent(self, host): """Stop agent @@ -574,13 +684,13 @@ def stop_agent(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Stopping agent {host}') - service_name = WAZUH_ANGENT_WINDOWS_SERVICE_NAME if is_windows(host) else 'wazuh-agent' + self.logger.debug(f"Stopping agent {host}") + service_name = WAZUH_ANGENT_WINDOWS_SERVICE_NAME if self.is_windows(host) else 'wazuh-agent' if self.is_agent(host): self.control_service(host, service_name, 'stopped') - self.logger.debug(f'Agent {host} stopped successfully') + self.logger.debug(f"Agent {host} stopped successfully") else: - raise ValueError(f'Host {host} is not an agent') + raise ValueError(f"Host {host} is not an agent") def stop_agents(self, agent_list=None, parallel=True): """Stop agents @@ -589,13 +699,13 @@ def stop_agents(self, agent_list=None, parallel=True): agent_list(list, optional): Agents list. Defaults to None parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Stopping agents: {agent_list}') + self.logger.info(f"Stopping agents: {agent_list}") if parallel: self.pool.map(self.stop_agent, agent_list) else: for agent in agent_list: self.restart_agent(agent) - self.logger.info(f'Agents stopped successfully: {agent_list}') + self.logger.info(f"Agents stopped successfully: {agent_list}") def stop_manager(self, host): """Stop manager @@ -603,12 +713,12 @@ def stop_manager(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Stopping manager {host}') + self.logger.debug(f"Stopping manager {host}") if self.is_manager(host): self.control_service(host, 'wazuh-manager', 'stopped', become=True) - self.logger.debug(f'Manager {host} stopped successfully') + self.logger.debug(f"Manager {host} stopped successfully") else: - raise ValueError(f'Host {host} is not a manager') + raise ValueError(f"Host {host} is not a manager") def stop_managers(self, manager_list, parallel=True): """Stop managers @@ -617,13 +727,13 @@ def stop_managers(self, manager_list, parallel=True): manager_list (list): Managers list parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Stopping managers: {manager_list}') + self.logger.info(f"Stopping managers: {manager_list}") if parallel: self.pool.map(self.stop_manager, manager_list) else: for manager in manager_list: self.restart_manager(manager) - self.logger.info(f'Stopping managers: {manager_list}') + self.logger.info(f"Stopping managers: {manager_list}") def start_agent(self, host): """Start agent @@ -631,13 +741,13 @@ def start_agent(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Starting agent {host}') - service_name = WAZUH_ANGENT_WINDOWS_SERVICE_NAME if is_windows(host) else 'wazuh-agent' + self.logger.debug(f"Starting agent {host}") + service_name = WAZUH_ANGENT_WINDOWS_SERVICE_NAME if self.is_windows(host) else 'wazuh-agent' if self.is_agent(host): self.control_service(host, service_name, 'started') - self.logger.debug(f'Agent {host} started successfully') + self.logger.debug(f"Agent {host} started successfully") else: - raise ValueError(f'Host {host} is not an agent') + raise ValueError(f"Host {host} is not an agent") def start_agents(self, agent_list, parallel=True): """Start agents @@ -646,13 +756,13 @@ def start_agents(self, agent_list, parallel=True): agent_list (list): Agents list parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Starting agents: {agent_list}') + self.logger.info(f"Starting agents: {agent_list}") if parallel: self.pool.map(self.start_agent, agent_list) else: for agent in agent_list: self.start_agent(agent) - self.logger.info(f'Agents started successfully: {agent_list}') + self.logger.info(f"Agents started successfully: {agent_list}") def start_manager(self, host): """Start manager @@ -660,12 +770,12 @@ def start_manager(self, host): Args: host (str): Hostname """ - self.logger.debug(f'Starting manager {host}') + self.logger.debug(f"Starting manager {host}") if self.is_manager(host): self.control_service(host, 'wazuh-manager', 'started', become=True) - self.logger.debug(f'Manager {host} started successfully') + self.logger.debug(f"Manager {host} started successfully") else: - raise ValueError(f'Host {host} is not a manager') + raise ValueError(f"Host {host} is not a manager") def start_managers(self, manager_list, parallel=True): """Start managers @@ -674,13 +784,13 @@ def start_managers(self, manager_list, parallel=True): manager_list (list): Managers list parallel (bool, optional): Parallel execution. Defaults to True. """ - self.logger.info(f'Starting managers: {manager_list}') + self.logger.info(f"Starting managers: {manager_list}") if parallel: self.pool.map(self.start_manager, manager_list) else: for manager in manager_list: self.start_manager(manager) - self.logger.info(f'Managers started successfully: {manager_list}') + self.logger.info(f"Managers started successfully: {manager_list}") def restart_environment(self, parallel=True): """Restart all agents and manager in the environment @@ -727,11 +837,11 @@ def stop_environment(self, parallel=True): self.pool.map(self.stop_agent, agent_list) else: self.logger.info(message='Stopping environment: Managers') - for manager in get_managers(): + for manager in self.get_managers(): self.stop_manager(manager) self.logger.info(message='Stopping environment: Agents') - for agent in get_agents(): + for agent in self.get_agents(): self.stop_agent(agent) self.logger.info('Stopping environment') @@ -754,11 +864,11 @@ def start_environment(self, parallel=True): self.pool.map(self.start_agent, agent_list) else: self.logger.info(message='Starting environment: Managers') - for manager in get_managers(): + for manager in self.get_managers(): self.start_manager(manager) self.logger.info(message='Starting environment: Agents') - for agent in get_agents(): + for agent in self.get_agents(): self.start_agent(agent) self.logger.info('Environment started successfully')