Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Refactor ipv6 http_proxy code #16928

Merged
merged 1 commit into from
Nov 21, 2024
Merged
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
1 change: 1 addition & 0 deletions conf/http_proxy.yaml.template
Original file line number Diff line number Diff line change
@@ -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
12 changes: 12 additions & 0 deletions conf/migrations.py
Original file line number Diff line number Diff line change
Expand Up @@ -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')
2 changes: 0 additions & 2 deletions conf/server.yaml.template
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
2 changes: 1 addition & 1 deletion pytest_fixtures/component/maintain.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
sat.enable_satellite_ipv6_http_proxy()
sat.enable_satellite_ipv6_http_proxy()

Arent we duplicating the sat here?

The object name sat itself prouds the reference onto which HTTP proxy is being enabled. Hence the explicit satellite keyword in function name should now be needed.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

I don't understand what you mean; your code suggestion has no change.

Copy link
Member

Choose a reason for hiding this comment

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

Suggested change
sat.enable_satellite_ipv6_http_proxy()
sat.enable_ipv6_http_proxy()

Copy link
Member

Choose a reason for hiding this comment

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

Isn't it better to be explicit in the name? This method does setup related to Satellite - it changes Satellite's settings through API.
I, as a code reader, find it more straightforward to read enable_satellite_ipv6_http_proxy or enable_sat_ipv6_http_proxy as it tells me that it does something with Satellite itself.

The enable_ipv6_http_proxy method can be defined in the ContentHost class to do something completely different from modifying Satellite settings.

Host object methods count is constantly increasing, therefore I advocate for explicitness - for keeping satellite or sat in the method name.

Copy link
Member

Choose a reason for hiding this comment

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

i agree that satellite is redundant here, the vast majority of the functionality in this repo acts against a Satellite and this method is defined under a Satellite class.

how about we really clarify what's going on with the name by changing the name to reflect that this is updating proxy settings to use an ipv6 proxy.

perhaps something like ensure_ipv6_proxy_settings

yield sat
else:
module_target_sat.register_to_cdn(pool_ids=settings.subscription.fm_rhn_poolid.split())
Expand Down
4 changes: 2 additions & 2 deletions pytest_fixtures/core/broker.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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


Expand Down
14 changes: 7 additions & 7 deletions pytest_fixtures/core/sat_cap_factory.py
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand All @@ -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()
Expand Down Expand Up @@ -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()
lpramuk marked this conversation as resolved.
Show resolved Hide resolved
yield new_cap
new_cap.teardown()
Broker(hosts=[new_cap]).checkin()
Expand Down Expand Up @@ -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]
Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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


Expand Down Expand Up @@ -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()
Expand Down
12 changes: 6 additions & 6 deletions robottelo/config/validators.py
Original file line number Diff line number Diff line change
Expand Up @@ -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),
Expand Down Expand Up @@ -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',
Copy link
Member

Choose a reason for hiding this comment

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

I think keeping the global HTTP Proxy for all satellite applications, the server conf is a good place.

HTTP Proxy conf is made to configure within the satellite, whereas IPV6 HTTP proxy is inside and outside as well to have cross network communication.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

HTTP Proxy conf is made to configure within the satellite It's not, it can be used to configure on system level as well. It's not different from Ipv6 HTTP proxy. It can do things ipv6 can do. so I don't see a reason to differentiate. #16928 (comment)

Copy link
Member

Choose a reason for hiding this comment

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

Another reason keeping it in server.yaml is that's the yaml sets global things for robottelo including the is_ipv6 switch. Though its duplicating the key from http_proxy yaml, having it under server.yaml and near to is_ipv6 setting increases the usability of robottelo.

Copy link
Contributor Author

@jameerpathan111 jameerpathan111 Nov 18, 2024

Choose a reason for hiding this comment

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

By that logic, we'll be making server.yaml unnecessarily bulky and a lot of other settings could fall into such reasoning. Not to mention that we'll be basically ignoring the very general rule of clustering similar settings in same yaml. But I'd leave the final decision to you/JPL. I can move it back to server.yaml

is_type_of=str,
when=Validator('server.is_ipv6', eq=True),
),
],
ipa=[
Validator(
Expand Down
4 changes: 2 additions & 2 deletions robottelo/host_helpers/contenthost_mixins.py
Original file line number Diff line number Diff line change
Expand Up @@ -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=''):
Expand Down
149 changes: 81 additions & 68 deletions robottelo/hosts.py
Original file line number Diff line number Diff line change
Expand Up @@ -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.')
ogajduse marked this conversation as resolved.
Show resolved Hide resolved
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.')
ogajduse marked this conversation as resolved.
Show resolved Hide resolved
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

Expand Down Expand Up @@ -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:
Expand Down Expand Up @@ -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)

Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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"""
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion tests/foreman/api/test_http_proxy.py
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down
Loading
Loading