Source code for airflow_pydantic.extras.balancer.query
from collections.abc import Callable
from fnmatch import fnmatch
from logging import getLogger
from random import choice
from typing import Literal
from pkn.pydantic import CallablePath
from pydantic import Field
from ...core import BaseModel
from .balancer import BalancerConfiguration
from .host import Host
__all__ = (
"BalancerHostQueryConfiguration",
"BalancerPortQueryConfiguration",
"HostQuery",
"PortQuery",
)
_log = getLogger(__name__)
[docs]
class BalancerHostQueryConfiguration(BaseModel):
kind: Literal["filter", "select"] = Field(
default="select",
description="Kind of query to perform, either 'filter' to return a list of matching hosts or 'select' to return a single host.",
)
balancer: BalancerConfiguration
name: str | list[str] | None = None
queue: str | list[str] | None = None
os: str | list[str] | None = None
tag: str | list[str] | None = None
custom: Callable | CallablePath | None = None
[docs]
def execute(
self,
) -> list[Host] | Host:
"""
Execute the query against the provided hosts and ports.
"""
hosts = self.balancer.all_hosts
name = self.name or []
queue = self.queue or []
os = self.os or []
tag = self.tag or []
if isinstance(name, str):
name = [name]
if isinstance(queue, str):
queue = [queue]
if isinstance(os, str):
os = [os]
if isinstance(tag, str):
tag = [tag]
candidates = [
host
for host in hosts
if (not name or any(fnmatch(host.name, n) for n in name))
and (not queue or any(fnmatch(host_queue, queue_pat) for queue_pat in queue for host_queue in host.queues))
and (not tag or any(fnmatch(host_tag, tag_pat) for tag_pat in tag for host_tag in host.tags))
and (not os or any(fnmatch(host.os, o) for o in os))
and (not self.custom or self.custom(host))
]
if not candidates:
raise RuntimeError(f"No host found for {name} / {queue} / {os} / {tag}")
if self.kind == "filter":
_log.info(f"Filtered hosts: {len(candidates)} found")
return candidates
# TODO more schemes, interrogate usage
ret = choice(candidates)
_log.info(f"Selected host: {ret.name} ({ret.os})")
return ret
# Alias
HostQuery = BalancerHostQueryConfiguration
[docs]
class BalancerPortQueryConfiguration(BaseModel):
kind: Literal["filter", "select"] = Field(
default="select",
description="Kind of query to perform, either 'filter' to return a list of matching hosts or 'select' to return a single host.",
)
balancer: BalancerConfiguration
name: str | list[str] | None = None
tag: str | list[str] | None = None
custom: Callable | CallablePath | None = None
[docs]
def execute(
self,
) -> list[Host] | Host:
"""
Execute the query against the provided hosts and ports.
"""
ports = self.balancer.all_ports
name = self.name or []
tag = self.tag or []
if isinstance(name, str):
name = [name]
if isinstance(tag, str):
tag = [tag]
candidates = [
port
for port in ports
if (not name or any(fnmatch(port.name, n) for n in name))
and (not tag or any(fnmatch(port_tag, tag_pat) for tag_pat in tag for port_tag in port.tags))
and (not self.custom or self.custom(port))
]
if not candidates:
raise RuntimeError(f"No port found for {name} / {tag}")
if self.kind == "filter":
_log.info(f"Filtered ports: {len(candidates)} found")
return candidates
# TODO more schemes, interrogate usage
ret = choice(candidates)
_log.info(f"Selected port: {ret._calc_name}")
return ret
# Alias
PortQuery = BalancerPortQueryConfiguration