Skip to content

Commit

Permalink
use generic connection interface from latest pytest-mh
Browse files Browse the repository at this point in the history
Latest version added an option to replace SSH connections with podman
or docker, therefore a generic interface was created. Most notably,
`host.ssh` was replaced with `host.conn`.
  • Loading branch information
pbrezina committed Aug 2, 2024
1 parent f0c03cb commit 5668acf
Show file tree
Hide file tree
Showing 36 changed files with 362 additions and 324 deletions.
11 changes: 6 additions & 5 deletions docs/config.rst
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ Basic definition
hosts:
- hostname: <dns host name>
role: <host role>
ssh:
conn:
type: ssh
host: <ssh host> (optional, defaults to host name)
port: <ssh port> (optional, defaults to 22)
username: <ssh username> (optional, defaults to "root")
Expand All @@ -41,11 +42,11 @@ domain has ``id`` attribute and defines the list of available hosts.
* ``hostname``: DNS host name, it may not necessarily be resolvable from the
machine that runs pytest
* ``role``: host role
* ``ssh.host``: ssh host to connect to (it may be a resolvable host name or an
* ``conn.host``: ssh host to connect to (it may be a resolvable host name or an
IP address), defaults to the value of ``hostname``
* ``ssh.port``: ssh port, defaults to 22
* ``ssh.username``: ssh username, defaults to ``root``
* ``ssh.password``: ssh password for the user, defaults to ``Secret123``
* ``conn.port``: ssh port, defaults to 22
* ``conn.username``: ssh username, defaults to ``root``
* ``conn.password``: ssh password for the user, defaults to ``Secret123``
* ``config``: additional configuration, place for custom options, see
:ref:`custom-config`
* ``artifacts``: list of artifacts that are automatically downloaded, see
Expand Down
4 changes: 2 additions & 2 deletions docs/guides/using-roles.rst
Original file line number Diff line number Diff line change
Expand Up @@ -461,10 +461,10 @@ needed, you can also run commands on the host directly:
@pytest.mark.topology(KnownTopology.AD)
def test_client(client: Client, ad: AD):
# Commands are executed in bash on Linux systems
client.host.ssh.run('echo "test"')
client.host.conn.run('echo "test"')
# And in Powershell on Windows
ad.host.ssh.run('Write-Output "test"')
ad.host.conn.run('Write-Output "test"')
.. seealso::

Expand Down
5 changes: 2 additions & 3 deletions docs/guides/using-ssh.rst
Original file line number Diff line number Diff line change
@@ -1,10 +1,9 @@
Using SSH
#########

You can use :class:`pytest_mh.ssh.SSHClient` to connect to any host as any
You can use :class:`pytest_mh.conn.ssh.SSHClient` to connect to any host as any
user. It is not recommended to instantiate this class on yourself but you should
rather use :meth:`pytest_mh.MultihostRole.ssh` to get the client
object.
rather use :meth:`pytest_mh.MultihostRole.ssh` to get the client object.

Once you establish SSH connections, you can run commands on the remote host in
both blocking and non-blocking mode.
Expand Down
2 changes: 1 addition & 1 deletion requirements.txt
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
jc
pytest
python-ldap
pytest-mh >= 1.0.17
pytest-mh >= 1.0.18
12 changes: 6 additions & 6 deletions sssd_test_framework/hosts/ad.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import PureWindowsPath
from typing import Any

from pytest_mh.ssh import SSHLog
from pytest_mh.conn import ProcessLogLevel

from .base import BaseDomainHost

Expand Down Expand Up @@ -88,7 +88,7 @@ def naming_context(self) -> str:
:rtype: str
"""
if not self.__naming_context:
result = self.ssh.run("Write-Host (Get-ADRootDSE).rootDomainNamingContext")
result = self.conn.run("Write-Host (Get-ADRootDSE).rootDomainNamingContext")
nc = result.stdout.strip()
if not nc:
raise ValueError("Unable to find default naming context")
Expand Down Expand Up @@ -118,7 +118,7 @@ def backup(self) -> Any:
"""
self.logger.info("Creating backup of Active Directory")

result = self.ssh.run(
result = self.conn.run(
rf"""
$basedn = '{self.naming_context}'
$sitesdn = "cn=sites,cn=configuration,$basedn"
Expand Down Expand Up @@ -151,7 +151,7 @@ def backup(self) -> Any:
Write-Output $tmpdir.FullName
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)

return PureWindowsPath(result.stdout.strip())
Expand Down Expand Up @@ -185,7 +185,7 @@ def restore(self, backup_data: Any | None) -> None:
backup_path = str(backup_data)
self.logger.info(f"Restoring Active Directory from {backup_path}")

self.ssh.run(
self.conn.run(
rf"""
$basedn = '{self.naming_context}'
$sitesdn = "cn=sites,cn=configuration,$basedn"
Expand Down Expand Up @@ -253,5 +253,5 @@ def restore(self, backup_data: Any | None) -> None:
# If we got here, make sure we exit with 0
Exit 0
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)
31 changes: 17 additions & 14 deletions sssd_test_framework/hosts/base.py
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
import ldap
from ldap.ldapobject import ReconnectLDAPObject
from pytest_mh import MultihostHost
from pytest_mh.ssh import SSHPowerShellProcess
from pytest_mh.conn import Powershell
from pytest_mh.utils.fs import LinuxFileSystem
from pytest_mh.utils.services import SystemdServices

Expand Down Expand Up @@ -86,10 +86,10 @@ def remove_backup(self, backup_data: Any | None) -> None:
else:
raise TypeError(f"Only PurePath is supported as backup_data, got {type(backup_data)}")

if self.ssh.shell is SSHPowerShellProcess:
self.ssh.exec(["Remove-Item", "-Force", "-Recurse", path])
if isinstance(self.conn.shell, Powershell):
self.conn.exec(["Remove-Item", "-Force", "-Recurse", path])
else:
self.ssh.exec(["rm", "-fr", path])
self.conn.exec(["rm", "-fr", path])

@abstractmethod
def start(self) -> None:
Expand Down Expand Up @@ -213,19 +213,22 @@ def __init__(self, *args, tls: bool = True, **kwargs) -> None:
"""Bind password ``config.bindpw``, defaults to ``Secret123``"""

# Lazy properties.
self.__conn: ReconnectLDAPObject | None = None
self.__ldap_conn: ReconnectLDAPObject | None = None
self.__naming_context: str | None = None

@property
@retry(on=ldap.SERVER_DOWN)
def conn(self) -> ReconnectLDAPObject:
def ldap_conn(self) -> ReconnectLDAPObject:
"""
LDAP connection (``python-ldap`` library).
:rtype: ReconnectLDAPObject
"""
if not self.__conn:
newconn = ReconnectLDAPObject(f"ldap://{self.ssh_host}")
if not self.__ldap_conn:
# Use host from SSH if possible, otherwise fallback to hostname
host = getattr(self.conn, "host", self.hostname)

newconn = ReconnectLDAPObject(f"ldap://{host}")
newconn.protocol_version = ldap.VERSION3
newconn.set_option(ldap.OPT_REFERRALS, 0)

Expand All @@ -235,9 +238,9 @@ def conn(self) -> ReconnectLDAPObject:
newconn.start_tls_s()

newconn.simple_bind_s(self.binddn, self.bindpw)
self.__conn = newconn
self.__ldap_conn = newconn

return self.__conn
return self.__ldap_conn

@property
def naming_context(self) -> str:
Expand All @@ -249,7 +252,7 @@ def naming_context(self) -> str:
"""
if not self.__naming_context:
attr = "defaultNamingContext"
result = self.conn.search_s("", ldap.SCOPE_BASE, attrlist=[attr])
result = self.ldap_conn.search_s("", ldap.SCOPE_BASE, attrlist=[attr])
if len(result) != 1:
raise ValueError(f"Unexpected number of results for rootDSE query: {len(result)}")

Expand All @@ -265,9 +268,9 @@ def disconnect(self) -> None:
"""
Disconnect LDAP connection.
"""
if self.__conn is not None:
self.__conn.unbind()
self.__conn = None
if self.__ldap_conn is not None:
self.__ldap_conn.unbind()
self.__ldap_conn = None

def ldap_result_to_dict(
self, result: list[tuple[str, dict[str, list[bytes]]]]
Expand Down
14 changes: 7 additions & 7 deletions sssd_test_framework/hosts/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import PurePosixPath
from typing import Any

from pytest_mh.ssh import SSHLog
from pytest_mh.conn import ProcessLogLevel

from .base import BaseBackupHost, BaseLinuxHost

Expand Down Expand Up @@ -39,7 +39,7 @@ def features(self) -> dict[str, bool]:
return self._features

self.logger.info(f"Detecting SSSD's features on {self.hostname}")
result = self.ssh.run(
result = self.conn.run(
"""
set -ex
Expand All @@ -52,7 +52,7 @@ def features(self) -> dict[str, bool]:
MANWIDTH=10000 man sssd.conf | grep -q "id_provider = ldap or id_provider = proxy" && \
echo "limited_enumeration" || :
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)

# Set default values
Expand Down Expand Up @@ -91,7 +91,7 @@ def backup(self) -> Any:
"""
self.logger.info("Creating backup of SSSD client")

result = self.ssh.run(
result = self.conn.run(
"""
set -ex
Expand All @@ -110,7 +110,7 @@ def backup(self) -> Any:
echo $path
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)

