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
52 changes: 52 additions & 0 deletions sssd_test_framework/utils/tools.py
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
from pytest_mh.utils.fs import LinuxFileSystem

__all__ = [
"AHostSv4Entry",
"GetentUtils",
"GroupEntry",
"LinuxToolsUtils",
Expand Down Expand Up @@ -601,6 +602,32 @@ def FromOutput(cls, stdout: str) -> HostsEntry:
return cls.FromList(result)


class AHostSv4Entry(object):
"""
Result of ``getent ahostsv4`` — first IPv4 from the first data line.

Same style as :class:`HostsEntry` (use the ``.ip`` field).
"""

def __init__(self, ip: str | None) -> None:
self.ip: str | None = ip
"""IPv4 dotted-quad (first column of ``getent ahostsv4`` output)."""
Comment on lines +612 to +614
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

For consistency with other entry classes in this file (such as HostsEntry), AHostSv4Entry should implement __str__ and __repr__ methods. This is helpful for debugging and logging during test execution.

    def __init__(self, ip: str | None) -> None: 
        self.ip: str | None = ip 
        """IPv4 dotted-quad (first column of ``getent ahostsv4`` output).""" 
 
    def __str__(self) -> str: 
        return f"({self.ip})" 
 
    def __repr__(self) -> str: 
        return str(self)


def __str__(self) -> str:
return f"({self.ip})"

def __repr__(self) -> str:
return str(self)

@classmethod
def FromOutput(cls, stdout: str) -> AHostSv4Entry | None:
for line in stdout.splitlines():
parts = line.split()
if parts:
return cls(ip=parts[0])
return None


class NetworksEntry(object):
"""
Result of ``getent networks``
Expand Down Expand Up @@ -1058,6 +1085,31 @@ def hosts(self, name: str, *, service: str | None = None) -> HostsEntry:
"""
return self.__exec(HostsEntry, "hosts", name, service)

def ahostsv4(self, name: str, *, service: str | None = None) -> AHostSv4Entry | None:
"""
Call ``getent ahostsv4`` and return the first IPv4 address from output.

For DNS lookups using ``dig`` (A, SRV, etc.), use
:meth:`NetworkUtils.dig <sssd_test_framework.utils.network.NetworkUtils.dig>`
on ``client.net`` instead of ad hoc shell commands in tests.

:param name: Host name or address to resolve.
:type name: str
:param service: Optional NSS service name (``getent -s``).
:type service: str | None, optional
:return: Parsed entry or ``None`` if lookup failed or produced no address.
:rtype: AHostSv4Entry | None
"""
args: list[str] = []
if service is not None:
args = ["-s", service]

command = self.host.conn.exec(["getent", *args, "ahostsv4", str(name)], raise_on_error=False)
if command.rc != 0:
return None

return AHostSv4Entry.FromOutput(command.stdout)

def networks(self, name: str, *, service: str | None = None) -> NetworksEntry:
"""
Call ``getent networks $name``
Expand Down
25 changes: 25 additions & 0 deletions tests/test_tools.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
"""Unit tests for :mod:`sssd_test_framework.utils.tools`."""

from __future__ import annotations

import pytest

from sssd_test_framework.utils.tools import AHostSv4Entry


@pytest.mark.parametrize(
"stdout, expected_ip",
[
("192.168.1.1 STREAM hostname.example\n", "192.168.1.1"),
("10.0.0.5 STREAM foo\n10.0.0.6 STREAM foo\n", "10.0.0.5"),
("", None),
("\n\n", None),
],
)
def test_ahostsv4_entry_from_output(stdout: str, expected_ip: str | None) -> None:
entry = AHostSv4Entry.FromOutput(stdout)
if expected_ip is None:
assert entry is None
else:
assert entry is not None
assert entry.ip == expected_ip
Loading