diff --git a/README.md b/README.md index 8fb4c7b..f5482ad 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,40 @@ -# stackhpc-openstack-tests -Automated testing for StackHPC OpenStack +# StackHPC OpenStack Tests + +Automated testing for StackHPC OpenStack. + +Provides test coverage of various aspects of OpenStack and related services, including: + +* OpenSearch +* Prometheus + +Tests are written using [pytest](https://docs.pytest.org/). + +## Installation + +Clone this repository. + +Create a virtual environment. + +```sh +python3 -m venv venv +``` + +Install stackhpc-openstack-tests and its dependencies. + +```sh +venv/bin/pip install -r /requirements.txt +``` + +## Usage + +Run all tests provided. + +```sh +py.test --pyargs stackhpc_openstack_tests +``` + +Or run tests from a specific submodule. + +```sh +py.test --pyargs stackhpc_openstack_tests.test_prometheus +``` diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..8b58343 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,3 @@ +opensearch-py==2.5.* +prometheus-api-client==0.5.* +pytest-testinfra==10.1.* diff --git a/setup.cfg b/setup.cfg new file mode 100644 index 0000000..7147947 --- /dev/null +++ b/setup.cfg @@ -0,0 +1,33 @@ +[metadata] +name = stackhpc-openstack-tests +version = 0.1.0 +summary = Automated testing for StackHPC OpenStack +description-file = README.md +author = Mark Goddard +author-email = mark@stackhpc.com +url = https://github.com/stackhpc/stackhpc-openstack-tests +python-requires = >=3.6 +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 3 + Programming Language :: Python :: 3.7 + Programming Language :: Python :: 3.8 + Programming Language :: Python :: 3.9 + Programming Language :: Python :: 3.10 + Programming Language :: Python :: 3.11 + Programming Language :: Python :: 3.12 + Programming Language :: Python :: 3 :: Only + Programming Language :: Python :: Implementation :: CPython + +[files] +packages = + stackhpc_openstack_tests + +[options] +install_requires = + pytest-testinfra diff --git a/setup.py b/setup.py new file mode 100644 index 0000000..3ae4c5c --- /dev/null +++ b/setup.py @@ -0,0 +1,18 @@ +# Copyright (c) 2024 StackHPC Ltd. + +# Licensed under the Apache License, Version 2.0 (the "License"); +# you may not use this file except in compliance with the License. +# You may obtain a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or +# implied. +# See the License for the specific language governing permissions and +# limitations under the License. + +import setuptools + +setuptools.setup() diff --git a/stackhpc_openstack_tests/__init__.py b/stackhpc_openstack_tests/__init__.py new file mode 100644 index 0000000..0e8b8f8 --- /dev/null +++ b/stackhpc_openstack_tests/__init__.py @@ -0,0 +1,13 @@ +# Copyright (c) 2024 StackHPC Ltd. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. diff --git a/stackhpc_openstack_tests/test_opensearch.py b/stackhpc_openstack_tests/test_opensearch.py new file mode 100644 index 0000000..b750f88 --- /dev/null +++ b/stackhpc_openstack_tests/test_opensearch.py @@ -0,0 +1,62 @@ +# Copyright (c) 2024 StackHPC Ltd. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +# TODO: +# * Dashboard login +# * Cluster health + +from opensearchpy import OpenSearch +import os +import pytest + +from stackhpc_openstack_tests import utils + + +@pytest.fixture +def opensearch() -> OpenSearch: + """Pytest fixture that creates an OpenSearch API client.""" + # https://opensearch.org/docs/latest/clients/python-low-level/ + opensearch_hosts = os.environ["OPENSEARCH_HOSTS"].split(",") + opensearch_port = os.environ["OPENSEARCH_PORT"] + opensearch_hosts = [ + {"host": host, "port": opensearch_port} for host in opensearch_hosts + ] + opensearch_tls = utils.str_to_bool(os.environ["OPENSEARCH_TLS"]) + if opensearch_tls: + opensearch_verify_certs = utils.str_to_bool( + os.environ["OPENSEARCH_VERIFY_CERTS"] + ) + else: + opensearch_verify_certs = True + return OpenSearch( + hosts=opensearch_hosts, + http_compress=True, + use_ssl=opensearch_tls, + verify_certs=opensearch_verify_certs, + ssl_show_warn=False, + ) + + +def test_opensearch_has_info_logs(opensearch): + """Check that OpenSearch has some INFO level logs.""" + query = { + "query": { + "match": { + "log_level": "INFO", + } + } + } + # https://opensearch-project.github.io/opensearch-py/api-ref/clients/opensearch_client.html#opensearchpy.OpenSearch.search + result = opensearch.search(body=query, index="flog-*", size=1) + assert len(result["hits"]["hits"]) == 1 diff --git a/stackhpc_openstack_tests/test_prometheus.py b/stackhpc_openstack_tests/test_prometheus.py new file mode 100644 index 0000000..7478ffa --- /dev/null +++ b/stackhpc_openstack_tests/test_prometheus.py @@ -0,0 +1,41 @@ +# Copyright (c) 2024 StackHPC Ltd. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + +import os +from prometheus_api_client import PrometheusConnect +import pytest + + +@pytest.fixture +def prom() -> PrometheusConnect: + """Pytest fixture that creates a Prometheus API client.""" + # https://github.com/4n4nd/prometheus-api-client-python/ + prometheus_url = os.environ["PROMETHEUS_URL"] + kwargs = {} + if "PROMETHEUS_USERNAME" in os.environ: + prometheus_username = os.environ["PROMETHEUS_USERNAME"] + prometheus_password = os.environ["PROMETHEUS_PASSWORD"] + kwargs["auth"] = (prometheus_username, prometheus_password) + return PrometheusConnect(url=prometheus_url, disable_ssl=True, **kwargs) + + +def test_prometheus_connection(prom): + """Check that Prometheus is accessible.""" + assert prom.check_prometheus_connection() + + +def test_prometheus_node_exporter_metrics(prom): + """Check that expected node exporter metrics exist.""" + metrics = prom.all_metrics() + assert "node_cpu_seconds_total" in metrics diff --git a/stackhpc_openstack_tests/utils.py b/stackhpc_openstack_tests/utils.py new file mode 100644 index 0000000..9b7b02b --- /dev/null +++ b/stackhpc_openstack_tests/utils.py @@ -0,0 +1,18 @@ +# Copyright (c) 2024 StackHPC Ltd. + +# Licensed under the Apache License, Version 2.0 (the "License"); you may +# not use this file except in compliance with the License. You may obtain +# a copy of the License at +# +# http://www.apache.org/licenses/LICENSE-2.0 +# +# Unless required by applicable law or agreed to in writing, software +# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT +# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the +# License for the specific language governing permissions and limitations +# under the License. + + +def str_to_bool(v): + """Convert a boolean true/false string to a bool.""" + return v.lower() == "true"