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 __future__ import annotations
|
||||||
|
|
||||||
from ipaddress import IPv4Network
|
from ipaddress import IPv4Network
|
||||||
from typing import Any, Iterable
|
from typing import Iterable
|
||||||
|
|
||||||
from decnet.topology.status import TopologyStatus
|
from decnet.topology.status import TopologyStatus
|
||||||
|
from decnet.web.db.repository import BaseRepository
|
||||||
|
|
||||||
|
|
||||||
class AllocatorExhausted(RuntimeError):
|
class AllocatorExhausted(RuntimeError):
|
||||||
@@ -34,6 +35,7 @@ class IPAllocator:
|
|||||||
self._pool: list[str] = [
|
self._pool: list[str] = [
|
||||||
str(ip) for ip in self._net.hosts() if str(ip) != self._gateway
|
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._taken: set[str] = set()
|
||||||
self._cursor = 0
|
self._cursor = 0
|
||||||
|
|
||||||
@@ -57,7 +59,7 @@ class IPAllocator:
|
|||||||
def reserve(self, ip: str) -> None:
|
def reserve(self, ip: str) -> None:
|
||||||
if ip == self._gateway:
|
if ip == self._gateway:
|
||||||
raise ValueError(f"{ip} is the gateway of {self._net.with_prefixlen}")
|
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}")
|
raise ValueError(f"{ip} not in {self._net.with_prefixlen}")
|
||||||
self._taken.add(ip)
|
self._taken.add(ip)
|
||||||
|
|
||||||
@@ -65,7 +67,7 @@ class IPAllocator:
|
|||||||
self._taken.discard(ip)
|
self._taken.discard(ip)
|
||||||
|
|
||||||
def is_free(self, ip: str) -> bool:
|
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:
|
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."""
|
"""All LAN subnets currently claimed by non-torn-down topologies."""
|
||||||
out: set[str] = set()
|
out: set[str] = set()
|
||||||
for status in _SUBNET_CLAIMING_STATES:
|
for status in _SUBNET_CLAIMING_STATES:
|
||||||
|
|||||||
@@ -5,12 +5,13 @@ from ipaddress import IPv4Address, IPv4Network
|
|||||||
from typing import Any
|
from typing import Any
|
||||||
|
|
||||||
from decnet.topology.allocator import IPAllocator
|
from decnet.topology.allocator import IPAllocator
|
||||||
|
from decnet.web.db.repository import BaseRepository
|
||||||
from decnet.topology.config import GeneratedTopology
|
from decnet.topology.config import GeneratedTopology
|
||||||
from decnet.topology.status import TopologyStatus, assert_transition
|
from decnet.topology.status import TopologyStatus, assert_transition
|
||||||
|
|
||||||
|
|
||||||
async def persist(
|
async def persist(
|
||||||
repo: Any,
|
repo: BaseRepository,
|
||||||
plan: GeneratedTopology,
|
plan: GeneratedTopology,
|
||||||
*,
|
*,
|
||||||
target_host_uuid: str | None = None,
|
target_host_uuid: str | None = None,
|
||||||
@@ -90,7 +91,7 @@ async def persist(
|
|||||||
|
|
||||||
|
|
||||||
async def transition_status(
|
async def transition_status(
|
||||||
repo: Any,
|
repo: BaseRepository,
|
||||||
topology_id: str,
|
topology_id: str,
|
||||||
new_status: str,
|
new_status: str,
|
||||||
reason: str | None = None,
|
reason: str | None = None,
|
||||||
@@ -107,7 +108,7 @@ async def transition_status(
|
|||||||
await repo.update_topology_status(topology_id, new_status, reason=reason)
|
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.
|
"""Load a topology + children into a single dict for callers.
|
||||||
|
|
||||||
Shape::
|
Shape::
|
||||||
|
|||||||
Reference in New Issue
Block a user