Skip to content

Commit 18662e7

Browse files
authored
Store users locally to improve performance when querying labs/nodes (#199)
1 parent c39062b commit 18662e7

3 files changed

Lines changed: 63 additions & 9 deletions

File tree

virl2_client/models/lab.py

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@
6060

6161
from .annotation import AnnotationType
6262
from .resource_pool import ResourcePool, ResourcePoolManagement
63+
from .user import UserManagement
6364

6465

6566
_LOGGER = logging.getLogger(__name__)
@@ -93,7 +94,6 @@ class Lab:
9394
"connector_mappings": "labs/{lab_id}/connector_mappings",
9495
"resource_pools": "labs/{lab_id}/resource_pools",
9596
"annotations": "labs/{lab_id}/annotations",
96-
"user_list": "users",
9797
}
9898

9999
def __init__(
@@ -110,6 +110,7 @@ def __init__(
110110
wait_time: int | float = 5,
111111
hostname: str | None = None,
112112
resource_pool_manager: ResourcePoolManagement | None = None,
113+
user_management: UserManagement | None = None,
113114
) -> None:
114115
"""
115116
A VIRL2 lab network topology.
@@ -129,6 +130,8 @@ def __init__(
129130
:param hostname: Hostname/IP and port for pyATS console terminal server.
130131
:param resource_pool_manager: ResourcePoolManagement object shared
131132
with parent ClientLibrary.
133+
:param user_management: UserManagement object shared with parent
134+
ClientLibrary for resolving user IDs to usernames.
132135
:raises VirlException: If the lab object is created without
133136
a resource pool manager.
134137
"""
@@ -201,6 +204,12 @@ def __init__(
201204
"because it is missing a resource pool manager."
202205
)
203206
self._resource_pool_manager = resource_pool_manager
207+
if user_management is None:
208+
raise VirlException(
209+
f"Lab object for lab {title or lab_id} cannot be created "
210+
"because it is missing a user management."
211+
)
212+
self._user_management = user_management
204213
self._resource_pools = []
205214
self._stale = False
206215
self._synced_configs = True
@@ -2458,10 +2467,7 @@ def _set_owner(
24582467
:param user_name: Username.
24592468
"""
24602469
if user_id:
2461-
url = self._url_for("user_list")
2462-
users = self._session.get(url).json()
2463-
for user in users:
2464-
if user["id"] == user_id:
2465-
user_name = user["username"]
2466-
break
2470+
resolved = self._user_management.get_username(user_id)
2471+
if resolved is not None:
2472+
user_name = resolved
24672473
self._owner = user_name

virl2_client/models/user.py

Lines changed: 41 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,7 @@
2020

2121
from __future__ import annotations
2222

23+
import time
2324
import warnings
2425
from typing import TYPE_CHECKING, Any
2526

@@ -36,8 +37,17 @@ class UserManagement:
3637
"user_id": "users/{username}/id",
3738
}
3839

39-
def __init__(self, session: httpx.Client) -> None:
40+
def __init__(
41+
self,
42+
session: httpx.Client,
43+
auto_sync: bool = True,
44+
auto_sync_interval: float = 1.0,
45+
) -> None:
4046
self._session = session
47+
self.auto_sync = auto_sync
48+
self.auto_sync_interval = auto_sync_interval
49+
self._last_sync_users_time = 0.0
50+
self._users_by_id: dict[str, dict] = {}
4151

4252
def _url_for(self, endpoint: str, **kwargs: str) -> str:
4353
"""
@@ -58,6 +68,36 @@ def users(self) -> list[dict]:
5868
url = self._url_for("users")
5969
return self._session.get(url).json()
6070

71+
def sync_users(self) -> None:
72+
"""Fetch all users from the server and store them locally."""
73+
user_list = self.users()
74+
self._users_by_id = {user["id"]: user for user in user_list}
75+
self._last_sync_users_time = time.time()
76+
77+
def sync_users_if_outdated(self) -> None:
78+
"""Synchronize the user list if the auto-sync interval has elapsed."""
79+
timestamp = time.time()
80+
if (
81+
self.auto_sync
82+
and timestamp - self._last_sync_users_time > self.auto_sync_interval
83+
):
84+
self.sync_users()
85+
86+
def get_username(self, user_id: str) -> str | None:
87+
"""Look up a username by user ID from the locally synced user list.
88+
89+
Triggers a sync if the local list is outdated. Returns ``None`` if
90+
the *user_id* is not found.
91+
92+
:param user_id: User UUID4.
93+
:returns: The username, or ``None`` if not found.
94+
"""
95+
self.sync_users_if_outdated()
96+
user = self._users_by_id.get(user_id)
97+
if user is not None:
98+
return user["username"]
99+
return None
100+
61101
def get_user(self, user_id: str) -> dict:
62102
"""
63103
Get user information.

virl2_client/virl2_client.py

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -382,7 +382,11 @@ def __init__(
382382

383383
self.definitions = NodeImageDefinitions(self._session)
384384
self.licensing = Licensing(self._session)
385-
self.user_management = UserManagement(self._session)
385+
self.user_management = UserManagement(
386+
self._session,
387+
auto_sync=self.auto_sync,
388+
auto_sync_interval=self.auto_sync_interval,
389+
)
386390
self.group_management = GroupManagement(self._session)
387391
self.system_management = SystemManagement(
388392
self._session,
@@ -648,6 +652,7 @@ def _create_imported_lab(
648652
auto_sync=self.auto_sync,
649653
auto_sync_interval=self.auto_sync_interval,
650654
resource_pool_manager=self.resource_pool_management,
655+
user_management=self.user_management,
651656
)
652657

653658
@locked
@@ -797,6 +802,7 @@ def create_lab(
797802
wait_max_iterations=self.convergence_wait_max_iter,
798803
wait_time=self.convergence_wait_time,
799804
resource_pool_manager=self.resource_pool_management,
805+
user_management=self.user_management,
800806
)
801807
lab._import_lab(result, created=True)
802808
self._labs[lab_id] = lab
@@ -884,10 +890,12 @@ def join_existing_lab(self, lab_id: str, sync_lab: bool = True) -> Lab:
884890
wait_max_iterations=self.convergence_wait_max_iter,
885891
wait_time=self.convergence_wait_time,
886892
resource_pool_manager=self.resource_pool_management,
893+
user_management=self.user_management,
887894
)
888895
if sync_lab:
889896
lab.import_lab(topology)
890897
lab._initialized = True
898+
lab._last_sync_topology_time = time.time()
891899
else:
892900
lab._owner = None
893901
self._labs[lab_id] = lab

0 commit comments

Comments
 (0)