fix(topology): cache IPAllocator host set; type repo params as BaseRepository
_host_set is computed once in __init__ — reserve() and is_free() were rebuilding the full host frozenset on every call. BaseRepository already existed; the Any annotations were just never updated.
This commit is contained in:
@@ -12,9 +12,10 @@ open one.
|
||||
from __future__ import annotations
|
||||
|
||||
from ipaddress import IPv4Network
|
||||
from typing import Any, Iterable
|
||||
from typing import Iterable
|
||||
|
||||
from decnet.topology.status import TopologyStatus
|
||||
from decnet.web.db.repository import BaseRepository
|
||||
|
||||
|
||||
class AllocatorExhausted(RuntimeError):
|
||||
@@ -34,6 +35,7 @@ class IPAllocator:
|
||||
self._pool: list[str] = [
|
||||
str(ip) for ip in self._net.hosts() if str(ip) != self._gateway
|
||||
]
|
||||
self._host_set: frozenset[str] = frozenset(str(h) for h in self._net.hosts())
|
||||
self._taken: set[str] = set()
|
||||
self._cursor = 0
|
||||
|
||||
@@ -57,7 +59,7 @@ class IPAllocator:
|
||||
def reserve(self, ip: str) -> None:
|
||||
if ip == self._gateway:
|
||||
raise ValueError(f"{ip} is the gateway of {self._net.with_prefixlen}")
|
||||
if ip not in {str(h) for h in self._net.hosts()}:
|
||||
if ip not in self._host_set:
|
||||
raise ValueError(f"{ip} not in {self._net.with_prefixlen}")
|
||||
self._taken.add(ip)
|
||||
|
||||
@@ -65,7 +67,7 @@ class IPAllocator:
|
||||
self._taken.discard(ip)
|
||||
|
||||
def is_free(self, ip: str) -> bool:
|
||||
return ip not in self._taken and ip in {str(h) for h in self._net.hosts()} and ip != self._gateway
|
||||
return ip not in self._taken and ip in self._host_set and ip != self._gateway
|
||||
|
||||
|
||||
class SubnetAllocator:
|
||||
@@ -148,7 +150,7 @@ _SUBNET_CLAIMING_STATES: frozenset[str] = frozenset(
|
||||
)
|
||||
|
||||
|
||||
async def reserved_subnets(repo: Any) -> set[str]:
|
||||
async def reserved_subnets(repo: BaseRepository) -> set[str]:
|
||||
"""All LAN subnets currently claimed by non-torn-down topologies."""
|
||||
out: set[str] = set()
|
||||
for status in _SUBNET_CLAIMING_STATES:
|
||||
|
||||
@@ -5,12 +5,13 @@ from ipaddress import IPv4Address, IPv4Network
|
||||
from typing import Any
|
||||
|
||||
from decnet.topology.allocator import IPAllocator
|
||||
from decnet.web.db.repository import BaseRepository
|
||||
from decnet.topology.config import GeneratedTopology
|
||||
from decnet.topology.status import TopologyStatus, assert_transition
|
||||
|
||||
|
||||
async def persist(
|
||||
repo: Any,
|
||||
repo: BaseRepository,
|
||||
plan: GeneratedTopology,
|
||||
*,
|
||||
target_host_uuid: str | None = None,
|
||||
@@ -90,7 +91,7 @@ async def persist(
|
||||
|
||||
|
||||
async def transition_status(
|
||||
repo: Any,
|
||||
repo: BaseRepository,
|
||||
topology_id: str,
|
||||
new_status: str,
|
||||
reason: str | None = None,
|
||||
@@ -107,7 +108,7 @@ async def transition_status(
|
||||
await repo.update_topology_status(topology_id, new_status, reason=reason)
|
||||
|
||||
|
||||
async def hydrate(repo: Any, topology_id: str) -> dict[str, Any] | None:
|
||||
async def hydrate(repo: BaseRepository, topology_id: str) -> dict[str, Any] | None:
|
||||
"""Load a topology + children into a single dict for callers.
|
||||
|
||||
Shape::
|
||||
|
||||
Reference in New Issue
Block a user