pddnsc/pddnsc/cli.py

134 lines
4.0 KiB
Python

""" модуль запуска """
import httpx
import asyncio
import toml
from .base import BaseFilterProvider, BaseSourceProvider, BaseOutputProvider, IPAddreses
from .plugins import use_plugins
from typing import Optional
def is_valid_addreses(addrs: IPAddreses, config) -> bool:
result = addrs.ipv4 or addrs.ipv6
if config.get("require_ipv4"):
result = result and addrs.ipv4
if config.get("require_ipv6"):
result = result and addrs.ipv6
return result
async def get_ip_addresses(config) -> Optional[IPAddreses]:
providers = BaseSourceProvider.registred.values()
ip_addresses = None
is_done = False
pending = [asyncio.create_task(p.fetch_all(), name=p.name) for p in providers]
while not is_done and pending:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for x in done:
ip_addresses = x.result()
if is_valid_addreses(ip_addresses, config):
is_done = True
break
ip_addresses = None
if pending:
gather = asyncio.gather(*pending)
gather.cancel()
try:
await gather
except asyncio.CancelledError:
pass
return ip_addresses
async def check_ip_addresses(ip_addresses):
providers = BaseFilterProvider.registred.values()
result = True
failed = ""
pending = [
asyncio.create_task(p.check(*ip_addresses), name=p.name) for p in providers
]
while result and pending:
done, pending = await asyncio.wait(pending, return_when=asyncio.FIRST_COMPLETED)
for x in done:
result = x.result()
if not result:
failed = x.get_name()
if pending:
gather = asyncio.gather(*pending)
gather.cancel()
try:
await gather
except asyncio.CancelledError:
pass
if not result:
print("failed filter:", failed)
return result
async def send_ip_addreses(ip_addresses):
providers = BaseOutputProvider.registred.values()
await asyncio.gather(
*(
asyncio.create_task(p.set_addrs(*ip_addresses), name=p.name)
for p in providers
)
)
def print_debug_info(config):
debug = config.get("debug", False)
if debug:
print("DEBUG info:")
print(
f"source classes: {[*BaseSourceProvider._childs]}, {[*map(str, BaseSourceProvider._childs.values())]}"
)
print(
f"filter classes: {[*BaseFilterProvider._childs]}, {[*map(str, BaseFilterProvider._childs.values())]}"
)
print(
f"output classes: {[*BaseOutputProvider._childs]}, {[*map(str, BaseOutputProvider._childs.values())]}"
)
print(
f"source providers: {[*BaseSourceProvider.registred]}, {[*map(str, BaseSourceProvider.registred.values())]}"
)
print(
f"filter providers: {[*BaseFilterProvider.registred]}, {[*map(str, BaseFilterProvider.registred.values())]}"
)
print(
f"output providers: {[*BaseOutputProvider.registred]}, {[*map(str, BaseOutputProvider.registred.values())]}"
)
async def app(config, ipv4t, ipv6t):
use_plugins(config, ipv4t, ipv6t)
print_debug_info(config)
ip_addreses = await get_ip_addresses(config)
if ip_addreses is None:
print("no IP addresses")
return
if not await check_ip_addresses(ip_addreses):
print("stop by filters")
return
await send_ip_addreses(ip_addreses)
print("done")
async def main():
config = toml.load("settings/config.toml")
async with httpx.AsyncHTTPTransport(
local_address="0.0.0.0", proxy=config.get("proxy_v4")
) as ipv4t, httpx.AsyncHTTPTransport(
local_address="::", proxy=config.get("proxy_v6")
) as ipv6t:
await app(config, ipv4t, ipv6t)
if __name__ == "__main__":
asyncio.run(main())