|
| 1 | +import asyncio |
| 2 | +import subprocess |
| 3 | +import sys |
| 4 | +import tempfile |
| 5 | + |
| 6 | +hosts_file = '/etc/hosts' |
| 7 | +begin_block = "# BEGIN DOCKER CONTAINERS" |
| 8 | +end_block = "# END DOCKER CONTAINERS" |
| 9 | + |
| 10 | +def parse_time_interval(interval_str): |
| 11 | + """Parse the time interval string into seconds.""" |
| 12 | + units = {'s': 1, 'm': 60, 'h': 3600, 'd': 86400} |
| 13 | + unit = interval_str[-1] |
| 14 | + if unit in units: |
| 15 | + try: |
| 16 | + value = int(interval_str[:-1]) |
| 17 | + return value * units[unit] |
| 18 | + except ValueError: |
| 19 | + raise ValueError("Invalid time interval format.") |
| 20 | + else: |
| 21 | + raise ValueError("Unsupported time unit.") |
| 22 | + |
| 23 | +def ensure_managed_section_exists(): |
| 24 | + """Ensures the managed section exists in the hosts file.""" |
| 25 | + with open(hosts_file, 'r+') as file: |
| 26 | + contents = file.read() |
| 27 | + # Check if both markers exist |
| 28 | + if begin_block not in contents or end_block not in contents: |
| 29 | + print(f"Appending managed section markers to {hosts_file}.", flush = True) |
| 30 | + # Move to the end of the file before appending |
| 31 | + file.write(f"\n{begin_block}\n{end_block}\n") |
| 32 | + |
| 33 | +async def update_hosts_file_async(): |
| 34 | + """ |
| 35 | + Asynchronously run the synchronous update_hosts_file function |
| 36 | + to avoid blocking the asyncio event loop. |
| 37 | + """ |
| 38 | + loop = asyncio.get_running_loop() |
| 39 | + await loop.run_in_executor(None, update_hosts_file) |
| 40 | + |
| 41 | +def update_hosts_file(): |
| 42 | + print(f"Updating hosts file", flush = True) |
| 43 | + |
| 44 | + # Create a temporary file |
| 45 | + with tempfile.NamedTemporaryFile(delete=False) as temp_file: |
| 46 | + hosts_file_tmp = temp_file.name |
| 47 | + |
| 48 | + # Build and execute the shell command |
| 49 | + shell_command = ( |
| 50 | + f"docker container ls -q | xargs -r docker container inspect | " |
| 51 | + f"jq -r '.[] | if (.NetworkSettings.Networks[].IPAddress | length > 0) then " |
| 52 | + f"\"\\(.NetworkSettings.Networks[].IPAddress) \\(.NetworkSettings.Networks[].Aliases | select(length > 0) | join(\" \")) " |
| 53 | + f"\\(.Name | sub(\"^/\"; \"\") | sub(\"_1$\"; \"\") | sub(\"-1$\"; \"\")).saltbox\" else " |
| 54 | + f"\"# no ip address: \\(.Name | sub(\"^/\"; \"\"))\" end' | " |
| 55 | + f"sed -ne \"/^{begin_block}$/ {{p; r /dev/stdin\" -e \":a; n; /^{end_block}$/ {{p; b}}; ba}}; p\" {hosts_file} " |
| 56 | + f"> {hosts_file_tmp}" |
| 57 | + ) |
| 58 | + |
| 59 | + subprocess.run(shell_command, shell=True, check=True) |
| 60 | + |
| 61 | + # Change file permission |
| 62 | + subprocess.run(['chmod', '644', hosts_file_tmp], check=True) |
| 63 | + |
| 64 | + # Move the temporary file to replace the original hosts file |
| 65 | + subprocess.run(['mv', hosts_file_tmp, hosts_file], check=True) |
| 66 | + |
| 67 | +async def periodic_update(interval_str): |
| 68 | + interval_seconds = parse_time_interval(interval_str) |
| 69 | + while True: |
| 70 | + # Schedule the synchronous function to run without blocking the asyncio event loop |
| 71 | + await update_hosts_file_async() |
| 72 | + await asyncio.sleep(interval_seconds) |
| 73 | + |
| 74 | +async def monitor_docker_events(): |
| 75 | + # Command to filter Docker events for container start and network disconnect |
| 76 | + cmd = "docker events --filter 'event=start' --filter 'event=disconnect'" |
| 77 | + # Start the subprocess |
| 78 | + process = await asyncio.create_subprocess_shell(cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE) |
| 79 | + |
| 80 | + print("Monitoring for Docker container start and network disconnect events", flush = True) |
| 81 | + async for line in process.stdout: |
| 82 | + print(f"Event received: {line.decode('utf-8').strip()}", flush = True) |
| 83 | + await update_hosts_file_async() |
| 84 | + |
| 85 | + # Wait for the process to exit (though in practice, this might run indefinitely) |
| 86 | + await process.wait() |
| 87 | + |
| 88 | +async def main(): |
| 89 | + # Ensure the managed section exists |
| 90 | + ensure_managed_section_exists() |
| 91 | + |
| 92 | + if len(sys.argv) < 2: |
| 93 | + print("Usage: python script.py <interval>", flush = True) |
| 94 | + return |
| 95 | + |
| 96 | + interval_str = sys.argv[1] |
| 97 | + # Start both the Docker event monitoring and the periodic update tasks |
| 98 | + await asyncio.gather( |
| 99 | + monitor_docker_events(), |
| 100 | + periodic_update(interval_str) |
| 101 | + ) |
| 102 | + |
| 103 | +if __name__ == '__main__': |
| 104 | + asyncio.run(main()) |
0 commit comments