Skip to content

Commit 4ed2709

Browse files
authored
feat (Configuration): add documentation to CS attributes; sub_from_preferred_username uses cache (#696)
* feat (Configuration): add attribute docstrings and enable use_attribute_docstrings for the pydantic models feat (Configuration): documenting other attributes fix: formatting and remove caching * feat (Configuration): use a cached_property for preferred usernames and subs lookups fix: reorder imports fix: set exclude=True on computed field fix (Configuration): Don't use a computed field. [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci [pre-commit.ci] auto fixes from pre-commit.com hooks for more information, see https://pre-commit.ci
1 parent 19bfadc commit 4ed2709

1 file changed

Lines changed: 129 additions & 8 deletions

File tree

diracx-core/src/diracx/core/config/schema.py

Lines changed: 129 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import os
44
from datetime import datetime
5+
from functools import cached_property
56
from typing import Annotated, Any, MutableMapping, TypeVar
67

78
from pydantic import BaseModel as _BaseModel
@@ -27,7 +28,9 @@
2728

2829

2930
class BaseModel(_BaseModel):
30-
model_config = ConfigDict(extra="forbid", frozen=True)
31+
model_config = ConfigDict(
32+
extra="forbid", frozen=True, use_attribute_docstrings=True
33+
)
3134

3235
@model_validator(mode="before")
3336
@classmethod
@@ -68,28 +71,51 @@ def legacy_adaptor(cls, v):
6871

6972
class UserConfig(BaseModel):
7073
PreferedUsername: str
74+
"""Preferred username for the user account."""
7175
DNs: list[str] = []
76+
"""Distinguished Names of the user's certificates (Mandatory for certificate-based authentication)."""
7277
Email: EmailStr | None = None
78+
"""User e-mail address (Mandatory for user registration)."""
7379
Suspended: list[str] = []
80+
"""List of VOs where the user is suspended."""
7481
Quota: int | None = None
82+
"""Quota assigned to the user, expressed in MBs."""
7583

7684

7785
class GroupConfig(BaseModel):
7886
AutoAddVOMS: bool = False
87+
"""Controls automatic addition of VOMS extension when creating proxies."""
7988
AutoUploadPilotProxy: bool = False
89+
"""Controls automatic Proxy upload for Pilot groups."""
8090
AutoUploadProxy: bool = False
91+
"""Controls automatic Proxy upload for users in this group."""
8192
JobShare: int = 1000
93+
"""Share of computing resources allocated to this group for fair share scheduling."""
8294
Properties: SerializableSet[SecurityProperty]
95+
"""Group properties (set permissions of the group users).
96+
97+
Examples: NormalUser, GenericPilot, ServiceAdministrator.
98+
"""
8399
Quota: int | None = None
100+
"""Group-specific quota override."""
84101
Users: SerializableSet[str]
102+
"""DIRAC user logins that belong to this group."""
85103
AllowBackgroundTQs: bool = False
104+
"""Allow background Task Queues for this group."""
86105
VOMSRole: str | None = None
106+
"""Role of the users in the VO (e.g., '/lhcb' for LHCb VO)."""
87107
AutoSyncVOMS: bool = False
108+
"""Automatically synchronize group membership with VOMS server."""
88109

89110

90111
class IdpConfig(BaseModel):
91112
URL: str
113+
"""The authorization server's issuer identifier.
114+
115+
This is a URL that uses the 'https' scheme and has no query or fragment components.
116+
"""
92117
ClientID: str
118+
"""OAuth 2.0 client identifier received after client registration with the identity provider."""
93119

94120
@property
95121
def server_metadata_url(self):
@@ -98,108 +124,187 @@ def server_metadata_url(self):
98124

99125
class SupportInfo(BaseModel):
100126
Email: str | None = None
127+
"""Support contact email address."""
101128
Webpage: str | None = None
129+
"""Support webpage URL."""
102130
Message: str = "Please contact system administrator"
131+
"""Default support message displayed to users."""
103132

104133

105134
class RegistryConfig(BaseModel):
106135
IdP: IdpConfig
136+
"""Registered identity provider associated with this VO."""
107137
Support: SupportInfo = Field(default_factory=SupportInfo)
138+
"""Support contact information for this VO."""
108139
DefaultGroup: str
140+
"""Default user group to be used for new users in this VO."""
109141
DefaultStorageQuota: float = 0
142+
"""Default storage quota in GB for users in this VO."""
110143
DefaultProxyLifeTime: int = 12 * 60 * 60
144+
"""Default proxy time expressed in seconds (default: 43200 = 12 hours)."""
111145
VOMSName: str | None = None
146+
"""Real VOMS VO name, if this VO is associated with VOMS VO."""
112147

113148
Users: MutableMapping[str, UserConfig]
149+
"""DIRAC users section, subsections represent the name of the user."""
114150
Groups: MutableMapping[str, GroupConfig]
151+
"""DIRAC groups section, subsections represent the name of the group."""
152+
153+
@cached_property
154+
def _preferred_username_to_sub(self) -> dict[str, str]:
155+
"""Compute reverse lookup map from preferred username to user sub."""
156+
return {user.PreferedUsername: sub for sub, user in self.Users.items()}
115157

116158
def sub_from_preferred_username(self, preferred_username: str) -> str:
117159
"""Get the user sub from the preferred username.
118160
119-
TODO: This could easily be cached or optimised
161+
Args:
162+
preferred_username: The preferred username to look up.
163+
164+
Returns:
165+
The user sub (subject identifier) for the given username.
166+
167+
Raises:
168+
KeyError: If no user with the given preferred username is found.
169+
120170
"""
121-
for sub, user in self.Users.items():
122-
if user.PreferedUsername == preferred_username:
123-
return sub
124-
raise KeyError(f"User {preferred_username} not found in registry")
171+
try:
172+
return self._preferred_username_to_sub[preferred_username]
173+
except KeyError:
174+
raise KeyError(f"User {preferred_username} not found in registry") from None
125175

126176

127177
class DIRACConfig(BaseModel):
128178
NoSetup: bool = False
179+
"""Flag to skip setup procedures during DIRAC initialization. Takes a boolean value. By default false."""
129180

130181

131182
class JobMonitoringConfig(BaseModel):
132183
GlobalJobsInfo: bool = True
184+
"""Enable global job information monitoring across all VOs."""
133185

134186

135187
class JobSchedulingConfig(BaseModel):
136188
EnableSharesCorrection: bool = False
189+
"""Enable correction of job shares based on historical usage."""
137190
MaxRescheduling: int = 3
191+
"""Maximum number of times a job can be rescheduled."""
138192

139193

140194
class ServicesConfig(BaseModel):
141195
Catalogs: MutableMapping[str, Any] | None = None
196+
"""Configuration for data catalog services."""
142197
JobMonitoring: JobMonitoringConfig = JobMonitoringConfig()
198+
"""Job monitoring service configuration."""
143199
JobScheduling: JobSchedulingConfig = JobSchedulingConfig()
200+
"""Job scheduling service configuration."""
144201

145202

146203
class JobDescriptionConfig(BaseModel):
147204
DefaultCPUTime: int = 86400
205+
"""Default CPU time limit for jobs in seconds (default: 24 hours)."""
148206
DefaultPriority: int = 1
207+
"""Default job priority."""
149208
MinCPUTime: int = 100
209+
"""Minimum allowed CPU time for jobs in seconds."""
150210
MinPriority: int = 0
211+
"""Minimum allowed job priority."""
151212
MaxCPUTime: int = 500000
213+
"""Maximum allowed CPU time for jobs in seconds."""
152214
MaxPriority: int = 10
215+
"""Maximum allowed job priority."""
153216
MaxInputData: int = 100
217+
"""Maximum number of input data files per job."""
154218
AllowedJobTypes: list[str] = ["User", "Test", "Hospital"]
219+
"""List of allowed job types."""
155220

156221

157222
class InputDataPolicyProtocolsConfig(BaseModel):
158223
Remote: list[str] = []
224+
"""List of protocols that should be considered as remote access methods (e.g., 'https', 'gsiftp', 'srm')."""
159225
Local: list[str] = []
226+
"""List of protocols that should be considered as local access methods (e.g., 'file', 'root')."""
160227

161228

162229
class InputDataPolicyConfig(BaseModel):
163230
# TODO: Remove this once the model is extended to support everything
164-
model_config = ConfigDict(extra="ignore", frozen=True)
231+
model_config = ConfigDict(
232+
extra="ignore", frozen=True, use_attribute_docstrings=True
233+
)
165234

166235
Default: str = "Default = DIRAC.WorkloadManagementSystem.Client.InputDataByProtocol"
236+
"""Default input data access policy. This is the fallback policy when no specific protocol is matched."""
167237
Download: str = "DIRAC.WorkloadManagementSystem.Client.DownloadInputData"
238+
"""Policy for downloading input data files to the local worker node before job execution."""
168239
Protocol: str = "DIRAC.WorkloadManagementSystem.Client.InputDataByProtocol"
240+
"""Policy for accessing input data directly via supported protocols without downloading."""
169241
AllReplicas: bool = True
242+
"""Whether to consider all available replicas when resolving input data locations."""
170243
Protocols: InputDataPolicyProtocolsConfig = InputDataPolicyProtocolsConfig()
244+
"""Protocol-specific configuration defining which protocols are available for remote and local access."""
171245
InputDataModule: str = "DIRAC.Core.Utilities.InputDataResolution"
246+
"""Module responsible for resolving input data locations and determining access methods."""
172247

173248

174249
class OperationsConfig(BaseModel):
175250
EnableSecurityLogging: bool = False
251+
"""Flag for globally disabling the use of the SecurityLogging service.
252+
253+
This is False by default, as should be migrated to use centralized logging.
254+
"""
176255
InputDataPolicy: InputDataPolicyConfig = InputDataPolicyConfig()
256+
"""Specify how jobs access their data. See InputDataResolution documentation for details."""
177257
JobDescription: JobDescriptionConfig = JobDescriptionConfig()
258+
"""Configuration for job description defaults and limits."""
178259
Services: ServicesConfig = ServicesConfig()
260+
"""Configuration for various DIRAC services."""
179261
SoftwareDistModule: str = "LocalSoftwareDist"
262+
"""Module used for software distribution."""
180263

181264
Cloud: MutableMapping[str, Any] | None = None
265+
"""Cloud computing configuration."""
182266
DataConsistency: MutableMapping[str, Any] | None = None
267+
"""Data consistency checking configuration."""
183268
DataManagement: MutableMapping[str, Any] | None = None
269+
"""Data management operations configuration."""
184270
EMail: MutableMapping[str, Any] | None = None
271+
"""Email notification configuration."""
185272
GaudiExecution: MutableMapping[str, Any] | None = None
273+
"""Gaudi framework execution configuration."""
186274
Hospital: MutableMapping[str, Any] | None = None
275+
"""Job recovery and hospital configuration."""
187276
JobScheduling: MutableMapping[str, Any] | None = None
277+
"""Advanced job scheduling configuration."""
188278
JobTypeMapping: MutableMapping[str, Any] | None = None
279+
"""Mapping of job types to execution environments."""
189280
LogFiles: MutableMapping[str, Any] | None = None
281+
"""Log file management configuration."""
190282
LogStorage: MutableMapping[str, Any] | None = None
283+
"""Log storage backend configuration."""
191284
Logging: MutableMapping[str, Any] | None = None
285+
"""General logging configuration."""
192286
Matching: MutableMapping[str, Any] | None = None
287+
"""Job matching configuration."""
193288
MonitoringBackends: MutableMapping[str, Any] | None = None
289+
"""Monitoring backend configuration."""
194290
NagiosConnector: MutableMapping[str, Any] | None = None
291+
"""Nagios monitoring integration configuration."""
195292
Pilot: MutableMapping[str, Any] | None = None
293+
"""Pilot job configuration."""
196294
Productions: MutableMapping[str, Any] | None = None
295+
"""Production management configuration."""
197296
Shares: MutableMapping[str, Any] | None = None
297+
"""Resource sharing configuration."""
198298
Shifter: MutableMapping[str, Any] | None = None
299+
"""Shifter proxy configuration."""
199300
SiteSEMappingByProtocol: MutableMapping[str, Any] | None = None
301+
"""Site storage element mapping by protocol."""
200302
TransformationPlugins: MutableMapping[str, Any] | None = None
303+
"""Data transformation plugin configuration."""
201304
Transformations: MutableMapping[str, Any] | None = None
305+
"""Data transformation system configuration."""
202306
ResourceStatus: MutableMapping[str, Any] | None = None
307+
"""Resource status monitoring configuration."""
203308

204309

205310
class ResourcesComputingConfig(BaseModel):
@@ -209,6 +314,10 @@ class ResourcesComputingConfig(BaseModel):
209314
# TODO: Figure out how to remove this in LHCbDIRAC and then consider
210315
# constraining there to be at least one entry
211316
OSCompatibility: MutableMapping[str, set[str]] = {}
317+
"""Compatibility matrix between DIRAC platforms and OS versions.
318+
319+
Used by SiteDirector to match TaskQueues to Computing Element capabilities.
320+
"""
212321

213322
@field_validator("OSCompatibility", mode="before")
214323
@classmethod
@@ -232,22 +341,34 @@ def ensure_self_compatibility(cls, v: dict[str, set[str]]) -> dict[str, set[str]
232341

233342
class ResourcesConfig(BaseModel):
234343
# TODO: Remove this once the model is extended to support everything
235-
model_config = ConfigDict(extra="ignore", frozen=True)
344+
model_config = ConfigDict(
345+
extra="ignore", frozen=True, use_attribute_docstrings=True
346+
)
236347

237348
Computing: ResourcesComputingConfig = ResourcesComputingConfig()
349+
"""Computing resource configuration."""
238350

239351

240352
class Config(BaseModel):
241353
DIRAC: DIRACConfig
354+
"""The DIRAC section contains general parameters needed in most installation types."""
242355
Operations: MutableMapping[str, OperationsConfig]
356+
"""Operations configuration per VO. The Defaults entry is automatically merged into each VO-specific config."""
243357
Registry: MutableMapping[str, RegistryConfig]
358+
"""Registry sections to register VOs, groups, users and hosts. See UserManagement documentation for details."""
244359
Resources: ResourcesConfig = ResourcesConfig()
360+
"""Resources configuration including computing elements, storage elements, and sites."""
245361

246362
LocalSite: Any = None
363+
"""Local site-specific configuration parameters."""
247364
LogLevel: Any = None
365+
"""Global logging level configuration."""
248366
MCTestingDestination: Any = None
367+
"""Monte Carlo testing destination configuration."""
249368
Systems: Any | None = None
369+
"""Systems configuration."""
250370
WebApp: Any = None
371+
"""Web application configuration parameters."""
251372

252373
@model_validator(mode="before")
253374
@classmethod

0 commit comments

Comments
 (0)