From 33bc7069e1daee67742d59eba0299fea97df7d15 Mon Sep 17 00:00:00 2001 From: Antonio Date: Tue, 19 Mar 2024 17:30:56 +0100 Subject: [PATCH] enhancement(#4843): Removing uninstall at the end, API password and user as parameter --- .../modules/testing/tests/conftest.py | 2 +- .../modules/testing/tests/helpers/__init__.py | 2 +- .../modules/testing/tests/helpers/agent.py | 2 +- .../modules/testing/tests/helpers/executor.py | 25 +- .../modules/testing/tests/helpers/generic.py | 25 +- .../modules/testing/tests/helpers/manager.py | 2 +- .../modules/testing/tests/helpers/utils.py | 344 ------------------ .../testing/tests/test_agent/__init__.py | 2 +- .../testing/tests/test_agent/test_install.py | 2 +- .../tests/test_agent/test_registration.py | 8 +- .../testing/tests/test_agent/test_restart.py | 2 +- .../testing/tests/test_agent/test_stop.py | 6 +- .../tests/test_agent/test_uninstall.py | 11 +- 13 files changed, 56 insertions(+), 377 deletions(-) delete mode 100755 deployability/modules/testing/tests/helpers/utils.py diff --git a/deployability/modules/testing/tests/conftest.py b/deployability/modules/testing/tests/conftest.py index a0e61d09b3..acab20fb7b 100755 --- a/deployability/modules/testing/tests/conftest.py +++ b/deployability/modules/testing/tests/conftest.py @@ -51,4 +51,4 @@ def targets(request) -> dict | None: def live(request) -> bool | None: live_value = request.config.getoption('live') - return live_value.lower() == 'true' if live_value else None \ No newline at end of file + return live_value.lower() == 'true' if live_value else None diff --git a/deployability/modules/testing/tests/helpers/__init__.py b/deployability/modules/testing/tests/helpers/__init__.py index 53f69d9375..ef12e0d043 100755 --- a/deployability/modules/testing/tests/helpers/__init__.py +++ b/deployability/modules/testing/tests/helpers/__init__.py @@ -1,3 +1,3 @@ from .manager import WazuhManager from .agent import WazuhAgent -from .generic import HostConfiguration, HostInformation, HostMonitor, CheckFiles \ No newline at end of file +from .generic import HostConfiguration, HostInformation, HostMonitor, CheckFiles diff --git a/deployability/modules/testing/tests/helpers/agent.py b/deployability/modules/testing/tests/helpers/agent.py index 3e4c635a61..953c6feb5d 100644 --- a/deployability/modules/testing/tests/helpers/agent.py +++ b/deployability/modules/testing/tests/helpers/agent.py @@ -236,4 +236,4 @@ def agent_status_report(wazuh_api: WazuhAPI) -> dict: Dict: Agent status report. """ response = requests.get(f"{wazuh_api.api_url}/agents/summary/status", headers=wazuh_api.headers, verify=False) - return eval(response.text)['data'] \ No newline at end of file + return eval(response.text)['data'] diff --git a/deployability/modules/testing/tests/helpers/executor.py b/deployability/modules/testing/tests/helpers/executor.py index afcd7b2e06..5f673ee6fb 100644 --- a/deployability/modules/testing/tests/helpers/executor.py +++ b/deployability/modules/testing/tests/helpers/executor.py @@ -61,26 +61,30 @@ def execute_commands(inventory_path, commands=[]) -> dict: class WazuhAPI: - def __init__(self, inventory_path): + def __init__(self, inventory_path, wazuh_user='wazuh', wazuh_password='wazuh'): self.inventory_path = inventory_path self.api_url = None self.headers = None + self.wazuh_user = wazuh_user + self.wazuh_password = self._get_wazuh_api_password() urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) self._authenticate() + + def _get_wazuh_api_password(self): + #------------Patch issue https://github.com/wazuh/wazuh-packages/issues/2883--------------------- + file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt ' + if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'): + Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar') + return Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","") + + def _authenticate(self): with open(self.inventory_path, 'r') as yaml_file: inventory_data = yaml.safe_load(yaml_file) - user = 'wazuh' - - #----Patch issue https://github.com/wazuh/wazuh-packages/issues/2883------------- - file_path = Executor.execute_command(self.inventory_path, 'pwd').replace("\n","") + '/wazuh-install-files/wazuh-passwords.txt ' - if not 'true' in Executor.execute_command(self.inventory_path, f'test -f {file_path} && echo "true" || echo "false"'): - Executor.execute_command(self.inventory_path, 'tar -xvf wazuh-install-files.tar') - password = Executor.execute_command(self.inventory_path, "grep api_password wazuh-install-files/wazuh-passwords.txt | head -n 1 | awk '{print $NF}'").replace("'","").replace("\n","") - #-------------------------------------------------------------------------------- - + user = self.wazuh_user + password = self.wazuh_password login_endpoint = 'security/user/authenticate' host = inventory_data.get('ansible_host') port = '55000' @@ -96,3 +100,4 @@ def _authenticate(self): 'Content-Type': 'application/json', 'Authorization': f'Bearer {token}' } + diff --git a/deployability/modules/testing/tests/helpers/generic.py b/deployability/modules/testing/tests/helpers/generic.py index 202f9b35e6..815aa21422 100644 --- a/deployability/modules/testing/tests/helpers/generic.py +++ b/deployability/modules/testing/tests/helpers/generic.py @@ -519,4 +519,27 @@ def isComponentActive(inventory_path, host_role) -> bool: Returns: bool: True/False """ - return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}') \ No newline at end of file + return 'active' == Executor.execute_command(inventory_path, f'systemctl is-active {host_role}') + + + +class Waits: + + @staticmethod + def dynamic_wait(expected_condition_func, cycles=10, waiting_time=10) -> None: + """ + Waits the process during assigned cycles for the assigned seconds + + Args: + expected_condition_func (lambda function): The function that returns True when the expected condition is met + cycles(int): Number of cycles + waiting_Time(int): Number of seconds per cycle + + """ + for _ in range(cycles): + if expected_condition_func(): + break + else: + time.sleep(waiting_time) + else: + raise RuntimeError(f'Time out') \ No newline at end of file diff --git a/deployability/modules/testing/tests/helpers/manager.py b/deployability/modules/testing/tests/helpers/manager.py index 8bd0e27dc4..f7b4b9a181 100644 --- a/deployability/modules/testing/tests/helpers/manager.py +++ b/deployability/modules/testing/tests/helpers/manager.py @@ -202,4 +202,4 @@ def get_manager_logs(wazuh_api: WazuhAPI) -> list: List: The logs of the manager. """ response = requests.get(f"{wazuh_api.api_url}/manager/logs", headers=wazuh_api.headers, verify=False) - return eval(response.text)['data']['affected_items'] \ No newline at end of file + return eval(response.text)['data']['affected_items'] diff --git a/deployability/modules/testing/tests/helpers/utils.py b/deployability/modules/testing/tests/helpers/utils.py deleted file mode 100755 index f73431dd12..0000000000 --- a/deployability/modules/testing/tests/helpers/utils.py +++ /dev/null @@ -1,344 +0,0 @@ -import chardet -import json -import psutil -import socket -import subprocess -import time - -from pathlib import Path - -from .constants import AGENTD_STATE, CLIENT_KEYS, WAZUH_CONTROL, AGENT_CONTROL - - -def run_command(binary: str, args: list = None) -> None: - """ - Run a Wazuh binary with the given arguments. - - Args: - binary (str): The binary to run. - args (list): The arguments to pass to the binary. - - Returns: - str: The output of the binary execution. - """ - if not args: - args = [] - - output = subprocess.run([binary] + args, stdout=subprocess.PIPE) - - return output.stdout.decode('utf-8') - - -def get_service() -> str: - """ - Retrieves the name of the Wazuh service running on the current platform. - - Returns: - str: The name of the Wazuh service. - - """ - - return run_command(WAZUH_CONTROL, ["info", "-t"]).strip() - - -def get_version() -> str: - """ - Retrieves the version of the Wazuh installation on the current platform. - - Returns: - str: The version of Wazuh installed. - - """ - - return run_command(WAZUH_CONTROL, ["info", "-v"]).strip() - - -def get_revision() -> str: - """ - Retrieves the version of the Wazuh installation on the current platform. - - Returns: - str: The version of Wazuh installed. - - """ - - return run_command(WAZUH_CONTROL, ["info", "-r"]).strip() - - -def get_service_status() -> str: - """ - Get the status of the Wazuh service. - - Returns: - str: The status of the Wazuh service. - """ - if get_service() == "agent": - service_name = "wazuh-agent" - else: - service_name = "wazuh-manager" - - return run_command("systemctl", ["is-active", service_name]).strip() - - -def get_daemons_status() -> dict: - """ - Get the status of the Wazuh daemons. - - Return: - dict: The daemons (keys) and their status(values). - """ - daemons_status = {} - - control_output = run_command(WAZUH_CONTROL, ["status"]) - - for line in control_output.split('\n'): - if "running" in line: - daemon_name = line.split(' ')[0] - status = line.replace(daemon_name, '').replace('.', '').lstrip() - daemons_status[daemon_name] = status - - return daemons_status - - -def get_registered_agents(): - """ - Get the registered agents on the manager. - - return: - list: The registered agents. - """ - registered_agents = [] - - control_output = run_command(AGENT_CONTROL, ["-l"]) - - for line in control_output.split('\n'): - if "ID:" in line: - agent_info = line.split(',') - agent_dict = { - 'ID': agent_info[0].split(':')[1].strip(), - 'Name': agent_info[1].split(':')[1].strip(), - 'IP': agent_info[2].split(':')[1].strip(), - 'Status': agent_info[3].strip() - } - registered_agents.append(agent_dict) - - return registered_agents - - -def get_agent_connection_status(agent_id: str = None, timeout: int = 60) -> str: - """ - Get the connection status of an agent. - - Args: - agent_id (str, optional): The ID of the agent. Defaults to None. - timeout (int, optional): Timeout in seconds for waiting on pending status. Defaults to 60 seconds. - - Raises: - ValueError: If the service is "server" and no agent_id is provided. - ValueError: If the agent is not found. - - Returns: - str: The connection status of the agent. - """ - if get_service() == "server" and not agent_id: - raise ValueError("Agent id is required for server service.") - - if get_service() == "server": - agent = [a for a in get_registered_agents() if a.get('ID') == agent_id] - - if not agent: - - raise ValueError("Agent not found.") - - status = agent[0].get('Status') - else: - start_time = time.time() - - while True: - agentd_output = subprocess.run( - ["sudo", "grep", "^status", AGENTD_STATE], stdout=subprocess.PIPE) - - agentd_output_decoded = agentd_output.stdout.decode('utf-8') - - status = agentd_output_decoded.split('=')[1].replace("'", "").strip() - - if status != 'pending' or (time.time() - start_time) >= timeout: - break - time.sleep(5) - - if status == 'pending': - raise TimeoutError("Timeout reached while waiting for pending status.") - - return status - - -def get_file_encoding(file_path: str) -> str: - """Detect and return the file encoding. - - Args: - file_path (str): File path to check. - - Returns: - encoding (str): File encoding. - """ - with open(file_path, 'rb') as f: - data = f.read() - if len(data) == 0: - return 'utf-8' - result = chardet.detect(data) - - return result['encoding'] - - -def file_monitor(monitored_file: str, target_string: str, timeout: int = 30) -> None: - """ - Monitor a file for a specific string. - - Args: - monitored_file (str): The file to monitor. - target_string (str): The string to look for in the file. - timeout (int, optional): The time to wait for the string to appear in the file. Defaults to 30. - - Returns: - None: Returns None if the string is not found within the timeout. - str: Returns the line containing the target string if found within the timeout. - """ - encoding = get_file_encoding(monitored_file) - - # Check in the current file content for the string. - with open(monitored_file, encoding=encoding) as _file: - for line in _file: - if target_string in line: - - return line - - # Start count to set the timeout. - start_time = time.time() - - # Start the file monitoring for future lines. - with open(monitored_file, encoding=encoding) as _file: - # Go to the end of the file. - _file.seek(0, 2) - while time.time() - start_time < timeout: - current_position = _file.tell() - line = _file.readline() - - if not line: - # No new line, wait for nex try. - _file.seek(current_position) - time.sleep(0.1) - else: - # New line, check if the string matches. - if target_string in line: - - return line - - -def check_agent_is_connected(agent_id: str, timeout: int = 60) -> bool: - """ - Wait for an agent to connect to the manager, returns true when it does. - - Args: - agent_id (str): The ID of the agent to wait for. - - Returns: - bool: True if the agent connects within the timeout, False otherwise. - """ - start_time = time.time() - - while time.time() - start_time < timeout: - status = get_agent_connection_status(agent_id) - if status in ["connected", "Active"]: - - return True - time.sleep(1) - - raise False - - -def read_json_file(filepath: str | Path) -> dict: - """ - Read a JSON file and return a dictionary. - - Args: - filepath (str, Path): Path to the JSON file. - - Returns: - dict: Dictionary with the JSON file content. - """ - with open(filepath) as f_json: - - return json.load(f_json) - - -def get_client_keys() -> list[dict]: - """ - Get the client keys from the client.keys file. - - Returns: - list: List of dictionaries with the client keys. - """ - clients = [] - with open(CLIENT_KEYS, 'r') as file: - lines = file.readlines() - for line in lines: - _id, name, address, password = line.strip().split() - client_info = { - "id": _id, - "name": name, - "address": address, - "password": password - } - clients.append(client_info) - - return clients - - -def is_port_in_use(port: int) -> bool: - """ - Check if a port is in use. - - Args: - port (int): The port to check. - - Returns: - bool: True if the port is in use, False otherwise. - """ - sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) - result = sock.connect_ex(('127.0.0.1', 80)) - sock.close() - - return True if result == 0 else False - - -def is_process_alive(process_name: str) -> bool: - """ - Check if a process is running. - - Args: - process_name (str): The name of the process to check. - - Returns: - bool: True if the process is running, False otherwise. - """ - - return process_name in (p.name() for p in psutil.process_iter()) - - -def dynamic_wait(expected_condition_func, cycles=10, waiting_time=10) -> None: - """ - Waits the process during assigned cycles for the assigned seconds - - Args: - expected_condition_func (function): The function that returns True when the expected condition is met - cycles(int): Number of cycles - waiting_Time(int): Number of seconds per cycle - - """ - for _ in range(cycles): - if expected_condition_func(): - break - else: - time.sleep(waiting_time) - else: - raise RuntimeError(f'Time out') \ No newline at end of file diff --git a/deployability/modules/testing/tests/test_agent/__init__.py b/deployability/modules/testing/tests/test_agent/__init__.py index 94f1e57d1e..139d3dc57d 100644 --- a/deployability/modules/testing/tests/test_agent/__init__.py +++ b/deployability/modules/testing/tests/test_agent/__init__.py @@ -1,3 +1,3 @@ from ..helpers.agent import WazuhAgent from ..helpers.manager import WazuhManager -from ..helpers.generic import HostConfiguration, CheckFiles, HostInformation, GeneralComponentActions \ No newline at end of file +from ..helpers.generic import HostConfiguration, CheckFiles, HostInformation, GeneralComponentActions diff --git a/deployability/modules/testing/tests/test_agent/test_install.py b/deployability/modules/testing/tests/test_agent/test_install.py index 00be163104..d32d215169 100644 --- a/deployability/modules/testing/tests/test_agent/test_install.py +++ b/deployability/modules/testing/tests/test_agent/test_install.py @@ -103,4 +103,4 @@ def test_version(wazuh_params): def test_revision(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params) \ No newline at end of file + assert wazuh_params['wazuh_revision'] in GeneralComponentActions.get_component_revision(agent_params) diff --git a/deployability/modules/testing/tests/test_agent/test_registration.py b/deployability/modules/testing/tests/test_agent/test_registration.py index 7ecb312934..35fba32078 100644 --- a/deployability/modules/testing/tests/test_agent/test_registration.py +++ b/deployability/modules/testing/tests/test_agent/test_registration.py @@ -2,8 +2,8 @@ from ..helpers.manager import WazuhManager, WazuhAPI from ..helpers.agent import WazuhAgent, WazuhAPI -from ..helpers.generic import GeneralComponentActions -from ..helpers.utils import dynamic_wait +from ..helpers.generic import GeneralComponentActions, Waits + @pytest.fixture def wazuh_params(request): @@ -67,9 +67,9 @@ def test_isActive(wazuh_params): assert GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') expected_condition_func = lambda: 'active' == WazuhAgent.get_agent_status(wazuh_api, agent_names) - dynamic_wait(expected_condition_func, cycles=10, waiting_time=20) + Waits.dynamic_wait(expected_condition_func, cycles=10, waiting_time=20) def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params) \ No newline at end of file + assert GeneralComponentActions.hasAgentClientKeys(agent_params) diff --git a/deployability/modules/testing/tests/test_agent/test_restart.py b/deployability/modules/testing/tests/test_agent/test_restart.py index 1d0d22989c..01d347ff30 100644 --- a/deployability/modules/testing/tests/test_agent/test_restart.py +++ b/deployability/modules/testing/tests/test_agent/test_restart.py @@ -65,4 +65,4 @@ def test_isActive(wazuh_params): def test_clientKeys(wazuh_params): for agent_names, agent_params in wazuh_params['agents'].items(): - assert GeneralComponentActions.hasAgentClientKeys(agent_params) \ No newline at end of file + assert GeneralComponentActions.hasAgentClientKeys(agent_params) diff --git a/deployability/modules/testing/tests/test_agent/test_stop.py b/deployability/modules/testing/tests/test_agent/test_stop.py index f661afdd7c..08c97b00f3 100644 --- a/deployability/modules/testing/tests/test_agent/test_stop.py +++ b/deployability/modules/testing/tests/test_agent/test_stop.py @@ -1,8 +1,8 @@ import pytest from ..helpers.agent import WazuhAgent, WazuhAPI -from ..helpers.generic import GeneralComponentActions -from ..helpers.utils import dynamic_wait +from ..helpers.generic import GeneralComponentActions, Waits + @pytest.fixture def wazuh_params(request): @@ -57,4 +57,4 @@ def test_stop(wazuh_params): assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) - dynamic_wait(expected_condition_func, cycles=10, waiting_time=20) \ No newline at end of file + Waits.dynamic_wait(expected_condition_func, cycles=10, waiting_time=20) diff --git a/deployability/modules/testing/tests/test_agent/test_uninstall.py b/deployability/modules/testing/tests/test_agent/test_uninstall.py index 1fbd89f5ad..06acc245e3 100644 --- a/deployability/modules/testing/tests/test_agent/test_uninstall.py +++ b/deployability/modules/testing/tests/test_agent/test_uninstall.py @@ -2,8 +2,7 @@ from ..helpers.manager import WazuhManager, WazuhAPI from ..helpers.agent import WazuhAgent -from ..helpers.generic import CheckFiles, HostInformation, GeneralComponentActions -from ..helpers.utils import dynamic_wait +from ..helpers.generic import CheckFiles, HostInformation, GeneralComponentActions, Waits from ..helpers.constants import WAZUH_ROOT @@ -43,7 +42,7 @@ def wazuh_params(request): targets = request.config.getoption('--targets') live = request.config.getoption('--live') - params = { + return { 'wazuh_version': wazuh_version, 'wazuh_revision': wazuh_revision, 'dependencies': dependencies, @@ -51,10 +50,6 @@ def wazuh_params(request): 'live': live } - yield params - - for agent_names, agent_params in params['agents'].items(): - GeneralComponentActions.component_restart(agent_params, 'wazuh-agent') @pytest.fixture(autouse=True) def setup_test_environment(wazuh_params): @@ -100,4 +95,4 @@ def test_isActive(wazuh_params): assert not GeneralComponentActions.isComponentActive(agent_params, 'wazuh-agent') expected_condition_func = lambda: 'disconnected' == WazuhAgent.get_agent_status(wazuh_api, agent_names) - dynamic_wait(expected_condition_func, cycles=10, waiting_time=20) \ No newline at end of file + Waits.dynamic_wait(expected_condition_func, cycles=10, waiting_time=20)