]> git-server-git.apps.pok.os.sepia.ceph.com Git - teuthology.git/commitdiff
orchestra/remote: add resolve_ip method
authorKyr Shatskyy <kyrylo.shatskyy@clyso.com>
Wed, 19 Mar 2025 13:49:51 +0000 (14:49 +0100)
committerKyr Shatskyy <kyrylo.shatskyy@clyso.com>
Wed, 23 Jul 2025 11:37:42 +0000 (13:37 +0200)
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 <kyrylo.shatskyy@clyso.com>
teuthology/orchestra/remote.py
teuthology/orchestra/test/test_remote.py

index 4212ab9fba8ce30f1b59972875cd202c5e39cfe5..36d695e3aaefd9767a2e765a3f7093a11f245646 100644 (file)
@@ -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'):
index ef9fb4406a592b12d44866d833127f5af5c2545e..1328bc83e107bc6b256b55c15a4c8bb80a95e271 100644 (file)
@@ -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"