From d925e7e1f75dd80eec316e81b5df0c86fb7f3b69 Mon Sep 17 00:00:00 2001 From: Selutario Date: Fri, 12 Aug 2022 14:58:27 +0200 Subject: [PATCH 1/4] Add system test to check Cluster sync status endpoint --- .../wazuh_testing/tools/__init__.py | 1 + .../wazuh_testing/tools/system.py | 24 ++++++ tests/system/test_cluster/conftest.py | 54 +++++++++++++ .../data/master_messages.yml | 8 ++ .../data/worker_messages.yml | 15 ++++ .../test_ruleset_sync_status.py | 76 +++++++++++++++++++ 6 files changed, 178 insertions(+) create mode 100644 tests/system/test_cluster/conftest.py create mode 100644 tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml create mode 100644 tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml create mode 100644 tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py diff --git a/deps/wazuh_testing/wazuh_testing/tools/__init__.py b/deps/wazuh_testing/wazuh_testing/tools/__init__.py index 42bd0bf2c8..66613ffc1d 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/__init__.py +++ b/deps/wazuh_testing/wazuh_testing/tools/__init__.py @@ -57,6 +57,7 @@ REMOTE_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-remoted.state') ANALYSIS_STATISTICS_FILE = os.path.join(WAZUH_PATH, 'var', 'run', 'wazuh-analysisd.state') UPGRADE_PATH = os.path.join(WAZUH_PATH, 'var', 'upgrade') + PYTHON_PATH = os.path.join(WAZUH_PATH, 'framework', 'python') AGENT_AUTH_BINARY_PATH = os.path.join(WAZUH_PATH, 'bin', 'agent-auth') diff --git a/deps/wazuh_testing/wazuh_testing/tools/system.py b/deps/wazuh_testing/wazuh_testing/tools/system.py index 294d5a4f3a..875a9ffe27 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/system.py +++ b/deps/wazuh_testing/wazuh_testing/tools/system.py @@ -92,6 +92,16 @@ def clear_file(self, host: str, file_path: str, check: bool = False): """ self.get_host(host).ansible("copy", f"dest={file_path} content='' force=yes", check=check) + def clear_file_without_recreate(self, host: str, file_path: str, check: bool = False): + """Truncate the specified file without overwriting/recreating it. + + Args: + host (str): Hostname + file_path (str): File path to be truncated + check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + """ + self.get_host(host).ansible('shell', f"truncate -s 0 {file_path}", check=check) + def get_file_content(self, host: str, file_path: str): """Get the content of the specified file. @@ -244,3 +254,17 @@ def run_shell(self, host: str, cmd: str, check: bool = False): stdout (str): The output of the command execution. """ return self.get_host(host).ansible('shell', cmd, check=check)['stdout'] + + def find_file(self, host: str, path: str, pattern: str, recurse: bool = False, use_regex: bool = False): + """Search and return information of a file inside a path. + Args: + host (str): Hostname + path (str): Path in which to search for the file that matches the pattern. + pattern (str): Restrict the files to be returned to those whose basenames match the pattern specified. + recurse (bool): If target is a directory, recursively descend into the directory looking for files. + use_regex (bool): If no, the patterns are file globs (shell), if yes, they are python regexes. + Returns: + Files (list): List of found files. + """ + return self.get_host(host).ansible("find", f"paths={path} patterns={pattern} recurse={recurse} " + f"use_regex={use_regex}") \ No newline at end of file diff --git a/tests/system/test_cluster/conftest.py b/tests/system/test_cluster/conftest.py new file mode 100644 index 0000000000..78f72e6968 --- /dev/null +++ b/tests/system/test_cluster/conftest.py @@ -0,0 +1,54 @@ +# Copyright (C) 2015-2022, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import copy +import json +from functools import reduce +from operator import getitem + +import pytest + +from wazuh_testing.tools import PYTHON_PATH + + +@pytest.fixture(scope='module') +def update_cluster_json(request): + """Update cluster.json file and restart cluster nodes. + + Update cluster.json file in each node and restart it before running the test. Then, the original content + is restored and the cluster nodes are restarted again. + + IMPORTANT: These variables must be defined in the module where this fixture is called: + - test_hosts (list): Cluster host names. + - host_manager (HostManager): Instance of HostManager. + - cluster_json_values (list of dicts): Each item of the list must follow the structure below. + {'key': ['', ''], 'value': } + """ + backup_json = {} + test_hosts = getattr(request.module, 'test_hosts') + host_manager = getattr(request.module, 'host_manager') + cluster_json_values = getattr(request.module, 'cluster_json_values') + + for host in test_hosts: + # Find cluster.json path. + cluster_json = host_manager.find_file(host, path=PYTHON_PATH, recurse=True, pattern='cluster.json' + )['files'][0]['path'] + cluster_conf = json.loads(host_manager.run_command(host, f"cat {cluster_json}")) + backup_json[host] = {'path': cluster_json, 'content': copy.deepcopy(cluster_conf)} + + # Update dict/nested_dicts. + for item in cluster_json_values: + reduce(getitem, item['key'][:-1], cluster_conf)[item['key'][-1]] = item['value'] + host_manager.modify_file_content(host=host, path=cluster_json, content=json.dumps(cluster_conf, indent=4)) + + # Restart manager. + host_manager.control_service(host=host, service='wazuh', state='restarted') + + yield + + # Restore cluster.json and restart. + for host in backup_json: + host_manager.modify_file_content(host=host, path=backup_json[host]['path'], + content=json.dumps(backup_json[host]['content'], indent=4)) + host_manager.control_service(host=host, service='wazuh-manager', state='restarted') diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml new file mode 100644 index 0000000000..4dab08e62b --- /dev/null +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml @@ -0,0 +1,8 @@ +--- +wazuh-master: + - regex: '.*\[Local integrity\] Starting.*' + path: "/var/ossec/logs/cluster.log" + timeout: 120 + - regex: '.*\[Local integrity\] Finished in .*' + path: "/var/ossec/logs/cluster.log" + timeout: 10 \ No newline at end of file diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml new file mode 100644 index 0000000000..d384ef2dbc --- /dev/null +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml @@ -0,0 +1,15 @@ +--- +wazuh-worker1: + - regex: ".*Integrity sync.*Starting.*" + path: "/var/ossec/logs/cluster.log" + timeout: 240 + - regex: ".*Integrity sync.*Finished in.*" + path: "/var/ossec/logs/cluster.log" + timeout: 30 +wazuh-worker2: + - regex: ".*Integrity sync.*Starting.*" + path: "/var/ossec/logs/cluster.log" + timeout: 240 + - regex: ".*Integrity sync.*Finished in.*" + path: "/var/ossec/logs/cluster.log" + timeout: 30 \ No newline at end of file diff --git a/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py new file mode 100644 index 0000000000..9a804b69b6 --- /dev/null +++ b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py @@ -0,0 +1,76 @@ +# Copyright (C) 2015-2022, Wazuh Inc. +# Created by Wazuh, Inc. . +# This program is free software; you can redistribute it and/or modify it under the terms of GPLv2 + +import os +import uuid + +from wazuh_testing.tools import WAZUH_PATH, WAZUH_LOGS_PATH +from wazuh_testing.tools.monitoring import HostMonitor +from wazuh_testing.tools.system import HostManager + +# Hosts +test_hosts = ['wazuh-master', 'wazuh-worker1', 'wazuh-worker2'] +worker_hosts = test_hosts[1:] + +# Data paths +test_data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data') +messages_path = os.path.join(test_data_path, 'master_messages.yml') +tmp_path = os.path.join(test_data_path, 'tmp') +inventory_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), + 'provisioning', 'agentless_cluster', 'inventory.yml') +host_manager = HostManager(inventory_path) +cluster_json_values = [ + {'key': ['intervals', 'worker', 'sync_integrity'], 'value': 120}, + {'key': ['intervals', 'master', 'recalculate_integrity'], 'value': 120}, +] + +rule_content = f""" + + + + 5716 + 1.1.1.1 + sshd: authentication failed from IP 1.1.1.1. + authentication_failed,pci_dss_10.2.4,pci_dss_10.2.5, + + +""" + + +def get_sync_status(api_token): + response = host_manager.make_api_call(host=test_hosts[0], method='GET', token=api_token, + endpoint='/cluster/ruleset/synchronization') + assert response['status'] == 200, f"Failed when trying to obtain cluster sync status: {response}" + assert response['json']['data']['total_affected_items'] == len(test_hosts) + return response['json']['data']['affected_items'] + + +def test_ruleset_sync_status(update_cluster_json): + """Check if 'GET /cluster/ruleset/synchronization' API endpoint returns correct sync status. + + Verify that, after changing a custom ruleset file in the master node and calling the API endpoint mentioned above, + the 'synced' status for all worker nodes in the response is False. Wait until an Integrity synchronization + is run. Now, the response for all workers should be 'synced: True'. + """ + api_token = host_manager.get_api_token(test_hosts[0]) + for host in test_hosts: + host_manager.clear_file_without_recreate(host=host, file_path=os.path.join(WAZUH_LOGS_PATH, 'cluster.log')) + + # Check that all workers are synced before starting. + assert all(item['synced'] for item in get_sync_status(api_token)) + + # Modify a custom rule file and verify that synced status is False for all workers. + host_manager.modify_file_content(host=test_hosts[0], + path=os.path.join(WAZUH_PATH, 'etc', 'rules', 'local_rules.xml'), + content=rule_content) + assert all(not item['synced'] for item in get_sync_status(api_token) if item['name'] != test_hosts[0]) + + # Wait until a Local Integrity task is run in the master and then, Integrity sync tasks are run in the workers. + HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'master_messages.yml'), + tmp_path=tmp_path).run() + HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'worker_messages.yml'), + tmp_path=tmp_path).run() + + # Verify that synced status is True for all cluster nodes again. + assert all(item['synced'] for item in get_sync_status(api_token)) From 63a298738d5edbd125f560d27ab1767a232d8ae5 Mon Sep 17 00:00:00 2001 From: Selutario Date: Tue, 16 Aug 2022 14:57:30 +0200 Subject: [PATCH 2/4] Add info to readme and improve docstrings --- deps/wazuh_testing/wazuh_testing/tools/system.py | 4 ++-- tests/system/README.md | 1 + tests/system/test_cluster/conftest.py | 6 ++++-- .../test_ruleset_sync_status/data/master_messages.yml | 2 +- .../test_ruleset_sync_status/data/worker_messages.yml | 2 +- 5 files changed, 9 insertions(+), 6 deletions(-) diff --git a/deps/wazuh_testing/wazuh_testing/tools/system.py b/deps/wazuh_testing/wazuh_testing/tools/system.py index 875a9ffe27..9fbab5c175 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/system.py +++ b/deps/wazuh_testing/wazuh_testing/tools/system.py @@ -93,7 +93,7 @@ def clear_file(self, host: str, file_path: str, check: bool = False): self.get_host(host).ansible("copy", f"dest={file_path} content='' force=yes", check=check) def clear_file_without_recreate(self, host: str, file_path: str, check: bool = False): - """Truncate the specified file without overwriting/recreating it. + """Truncate the specified file without recreating it. Args: host (str): Hostname @@ -267,4 +267,4 @@ def find_file(self, host: str, path: str, pattern: str, recurse: bool = False, u Files (list): List of found files. """ return self.get_host(host).ansible("find", f"paths={path} patterns={pattern} recurse={recurse} " - f"use_regex={use_regex}") \ No newline at end of file + f"use_regex={use_regex}") diff --git a/tests/system/README.md b/tests/system/README.md index e2766974fd..e34e8e791d 100644 --- a/tests/system/README.md +++ b/tests/system/README.md @@ -114,6 +114,7 @@ required an specific testing environment located in `wazuh-qa/tests/system/provi | test_cluster/test_agent_key_polling | basic_cluster | | test_cluster/test_agent_files_deletion | basic_cluster | | test_cluster/test_integrity_sync | agentless_cluster | +| test_cluster/test_ruleset_sync_status | agentless_cluster | | test_jwt_invalidation | agentless_cluster | ### Test structure diff --git a/tests/system/test_cluster/conftest.py b/tests/system/test_cluster/conftest.py index 78f72e6968..1fc528b49c 100644 --- a/tests/system/test_cluster/conftest.py +++ b/tests/system/test_cluster/conftest.py @@ -22,8 +22,10 @@ def update_cluster_json(request): IMPORTANT: These variables must be defined in the module where this fixture is called: - test_hosts (list): Cluster host names. - host_manager (HostManager): Instance of HostManager. - - cluster_json_values (list of dicts): Each item of the list must follow the structure below. - {'key': ['', ''], 'value': } + - cluster_json_values (list of dicts): Each item of the list must follow the structure below. This example: + {'key': ['', ''], 'value': } + would replace this value: + {'dict_key_A': {'dict_key_AA': , 'dict_key_AB': 'unchanged_value', ...}, 'dict_key_B': ...} """ backup_json = {} test_hosts = getattr(request.module, 'test_hosts') diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml index 4dab08e62b..aec2c846b8 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml @@ -5,4 +5,4 @@ wazuh-master: timeout: 120 - regex: '.*\[Local integrity\] Finished in .*' path: "/var/ossec/logs/cluster.log" - timeout: 10 \ No newline at end of file + timeout: 10 diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml index d384ef2dbc..4b48f02ba1 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml @@ -12,4 +12,4 @@ wazuh-worker2: timeout: 240 - regex: ".*Integrity sync.*Finished in.*" path: "/var/ossec/logs/cluster.log" - timeout: 30 \ No newline at end of file + timeout: 30 From e59301b1f4a165f0187deac92eef5a1ed526db4a Mon Sep 17 00:00:00 2001 From: Selutario Date: Wed, 17 Aug 2022 12:28:30 +0200 Subject: [PATCH 3/4] Update changelog and rename message files --- CHANGELOG.md | 1 + .../data/{master_messages.yml => master_messages.yaml} | 0 .../data/{worker_messages.yml => worker_messages.yaml} | 0 .../test_ruleset_sync_status/test_ruleset_sync_status.py | 5 ++--- 4 files changed, 3 insertions(+), 3 deletions(-) rename tests/system/test_cluster/test_ruleset_sync_status/data/{master_messages.yml => master_messages.yaml} (100%) rename tests/system/test_cluster/test_ruleset_sync_status/data/{worker_messages.yml => worker_messages.yaml} (100%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 693a8f818a..bc84098765 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,6 +9,7 @@ Release report: TBD ## Added - Add Integratord IT - new test_integratord suite ([#3125](https://github.com/wazuh/wazuh-qa/pull/3125)) \- (Framework + Tests) +- Add system test to check synchronization status in the cluster ([#3180](https://github.com/wazuh/wazuh-qa/pull/3180)) \- (Framework + Tests) ### Changed diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml similarity index 100% rename from tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yml rename to tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml similarity index 100% rename from tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yml rename to tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml diff --git a/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py index 9a804b69b6..492aa661a2 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py +++ b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py @@ -15,7 +15,6 @@ # Data paths test_data_path = os.path.join(os.path.dirname(os.path.realpath(__file__)), 'data') -messages_path = os.path.join(test_data_path, 'master_messages.yml') tmp_path = os.path.join(test_data_path, 'tmp') inventory_path = os.path.join(os.path.dirname(os.path.dirname(os.path.dirname(os.path.abspath(__file__)))), 'provisioning', 'agentless_cluster', 'inventory.yml') @@ -67,9 +66,9 @@ def test_ruleset_sync_status(update_cluster_json): assert all(not item['synced'] for item in get_sync_status(api_token) if item['name'] != test_hosts[0]) # Wait until a Local Integrity task is run in the master and then, Integrity sync tasks are run in the workers. - HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'master_messages.yml'), + HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'master_messages.yaml'), tmp_path=tmp_path).run() - HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'worker_messages.yml'), + HostMonitor(inventory_path=inventory_path, messages_path=os.path.join(test_data_path, 'worker_messages.yaml'), tmp_path=tmp_path).run() # Verify that synced status is True for all cluster nodes again. From 71849ebdecebe5b8bd1a4436212fd03ea0fc7989 Mon Sep 17 00:00:00 2001 From: Selutario Date: Wed, 24 Aug 2022 11:24:48 +0200 Subject: [PATCH 4/4] Update files to meet linter requirements --- .../wazuh_testing/tools/__init__.py | 1 - .../wazuh_testing/tools/system.py | 41 +++++++++++-------- .../data/master_messages.yaml | 9 ++-- .../data/worker_messages.yaml | 17 ++++---- .../test_ruleset_sync_status.py | 8 ++++ 5 files changed, 45 insertions(+), 31 deletions(-) diff --git a/deps/wazuh_testing/wazuh_testing/tools/__init__.py b/deps/wazuh_testing/wazuh_testing/tools/__init__.py index 66613ffc1d..d045c48d0b 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/__init__.py +++ b/deps/wazuh_testing/wazuh_testing/tools/__init__.py @@ -60,7 +60,6 @@ PYTHON_PATH = os.path.join(WAZUH_PATH, 'framework', 'python') AGENT_AUTH_BINARY_PATH = os.path.join(WAZUH_PATH, 'bin', 'agent-auth') - try: import grp import pwd diff --git a/deps/wazuh_testing/wazuh_testing/tools/system.py b/deps/wazuh_testing/wazuh_testing/tools/system.py index 9fbab5c175..2a7f6470bd 100644 --- a/deps/wazuh_testing/wazuh_testing/tools/system.py +++ b/deps/wazuh_testing/wazuh_testing/tools/system.py @@ -42,7 +42,7 @@ def move_file(self, host: str, src_path: str, dest_path: str = '/var/ossec/etc/o host (str): Hostname src_path (str): Source path dest_path (str): Destination path - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be applied. """ self.get_host(host).ansible("copy", f"src={src_path} dest={dest_path} owner=wazuh group=wazuh mode=0775", check=check) @@ -56,7 +56,8 @@ def add_block_to_file(self, host: str, path: str, replace: str, before: str, aft replace (str): Text to be inserted in the file before (str): Lower stop of the block to be replaced after (str): Upper stop of the block to be replaced - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False`. """ replace = f'{after}{replace}{before}' self.get_host(host).ansible("replace", fr"path={path} regexp='{after}[\s\S]+{before}' replace='{replace}'", @@ -76,7 +77,8 @@ def control_service(self, host: str, service: str = 'wazuh', state: str = "start host (str): Hostname service (str): Service to be controlled state (str): Final state in which service must end - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False`. """ if service == 'wazuh': service = 'wazuh-agent' if 'agent' in host else 'wazuh-manager' @@ -88,7 +90,8 @@ def clear_file(self, host: str, file_path: str, check: bool = False): Args: host (str): Hostname file_path (str): File path to be truncated - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False` """ self.get_host(host).ansible("copy", f"dest={file_path} content='' force=yes", check=check) @@ -98,7 +101,8 @@ def clear_file_without_recreate(self, host: str, file_path: str, check: bool = F Args: host (str): Hostname file_path (str): File path to be truncated - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False` """ self.get_host(host).ansible('shell', f"truncate -s 0 {file_path}", check=check) @@ -148,10 +152,11 @@ def apply_api_config(self, api_config: str or dict = None, host_list: list = Non """Apply the API configuration described in the yaml file or in the dictionary. Args: - api_config (str,dict): Configuration to be applied. If it is a string, it will try to load the YAML in that path. If it is a dictionary, it will apply that configuration to every host in `host_list`. + api_config (str,dict): Configuration to be applied. If it is a string, it will try to load the YAML in that + path. If it is a dictionary, it will apply that configuration to every host in `host_list`. host_list (list, optional): List of hosts to apply the configuration in. Default `None` - dest_path (str, optional): Path where the API configuration is. Default `/var/ossec/api/configuration/api.yaml` - clear_log (bool, optional): Boolean to decide if it must truncate the 'api.log' after restarting the API or not. + dest_path (str, optional): Path where the API configuration is. + clear_log (bool, optional): Boolean to decide if it must truncate the 'api.log' after restarting the API. """ if isinstance(api_config, str): with open(api_config, 'r') as config_yml: @@ -177,7 +182,7 @@ def get_api_token(self, host, user='wazuh', password='wazuh', auth_context=None, password (str, optional): API password. Default `wazuh` auth_context (dict, optional): Authorization context body. Default `None` port (int, optional): API port. Default `55000` - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be applied. Default `False` Returns: @@ -193,10 +198,11 @@ def get_api_token(self, host, user='wazuh', password='wazuh', auth_context=None, login_body = '' try: - token_response = self.get_host(host).ansible('uri', f'url=https://localhost:{port}{login_endpoint} ' - f'user={user} password={password} method={login_method} ' - f'{login_body} validate_certs=no force_basic_auth=yes', - check=check) + token_response = self.get_host(host).ansible( + 'uri', + f'url=https://localhost:{port}{login_endpoint} user={user} password={password} method={login_method} ' + f'{login_body} validate_certs=no force_basic_auth=yes', + check=check) return token_response['json']['data']['token'] except KeyError: raise KeyError(f'Failed to get token: {token_response}') @@ -211,7 +217,8 @@ def make_api_call(self, host, port=55000, method='GET', endpoint='/', request_bo endpoint (str, optional): Request endpoint. It must start with '/'.. Default `/` request_body ( dict, optional) : Request body. Default `None` token (str, optional): Request token. Default `None` - check ( bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check ( bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False` Returns: API response (dict) : Return the response in JSON format. @@ -233,7 +240,8 @@ def run_command(self, host: str, cmd: str, check: bool = False): Args: host (str) : Hostname cmd (str): Command to execute - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False` Returns: stdout (str): The output of the command execution. @@ -248,7 +256,8 @@ def run_shell(self, host: str, cmd: str, check: bool = False): Args: host (str) : Hostname cmd (str): Shell command to execute - check (bool, optional): Ansible check mode("Dry Run")(https://docs.ansible.com/ansible/latest/user_guide/playbooks_checkmode.html), by default it is enabled so no changes will be applied. Default `False` + check (bool, optional): Ansible check mode("Dry Run"), by default it is enabled so no changes will be + applied. Default `False` Returns: stdout (str): The output of the command execution. diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml index aec2c846b8..dfeae7898d 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/master_messages.yaml @@ -1,8 +1,7 @@ ---- wazuh-master: - - regex: '.*\[Local integrity\] Starting.*' - path: "/var/ossec/logs/cluster.log" + - regex: .*\[Local integrity\] Starting.* + path: /var/ossec/logs/cluster.log timeout: 120 - - regex: '.*\[Local integrity\] Finished in .*' - path: "/var/ossec/logs/cluster.log" + - regex: .*\[Local integrity\] Finished in .* + path: /var/ossec/logs/cluster.log timeout: 10 diff --git a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml index 4b48f02ba1..7e8fac0d67 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml +++ b/tests/system/test_cluster/test_ruleset_sync_status/data/worker_messages.yaml @@ -1,15 +1,14 @@ ---- wazuh-worker1: - - regex: ".*Integrity sync.*Starting.*" - path: "/var/ossec/logs/cluster.log" + - regex: .*Integrity sync.*Starting.* + path: /var/ossec/logs/cluster.log timeout: 240 - - regex: ".*Integrity sync.*Finished in.*" - path: "/var/ossec/logs/cluster.log" + - regex: .*Integrity sync.*Finished in.* + path: /var/ossec/logs/cluster.log timeout: 30 wazuh-worker2: - - regex: ".*Integrity sync.*Starting.*" - path: "/var/ossec/logs/cluster.log" + - regex: .*Integrity sync.*Starting.* + path: /var/ossec/logs/cluster.log timeout: 240 - - regex: ".*Integrity sync.*Finished in.*" - path: "/var/ossec/logs/cluster.log" + - regex: .*Integrity sync.*Finished in.* + path: /var/ossec/logs/cluster.log timeout: 30 diff --git a/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py index 492aa661a2..9612804748 100644 --- a/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py +++ b/tests/system/test_cluster/test_ruleset_sync_status/test_ruleset_sync_status.py @@ -38,6 +38,14 @@ def get_sync_status(api_token): + """Get ruleset sync status of cluster nodes. + + Args: + api_token (str): Usable API token. + + Returns: + list: Dictionaries containing node-name (str) and synced status (bool). + """ response = host_manager.make_api_call(host=test_hosts[0], method='GET', token=api_token, endpoint='/cluster/ruleset/synchronization') assert response['status'] == 200, f"Failed when trying to obtain cluster sync status: {response}"