import socket from typing import Optional from functools import lru_cache @lru_cache(maxsize=1024) def get_hostname(ip: str) -> Optional[str]: try: # Python's equivalent to Resolv.getname(ip) # returns (hostname, aliaslist, ipaddrlist) return socket.gethostbyaddr(ip)[0] except socket.error: return None @lru_cache(maxsize=1024) def get_ip(hostname: str) -> Optional[str]: try: return socket.gethostbyname(hostname) except socket.error: return None def is_valid_hostname(hostname: str) -> bool: """ Checks if a hostname resolves to an IP. """ if not hostname: return False return get_ip(hostname) is not None def to_mgt_ip(name_or_ip: str) -> Optional[str]: """ Mimics the Ruby script's to_mgt_ip logic: 1. Reverse lookup IP to get FQDN. 2. Construct management FQDN ({host}.ds.gc.ca or .pre-ds.gc.ca). 3. Resolve that management FQDN to an IP. 4. Return the Management FQDN if successful. """ # In Ruby script, input 'name' is often an IP address from the WIF source column. # Step 1: Reverse Lookup fqdn = get_hostname(name_or_ip) if not fqdn: # If input is already a name, use it? Ruby script assumes it gets a name from Resolv.getname(ip) # If name_or_ip is NOT an IP, gethostbyaddr might fail or behave differently. # But if it's already a name, we can try using it. fqdn = name_or_ip short_name = fqdn.split('.')[0] # Step 2 & 3: Try suffixes suffixes = ['.ds.gc.ca', '.pre-ds.gc.ca'] for suffix in suffixes: mgt_dns = short_name + suffix resolved_ip = get_ip(mgt_dns) if resolved_ip: # Ruby: return mgt_dns if mgt_ip.to_s.length > 4 return mgt_dns # print(f"Warning: {name_or_ip} could not be resolved to a management address.") return None