Source code for qcportal.managers.models
from __future__ import annotations
from datetime import datetime
from enum import Enum
from typing import Optional, Dict, Any, List
from dateutil.parser import parse as date_parser
try:
from pydantic.v1 import BaseModel, Field, constr, validator, Extra, PrivateAttr
except ImportError:
from pydantic import BaseModel, Field, constr, validator, Extra, PrivateAttr
from qcportal.base_models import RestModelBase, QueryProjModelBase
from ..base_models import QueryIteratorBase
[docs]
class ManagerStatusEnum(str, Enum):
"""
The state of a Queue Manager. The states which are available are a finite set.
"""
active = "active"
inactive = "inactive"
@classmethod
def _missing_(cls, name):
"""Attempts to find the correct status in a case-insensitive way
If a string being converted to a ManagerStatusEnum is missing, then this function
will convert the case and try to find the appropriate status.
"""
name = name.lower()
# Search this way rather than doing 'in' since we are comparing
# a string to an enum
for status in cls:
if name == status:
return status
[docs]
class ManagerName(BaseModel):
class Config:
extra = Extra.forbid
cluster: str
hostname: str
uuid: str
@property
def fullname(self):
return self.cluster + "-" + self.hostname + "-" + self.uuid
def __str__(self):
return self.fullname
[docs]
class ComputeManager(BaseModel):
class Config:
extra = Extra.forbid
id: int = Field(...)
name: str = Field(...)
cluster: str = Field(...)
hostname: str
username: Optional[str]
tags: List[str]
claimed: int
successes: int
failures: int
rejected: int
total_cpu_hours: float
active_tasks: int
active_cores: int
active_memory: float
status: ManagerStatusEnum
created_on: datetime
modified_on: datetime
manager_version: str
programs: Dict[str, List[str]]
_client: Any = PrivateAttr(None)
_base_url: Optional[str] = PrivateAttr(None)
[docs]
def propagate_client(self, client):
self._client = client
self._base_url = f"api/v1/managers/{self.name}"
[docs]
class ManagerActivationBody(RestModelBase):
name_data: ManagerName = Field(..., description="Name information about this manager")
manager_version: str = Field(..., description="Version of the manager itself")
username: Optional[str] = Field(..., description="Username this manager is connected with")
programs: Dict[constr(to_lower=True), List[str]] = Field(..., description="Programs available on this manager")
tags: List[constr(to_lower=True)] = Field(..., description="Tags this manager will compute for")
[docs]
@validator("tags")
def validate_tags(cls, v):
v = [x for x in v if len(x) > 0]
if len(v) == 0:
raise ValueError("'tags' field contains no non-zero-length tags")
return list(dict.fromkeys(v)) # remove duplicates, maintaining order (in python 3.6+)
[docs]
@validator("programs")
def validate_programs(cls, v):
# Remove programs of zero length
v = {x: y for x, y in v.items() if len(x) > 0}
if len(v) == 0:
raise ValueError("'programs' field contains no non-zero-length programs")
return v
[docs]
class ManagerUpdateBody(RestModelBase):
status: ManagerStatusEnum
active_tasks: int
active_cores: int
active_memory: float
total_cpu_hours: float
[docs]
class ManagerQueryFilters(QueryProjModelBase):
manager_id: Optional[List[int]] = None
name: Optional[List[str]] = None
cluster: Optional[List[str]] = None
hostname: Optional[List[str]] = None
status: Optional[List[ManagerStatusEnum]] = None
modified_before: Optional[datetime] = None
modified_after: Optional[datetime] = None
[docs]
@validator("modified_before", "modified_after", pre=True)
def parse_dates(cls, v):
if isinstance(v, str):
return date_parser(v)
return v
[docs]
class ManagerQueryIterator(QueryIteratorBase[ComputeManager]):
"""
Iterator for manager queries
This iterator transparently handles batching and pagination over the results
of a manager query
"""
def __init__(self, client, query_filters: ManagerQueryFilters):
"""
Construct an iterator
Parameters
----------
client
QCPortal client object used to contact/retrieve data from the server
query_filters
The actual query information to send to the server
"""
batch_limit = client.api_limits["get_managers"] // 4
QueryIteratorBase.__init__(self, client, query_filters, batch_limit)
def _request(self) -> List[ComputeManager]:
managers = self._client.make_request(
"post",
"api/v1/managers/query",
List[ComputeManager],
body=self._query_filters,
)
for m in managers:
m.propagate_client(self._client)
return managers