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
13 changes: 13 additions & 0 deletions conftest.py
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import pytest

from robottelo.config import settings

pytest_plugins = [
# Plugins
'pytest_plugins.auto_vault',
Expand Down Expand Up @@ -93,3 +95,14 @@ def pytest_runtest_makereport(item, call):
# be "setup", "call", "teardown"

setattr(item, "report_" + report.when, report)


@pytest.hookimpl(hookwrapper=True)
def pytest_runtest_protocol(item, nextitem):
"""Set version source to upstream for tests marked with foremanctl."""
if item.get_closest_marker('foremanctl'):
settings.set('server.version.source', 'upstream')
yield
settings.set('server.version.source', 'internal')
else:
yield
1 change: 1 addition & 0 deletions pytest_plugins/markers.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@ def pytest_configure(config):
"ldap: Tests related to ldap authentication",
"no_compose : Skip the marked sanity test for nightly compose",
"network: Restrict test to specific network environments",
"foremanctl: Tests that require foremanctl",
]
markers.extend(module_markers())
for marker in markers:
Expand Down
6 changes: 5 additions & 1 deletion robottelo/config/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,11 @@
Validator('server.hostname', is_type_of=str),
Validator('server.hostnames', must_exist=True, is_type_of=list),
Validator('server.version.release', must_exist=True),
Validator('server.version.source', default='internal', is_in=['internal', 'ga', 'nightly']),
Validator(
'server.version.source',
default='internal',
is_in=['internal', 'ga', 'nightly', 'upstream'],
),
Validator('server.version.rhel_version', must_exist=True, cast=str),
Validator(
'server.xdist_behavior', must_exist=True, is_in=['run-on-one', 'balance', 'on-demand']
Expand Down
55 changes: 55 additions & 0 deletions robottelo/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -1577,6 +1577,12 @@ def setup_satellite_repos(self):
satellite_repo=settings.repos.satellite_repo,
satmaintenance_repo=settings.repos.satmaintenance_repo,
)
elif settings.server.version.source == 'upstream':
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When is this used?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Ooh, the tests are marked with new_installer and that sets the version source to upstream?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Which packages do you want to install from the upstream repos?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