return PurePosixPath(result.stdout_lines[-1].strip())
Expand All @@ -131,7 +131,7 @@ def restore(self, backup_data: Any | None) -> None:
backup_path = str(backup_data)

self.logger.info(f"Restoring SSSD data from {backup_path}")
self.ssh.run(
self.conn.run(
f"""
set -ex
Expand All @@ -149,5 +149,5 @@ def restore(self, backup_data: Any | None) -> None:
restore "{backup_path}/logs" /var/log/sssd
restore "{backup_path}/lib" /var/lib/sss
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)
16 changes: 8 additions & 8 deletions sssd_test_framework/hosts/ipa.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import PurePosixPath
from typing import Any

from pytest_mh.ssh import SSHLog
from pytest_mh.conn import ProcessLogLevel

from ..misc.ssh import retry_command
from .base import BaseDomainHost, BaseLinuxHost
Expand Down Expand Up @@ -79,15 +79,15 @@ def features(self) -> dict[str, bool]:

self.logger.info(f"Detecting features on {self.hostname}")

result = self.ssh.run(
result = self.conn.run(
"""
set -ex
[ -f "/usr/libexec/sssd/passkey_child" ] && \
ipa help user | grep user-add-passkey 1> /dev/null && \
echo "passkey" || :
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)

# Set default values
Expand All @@ -104,7 +104,7 @@ def kinit(self) -> None:
"""
Obtain ``admin`` user Kerberos TGT.
"""
self.ssh.exec(["kinit", "admin"], input=self.adminpw)
self.conn.exec(["kinit", "admin"], input=self.adminpw)

def start(self) -> None:
self.svc.start("ipa.service")
Expand All @@ -126,7 +126,7 @@ def backup(self) -> Any:
# Race condition: https://pagure.io/freeipa/issue/9584
@retry_command(delay=0, match_stderr="Unable to add LDIF task: This entry already exists")
def _backup():
return self.ssh.run(
return self.conn.run(
"""
set -ex
Expand All @@ -148,7 +148,7 @@ def _backup():
echo $path
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)

self.logger.info("Creating backup of IPA server")
Expand All @@ -174,7 +174,7 @@ def restore(self, backup_data: Any | None) -> None:
backup_path = str(backup_data)
self.logger.info(f"Restoring IPA server from {backup_path}")

self.ssh.run(
self.conn.run(
f"""
set -ex
Expand All @@ -194,6 +194,6 @@ def restore(self, backup_data: Any | None) -> None:
restore "{backup_path}/logs" /var/log/sssd
restore "{backup_path}/lib" /var/lib/sss
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)
self.svc.restart("sssd.service")
8 changes: 4 additions & 4 deletions sssd_test_framework/hosts/kdc.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
from pathlib import PurePosixPath
from typing import Any

from pytest_mh.ssh import SSHLog
from pytest_mh.conn import ProcessLogLevel

from .base import BaseDomainHost, BaseLinuxHost

Expand Down Expand Up @@ -62,14 +62,14 @@ def backup(self) -> Any:
"""
self.logger.info("Creating backup of KDC")

result = self.ssh.run(
result = self.conn.run(
"""
set -e
path=`mktemp`
kdb5_util dump $path && rm -f "$path.dump_ok"
echo $path
""",
log_level=SSHLog.Error,
log_level=ProcessLogLevel.Error,
)
return PurePosixPath(result.stdout_lines[-1].strip())

Expand All @@ -89,4 +89,4 @@ def restore(self, backup_data: Any | None) -> None:
backup_path = str(backup_data)
self.logger.info(f"Restoring KDC from {backup_path}")

self.ssh.run(f'kdb5_util load "{backup_path}"', log_level=SSHLog.Error)
self.conn.run(f'kdb5_util load "{backup_path}"', log_level=ProcessLogLevel.Error)
Loading

0 comments on commit 5668acf

Please sign in to comment.