Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 7 additions & 0 deletions src/integrationtest/integrationtest_commandline.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,13 @@ def pytest_addoption(parser):
help="Whether to disable the Connectivity Service for this test",
required=False
)
parser.addoption(
"--skip-resource-checks",
action="store_true",
default=False,
help="Whether to skip the node resource (CPU/Memory) checks for this test",
required=False
)

def pytest_configure(config):
for opt in ("--nanorc-path",):
Expand Down
31 changes: 30 additions & 1 deletion src/integrationtest/integrationtest_drunc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import os
import conffwk
from integrationtest.integrationtest_commandline import file_exists
from integrationtest.resource_validation import ResourceValidator
from integrationtest.data_classes import (
CreateConfigResult,
ProcessManagerChoice,
Expand Down Expand Up @@ -89,7 +90,31 @@ def process_manager_type(request, tmp_path_factory):
yield result

@pytest.fixture(scope="module")
def create_config_files(request, tmp_path_factory):
def check_system_resources(request):
"""Check that the system resources (CPU, Memory) are sufficient for the test
The required and recommended resources are taken from the
`resource_validator` variable in the global scope of the test
module, which should be an instance of ResourceValidator. If the
required resources are not present, then the test is skipped. If
the recommended resources are not present, then a warning is printed
"""
skip_resource_checks = request.config.getoption("--skip-resource-checks")

resval = getattr(request.module, "resource_validator", ResourceValidator())
if not resval.required_resources_are_present:
resval_report_string = resval.get_required_resources_report()
print(f"\n\N{LARGE YELLOW CIRCLE} {resval_report_string}")
resval_summary_string = resval.get_required_resources_summary()
if not skip_resource_checks:
pytest.skip(f"{resval_summary_string}")
if not resval.recommended_resources_are_present:
resval_report_string = resval.get_recommended_resources_report()
print(f"\n*** Note: {resval_report_string}")

yield True

@pytest.fixture(scope="module")
def create_config_files(request, tmp_path_factory, check_system_resources):
"""Run the confgen to produce the configuration json files

The name of the module to use is taken (indirectly) from the
Expand All @@ -101,11 +126,15 @@ def create_config_files(request, tmp_path_factory):
produced by one pytest module

"""
dummy_resource_check = check_system_resources
drunc_config = request.param

disable_connectivity_service = request.config.getoption(
"--disable-connectivity-service"
)
skip_resource_checks = request.config.getoption(
"--skip-resource-checks"
)

config_dir = tmp_path_factory.mktemp("config")
boot_file = config_dir / "boot.json"
Expand Down
65 changes: 52 additions & 13 deletions src/integrationtest/resource_validation.py
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,14 @@
# Here is pseudo-code for using this utility:
#
# import integrationtest.resource_validation as resource_validation
# resval = resource_validation.ResourceValidator()
# resval.cpu_count_needs(32, 64)
# resval.free_memory_needs(28)
# # MUST be named "resource_validator"
# resource_validator = resource_validation.ResourceValidator()
# resource_validator.cpu_count_needs(32, 64)
# resource_validator.free_memory_needs(28)
# # set other minimum values, if desired
# resval_debug_string = resval.get_debug_string()
# resval_debug_string = resource_validator.get_debug_string()
# print(f"{resval_debug_string}")
# # then, in one of the pytest "tests"
# if not resval.required_resources_are_present
# resval_full_report = resval.get_required_resources_report()
# print(f"{resval_full_report}")
# resval_summary_report = resval.get_required_resources_summary()
# pytest.skip(f"{resval_summary_report}")
# if not resval.recommended_resources_are_present
# resval_full_report = resval.get_recommended_resources_report()
# print(f"{resval_full_report}")
# # The check_system_resources fixture will check that the system has the required resources

import os
import psutil
Expand Down Expand Up @@ -182,6 +175,52 @@ def total_disk_space_needs(self, path_of_interest, required_total_disk_space=-1,
self.recommended_resource_report_string = self.recommended_resource_report_header
self.recommended_resource_report_string += f"\n{self.report_indentation} Total disk space on \"{path_of_interest}\" is {total_disk_space_gb} GB, recommended amount is {recommended_total_disk_space}."

# method to specify the regular expression which the hostname should match
def require_host_match(self, required_host_regex):
hostname = os.uname().nodename
self.debug_string += f"\nDEBUG: Hostname is \"{hostname}\", required regex is \"{required_host_regex}\""
if not re.match(required_host_regex, hostname):
self.this_computer_has_sufficient_resources = False
self.required_resources_are_present = False
if len(self.required_resource_report_string) == 0:
self.required_resource_report_string = self.required_resource_report_header
self.required_resource_report_string += f"\n{self.report_indentation} Hostname is \"{hostname}\", which does not match the required regex \"{required_host_regex}\"."

# method to check connectivity to certain hosts
def require_host_connectivity(self, required_host_list):
# Set up the software environment on each of the 4 computers needed for this test.
# This serves two purposes: it verifies that we can ssh to
# those computers (so we know that we are running at EHN1, etc.), and it pre-loads
# the software release from CVMFS onto all of those computers (so the startup of
# DAQ apps such as the ConnectivityServer don't take a long time initially).
import subprocess
computers_that_are_unreachable = []
sw_area_root = os.environ.get("DBT_AREA_ROOT")
if sw_area_root is not None:
for needed_computer in required_host_list:
print("")
print(f"Confirming that we can ssh to {needed_computer}...")
proc = subprocess.Popen(f"ssh {needed_computer} 'cd {sw_area_root}; . ./env.sh'", shell=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
proc.communicate()
retval = proc.returncode
if retval != 0:
computers_that_are_unreachable.append(needed_computer)
else:
self.debug_string += "\nDEBUG: DBT_AREA_ROOT environment variable is not set, so connectivity checks were not performed."
self.this_computer_has_sufficient_resources = False
self.required_resources_are_present = False
if len(self.required_resource_report_string) == 0:
self.required_resource_report_string = self.required_resource_report_header
self.required_resource_report_string += f"\n{self.report_indentation} Unable to determine the value of the DBT_AREA_ROOT env var."

if len(computers_that_are_unreachable) > 0:
self.this_computer_has_sufficient_resources = False
self.required_resources_are_present = False
if len(self.required_resource_report_string) == 0:
self.required_resource_report_string = self.required_resource_report_header
for unreachable_computer in computers_that_are_unreachable:
self.required_resource_report_string += f"\n{self.report_indentation} Unable to ssh to {unreachable_computer}."

def get_debug_string(self):
return self.debug_string

Expand Down