For setup-hammer, it needs related hammer plugins for katello and REX

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Don't you have Satellite repos enabled and can get them from there?
(Not that I'm complaining that you test upstream bits, just curious to understand which bits exactly)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Satellite repos might conflict, so that's why I prefer to validate using upstream bits so its clean upstream installation until foremanctl isn't available in Satellite repos

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If we're going this route, could we use the existing property instead?

Suggested change
elif settings.server.version.source == 'upstream':
elif self.is_upstream:

This would eliminate the need for pytest_runtest_protocol implementation, correct?

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah, I saw is_upstream checks if satellite or satellite-capsule packages isn't present, which IMHO won't help, when we have satellite package installed(w/o satellite-installer configured) and foremanctl present.
While, settings.server.version.source setting can be used for upstream only deployments as well in future, which we don't test as of now, but it could be something we would need in future

self.create_custom_repos(
foreman='https://yum.theforeman.org/nightly/el9/x86_64/',
foreman_plugins='https://yum.theforeman.org/plugins/nightly/el9/x86_64/',
katello='https://yum.theforeman.org/katello/nightly/katello/el9/x86_64/',
)
else:
# get ohsnap repofile
self.download_repofile(
Expand Down Expand Up @@ -1955,6 +1961,55 @@ def install_satellite_or_capsule_package(self):
self.enable_satellite_or_capsule_module_for_rhel8()
assert self.execute(f'dnf -y install {self.product_rpm_name}').status == 0

def install_satellite_foremanctl(self, enable_fapolicyd=False, enable_fips=False):
# Enable RHEL and Satellite repos
self.register_to_cdn()
self.setup_rhel_repos()
self.setup_satellite_repos()
assert self.execute('dnf copr enable -y @theforeman/foremanctl rhel-9-x86_64').status == 0
assert self.execute('dnf install -y foremanctl').status == 0

if enable_fapolicyd:
assert self.execute('dnf -y install fapolicyd').status == 0
assert self.execute('systemctl enable --now fapolicyd').status == 0
assert self.execute('systemctl is-active fapolicyd').status == 0
if enable_fips:
Broker().execute(
workflow='enable-fips',
target_vm=self.name,
)
self.connect()
assert self.is_fips_enabled()

# Configure Satellite firewall to open communication
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The same code is present in tests/foreman/installer/test_installer.py::install_satellite and tests/foreman/installer/test_installer.py::test_capsule_installation (minus policy rule name)
Then also in tests/foreman/cli/test_rhcloud_iop.py::test_positive_install_iop_custom_certs, and robottelo/hosts.py::capsule_setup and setup_firewall

Maybe we should not add yet another copy, but instead make setup_firewall do the right thing in all cases?
(Not necessarily blocking this PR, but this seems like a mess)

Copy link
Member Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good point, will do it in a separate PR

assert (
self.execute(
'(which firewall-cmd || dnf -y install firewalld) && systemctl enable --now firewalld'
).status
== 0
), 'firewalld is not present and can\'t be installed'
assert (
self.execute(
'firewall-cmd --permanent --add-service RH-Satellite-6 && firewall-cmd --reload'
).status
== 0
)
# Install Satellite and return result
assert (
self.execute(
f'foremanctl deploy --foreman-initial-admin-username {settings.server.admin_username} --foreman-initial-admin-password {settings.server.admin_password}',
timeout='30m',
).status
== 0
)
assert (
self.execute(
'foremanctl deploy --add-feature foreman-proxy --add-feature hammer'
).status
== 0
)
return

def query_db(self, query, db='foreman', output_format='json'):
"""Execute a PostgreSQL query and return the result.

Expand Down
121 changes: 121 additions & 0 deletions tests/foreman/installer/test_new_installer.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
"""Smoke tests to check installation health

:Requirement: Installation

:CaseAutomation: Automated

:CaseComponent: Installation

:Team: Rocket

:CaseImportance: Critical

"""

from broker import Broker
import pytest

from robottelo.config import settings
from robottelo.hosts import Satellite, get_sat_rhel_version

pytestmark = [pytest.mark.foremanctl, pytest.mark.build_sanity, pytest.mark.upgrade]

SATELLITE_SERVICES = [
'candlepin',
'dynflow-sidekiq@orchestrator',
'dynflow-sidekiq@worker',
'dynflow-sidekiq@worker-hosts-queue',
'foreman-proxy',
'foreman',
'httpd',
'postgresql',
'pulp-api',
'pulp-content',
'pulp-worker@*',
'redis',
]


def common_sat_install_assertions(satellite):
# no errors/failures in journald
result = satellite.execute(
r'journalctl --quiet --no-pager --boot --priority err -u "dynflow-sidekiq*" -u "foreman-proxy" -u "foreman" -u "httpd" -u "postgresql" -u "pulp-api" -u "pulp-content" -u "pulp-worker*" -u "redis" -u "candlepin"'
)
assert not result.stdout
# no errors/failures in /var/log/httpd/*
result = satellite.execute(r'grep -iR "error" /var/log/httpd/*')
assert not result.stdout
# # no errors/failures in /var/log/candlepin/*
result = satellite.execute(r'grep -iR "error" /var/log/candlepin/*')
assert not result.stdout
httpd_log = satellite.execute('journalctl --unit=httpd')
assert 'WARNING' not in httpd_log.stdout


@pytest.fixture(scope='module')
def module_sat_ready_rhel(request):
with Broker(
workflow=settings.server.deploy_workflows.os,
deploy_rhel_version=get_sat_rhel_version().major,
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Could we rely directly on settings here?

Suggested change
deploy_rhel_version=get_sat_rhel_version().major,
deploy_rhel_version=settings.server.version.release,

Copy link
Member Author

@Gauravtalreja1 Gauravtalreja1 Dec 3, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

settings.server.version.release could be stream/6.18.z, and not rhel_ver, but either we use settings.server.versio.rhel_version or we could use get_sat_rhel_version() I don't see the difference, and intent is to just fetch the rhel_major_ver :)

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Oh yes, my bad. I meant settings.server.version.rhel_version. The Satellite host does not exist yet, so we can not fetch it from an existing host object, so the only place remaining is settings.server. version.rhel_version settings.

I leave settings.robottelo.rhel_version aside as I'd like to get that out from robottelo one day.

deploy_flavor=settings.flavors.default,
deploy_network_type=settings.server.network_type,
host_class=Satellite,
) as sat:
sat.install_satellite_foremanctl(
enable_fapolicyd=(request.param == 'fapolicyd'), enable_fips=(request.param == 'fips')
)
yield sat


@pytest.mark.first_sanity
@pytest.mark.parametrize('module_sat_ready_rhel', ['default', 'fips', 'fapolicyd'], indirect=True)
def test_satellite_installation_with_foremanctl(module_sat_ready_rhel):
"""Run a basic Satellite installation

:id: 661206f3-2eec-403c-af26-3c5cadcd5769

:steps:
1. Get RHEL Host
2. Configure satellite repos
3. Install satellite using foremanctl
4. Run foremanctl deploy

:expectedresults:
1. foremanctl deploy runs successfully
2. no unexpected errors in logs
"""
common_sat_install_assertions(module_sat_ready_rhel)


@pytest.mark.parametrize('service', SATELLITE_SERVICES)
def test_positive_check_installer_service_running(service, module_sat_ready_rhel):
"""Check if all Satellite services is running

:id: 5389c174-7ab1-4e9d-b2aa-66d80fd6dc5h

:steps:
1. Verify a service is active with systemctl is-active

:expectedresults: All Satellite services are active
"""
is_active = module_sat_ready_rhel.execute(f'systemctl is-active {service}')
status = module_sat_ready_rhel.execute(f'systemctl status {service}')
assert is_active.status == 0, status.stdout


def test_positive_check_installer_hammer_ping(module_sat_ready_rhel):
"""Check if hammer ping reports all services as ok

:id: 85fd4388-6d94-42f5-bed2-24be38e9f111

:steps:
1. Run the 'hammer ping' command on satellite.

:expectedresults: All services are active (running)
"""
response = module_sat_ready_rhel.api.Ping().search_json()
assert response['status'] == 'ok' # overall status
services = response['services']
assert all([service['status'] == 'ok' for service in services.values()]), (
'Not all services seem to be up and running!'
)