2 Commits

Author SHA1 Message Date
a13fc5b282 Strip .prod.global.gc.ca from hostnames
All checks were successful
Build and Release / Build Windows Exe (push) Successful in 11s
2026-02-06 16:59:56 -05:00
dcddd88cbc Implement DNS caching and verbose logging
All checks were successful
Build and Release / Build Windows Exe (push) Successful in 10s
2026-02-06 16:33:13 -05:00
4 changed files with 38 additions and 4 deletions

View File

@@ -55,5 +55,12 @@ class TestInventoryKeys(unittest.TestCase):
self.assertIn("good_name", hosts) self.assertIn("good_name", hosts)
self.assertNotIn("bad_name", hosts) self.assertNotIn("bad_name", hosts)
def test_suffix_stripping(self):
from wif2ansible.parsers import clean_hostname
self.assertEqual(clean_hostname("server.prod.global.gc.ca"), "server")
self.assertEqual(clean_hostname("server.PROD.GLOBAL.GC.CA"), "server")
self.assertEqual(clean_hostname("nosuffix"), "nosuffix")
self.assertEqual(clean_hostname("other.suffix.com"), "other.suffix.com")
if __name__ == '__main__': if __name__ == '__main__':
unittest.main() unittest.main()

View File

@@ -39,13 +39,20 @@ def generate_inventory(servers: Dict[str, Server], flows: List[Flow]) -> Dict[st
# Process flows # Process flows
match_count = 0 match_count = 0
drop_count = 0 drop_count = 0
total_flows = len(flows)
for flow in flows: print(f"Starting inventory generation for {total_flows} flows...")
for idx, flow in enumerate(flows, 1):
if idx % 10 == 0:
print(f"Processing flow {idx}/{total_flows}...")
# Find source server # Find source server
server = ip_to_server.get(flow.source_ip) server = ip_to_server.get(flow.source_ip)
if not server: if not server:
# Try DNS resolution (Public IP -> Management FQDN) # Try DNS resolution (Public IP -> Management FQDN)
print(f"Flow {idx}: Source {flow.source_ip} not found in map. Attempting DNS resolution...")
mgt_dns = to_mgt_ip(flow.source_ip) mgt_dns = to_mgt_ip(flow.source_ip)
if mgt_dns: if mgt_dns:
# mgt_dns might be "server.ds.gc.ca". # mgt_dns might be "server.ds.gc.ca".
@@ -60,9 +67,11 @@ def generate_inventory(servers: Dict[str, Server], flows: List[Flow]) -> Dict[st
if not server: if not server:
drop_count += 1 drop_count += 1
if drop_count <= 5: # Debug spam limit if drop_count <= 10: # Increased debug spam limit
print(f"Dropping flow {flow.flow_id}: Source {flow.source_ip} (Mgt: {mgt_dns}) not found in Servers tab.") print(f"Dropping flow {flow.flow_id} ({idx}/{total_flows}): Source {flow.source_ip} (Mgt: {mgt_dns}) resolved but not found in Servers tab.")
continue continue
else:
print(f"Flow {idx}: Resolved {flow.source_ip} -> {server.hostname or server.reference}")
match_count += 1 match_count += 1
@@ -108,10 +117,16 @@ def generate_inventory(servers: Dict[str, Server], flows: List[Flow]) -> Dict[st
print(f"Warning: No resolvable name found for {server.primary_ip} (Candidates: {candidates}). Using IP.") print(f"Warning: No resolvable name found for {server.primary_ip} (Candidates: {candidates}). Using IP.")
final_host_key = server.primary_ip final_host_key = server.primary_ip
host_key = final_host_key # Final cleanup: Strip suffixes if user requested
from .parsers import clean_hostname
host_key = clean_hostname(final_host_key)
if host_key not in inventory_hosts: if host_key not in inventory_hosts:
host_vars = server.get_ansible_vars() host_vars = server.get_ansible_vars()
# Ensure proper ansible_host is set if key is not IP
if host_key != server.primary_ip and server.primary_ip:
host_vars['ansible_host'] = server.primary_ip
host_vars['flows'] = [] host_vars['flows'] = []
inventory_hosts[host_key] = host_vars inventory_hosts[host_key] = host_vars

View File

@@ -1,6 +1,8 @@
import socket import socket
from typing import Optional from typing import Optional
from functools import lru_cache
@lru_cache(maxsize=1024)
def get_hostname(ip: str) -> Optional[str]: def get_hostname(ip: str) -> Optional[str]:
try: try:
# Python's equivalent to Resolv.getname(ip) # Python's equivalent to Resolv.getname(ip)
@@ -9,6 +11,7 @@ def get_hostname(ip: str) -> Optional[str]:
except socket.error: except socket.error:
return None return None
@lru_cache(maxsize=1024)
def get_ip(hostname: str) -> Optional[str]: def get_ip(hostname: str) -> Optional[str]:
try: try:
return socket.gethostbyname(hostname) return socket.gethostbyname(hostname)

View File

@@ -72,6 +72,15 @@ def clean_reference(ref: str) -> str:
# Remove leading/trailing whitespace # Remove leading/trailing whitespace
return s.strip() return s.strip()
def clean_hostname(name: str) -> str:
"""
Strips specific suffixes like .prod.global.gc.ca to get shortname.
"""
if not name:
return ""
# Case insensitive strip
return re.sub(r'\.prod\.global\.gc\.ca$', '', name, flags=re.IGNORECASE)
def parse_ip(ip_str: str) -> List[str]: def parse_ip(ip_str: str) -> List[str]:
"""Finds all IPv4 addresses in a string.""" """Finds all IPv4 addresses in a string."""
if not ip_str: if not ip_str: