From: Kyr Shatskyy Date: Wed, 19 Mar 2025 13:49:51 +0000 (+0100) Subject: orchestra/remote: add resolve_ip method X-Git-Tag: 1.2.2~11^2~3 X-Git-Url: http://git-server-git.apps.pok.os.sepia.ceph.com/?a=commitdiff_plain;h=8c5ce421eab7e5a4abd8e19ee8a8aba5abdefd99;p=teuthology.git orchestra/remote: add resolve_ip method Add utility method to resolve a hostname from within remote host. This is useful to resolve ip address of the remote host itself, because getting ip address from transport object of ssh is not suitable because it may have only bastion host and port, which is not relevant for the purpose of configuring a cluster. Signed-off-by: Kyr Shatskyy --- diff --git a/teuthology/orchestra/remote.py b/teuthology/orchestra/remote.py index 4212ab9fb..36d695e3a 100644 --- a/teuthology/orchestra/remote.py +++ b/teuthology/orchestra/remote.py @@ -461,6 +461,46 @@ class Remote(RemoteShell): return raise RuntimeError("Could not determine interface/CIDR!") + + def resolve_ip(self, name=None, ipv='4') -> str: + """ + Resolve IP address of the remote host via remote host + + Because remote object maybe behind bastion host we need + the remote host address resolvable from remote side. + So in order to the ip address we just call `host` remotely + and parse output like: + 'smithi001.front.sepia.ceph.com has address 172.21.15.1\n' + + :param name: hostname to resolve, by defaults remote host itself. + :param ipv: the IP version, 4 or 6, defaults to 4. + + :raises: :class:`Exception`: when the hostname cannot be resolved. + :raises: :class:`ValueError`: when the ipv argument mismatch 4 or 6. + + :returns: str object, the ip addres of the remote host. + """ + hostname = name or self.hostname + version = str(ipv) + if version in ['4', '6']: + remote_host_ip = self.sh(f'host -{ipv} {hostname}') + else: + raise ValueError(f'Unknown IP version {ipv}, expected 4 or 6') + # `host -4` or `host -6` may have multiline output: a host can have + # several address; thus try and find the first suitable + for info in remote_host_ip.split("\n"): + if version == '4' and 'has address' in info: + (host, ip) = info.strip().split(' has address ') + if hostname in host: + return ip + elif version == '6' and 'has IPv6 address' in info: + (host, ip) = info.strip().split(' has IPv6 address ') + if hostname in host: + return ip + else: + raise Exception(f'Cannot get IPv{ipv} address for the host "{hostname}" via remote "{self.hostname}"') + + @property def hostname(self): if not hasattr(self, '_hostname'): diff --git a/teuthology/orchestra/test/test_remote.py b/teuthology/orchestra/test/test_remote.py index ef9fb4406..1328bc83e 100644 --- a/teuthology/orchestra/test/test_remote.py +++ b/teuthology/orchestra/test/test_remote.py @@ -221,6 +221,31 @@ class TestRemote(object): rem2._runner = m_run assert not rem2.is_container + @patch("teuthology.orchestra.remote.Remote.sh") + def test_resolve_ip(self, m_sh): + r = remote.Remote(name="jdoe@xyzzy.example.com", ssh=self.m_ssh) + m_sh.return_value = "smithi001.front.sepia.ceph.com has address 172.21.15.1\n" + ip4 = remote.Remote.resolve_ip(r, 'smithi001') + assert ip4 == "172.21.15.1" + m_sh.return_value = "\n" + try: + ip4 = remote.Remote.resolve_ip(r, 'smithi001') + except Exception as e: + assert 'Cannot get IPv4 address' in str(e) + try: + ip4 = remote.Remote.resolve_ip(r, 'smithi001', 5) + except Exception as e: + assert 'Unknown IP version' in str(e) + + m_sh.return_value = ("google.com has address 142.251.37.14\n" + "google.com has IPv6 address 2a00:1450:4016:80b::200e\n" + "google.com mail is handled by 10 smtp.google.com.\n") + ip4 = remote.Remote.resolve_ip(r, 'google.com') + assert ip4 == "142.251.37.14" + ip6 = remote.Remote.resolve_ip(r, 'google.com', '6') + assert ip6 == "2a00:1450:4016:80b::200e" + + @patch("teuthology.orchestra.remote.Remote.run") def test_write_file(self, m_run): file = "fakefile"