diff --git a/conf/http_proxy.yaml.template b/conf/http_proxy.yaml.template index 44dae52f74e..07ff2197bf6 100644 --- a/conf/http_proxy.yaml.template +++ b/conf/http_proxy.yaml.template @@ -1,5 +1,6 @@ HTTP_PROXY: UN_AUTH_PROXY_URL: # http://proxy-01.example.com:3423 + HTTP_PROXY_IPV6_URL: # http://proxy-01.ipv6.example.com:3423 AUTH_PROXY_URL: # http://proxy-02.example.com:3423 USERNAME: auth-proxy-user PASSWORD: auth-proxy-password diff --git a/conf/migrations.py b/conf/migrations.py index b263fa352d6..7b03c845ce3 100644 --- a/conf/migrations.py +++ b/conf/migrations.py @@ -35,3 +35,15 @@ def migration_231129_deploy_workflow(settings, data): logger.info( f'Migrated {product_type}.DEPLOY_WORKFLOW to {product_type}.DEPLOY_WORKFLOWS' ) + + +def migration_241120_http_proxy_ipv6_url(settings, data): + """Migrates server.http_proxy_ipv6_url to http_proxy.http_proxy_ipv6_url""" + if ( + settings.server.get('http_proxy_ipv6_url') + and isinstance(settings.server.http_proxy_ipv6_url, str) + and not settings.http_proxy.get('http_proxy_ipv6_url') + ): + data.http_proxy = {} + data.http_proxy.http_proxy_ipv6_url = settings.server.http_proxy_ipv6_url + logger.info('Migrated SERVER.HTTP_PROXY_IPv6_URL to HTTP_PROXY.HTTP_PROXY_IPV6_URL') diff --git a/conf/server.yaml.template b/conf/server.yaml.template index ee482fa7e26..5f917de4a90 100644 --- a/conf/server.yaml.template +++ b/conf/server.yaml.template @@ -15,8 +15,6 @@ SERVER: RHEL_VERSION: '9' # If the the satellite server is IPv6 server IS_IPV6: False - # HTTP Proxy url for IPv6 satellite to connect for outer world access - HTTP_PROXY_IPv6_URL: # run-on-one - All xdist runners default to the first satellite # balance - xdist runners will be split between available satellites # on-demand - any xdist runner without a satellite will have a new one provisioned. diff --git a/pytest_fixtures/component/maintain.py b/pytest_fixtures/component/maintain.py index 1e0e264e0ca..dc1452d88fd 100644 --- a/pytest_fixtures/component/maintain.py +++ b/pytest_fixtures/component/maintain.py @@ -25,7 +25,7 @@ def module_stash(request): def sat_maintain(request, module_target_sat, module_capsule_configured): if settings.remotedb.server: sat = Satellite(settings.remotedb.server) - sat.enable_ipv6_http_proxy() + sat.enable_satellite_ipv6_http_proxy() yield sat else: module_target_sat.register_to_cdn(pool_ids=settings.subscription.fm_rhn_poolid.split()) diff --git a/pytest_fixtures/core/broker.py b/pytest_fixtures/core/broker.py index 9e63ce92a14..83ff3d1dfde 100644 --- a/pytest_fixtures/core/broker.py +++ b/pytest_fixtures/core/broker.py @@ -24,7 +24,7 @@ def _target_sat_imp(request, _default_sat, satellite_factory): """This is the actual working part of the following target_sat fixtures""" if request.node.get_closest_marker(name='destructive'): new_sat = satellite_factory() - new_sat.enable_ipv6_http_proxy() + new_sat.enable_satellite_ipv6_http_proxy() yield new_sat new_sat.teardown() Broker(hosts=[new_sat]).checkin() @@ -34,7 +34,7 @@ def _target_sat_imp(request, _default_sat, satellite_factory): yield installer_sat else: if _default_sat: - _default_sat.enable_ipv6_http_proxy() + _default_sat.enable_satellite_ipv6_http_proxy() yield _default_sat diff --git a/pytest_fixtures/core/sat_cap_factory.py b/pytest_fixtures/core/sat_cap_factory.py index ba245ee3493..07dc6b4876e 100644 --- a/pytest_fixtures/core/sat_cap_factory.py +++ b/pytest_fixtures/core/sat_cap_factory.py @@ -31,7 +31,7 @@ def resolve_deploy_args(args_dict): def _target_satellite_host(request, satellite_factory): if 'sanity' not in request.config.option.markexpr: new_sat = satellite_factory() - new_sat.enable_ipv6_http_proxy() + new_sat.enable_satellite_ipv6_http_proxy() yield new_sat new_sat.teardown() Broker(hosts=[new_sat]).checkin() @@ -49,7 +49,7 @@ def cached_capsule_cdn_register(hostname=None): def _target_capsule_host(request, capsule_factory): if 'sanity' not in request.config.option.markexpr and not request.config.option.n_minus: new_cap = capsule_factory() - new_cap.enable_ipv6_http_proxy() + new_cap.enable_ipv6_dnf_and_rhsm_proxy() yield new_cap new_cap.teardown() Broker(hosts=[new_cap]).checkin() @@ -96,7 +96,7 @@ def factory(retry_limit=3, delay=300, workflow=None, **broker_args): def large_capsule_host(capsule_factory): """A fixture that provides a Capsule based on config settings""" new_cap = capsule_factory(deploy_flavor=settings.flavors.custom_db) - new_cap.enable_ipv6_http_proxy() + new_cap.enable_ipv6_dnf_and_rhsm_proxy() yield new_cap new_cap.teardown() Broker(hosts=[new_cap]).checkin() @@ -242,7 +242,7 @@ def module_lb_capsule(retry_limit=3, delay=300, **broker_args): ) cap_hosts = wait_for(hosts.checkout, timeout=timeout, delay=delay) - [cap.enable_ipv6_http_proxy() for cap in cap_hosts.out] + [cap.enable_ipv6_dnf_and_rhsm_proxy() for cap in cap_hosts.out] yield cap_hosts.out [cap.teardown() for cap in cap_hosts.out] @@ -277,7 +277,7 @@ def parametrized_enrolled_sat( ): """Yields a Satellite enrolled into [IDM, AD] as parameter.""" new_sat = satellite_factory() - new_sat.enable_ipv6_http_proxy() + new_sat.enable_satellite_ipv6_http_proxy() ipa_host = IPAHost(new_sat) new_sat.register_to_cdn() if 'IDM' in request.param: @@ -337,7 +337,7 @@ def cap_ready_rhel(): 'workflow': settings.capsule.deploy_workflows.os, } with Broker(**deploy_args, host_class=Capsule) as host: - host.enable_ipv6_http_proxy() + host.enable_ipv6_dnf_and_rhsm_proxy() yield host @@ -395,7 +395,7 @@ def installer_satellite(request): ).get_command(), timeout='30m', ) - sat.enable_ipv6_http_proxy() + sat.enable_satellite_ipv6_http_proxy() if 'sanity' in request.config.option.markexpr: configure_nailgun() configure_airgun() diff --git a/robottelo/config/validators.py b/robottelo/config/validators.py index d3f254499d7..e5ac9fed416 100644 --- a/robottelo/config/validators.py +++ b/robottelo/config/validators.py @@ -36,12 +36,6 @@ Validator('server.ssh_password', default=None), Validator('server.verify_ca', default=False), Validator('server.is_ipv6', is_type_of=bool, default=False), - # validate http_proxy_ipv6_url only if is_ipv6 is True - Validator( - 'server.http_proxy_ipv6_url', - is_type_of=str, - when=Validator('server.is_ipv6', eq=True), - ), ], content_host=[ Validator('content_host.default_rhel_version', must_exist=True), @@ -161,6 +155,12 @@ 'http_proxy.password', must_exist=True, ), + # validate http_proxy_ipv6_url only if server.is_ipv6 is True + Validator( + 'http_proxy.http_proxy_ipv6_url', + is_type_of=str, + when=Validator('server.is_ipv6', eq=True), + ), ], ipa=[ Validator( diff --git a/robottelo/host_helpers/contenthost_mixins.py b/robottelo/host_helpers/contenthost_mixins.py index 4433334ace7..d5f8043e807 100644 --- a/robottelo/host_helpers/contenthost_mixins.py +++ b/robottelo/host_helpers/contenthost_mixins.py @@ -110,11 +110,11 @@ def download_repofile(self, product=None, release=None, snap='', proxy=None): """Downloads the tools/client, capsule, or satellite repos on the machine""" product, release, v_major, _ = self._dogfood_helper(product, release) if not proxy and settings.server.is_ipv6: - proxy = settings.server.http_proxy_ipv6_url + proxy = settings.http_proxy.http_proxy_ipv6_url url = dogfood_repofile_url(settings.ohsnap, product, release, v_major, snap, proxy=proxy) command = f'curl -o /etc/yum.repos.d/{product}.repo -L {url}' if settings.server.is_ipv6: - command += f' -x {settings.server.http_proxy_ipv6_url}' + command += f' -x {settings.http_proxy.http_proxy_ipv6_url}' self.execute(command) def dogfood_repository(self, repo=None, product=None, release=None, snap=''): diff --git a/robottelo/hosts.py b/robottelo/hosts.py index 642824ebf75..e83dc3f3ea5 100644 --- a/robottelo/hosts.py +++ b/robottelo/hosts.py @@ -854,22 +854,39 @@ def put_ssh_key(self, source_key_path, destination_key_name): raise CLIFactoryError(f'Failed to chmod ssh key file:\n{result.stderr}') def enable_rhsm_proxy(self, hostname, port=None): - """Configures proxy for subscription manager""" + """Configures HTTP proxy for subscription manager""" cmd = f"subscription-manager config --server.proxy_hostname={hostname}" if port: cmd += f' --server.proxy_port={port}' + logger.info(f'Configuring {hostname} HTTP proxy for subscription manager.') self.execute(cmd) def enable_dnf_proxy(self, hostname, scheme=None, port=None): - """Configures proxy for dnf""" + """Configures HTTP proxy for dnf""" if not scheme: scheme = 'http' cmd = f"echo -e 'proxy = {scheme}://{hostname}" if port: cmd += f':{port}' cmd += "' >> /etc/dnf/dnf.conf" + logger.info(f'Configuring {hostname} HTTP proxy for dnf.') self.execute(cmd) + def disable_rhsm_proxy(self): + """Disables HTTP proxy for subscription manager""" + self.execute('subscription-manager remove server.proxy_hostname server.proxy_port') + + def disable_dnf_proxy(self): + """Disable HTTP proxy for dnf""" + self.execute('sed -i "/^proxy/d" /etc/dnf/dnf.conf') + + def enable_ipv6_dnf_and_rhsm_proxy(self): + """Execute procedures for enabling rhsm and dnf IPv6 HTTP Proxy""" + if self.ipv6: + url = urlparse(settings.http_proxy.http_proxy_ipv6_url) + self.enable_rhsm_proxy(url.hostname, url.port) + self.enable_dnf_proxy(url.hostname, url.scheme, url.port) + def add_authorized_key(self, pub_key): """Inject a public key into the authorized keys file @@ -994,7 +1011,7 @@ def configure_puppet( # sat6 under the capsule --> certifcates or on capsule via cli "puppetserver # ca list", so that we sign it. self.execute('/opt/puppetlabs/bin/puppet agent -t') - proxy_host = Host(hostname=proxy_hostname, ipv6=settings.server.is_ipv6) + proxy_host = Host(hostname=proxy_hostname, ipv6=self.ipv6) proxy_host.execute(f'puppetserver ca sign --certname {cert_name}') if run_puppet_agent: @@ -1441,8 +1458,8 @@ def register_to_cdn(self, pool_ids=None): self.reset_rhsm() # Enabling proxy for IPv6 - if settings.server.is_ipv6: - url = urlparse(settings.server.http_proxy_ipv6_url) + if self.ipv6: + url = urlparse(settings.http_proxy.http_proxy_ipv6_url) self.enable_rhsm_proxy(url.hostname, url.port) self.enable_dnf_proxy(url.hostname, url.scheme, url.port) @@ -1611,19 +1628,6 @@ def enable_capsule_downstream_repos(self): snap=settings.capsule.version.snap, ) - def enable_ipv6_http_proxy(self): - """Execute procedures for enabling IPv6 HTTP Proxy on Capsule using SM""" - if settings.server.is_ipv6: - url = urlparse(settings.server.http_proxy_ipv6_url) - self.enable_rhsm_proxy(url.hostname, url.port) - self.enable_dnf_proxy(url.hostname, url.scheme, url.port) - self.ipv6 = settings.server.is_ipv6 - - def disable_ipv6_http_proxy(self): - """Executes procedures for disabling IPv6 HTTP Proxy on Capsule""" - if settings.server.is_ipv6: - self.execute('subscription-manager remove server.proxy_hostname server.proxy_port') - def capsule_setup(self, sat_host=None, capsule_cert_opts=None, **installer_kwargs): """Prepare the host and run the capsule installer""" self._satellite = sat_host or Satellite() @@ -1789,56 +1793,6 @@ def _swap_nailgun(self, new_version): to_clear = [k for k in sys.modules if 'nailgun' in k] [sys.modules.pop(k) for k in to_clear] - def enable_ipv6_http_proxy(self): - """Execute procedures for enabling IPv6 HTTP Proxy""" - if not settings.server.is_ipv6: - logger.warning( - 'The IPv6 HTTP Proxy setting is not enabled. Skipping the IPv6 HTTP Proxy setup.' - ) - return None - self.ipv6 = settings.server.is_ipv6 - proxy_name = 'Robottelo IPv6 Automation Proxy' - if not self.cli.HttpProxy.exists(search=('name', proxy_name)): - http_proxy = self.api.HTTPProxy( - name=proxy_name, url=settings.server.http_proxy_ipv6_url - ).create() - else: - logger.info( - 'The IPv6 HTTP Proxy is already enabled. Skipping the IPv6 HTTP Proxy setup.' - ) - http_proxy = self.api.HTTPProxy().search(query={'search': f'name="{proxy_name}"'})[0] - # Setting HTTP Proxy as default in the settings - self.cli.Settings.set( - { - 'name': 'content_default_http_proxy', - 'value': proxy_name, - } - ) - self.cli.Settings.set( - { - 'name': 'http_proxy', - 'value': settings.server.http_proxy_ipv6_url, - } - ) - return http_proxy - - def disable_ipv6_http_proxy(self, http_proxy): - """Execute procedures for disabling IPv6 HTTP Proxy""" - if http_proxy: - http_proxy.delete() - self.cli.Settings.set( - { - 'name': 'content_default_http_proxy', - 'value': '', - } - ) - self.cli.Settings.set( - { - 'name': 'http_proxy', - 'value': '', - } - ) - @property def api(self): """Import all nailgun entities and wrap them under self.api""" @@ -1984,6 +1938,65 @@ def satellite(self): return self return self._satellite + def enable_satellite_http_proxy(self): + """Execute procedures for setting HTTP Proxy in Satellite settings. + Sets an HTTP proxy for all outgoing HTTP(S) connections from Satellite and + default HTTP proxy for syncing content. + """ + http_proxy_name = 'IPv4 HTTP Proxy for Content sync' + http_proxy_url = settings.http_proxy.un_auth_proxy_url + if self.ipv6: + http_proxy_name = 'IPv6 HTTP Proxy for Content sync' + http_proxy_url = settings.http_proxy.http_proxy_ipv6_url + if not self.cli.HttpProxy.exists(search=('name', http_proxy_name)): + http_proxy = self.api.HTTPProxy(name=http_proxy_name, url=http_proxy_url).create() + else: + logger.info('The HTTP Proxy is already enabled. Skipping the HTTP Proxy setup.') + http_proxy = self.api.HTTPProxy().search(query={'search': f'name="{http_proxy_name}"'})[ + 0 + ] + # Setting HTTP Proxy as default in the settings + logger.info( + f'Setting {http_proxy_name} as content_default_http_proxy in Satellite settings.' + ) + self.cli.Settings.set( + { + 'name': 'content_default_http_proxy', + 'value': http_proxy_name, + } + ) + logger.info(f'Setting {http_proxy_name} as general http_proxy in Satellite settings.') + self.cli.Settings.set( + { + 'name': 'http_proxy', + 'value': http_proxy_url, + } + ) + return http_proxy + + def disable_satellite_http_proxy(self, http_proxy): + """Execute procedures for disabling HTTP Proxy in Satellite settings.""" + if http_proxy: + http_proxy.delete() + self.cli.Settings.set( + { + 'name': 'content_default_http_proxy', + 'value': '', + } + ) + self.cli.Settings.set( + { + 'name': 'http_proxy', + 'value': '', + } + ) + + def enable_satellite_ipv6_http_proxy(self): + """Execute procedures for setting ipv6 HTTP Proxy in Satellite settings, rhsm and dnf.""" + if self.ipv6: + self.enable_satellite_http_proxy() + self.enable_ipv6_dnf_and_rhsm_proxy() + def is_remote_db(self): return ( self.execute(f'grep "db_manage: false" {constants.SATELLITE_ANSWER_FILE}').status == 0 diff --git a/tests/foreman/api/test_http_proxy.py b/tests/foreman/api/test_http_proxy.py index a0a9a7d4eb0..5d8cc686438 100644 --- a/tests/foreman/api/test_http_proxy.py +++ b/tests/foreman/api/test_http_proxy.py @@ -60,7 +60,7 @@ def test_positive_end_to_end( setup_http_proxy, module_target_sat, module_org, module_repos_collection_with_manifest ): - """End-to-end test for HTTP Proxy related scenarios. + """End-to-end test for HTTP proxy related scenarios. :id: 38df5479-9127-49f3-a30e-26b33655971a diff --git a/tests/foreman/installer/test_installer.py b/tests/foreman/installer/test_installer.py index 60d9d05ce0f..80d5faff8d5 100644 --- a/tests/foreman/installer/test_installer.py +++ b/tests/foreman/installer/test_installer.py @@ -301,7 +301,7 @@ def sat_default_install(module_sat_ready_rhels): ] sat = module_sat_ready_rhels.pop() install_satellite(sat, installer_args) - sat.enable_ipv6_http_proxy() + sat.enable_satellite_ipv6_http_proxy() return sat @@ -314,7 +314,8 @@ def sat_fapolicyd_install(module_sat_ready_rhels): ] sat = module_sat_ready_rhels.pop() install_satellite(sat, installer_args, enable_fapolicyd=True) - sat.enable_ipv6_http_proxy() + sat.enable_ipv6_dnf_and_rhsm_proxy() + sat.enable_satellite_http_proxy() return sat @@ -331,7 +332,7 @@ def sat_non_default_install(module_sat_ready_rhels): ] sat = module_sat_ready_rhels.pop() install_satellite(sat, installer_args, enable_fapolicyd=True) - sat.enable_ipv6_http_proxy() + sat.enable_satellite_ipv6_http_proxy() sat.execute('dnf -y --disableplugin=foreman-protector install foreman-discovery-image') return sat @@ -340,14 +341,8 @@ def sat_non_default_install(module_sat_ready_rhels): @pytest.mark.tier1 @pytest.mark.pit_server @pytest.mark.build_sanity -@pytest.mark.parametrize( - 'setting_update', - [f'http_proxy={settings.http_proxy.un_auth_proxy_url}'], - indirect=True, - ids=["un_auth_proxy"], -) def test_capsule_installation( - pytestconfig, sat_fapolicyd_install, cap_ready_rhel, module_sca_manifest, setting_update + pytestconfig, sat_fapolicyd_install, cap_ready_rhel, module_sca_manifest ): """Run a basic Capsule installation with fapolicyd