Source code for rmote.tools.hostname

from pathlib import Path

from rmote.protocol import Tool

_HOSTNAME_FILE = Path("/etc/hostname")
_HOSTS_FILE = Path("/etc/hosts")
_PROC_HOSTNAME = Path("/proc/sys/kernel/hostname")


[docs] class Hostname(Tool): """Manage system hostname and ``/etc/hosts`` entries. Requires root for mutating operations. Change the hostname in a disposable container with its own UTS namespace:: >>> from rmote.tools import FileSystem >>> remote = getfixture("debian_tool") >>> remote(Hostname.set, "rmote-example") True >>> remote(Hostname.set, "rmote-example") False >>> remote(Hostname.get) 'rmote-example' >>> remote(FileSystem.read_str, "/etc/hostname").strip() 'rmote-example' >>> remote(Hostname.hosts_entry, "192.0.2.1", "web") True >>> remote(Hostname.hosts_entry, "192.0.2.1", "web") False """
[docs] @staticmethod def get() -> str: """Return the current running hostname. Reads from ``/proc/sys/kernel/hostname`` which always reflects the live kernel value regardless of what ``/etc/hostname`` contains. Returns: The hostname string. """ return _PROC_HOSTNAME.read_text().strip()
[docs] @staticmethod def set(name: str) -> bool: """Set the system hostname idempotently. Applies the change immediately by writing to ``/proc/sys/kernel/hostname`` and persists it to ``/etc/hostname``. No external commands are required. Args: name: The desired hostname. Returns: ``True`` if the hostname was changed, ``False`` if it was already set to *name*. """ current = _PROC_HOSTNAME.read_text().strip() if current == name: return False _PROC_HOSTNAME.write_text(name + "\n") _HOSTNAME_FILE.write_text(name + "\n") return True
[docs] @staticmethod def hosts_entry(ip: str, *names: str) -> bool: """Ensure ``/etc/hosts`` has an entry mapping *ip* to *names*. If a line for *ip* already exists with exactly the same hostnames (in the same order), this is a no-op. If it exists with different hostnames, the line is replaced in-place. If no line exists for *ip*, one is appended. Comment lines and blank lines are left untouched. Args: ip: The IP address for the entry (IPv4 or IPv6). *names: One or more hostnames to associate with *ip*. Returns: ``True`` if ``/etc/hosts`` was modified. Raises: ValueError: If no *names* are provided. """ if not names: raise ValueError("At least one hostname name is required") desired_line = ip + "\t" + " ".join(names) original = _HOSTS_FILE.read_text() if _HOSTS_FILE.exists() else "" lines = original.splitlines(keepends=True) for i, line in enumerate(lines): stripped = line.strip() if stripped.startswith("#") or not stripped: continue parts = stripped.split() if parts and parts[0] == ip: reconstructed = ip + "\t" + " ".join(parts[1:]) if reconstructed == desired_line: return False lines[i] = desired_line + "\n" _HOSTS_FILE.write_text("".join(lines)) return True # Not found — append sep = "" if original.endswith("\n") or not original else "\n" _HOSTS_FILE.write_text(original + sep + desired_line + "\n") return True