diff --git a/AGENTS.md b/AGENTS.md index 468ec761..c880a795 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -1,3 +1,7 @@ +--- +apply: always +mode: all +--- # Exordos Core Agent Guide ## 1. Think Before Coding diff --git a/exordos_core/agent/universal/clients/rest.py b/exordos_core/agent/universal/clients/rest.py index 38e49e4f..22e24327 100644 --- a/exordos_core/agent/universal/clients/rest.py +++ b/exordos_core/agent/universal/clients/rest.py @@ -30,13 +30,13 @@ class GCRestApiBackendClient(rest.RestApiBackendClient): def __init__( self, http_client: http.CollectionBaseClient, - collection_map: tp.Dict[str, str], + collection_map: dict[str, str], project_id: sys_uuid.UUID, ) -> None: super().__init__(http_client=http_client, collection_map=collection_map) self._project_id = str(project_id) - def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def create(self, resource: models.Resource) -> dict[str, tp.Any]: """Creates the resource. Returns the created resource.""" # Inject mandatory fields resource.value["uuid"] = str(resource.uuid) @@ -48,7 +48,7 @@ def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: return super().create(resource) - def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def update(self, resource: models.Resource) -> dict[str, tp.Any]: """Update the resource. Returns the updated resource.""" # FIXME(akremenetsky): Not the best implementation # Remove popential RO fields @@ -61,6 +61,6 @@ def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: return super().update(resource) - def list(self, kind: str) -> tp.List[tp.Dict[str, tp.Any]]: + def list(self, kind: str) -> list[dict[str, tp.Any]]: """Lists all resources by kind.""" return super().list(kind, project_id=self._project_id) diff --git a/exordos_core/agent/universal/drivers/secret/backend/cert.py b/exordos_core/agent/universal/drivers/secret/backend/cert.py index a4bddcb0..b223c505 100644 --- a/exordos_core/agent/universal/drivers/secret/backend/cert.py +++ b/exordos_core/agent/universal/drivers/secret/backend/cert.py @@ -44,7 +44,7 @@ def __init__( ) -> None: self._dns_client = dns_client self._admin_email = admin_email - self._client_acme: tp.Optional[acme_lib_client.ClientV2] = None + self._client_acme: acme_lib_client.ClientV2 | None = None self._private_key = acme.get_or_create_client_private_key(private_key_path) def _get_or_create_acme_client(self) -> acme_lib_client.ClientV2: @@ -54,7 +54,7 @@ def _get_or_create_acme_client(self) -> acme_lib_client.ClientV2: ) return self._client_acme - def get(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def get(self, resource: models.Resource) -> dict[str, tp.Any]: """Get the resource value in dictionary format.""" try: cert = driver_dm.Certificate.objects.get_one( @@ -67,7 +67,7 @@ def get(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: return cert.to_resource_value() - def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def create(self, resource: models.Resource) -> dict[str, tp.Any]: """Creates the resource. Returns the created resource.""" try: self.get(resource) @@ -95,7 +95,7 @@ def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: driver_cert.save() return driver_cert.to_resource_value() - def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def update(self, resource: models.Resource) -> dict[str, tp.Any]: """Update the resource. Returns the updated resource.""" try: cert = driver_dm.Certificate.objects.get_one( @@ -135,7 +135,7 @@ def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: driver_cert.save() return driver_cert.to_resource_value() - def list(self, kind: str, **kwargs) -> tp.List[tp.Dict[str, tp.Any]]: + def list(self, kind: str, **kwargs) -> list[dict[str, tp.Any]]: """Lists all resources by kind.""" certs = driver_dm.Certificate.objects.get_all() diff --git a/exordos_core/agent/universal/drivers/secret/backend/password.py b/exordos_core/agent/universal/drivers/secret/backend/password.py index aee4f728..3add63a7 100644 --- a/exordos_core/agent/universal/drivers/secret/backend/password.py +++ b/exordos_core/agent/universal/drivers/secret/backend/password.py @@ -34,7 +34,7 @@ class DatabasePasswordBackendClient(base.AbstractBackendClient): """Secret Backend client based on SQL database.""" - def get(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def get(self, resource: models.Resource) -> dict[str, tp.Any]: """Get the resource value in dictionary format.""" try: driver_password = driver_dm.Password.objects.get_one( @@ -61,7 +61,7 @@ def _gen_password(self, password: secret_dm.Password) -> str: else: return password.value - def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def create(self, resource: models.Resource) -> dict[str, tp.Any]: """Creates the resource. Returns the created resource.""" try: self.get(resource) @@ -95,7 +95,7 @@ def create(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: driver_password.save() return driver_password.meta - def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: + def update(self, resource: models.Resource) -> dict[str, tp.Any]: """Update the resource. Returns the updated resource.""" target = secret_dm.Password.from_ua_resource(resource) @@ -116,7 +116,7 @@ def update(self, resource: models.Resource) -> tp.Dict[str, tp.Any]: return actual.meta - def list(self, kind: str, **kwargs) -> tp.List[tp.Dict[str, tp.Any]]: + def list(self, kind: str, **kwargs) -> list[dict[str, tp.Any]]: """Lists all resources by kind.""" secrets = driver_dm.Password.objects.get_all() return [s.meta for s in secrets] diff --git a/exordos_core/agent/universal/drivers/secret/cert.py b/exordos_core/agent/universal/drivers/secret/cert.py index 71f01004..a6f5d30a 100644 --- a/exordos_core/agent/universal/drivers/secret/cert.py +++ b/exordos_core/agent/universal/drivers/secret/cert.py @@ -15,7 +15,6 @@ # under the License. import logging -import typing as tp from gcl_certbot_plugin import clients as dns_clients from gcl_sdk.agents.universal.drivers import direct @@ -58,6 +57,6 @@ def __init__( super().__init__(storage=storage, client=client) - def get_capabilities(self) -> tp.List[str]: + def get_capabilities(self) -> list[str]: """Returns a list of capabilities supported by the driver.""" return ["certificate"] diff --git a/exordos_core/agent/universal/drivers/secret/dm/models.py b/exordos_core/agent/universal/drivers/secret/dm/models.py index e8e77144..fda459e9 100644 --- a/exordos_core/agent/universal/drivers/secret/dm/models.py +++ b/exordos_core/agent/universal/drivers/secret/dm/models.py @@ -36,7 +36,7 @@ class Secret( default=sc.SecretStatus.NEW.value, ) # Some additional metadata about the secret - meta = properties.property(types.Dict(), default=lambda: {}) + meta = properties.property(types.Dict(), default=dict) class Password(Secret, orm.SQLStorableMixin): @@ -109,7 +109,7 @@ def is_under_threshold(self) -> bool: delta = self.expiration_at - datetime.datetime.now(tz=datetime.timezone.utc) return delta.days < self.meta["expiration_threshold"] - def to_resource_value(self) -> tp.Dict[str, tp.Any]: + def to_resource_value(self) -> dict[str, tp.Any]: expiration_at = self.expiration_at.replace(tzinfo=datetime.timezone.utc) expiration_at = expiration_at.strftime(c.DEFAULT_DATETIME_FORMAT) diff --git a/exordos_core/agent/universal/drivers/secret/password.py b/exordos_core/agent/universal/drivers/secret/password.py index c2a3709a..67750cbd 100644 --- a/exordos_core/agent/universal/drivers/secret/password.py +++ b/exordos_core/agent/universal/drivers/secret/password.py @@ -15,7 +15,6 @@ # under the License. import logging -import typing as tp from gcl_sdk.agents.universal.drivers import direct from gcl_sdk.agents.universal.storage import fs @@ -38,6 +37,6 @@ def __init__(self, storage_path: str = PASSWORD_TARGET_FIELDS_STORAGE): super().__init__(storage=storage, client=client) - def get_capabilities(self) -> tp.List[str]: + def get_capabilities(self) -> list[str]: """Returns a list of capabilities supported by the driver.""" return ["password"] diff --git a/exordos_core/boot_api/api/routes.py b/exordos_core/boot_api/api/routes.py index be9246f9..45e2463d 100644 --- a/exordos_core/boot_api/api/routes.py +++ b/exordos_core/boot_api/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from gcl_sdk.agents.universal.orch_api import routes as orch_routes from gcl_sdk.agents.universal.status_api import routes as status_routes from restalchemy.api import routes @@ -25,13 +27,13 @@ class NetbootRoute(routes.Route): """Handler for /v1/boots/ endpoint""" __controller__ = controllers.NetBootController - __allow_methods__ = [routes.GET] + __allow_methods__: tp.ClassVar[list] = [routes.GET] class UniversalAgentsRoute(orch_routes.UniversalAgentsRoute): """Handler for /v1/agents/ endpoint""" - __allow_methods__ = [ + __allow_methods__: tp.ClassVar[list] = [ routes.GET, routes.CREATE, routes.UPDATE, @@ -43,7 +45,7 @@ class ApiEndpointRoute(routes.Route): """Handler for /v1/ endpoint""" __controller__ = controllers.ApiEndpointController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] nodes = routes.route(status_routes.NodesRoute) boots = routes.route(NetbootRoute) diff --git a/exordos_core/boot_api/dm/models.py b/exordos_core/boot_api/dm/models.py index 4f9e1107..49528620 100644 --- a/exordos_core/boot_api/dm/models.py +++ b/exordos_core/boot_api/dm/models.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. + import typing as tp from restalchemy.dm import types @@ -25,7 +26,7 @@ class MachineNetboot(models.Machine): - __custom_properties__ = { + __custom_properties__: tp.ClassVar[dict] = { "gc_host": types.String(max_length=255), "gc_boot_api": types.String(max_length=255), "kernel": types.AllowNone(types.String(max_length=255)), @@ -36,8 +37,8 @@ def __init__( self, gc_host: str = LOCAL_GC_HOST, gc_boot_api: str = LOCAL_GC_BOOT_API, - kernel: tp.Optional[str] = None, - initrd: tp.Optional[str] = None, + kernel: str | None = None, + initrd: str | None = None, *args, **kwargs, ): @@ -49,8 +50,8 @@ def restore_from_storage( cls, gc_host: str = LOCAL_GC_HOST, gc_boot_api: str = LOCAL_GC_BOOT_API, - kernel: tp.Optional[str] = None, - initrd: tp.Optional[str] = None, + kernel: str | None = None, + initrd: str | None = None, **kwargs, ): obj = super().restore_from_storage(**kwargs) @@ -61,8 +62,8 @@ def set_netboot_params( self, gc_host: str, gc_boot_api: str, - kernel: tp.Optional[str], - initrd: tp.Optional[str], + kernel: str | None, + initrd: str | None, ) -> None: self.gc_host = gc_host self.gc_boot_api = gc_boot_api diff --git a/exordos_core/bootstrap/defaults.py b/exordos_core/bootstrap/defaults.py index 7c191158..2dd624d0 100644 --- a/exordos_core/bootstrap/defaults.py +++ b/exordos_core/bootstrap/defaults.py @@ -154,15 +154,13 @@ def add_core_set( continue p = compute_models.Port.restore_from_simple_view( - **dict( - subnet=str(c.MAIN_SUBNET_UUID), - source=spec["stand"]["network"]["name"], - node=str(node.uuid), - ipv4=port["ip"], - mac=port["mac"], - status="ACTIVE", - project_id=str(c.SERVICE_PROJECT_ID), - ) + subnet=str(c.MAIN_SUBNET_UUID), + source=spec["stand"]["network"]["name"], + node=str(node.uuid), + ipv4=port["ip"], + mac=port["mac"], + status="ACTIVE", + project_id=str(c.SERVICE_PROJECT_ID), ) p.insert() return node_set @@ -291,7 +289,7 @@ def init_secrets( default_user.secret_hash = admin_secret_hash default_user.save() - return None + return def _net_range(network: ipaddress.IPv4Network, start_offset: int) -> str: @@ -328,7 +326,6 @@ def apply_flat_network(stand: dict[str, tp.Any]) -> None: network.insert() LOG.info("Created network %s", network.uuid) - # main_net = ipaddress.ip_network(stand["network"]["cidr"]) boot_net = ipaddress.ip_network(stand["boot_network"]["cidr"]) diff --git a/exordos_core/cmd/boot_api.py b/exordos_core/cmd/boot_api.py index ac957c5d..2704ab76 100644 --- a/exordos_core/cmd/boot_api.py +++ b/exordos_core/cmd/boot_api.py @@ -88,7 +88,7 @@ def main(): wsgi_app=app.build_wsgi_application(), host=CONF[DOMAIN].bind_host, port=CONF[DOMAIN].bind_port, - bjoern_kwargs=dict(reuse_port=True), + bjoern_kwargs={"reuse_port": True}, ) service.add_setup( diff --git a/exordos_core/cmd/bootstrap.py b/exordos_core/cmd/bootstrap.py index ade2212b..86a31eef 100644 --- a/exordos_core/cmd/bootstrap.py +++ b/exordos_core/cmd/bootstrap.py @@ -129,7 +129,6 @@ def _apply_flat_network(stand: dict[str, tp.Any]) -> None: network.insert() LOG.info("Created network %s", network.uuid) - # main_net = ipaddress.ip_network(stand["network"]["cidr"]) boot_net = ipaddress.ip_network(stand["boot_network"]["cidr"]) diff --git a/exordos_core/cmd/bootstrap_templates.py b/exordos_core/cmd/bootstrap_templates.py index f76d9026..32b5ee51 100644 --- a/exordos_core/cmd/bootstrap_templates.py +++ b/exordos_core/cmd/bootstrap_templates.py @@ -115,7 +115,7 @@ def _persisted_path(dst_path: str) -> str: - dst_path = dst_path[1:] if dst_path.startswith("/") else dst_path + dst_path = dst_path.removeprefix("/") return os.path.join(c.DATA_DIR, dst_path) diff --git a/exordos_core/cmd/orch_api.py b/exordos_core/cmd/orch_api.py index 902c52ac..c70ccf51 100644 --- a/exordos_core/cmd/orch_api.py +++ b/exordos_core/cmd/orch_api.py @@ -68,7 +68,7 @@ def main(): wsgi_app=app.build_wsgi_application(), host=CONF[DOMAIN].bind_host, port=CONF[DOMAIN].bind_port, - bjoern_kwargs=dict(reuse_port=True), + bjoern_kwargs={"reuse_port": True}, ) service.add_setup( diff --git a/exordos_core/cmd/status_api.py b/exordos_core/cmd/status_api.py index 5c84d31c..782fc14e 100644 --- a/exordos_core/cmd/status_api.py +++ b/exordos_core/cmd/status_api.py @@ -76,7 +76,7 @@ def main(): wsgi_app=app.build_wsgi_application(), host=CONF[DOMAIN].bind_host, port=CONF[DOMAIN].bind_port, - bjoern_kwargs=dict(reuse_port=True), + bjoern_kwargs={"reuse_port": True}, ) service.add_setup( diff --git a/exordos_core/cmd/user_api.py b/exordos_core/cmd/user_api.py index 8de80d56..bc0e74a2 100644 --- a/exordos_core/cmd/user_api.py +++ b/exordos_core/cmd/user_api.py @@ -122,7 +122,7 @@ def main(): ), host=CONF[DOMAIN].bind_host, port=CONF[DOMAIN].bind_port, - bjoern_kwargs=dict(reuse_port=True), + bjoern_kwargs={"reuse_port": True}, ) service.add_setup( diff --git a/exordos_core/common/config.py b/exordos_core/common/config.py index bad3dae7..bb6e5d27 100644 --- a/exordos_core/common/config.py +++ b/exordos_core/common/config.py @@ -15,35 +15,31 @@ # under the License. import logging -import typing as tp from oslo_config import cfg from exordos_core import version from exordos_core.common import constants +LOG = logging.getLogger(__name__) + GLOBAL_SERVICE_NAME = constants.GLOBAL_SERVICE_NAME _CONFIG_NOT_FOUND_MESSAGE = ( - "Unable to find configuration file in the" - " default search paths (~/.%(service_name)s/, ~/," - " /etc/%(service_name)s/, /etc/) and the '--config-file' option!" - % {"service_name": GLOBAL_SERVICE_NAME} + f"Unable to find configuration file in the" + f" default search paths (~/.{GLOBAL_SERVICE_NAME}/, ~/," + f" /etc/{GLOBAL_SERVICE_NAME}/, /etc/) and the '--config-file' option!" ) -def parse(args, conf: tp.Optional[cfg.ConfigOpts] = None): +def parse(args, conf: cfg.ConfigOpts | None = None): if not conf: conf = cfg.CONF conf( args=args, project=GLOBAL_SERVICE_NAME, - version="%s %s" - % ( - GLOBAL_SERVICE_NAME.capitalize(), - version.version_info, - ), + version=f"{GLOBAL_SERVICE_NAME.capitalize()} {version.version_info}", ) if not conf.config_file: - logging.warning(_CONFIG_NOT_FOUND_MESSAGE) + LOG.warning(_CONFIG_NOT_FOUND_MESSAGE) return conf.config_file diff --git a/exordos_core/common/dm/targets.py b/exordos_core/common/dm/targets.py index 11b86083..cba65f31 100644 --- a/exordos_core/common/dm/targets.py +++ b/exordos_core/common/dm/targets.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import typing as tp import uuid as sys_uuid from restalchemy.dm import filters as dm_filters @@ -27,11 +26,11 @@ class AbstractTarget(types_dynamic.AbstractKindModel, models.SimpleViewMixin): - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: """Returns list of target nodes where config should be deployed.""" return [] - def owners(self) -> tp.List[sys_uuid.UUID]: + def owners(self) -> list[sys_uuid.UUID]: """Return list of owners objects where config bind to. For instance, the simplest case if an ordinary node config. @@ -55,10 +54,10 @@ class NodeTarget(AbstractTarget): def from_node(cls, node: sys_uuid.UUID) -> "NodeTarget": return cls(node=node) - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: return [self.node] - def owners(self) -> tp.List[sys_uuid.UUID]: + def owners(self) -> list[sys_uuid.UUID]: """It's the simplest case with an ordinary node config. In that case, the owner and target is the node itself. @@ -66,7 +65,7 @@ def owners(self) -> tp.List[sys_uuid.UUID]: """ return [self.node] - def _fetch_nodes(self) -> tp.List[nm.Node]: + def _fetch_nodes(self) -> list[nm.Node]: return nm.Node.objects.get_all(filters={"uuid": str(self.node)}) def are_owners_alive(self) -> bool: @@ -82,10 +81,10 @@ class NodeSetTarget(AbstractTarget): def from_node_set(cls, node_set: sys_uuid.UUID) -> "NodeSetTarget": return cls(node_set=node_set) - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: return [node.uuid for node in self._fetch_nodes()] - def owners(self) -> tp.List[sys_uuid.UUID]: + def owners(self) -> list[sys_uuid.UUID]: """It's the simplest case with an ordinary node config. In that case, the owner and target is the node itself. @@ -93,7 +92,7 @@ def owners(self) -> tp.List[sys_uuid.UUID]: """ return [self.node_set] - def _fetch_nodes(self) -> tp.List[nm.Node]: + def _fetch_nodes(self) -> list[nm.Node]: return nm.Node.objects.get_all( filters={"node_set": dm_filters.EQ(str(self.node_set))} ) diff --git a/exordos_core/common/system.py b/exordos_core/common/system.py index 1bd6ea4a..040bf7b5 100644 --- a/exordos_core/common/system.py +++ b/exordos_core/common/system.py @@ -14,6 +14,7 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import os import subprocess import typing as tp @@ -21,6 +22,8 @@ import netaddr +LOG = logging.getLogger(__name__) + def system_uuid() -> sys_uuid.UUID: """Return system uuid""" @@ -44,7 +47,7 @@ def get_memory(meminfo_path: str = "/proc/meminfo") -> int: raise RuntimeError(f"Unable to find MemTotal in {meminfo_path}") -def get_ifaces(skip_virtual: bool = True) -> tp.List[tp.Dict[str, tp.Any]]: +def get_ifaces(skip_virtual: bool = True) -> list[dict[str, tp.Any]]: """Return interfaces information.""" ifaces = os.listdir("/sys/class/net") virtual_ifaces = set(os.listdir("/sys/devices/virtual/net")) @@ -74,17 +77,21 @@ def get_ifaces(skip_virtual: bool = True) -> tp.List[tp.Dict[str, tp.Any]]: ipv4, _ = value.split("/") ipv4_address = netaddr.IPAddress(ipv4) mask = netaddr.IPNetwork(value).netmask - except Exception: + except ( + netaddr.core.AddrFormatError, + IndexError, + subprocess.CalledProcessError, + ): # Unable to detect IPv4 address - pass - - iface_spec = dict( - name=iface, - mac=mac_address, - mtu=int(mtu), - ipv4_addresses=(ipv4_address,) if ipv4_address is not None else (), - masks=(mask,) if mask is not None else (), - ) + LOG.debug("Unable to detect IPv4 address for interface %s", iface) + + iface_spec = { + "name": iface, + "mac": mac_address, + "mtu": int(mtu), + "ipv4_addresses": (ipv4_address,) if ipv4_address is not None else (), + "masks": (mask,) if mask is not None else (), + } result.append(iface_spec) return result diff --git a/exordos_core/common/utils.py b/exordos_core/common/utils.py index 4227f002..56690d66 100644 --- a/exordos_core/common/utils.py +++ b/exordos_core/common/utils.py @@ -85,7 +85,7 @@ def remove_nested_dm(dm_class, parent_field_name, parent, session=None, **kwargs ) -def get_or_create_uuid_from_dict(data: tp.Dict[str, tp.Any]) -> sys_uuid.UUID: +def get_or_create_uuid_from_dict(data: dict[str, tp.Any]) -> sys_uuid.UUID: return sys_uuid.UUID(data.get("uuid", str(sys_uuid.uuid4()))) diff --git a/exordos_core/compute/agents/universal/drivers/pool.py b/exordos_core/compute/agents/universal/drivers/pool.py index c66c3328..e53d33f7 100644 --- a/exordos_core/compute/agents/universal/drivers/pool.py +++ b/exordos_core/compute/agents/universal/drivers/pool.py @@ -44,7 +44,7 @@ class RootVolumeNotFound(ua_driver_exc.AgentDriverException): class MetaPool(meta.MetaCoordinatorDataPlaneModel): """Machine pool meta model.""" - __driver_map__ = {} + __driver_map__: tp.ClassVar[dict] = {} driver_spec = properties.property( types_dynamic.KindModelSelectorType( @@ -109,7 +109,7 @@ def load_driver(self) -> driver_base.AbstractPoolDriver: self.__driver_map__[driver_key] = driver return driver - def get_meta_model_fields(self) -> tp.Optional[tp.Set[str]]: + def get_meta_model_fields(self) -> set[str] | None: """Return a list of meta fields or None. Meta fields are the fields that cannot be fetched from @@ -294,9 +294,7 @@ def _to_dp_volume(self) -> models.MachineVolume: def _is_root_volume(self) -> bool: return self.machine and self.index == 0 - def _has_storage_capacity( - self, pool: MetaPool, size: tp.Optional[int] = None - ) -> bool: + def _has_storage_capacity(self, pool: MetaPool, size: int | None = None) -> bool: if not pool.storage_pools: return False @@ -306,12 +304,12 @@ def _has_storage_capacity( # but we need to support multiple storage pools return pool.storage_pools[0].has_capacity(size) - def _allocate_capacity(self, pool: MetaPool, size: tp.Optional[int] = None) -> None: + def _allocate_capacity(self, pool: MetaPool, size: int | None = None) -> None: size = size if size is not None else self.size storage_pool = pool.storage_pools[0] storage_pool.allocate_capacity(size) - def get_meta_model_fields(self) -> tp.Optional[tp.Set[str]]: + def get_meta_model_fields(self) -> set[str] | None: """Return a list of meta fields or None. Meta fields are the fields that cannot be fetched from @@ -378,7 +376,7 @@ def dump_to_dp(self, pool: MetaPool) -> None: self._attach_volume(pool, driver, dp_volume) - def restore_from_dp(self, pool: tp.Optional[MetaPool]) -> None: + def restore_from_dp(self, pool: MetaPool | None) -> None: """Load the pool information.""" # Prevent actualization when pool is not provided if pool is None: @@ -559,22 +557,19 @@ def _from_dp_machine( def _has_enough_resources( self, pool: MetaPool, - cores: tp.Optional[int] = None, - ram: tp.Optional[int] = None, + cores: int | None = None, + ram: int | None = None, ) -> bool: if cores is not None and pool.avail_cores < cores: return False - if ram is not None and pool.avail_ram < ram: - return False - - return True + return not (ram is not None and pool.avail_ram < ram) def _allocate_resources( self, pool: MetaPool, - cores: tp.Optional[int] = None, - ram: tp.Optional[int] = None, + cores: int | None = None, + ram: int | None = None, ) -> None: if cores is not None: pool.avail_cores -= cores @@ -582,7 +577,7 @@ def _allocate_resources( if ram is not None: pool.avail_ram -= ram - def get_meta_model_fields(self) -> tp.Optional[tp.Set[str]]: + def get_meta_model_fields(self) -> set[str] | None: """Return a list of meta fields or None. Meta fields are the fields that cannot be fetched from @@ -664,7 +659,7 @@ def dump_to_dp(self, pool: MetaPool, volumes: tp.Collection[MetaVolume]) -> None self._allocate_resources(pool, self.cores, self.ram) def restore_from_dp( - self, pool: tp.Optional[MetaPool], volumes: tp.Collection[MetaVolume] + self, pool: MetaPool | None, volumes: tp.Collection[MetaVolume] ) -> None: """Load the machine from the data plane.""" # Prevent actualization when pool is not provided @@ -797,13 +792,13 @@ def update_on_dp(self, pool: MetaPool, volumes: tp.Collection[MetaVolume]) -> No class PoolAgentDriver(meta.MetaCoordinatorAgentDriver): # Order matters - __model_map__ = { + __model_map__: tp.ClassVar[dict] = { "pool": MetaPool, "pool_volume": MetaVolume, "pool_machine": MetaMachine, } - __coordinator_map__ = { + __coordinator_map__: tp.ClassVar[dict] = { "pool": {}, "pool_volume": { "pool": { diff --git a/exordos_core/compute/builders/node.py b/exordos_core/compute/builders/node.py index affb5c09..e77d2b55 100644 --- a/exordos_core/compute/builders/node.py +++ b/exordos_core/compute/builders/node.py @@ -32,7 +32,7 @@ class Node( ua_models.InstanceMixin, ua_models.DependenciesExistReadinessMixin, ): - __tracked_instances_model_map__ = { + __tracked_instances_model_map__: tp.ClassVar[dict] = { "machine": models.Machine, } diff --git a/exordos_core/compute/builders/node_set.py b/exordos_core/compute/builders/node_set.py index 02ee8e17..54f12e88 100644 --- a/exordos_core/compute/builders/node_set.py +++ b/exordos_core/compute/builders/node_set.py @@ -31,7 +31,7 @@ class NodeSetBuilderService(builder.CoreInfraBuilder): def __init__( self, - instance_model: tp.Type[models.NodeSet], + instance_model: type[models.NodeSet], project_id: sys_uuid.UUID, ): super().__init__(instance_model) diff --git a/exordos_core/compute/builders/pool.py b/exordos_core/compute/builders/pool.py index 9db304a9..3915d19b 100644 --- a/exordos_core/compute/builders/pool.py +++ b/exordos_core/compute/builders/pool.py @@ -99,7 +99,7 @@ def _set_machine_ctx( def _get_machine_ctx( self, machine: pool_models.Machine, - ) -> tp.Optional[tp.Tuple[models.Port, models.MachineVolume]]: + ) -> tuple[models.Port, models.MachineVolume] | None: """Get the machine context.""" if machine.uuid not in self._iteration_context: return None @@ -111,7 +111,7 @@ def _get_machine_ctx( def _fetch_machine_deps( self, machine: pool_models.Machine, - ) -> tp.Tuple[tp.Collection[models.Port], tp.Collection[models.MachineVolume]]: + ) -> tuple[tp.Collection[models.Port], tp.Collection[models.MachineVolume]]: """Fetch the machine dependencies.""" ports = models.Port.objects.get_all( filters={"node": dm_filters.EQ(machine.node.uuid)} @@ -124,7 +124,7 @@ def _fetch_machine_deps( def _get_or_fetch_machine_ctx( self, machine: pool_models.Machine, - ) -> tp.Tuple[tp.Optional[models.Port], tp.Optional[models.MachineVolume]]: + ) -> tuple[models.Port | None, models.MachineVolume | None]: """Get or fetch the machine context.""" # Prepare dependencies. Firstly try to get them from the iteration # context. If they are not found, fetch them from the database. @@ -148,7 +148,7 @@ def _has_enough_resources_in_pool( self, pool: pool_models.Pool, target_machine: pool_models.Machine, - actual_machine: tp.Optional[pool_models.Machine] = None, + actual_machine: pool_models.Machine | None = None, ) -> bool: """Check if the pool has enough resources to create the machine.""" # Calculate how many resources we need to create the machine @@ -248,16 +248,10 @@ def _actualize_machine_derivatives_on_create_update( self, machine: pool_models.Machine, machine_pool_pair: ( - tp.Optional[ - tp.Tuple[pool_models.PoolMachine, tp.Optional[pool_models.PoolMachine]] - ] + tuple[pool_models.PoolMachine, pool_models.PoolMachine | None] | None ) = None, machine_guest_pair: ( - tp.Optional[ - tp.Tuple[ - pool_models.GuestMachine, tp.Optional[pool_models.GuestMachine] - ] - ] + tuple[pool_models.GuestMachine, pool_models.GuestMachine | None] | None ) = None, ) -> tp.Collection[pool_models.PoolMachine | pool_models.GuestMachine]: """Actualize the machine derivatives.""" @@ -335,11 +329,9 @@ def _actualize_machine_derivatives_on_outdate( self, machine: pool_models.Machine, derivative_pairs: tp.Collection[ - tp.Tuple[ + tuple[ ua_models.TargetResourceKindAwareMixin, # The target resource - tp.Optional[ - ua_models.TargetResourceKindAwareMixin - ], # The actual resource + ua_models.TargetResourceKindAwareMixin | None, # The actual resource ] ], ) -> tp.Collection[ua_models.TargetResourceKindAwareMixin]: @@ -395,8 +387,8 @@ def _actualize_machine_derivatives_on_outdate( def _actualize_machine_status( self, machine: pool_models.Machine, - pool_machine: tp.Optional[pool_models.PoolMachine], - guest_machine: tp.Optional[pool_models.GuestMachine], + pool_machine: pool_models.PoolMachine | None, + guest_machine: pool_models.GuestMachine | None, ) -> None: """Actualize the machine status.""" @@ -456,7 +448,7 @@ def _pre_delete_machine_resource(self, resource: ua_models.TargetResource) -> No if agents: target_resources = ua_models.TargetResource.objects.get_all( filters={ - "agent": dm_filters.In((a.uuid for a in agents)), + "agent": dm_filters.In(a.uuid for a in agents), } ) @@ -474,7 +466,7 @@ def _has_enough_space_in_pool( self, pool: pool_models.Pool, target_volume: pool_models.MachineVolume, - actual_volume: tp.Optional[pool_models.MachineVolume] = None, + actual_volume: pool_models.MachineVolume | None = None, ) -> bool: """Check if the pool has enough resources to create the machine.""" # Calculate how many resources we need to create the machine @@ -556,7 +548,7 @@ def _can_update_volume( # Builder lifecycle hooks - def prepare_iteration(self) -> tp.Dict[str, tp.Any]: + def prepare_iteration(self) -> dict[str, tp.Any]: """Perform actions before iteration and return the iteration context. The result is a dictionary that is passed to the iteration context. @@ -633,11 +625,9 @@ def update_instance_derivatives( instance: pool_models.Machine, resource: ua_models.TargetResource, derivative_pairs: tp.Collection[ - tp.Tuple[ + tuple[ ua_models.TargetResourceKindAwareMixin, # The target resource - tp.Optional[ - ua_models.TargetResourceKindAwareMixin - ], # The actual resource + ua_models.TargetResourceKindAwareMixin | None, # The actual resource ] ], ) -> tp.Collection[pool_models.PoolMachine | pool_models.GuestMachine]: @@ -746,11 +736,9 @@ def actualize_outdated_instance_derivatives( self, instance: pool_models.Machine, derivative_pairs: tp.Collection[ - tp.Tuple[ + tuple[ ua_models.TargetResourceKindAwareMixin, # The target resource - tp.Optional[ - ua_models.TargetResourceKindAwareMixin - ], # The actual resource + ua_models.TargetResourceKindAwareMixin | None, # The actual resource ] ], ) -> tp.Collection[ua_models.TargetResourceKindAwareMixin]: diff --git a/exordos_core/compute/builders/volume.py b/exordos_core/compute/builders/volume.py index dc22eda8..7a673c89 100644 --- a/exordos_core/compute/builders/volume.py +++ b/exordos_core/compute/builders/volume.py @@ -33,7 +33,7 @@ class Volume( ua_models.InstanceMixin, ua_models.DependenciesExistReadinessMixin, ): - __tracked_instances_model_map__ = { + __tracked_instances_model_map__: tp.ClassVar[dict] = { "pool_volume": models.MachineVolume, } @@ -76,7 +76,7 @@ def __init__( # Internal methods def _actualize_machine_volume( - self, target: Volume, actual: tp.Optional[Volume] = None + self, target: Volume, actual: Volume | None = None ) -> None: """Update volume based on actual node data.""" # Check if volumes are already up to date @@ -123,7 +123,7 @@ def post_create_instance_resource( self, instance: Volume, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """The hook is performed after saving instance resource. diff --git a/exordos_core/compute/dm/models.py b/exordos_core/compute/dm/models.py index f6aff45a..3c711303 100644 --- a/exordos_core/compute/dm/models.py +++ b/exordos_core/compute/dm/models.py @@ -1,4 +1,4 @@ -# Copyright 2025 Genesis Corporation. +# Copyright 2025-2026 Genesis Corporation. # # All Rights Reserved. # @@ -14,10 +14,13 @@ # License for the specific language governing permissions and limitations # under the License. +import logging import random import typing as tp import uuid as sys_uuid +LOG = logging.getLogger(__name__) + from gcl_sdk.agents.universal.dm import models as ua_models from gcl_sdk.infra.dm import models as infra_models import netaddr @@ -46,7 +49,7 @@ class IPRange(types.BaseType): SEPARATOR = "-" def __init__(self, **kwargs): - super(IPRange, self).__init__(openapi_type="string", **kwargs) + super().__init__(openapi_type="string", **kwargs) def validate(self, value): return isinstance(value, netaddr.IPRange) @@ -198,7 +201,7 @@ class MachinePool( models.SimpleViewMixin, ): __tablename__ = "machine_pools" - __driver_map__ = {} + __driver_map__: tp.ClassVar[dict] = {} driver_spec = properties.property( types_dynamic.KindModelSelectorType( @@ -235,7 +238,7 @@ class MachinePool( default=list, ) - def load_driver(self) -> tp.Type["AbstractPoolDriver"]: + def load_driver(self) -> type["AbstractPoolDriver"]: """ Load the driver for the machine pool. @@ -261,9 +264,8 @@ def load_driver(self) -> tp.Type["AbstractPoolDriver"]: driver = class_(self) self.__driver_map__[driver_key] = driver return driver - except Exception: - # Just try another driver - pass + except (ImportError, AttributeError, TypeError): + LOG.debug("Failed to load driver %s", driver_key) raise ValueError(f"Driver for spec '{self.driver_spec}' not found") @@ -346,7 +348,7 @@ class Node( orm.SQLStorableWithJSONFieldsMixin, ): __tablename__ = "nodes" - __jsonfields__ = ["default_network"] + __jsonfields__: tp.ClassVar[list] = ["default_network"] uuid = properties.property( types.UUID(), @@ -590,11 +592,11 @@ class Network( models.SimpleViewMixin, ): __tablename__ = "compute_networks" - __driver_map__ = {} + __driver_map__: tp.ClassVar[dict] = {} - driver_spec = properties.property(types.Dict(), default=lambda: {}) + driver_spec = properties.property(types.Dict(), default=dict) - def load_driver(self) -> tp.Type["AbstractNetworkDriver"]: + def load_driver(self) -> type["AbstractNetworkDriver"]: driver_key = str(self.driver_spec) if driver_key in self.__driver_map__: @@ -607,9 +609,8 @@ def load_driver(self) -> tp.Type["AbstractNetworkDriver"]: driver = class_(self) self.__driver_map__[driver_key] = driver return driver - except Exception: - # Just try another driver - pass + except (ImportError, AttributeError, TypeError): + LOG.debug("Failed to load driver %s", driver_key) raise ValueError(f"Driver for spec '{self.driver_spec}' not found") @@ -620,7 +621,7 @@ class Subnet( models.SimpleViewMixin, ): __tablename__ = "compute_subnets" - __jsonfields__ = ["dns_servers", "routers"] + __jsonfields__: tp.ClassVar[list] = ["dns_servers", "routers"] network = properties.property(types.UUID()) cidr = properties.property( @@ -643,7 +644,7 @@ class Subnet( dns_servers = properties.property( types.AllowNone(types.TypedList(types.String(min_length=1, max_length=128))), - default=lambda: [], + default=list, ) routers = properties.property( types.AllowNone( @@ -656,7 +657,7 @@ class Subnet( ) ) ), - default=lambda: [], + default=list, ) next_server = properties.property( types.AllowNone(types.String(max_length=256)), default=None @@ -664,14 +665,14 @@ class Subnet( def port( self, - target_ipv4: tp.Optional[netaddr.IPAddress] = None, - ipv4: tp.Optional[netaddr.IPAddress] = None, - target_mask: tp.Optional[netaddr.IPAddress] = None, - mask: tp.Optional[netaddr.IPAddress] = None, - mac: tp.Optional[str] = None, - node_uuid: tp.Optional[sys_uuid.UUID] = None, - machine_uuid: tp.Optional[sys_uuid.UUID] = None, - project_id: tp.Optional[str] = None, + target_ipv4: netaddr.IPAddress | None = None, + ipv4: netaddr.IPAddress | None = None, + target_mask: netaddr.IPAddress | None = None, + mask: netaddr.IPAddress | None = None, + mac: str | None = None, + node_uuid: sys_uuid.UUID | None = None, + machine_uuid: sys_uuid.UUID | None = None, + project_id: str | None = None, ) -> "Port": port = Port( subnet=self.uuid, @@ -689,7 +690,7 @@ def port( @property def ip_range_pair( self, - ) -> tp.Optional[tp.Tuple[netaddr.IPAddress, netaddr.IPAddress]]: + ) -> tuple[netaddr.IPAddress, netaddr.IPAddress] | None: if self.ip_range is None: return None @@ -701,7 +702,7 @@ def ip_range_pair( @property def ip_discovery_range_pair( self, - ) -> tp.Optional[tp.Tuple[netaddr.IPAddress, netaddr.IPAddress]]: + ) -> tuple[netaddr.IPAddress, netaddr.IPAddress] | None: if self.ip_discovery_range is None: return None @@ -755,9 +756,9 @@ def generate_mac(virtual_machine: bool = True) -> str: octets = tuple(random.randint(0, 255) for _ in range(5)) if virtual_machine: - return "52:54:00:%02x:%02x:%02x" % octets[2:] + return f"52:54:00:{octets[2]:02x}:{octets[3]:02x}:{octets[4]:02x}" - return "a9:%02x:%02x:%02x:%02x:%02x" % octets + return f"a9:{octets[0]:02x}:{octets[1]:02x}:{octets[2]:02x}:{octets[3]:02x}:{octets[4]:02x}" @classmethod def from_boot_network(cls): @@ -828,7 +829,7 @@ class Interface( mtu = properties.property(types.Integer(min_value=1, max_value=65536), default=1500) @classmethod - def from_system(cls) -> tp.List["Interface"]: + def from_system(cls) -> list["Interface"]: ifaces = [] system_uuid = system.system_uuid() for iface in system.get_ifaces(): diff --git a/exordos_core/compute/node_set/dm/models.py b/exordos_core/compute/node_set/dm/models.py index 92786ef2..774c1efd 100644 --- a/exordos_core/compute/node_set/dm/models.py +++ b/exordos_core/compute/node_set/dm/models.py @@ -36,7 +36,7 @@ def get_resource_kind(cls) -> str: class NodeSet(compute_models.NodeSet): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "set_agent_node": Node, "set_agent_volume": Volume, } @@ -44,8 +44,8 @@ class NodeSet(compute_models.NodeSet): def gen_nodes( self, project_id: sys_uuid.UUID, - placement_policies: tp.Collection[compute_models.PlacementPolicy] = tuple(), - node_uuids: tp.Collection[sys_uuid.UUID] = tuple(), + placement_policies: tp.Collection[compute_models.PlacementPolicy] = (), + node_uuids: tp.Collection[sys_uuid.UUID] = (), ) -> tp.Collection[Node]: """Generate nodes for the node set.""" # FIXME(akremenetsky): Perhaps this method should be moved to diff --git a/exordos_core/compute/pool/dm/models.py b/exordos_core/compute/pool/dm/models.py index 5058d9ae..8594426a 100644 --- a/exordos_core/compute/pool/dm/models.py +++ b/exordos_core/compute/pool/dm/models.py @@ -31,7 +31,7 @@ class SchedulableToAgentFromAgentFieldMixin(ua_models.SchedulableToAgentMixin): - def schedule_to_ua_agent(self, **kwargs) -> tp.Optional[sys_uuid.UUID]: + def schedule_to_ua_agent(self, **kwargs) -> sys_uuid.UUID | None: """Schedule the resource to the UA agent. The method returns the node UUID that is equal to the @@ -43,7 +43,7 @@ def schedule_to_ua_agent(self, **kwargs) -> tp.Optional[sys_uuid.UUID]: class SchedulableToAgentFromPoolMixin(ua_models.SchedulableToAgentMixin): def schedule_to_ua_agent( self, builder: sdk_builder.UniversalBuilderService, **kwargs - ) -> tp.Optional[sys_uuid.UUID]: + ) -> sys_uuid.UUID | None: """Schedule the resource to the UA agent. The method returns the node UUID that is equal to the @@ -73,8 +73,8 @@ def get_resource_kind(cls) -> str: @classmethod def get_filter_clause( - cls, builder: sys_uuid.UUID, pools: tp.List["Pool"] - ) -> tp.Optional[tp.Dict[str, dm_filters.AbstractClause]]: + cls, builder: sys_uuid.UUID, pools: list["Pool"] + ) -> dict[str, dm_filters.AbstractClause] | None: """Get filter clause for the instance model. The clause is returned back to the service to take a chance for @@ -109,8 +109,8 @@ def get_resource_kind(cls) -> str: @classmethod def get_filter_clause( - cls, builder: sys_uuid.UUID, pools: tp.List[Pool] - ) -> tp.Optional[tp.Dict[str, dm_filters.AbstractClause]]: + cls, builder: sys_uuid.UUID, pools: list[Pool] + ) -> dict[str, dm_filters.AbstractClause] | None: """Get filter clause for the instance model. The clause is returned back to the service to take a chance for @@ -204,7 +204,7 @@ def from_machine_and_port( cls, machine: "Machine", port: models.Port, - agent_uuid: tp.Optional[str] = None, + agent_uuid: str | None = None, ) -> "PoolMachine": return cls( uuid=machine.uuid, @@ -268,7 +268,7 @@ class Machine( models.Machine, ua_models.InstanceWithDerivativesMixin, ): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "pool_machine": PoolMachine, "guest_machine": GuestMachine, } @@ -282,8 +282,8 @@ def get_resource_kind(cls) -> str: @classmethod def get_filter_clause( - cls, builder: sys_uuid.UUID, pools: tp.List[Pool] - ) -> tp.Optional[tp.Dict[str, dm_filters.AbstractClause]]: + cls, builder: sys_uuid.UUID, pools: list[Pool] + ) -> dict[str, dm_filters.AbstractClause] | None: """Get filter clause for the instance model. The clause is returned back to the service to take a chance for diff --git a/exordos_core/compute/pool/drivers/base.py b/exordos_core/compute/pool/drivers/base.py index 647d3d85..95590369 100644 --- a/exordos_core/compute/pool/drivers/base.py +++ b/exordos_core/compute/pool/drivers/base.py @@ -33,9 +33,9 @@ def get_pool_info(self) -> models.MachinePool: @abc.abstractmethod def list_pool_resources( self, - ) -> tp.Tuple[ + ) -> tuple[ models.MachinePool, - tp.Collection[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]], + tp.Collection[tuple[models.Machine, tuple[models.Port, ...]]], tp.Collection[models.MachineVolume], ]: """List pool resources.""" @@ -43,7 +43,7 @@ def list_pool_resources( @abc.abstractmethod def list_machines( self, - ) -> tp.List[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]]: + ) -> list[tuple[models.Machine, tuple[models.Port, ...]]]: """Return machine list from data plane.""" @abc.abstractmethod @@ -52,7 +52,7 @@ def create_machine( machine: models.Machine, volumes: tp.Iterable[models.MachineVolume], ports: tp.Iterable[models.Port], - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Create a new machine.""" @abc.abstractmethod @@ -64,7 +64,7 @@ def delete_machine( @abc.abstractmethod def get_machine( self, machine: sys_uuid.UUID - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Get machine from data plane.""" @abc.abstractmethod @@ -97,7 +97,7 @@ def detach_port(self, machine: models.Machine, port: models.Port) -> None: @abc.abstractmethod def list_volumes( - self, machine: tp.Optional[models.Machine] = None + self, machine: models.Machine | None = None ) -> tp.Iterable[models.MachineVolume]: """Return volume list from data plane.""" @@ -121,7 +121,7 @@ def reset_machine(self, machine: models.Machine) -> None: def recreate_machine( self, machine: models.Machine, - ports: tp.Optional[tp.Collection[models.Port]] = None, + ports: tp.Collection[models.Port] | None = None, ) -> None: """Recreate the machine.""" @@ -159,9 +159,9 @@ def get_pool_info(self) -> models.MachinePool: def list_pool_resources( self, - ) -> tp.Tuple[ + ) -> tuple[ models.MachinePool, - tp.Collection[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]], + tp.Collection[tuple[models.Machine, tuple[models.Port, ...]]], tp.Collection[models.MachineVolume], ]: """List pool resources.""" @@ -175,7 +175,7 @@ def list_pool_resources( def list_machines( self, - ) -> tp.Collection[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]]: + ) -> tp.Collection[tuple[models.Machine, tuple[models.Port, ...]]]: """Create a machine.""" return [] @@ -184,7 +184,7 @@ def create_machine( machine: models.Machine, volumes: tp.Iterable[models.MachineVolume], ports: tp.Iterable[models.Port], - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Create a machine.""" return machine, ports @@ -195,7 +195,7 @@ def delete_machine( def get_machine( self, machine: sys_uuid.UUID - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Get machine from data plane.""" # Dummy implementation - return a dummy machine return ( @@ -208,19 +208,17 @@ def get_machine( pool_id=sys_uuid.uuid4(), project_id=sys_uuid.uuid4(), ), - tuple(), + (), ) def create_volume(self, volume: models.MachineVolume) -> models.MachineVolume: """Create a new volume.""" - pass def delete_volume(self, volume: models.MachineVolume) -> None: """Delete the volume from data plane.""" - pass def list_volumes( - self, machine: tp.Optional[models.Machine] = None + self, machine: models.Machine | None = None ) -> tp.Iterable[models.MachineVolume]: """Return volume list from data plane.""" return [] @@ -255,7 +253,7 @@ def reset_machine(self, machine: models.Machine) -> None: def recreate_machine( self, machine: models.Machine, - ports: tp.Optional[tp.Collection[models.Port]] = None, + ports: tp.Collection[models.Port] | None = None, ) -> None: """Recreate the machine.""" diff --git a/exordos_core/compute/pool/drivers/libvirt.py b/exordos_core/compute/pool/drivers/libvirt.py index ded3b8a2..ced1c33a 100644 --- a/exordos_core/compute/pool/drivers/libvirt.py +++ b/exordos_core/compute/pool/drivers/libvirt.py @@ -101,7 +101,7 @@ def wrapper(self, *args: tp.Any, **kwargs: tp.Any) -> tp.Any: if getattr(self, "_dry_run", False): # Log method name and all parameters arg_strs = [repr(arg) for arg in args] - kwarg_strs = [f"{k}={repr(v)}" for k, v in kwargs.items()] + kwarg_strs = [f"{k}={v!r}" for k, v in kwargs.items()] all_params = ", ".join(arg_strs + kwarg_strs) LOG.info("DRY RUN: %s(%s)", func.__name__, all_params) @@ -227,8 +227,8 @@ def add_element( cls, document: minidom.Document, tag_name: str, - parent: tp.Optional[minidom.Element] = None, - text: tp.Optional[str] = None, + parent: minidom.Element | None = None, + text: str | None = None, **kwargs, ) -> None: root = parent or document.firstChild @@ -261,9 +261,9 @@ def document_set_tag( cls, docement: minidom.Document, tag_name: str, - text: tp.Optional[str] = None, - meta_tag: tp.Optional[str] = None, - parent: tp.Optional[minidom.Element] = None, + text: str | None = None, + meta_tag: str | None = None, + parent: minidom.Element | None = None, **kwargs, ) -> None: root = parent or docement.firstChild @@ -286,7 +286,7 @@ def document_meta_set_tag( cls, docement: minidom.Document, tag: str, - text: tp.Optional[str] = None, + text: str | None = None, **kwargs, ) -> None: # Remove the old value from the meta @@ -436,11 +436,11 @@ def domain_add_disk( def interface_xml( cls, iface_type: NetworkType = "network", - source: tp.Optional[str] = None, + source: str | None = None, model: str = "virtio", mtu: int = 1450, - mac: tp.Optional[str] = None, - rom: tp.Optional[str] = None, + mac: str | None = None, + rom: str | None = None, ) -> str: interface = ET.Element("interface", type=iface_type) @@ -471,11 +471,11 @@ def domain_add_interface( cls, domain: minidom.Document, iface_type: NetworkType = "network", - source: tp.Optional[str] = None, + source: str | None = None, model: str = "virtio", mtu: int = 1450, - mac: tp.Optional[str] = None, - rom: tp.Optional[str] = None, + mac: str | None = None, + rom: str | None = None, ) -> None: interface_xml = cls.interface_xml( iface_type=iface_type, @@ -500,7 +500,7 @@ def set_vcpu(self, cores: int) -> None: def set_memory(self, memory: int) -> None: return self.domain_set_memory(self._domain, memory) - def set_image(self, image: tp.Optional[str]) -> None: + def set_image(self, image: str | None) -> None: if image is None: return return self.domain_set_image(self._domain, image) @@ -522,11 +522,11 @@ def add_disk( def add_interface( self, iface_type: NetworkType = "network", - source: tp.Optional[str] = None, + source: str | None = None, model: str = "virtio", mtu: int = 1450, - mac: tp.Optional[str] = None, - rom: tp.Optional[str] = None, + mac: str | None = None, + rom: str | None = None, ) -> None: return self.domain_add_interface( self._domain, @@ -586,14 +586,14 @@ def _domain2machine_name(self, name: str) -> str: def _machine2domain_name(self, machine: models.Machine) -> str: machine_prefix = self._spec.machine_prefix or "" uuid_prefix = str(machine.uuid)[:8] + "-" - return f"{machine_prefix}{uuid_prefix}{str(machine.name)}" + return f"{machine_prefix}{uuid_prefix}{machine.name!s}" def _console_log_path(self, machine: models.Machine) -> str: return f"{CONSOLE_LOG_DIR}/{self._machine2domain_name(machine)}.console.log" def _domain2machine( - self, domain: libvirt.virDomain, element: tp.Optional[ET.Element] = None - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + self, domain: libvirt.virDomain, element: ET.Element | None = None + ) -> tuple[models.Machine, tuple[models.Port, ...]]: element = element or ET.fromstring(domain.XMLDesc()) cores_xml = element.find(f".//{{{GENESIS_NS}}}vcpu") @@ -670,8 +670,8 @@ def _volume_name(self, volume: libvirt.virStorageVol) -> str: def _vir_volume2machine_volume( self, volume: libvirt.virStorageVol, - machine_uuid: tp.Optional[sys_uuid.UUID] = None, - index: tp.Optional[int] = None, + machine_uuid: sys_uuid.UUID | None = None, + index: int | None = None, ) -> models.MachineVolume: index = index if index is not None else MAX_VOLUME_INDEX @@ -689,7 +689,7 @@ def _vir_volume2machine_volume( status=nc.VolumeStatus.ACTIVE.value, ) - def _list_interfaces(self, machine: models.Machine) -> tp.List[models.Port]: + def _list_interfaces(self, machine: models.Machine) -> list[models.Port]: """List all interfaces of the machine.""" domain = self._client.lookupByUUIDString(str(machine.uuid)) element = ET.fromstring(domain.XMLDesc()) @@ -720,9 +720,9 @@ def _list_interfaces(self, machine: models.Machine) -> tp.List[models.Port]: def _volume_attachments( self, - domains: tp.Collection[tp.Tuple[libvirt.virDomain, ET.Element]], + domains: tp.Collection[tuple[libvirt.virDomain, ET.Element]], volumes: tp.Collection[libvirt.virStorageVol], - ) -> tp.Dict[libvirt.virStorageVol, tp.Optional[tp.Tuple[libvirt.virDomain, int]]]: + ) -> dict[libvirt.virStorageVol, tuple[libvirt.virDomain, int] | None]: result = {v: None for v in volumes} path_map = {v.path(): v for v in volumes} @@ -754,9 +754,9 @@ def _volume_attachments( def _list_volumes( self, - domains: tp.Collection[tp.Tuple[libvirt.virDomain, ET.Element]], + domains: tp.Collection[tuple[libvirt.virDomain, ET.Element]], volumes: tp.Collection[libvirt.virStorageVol], - ) -> tp.List[models.MachineVolume]: + ) -> list[models.MachineVolume]: attachments = self._volume_attachments(domains, volumes) result = [] @@ -769,15 +769,15 @@ def _list_volumes( result.append( self._vir_volume2machine_volume(volume, machine_uuid, index=idx) ) - except Exception: + except (libvirt.libvirtError, ValueError, KeyError): LOG.debug("Failed to parse volume %s", volume.name()) return result def _list_machines( self, - domains: tp.Collection[tp.Tuple[libvirt.virDomain, tp.Optional[ET.Element]]], - ) -> tp.List[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]]: + domains: tp.Collection[tuple[libvirt.virDomain, ET.Element | None]], + ) -> list[tuple[models.Machine, tuple[models.Port, ...]]]: """Return machine list from data plane.""" # If the filter prefix is not set, return all domains if not self._spec.machine_prefix: @@ -811,7 +811,7 @@ def _fill_thin_storage_pool( def _find_attached_volume_element( self, domain: ET.Element, volume: models.MachineVolume - ) -> tp.Optional[ET.Element]: + ) -> ET.Element | None: # Check the volume is attached to the domain for disk in domain.find("devices").findall("disk"): # Check source and path @@ -857,7 +857,7 @@ def _is_legacy_domain(self, domain: ET.Element) -> bool: sys_uuid.UUID(vol) sys_uuid.UUID(machine[:36]) return True - except Exception: + except (ValueError, AttributeError): return False return False @@ -873,9 +873,9 @@ def get_pool_info(self) -> models.MachinePool: def list_pool_resources( self, - ) -> tp.Tuple[ + ) -> tuple[ models.MachinePool, - tp.Collection[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]], + tp.Collection[tuple[models.Machine, tuple[models.Port, ...]]], tp.Collection[models.MachineVolume], ]: pool = self.get_pool_info() @@ -904,7 +904,7 @@ def list_pool_resources( return pool, (storage_pool,), machines, volumes def list_volumes( - self, machine: tp.Optional[models.Machine] = None + self, machine: models.Machine | None = None ) -> tp.Iterable[models.MachineVolume]: storage_pool = self._client.storagePoolLookupByName(self._spec.storage_pool) volumes = storage_pool.listAllVolumes() @@ -969,12 +969,11 @@ def create_volume(self, volume: models.MachineVolume) -> models.MachineVolume: # Workaround until https://gitlab.com/libvirt/libvirt/-/commit/29f3c67837cc10dca3023f0bfd50414244c1bbc3 pool_xml = ET.fromstring(storage_pool.XMLDesc()) pool_type = StoragePoolType(pool_xml.get("type")) - if pool_type.value == "zfs": - if storage_pool.isActive(): - storage_pool.refresh() - LOG.warning( - "Due to libvirt<12.4 bug, ZFS storage pool was explicitly refreshed." - ) + if pool_type.value == "zfs" and storage_pool.isActive(): + storage_pool.refresh() + LOG.warning( + "Due to libvirt<12.4 bug, ZFS storage pool was explicitly refreshed." + ) LOG.debug("The volume %s has been created", volume.uuid) return volume @@ -1245,7 +1244,7 @@ def detach_port(self, machine: models.Machine, port: models.Port) -> None: def list_machines( self, - ) -> tp.List[tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]]: + ) -> list[tuple[models.Machine, tuple[models.Port, ...]]]: """Return machine list from data plane.""" domains = self._client.listAllDomains() return self._list_machines(tuple((d, None) for d in domains)) @@ -1256,7 +1255,7 @@ def create_machine( volumes: tp.Iterable[models.MachineVolume], ports: tp.Iterable[models.Port], legacy_machine: bool = False, - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Create a new LibVirt domain.""" # NOTE(akremenetsky): Unable to apply the dry_run decorator because of # the complex return type @@ -1364,7 +1363,7 @@ def delete_machine( def get_machine( self, machine: sys_uuid.UUID - ) -> tp.Tuple[models.Machine, tp.Tuple[models.Port, ...]]: + ) -> tuple[models.Machine, tuple[models.Port, ...]]: """Get machine from data plane.""" domain = self._client.lookupByUUIDString(str(machine)) return self._domain2machine(domain) @@ -1476,7 +1475,7 @@ def rename_machine(self, machine: models.Machine, name: str) -> None: def recreate_machine( self, machine: models.Machine, - ports: tp.Optional[tp.Collection[models.Port]] = None, + ports: tp.Collection[models.Port] | None = None, ) -> None: """Recreate the machine.""" if ports is None: @@ -1499,7 +1498,7 @@ def recreate_machine( ) LOG.debug("The domain %s was recreated", machine.uuid) - def list_storage_pools(self) -> tp.List[models.ThinStoragePool]: + def list_storage_pools(self) -> list[models.ThinStoragePool]: """List storage pools.""" pools = [] _pools = self._client.listAllStoragePools() diff --git a/exordos_core/compute/scheduler/driver/base.py b/exordos_core/compute/scheduler/driver/base.py index f20c7739..a568886b 100644 --- a/exordos_core/compute/scheduler/driver/base.py +++ b/exordos_core/compute/scheduler/driver/base.py @@ -40,7 +40,7 @@ class MachinePoolAbstractFilter(abc.ABC): def filter( self, node: NodeBundle, - pools: tp.List[MachinePoolBundle], + pools: list[MachinePoolBundle], ) -> tp.Iterable[MachinePoolBundle]: """Filter out pools that are not suitable for the node.""" @@ -49,7 +49,7 @@ class MachinePoolAbstractWeighter(abc.ABC): @abc.abstractmethod def weight( self, - pools: tp.List[MachinePoolBundle], + pools: list[MachinePoolBundle], ) -> tp.Iterable[float]: """Assign weights to machine pools. @@ -64,7 +64,7 @@ class MachineAbstractFilter(abc.ABC): def filter( self, node: NodeBundle, - machines: tp.List[MachineBundle], + machines: list[MachineBundle], ) -> tp.Iterable[MachineBundle]: """Filter out machines that are not suitable for the node.""" @@ -73,7 +73,7 @@ class MachineAbstractWeighter(abc.ABC): @abc.abstractmethod def weight( self, - machines: tp.List[MachineBundle], + machines: list[MachineBundle], ) -> tp.Iterable[float]: """Assign weights to machines. diff --git a/exordos_core/compute/scheduler/driver/filters/affinity.py b/exordos_core/compute/scheduler/driver/filters/affinity.py index e2637c9f..84422d28 100644 --- a/exordos_core/compute/scheduler/driver/filters/affinity.py +++ b/exordos_core/compute/scheduler/driver/filters/affinity.py @@ -25,7 +25,7 @@ class DummySoftAntiAffinityFilter(base.MachinePoolAbstractFilter): def filter( self, node: base.NodeBundle, - pools: tp.List[base.MachinePoolBundle], + pools: list[base.MachinePoolBundle], ) -> tp.Iterable[base.MachinePoolBundle]: """Filter out pools that are not suitable for the node.""" # Get all policies for the node diff --git a/exordos_core/compute/scheduler/driver/filters/available.py b/exordos_core/compute/scheduler/driver/filters/available.py index 105f2737..52fed3e2 100644 --- a/exordos_core/compute/scheduler/driver/filters/available.py +++ b/exordos_core/compute/scheduler/driver/filters/available.py @@ -22,7 +22,7 @@ class CoresRamAvailableFilter(base.MachinePoolAbstractFilter): def filter( self, node: base.NodeBundle, - pools: tp.List[base.MachinePoolBundle], + pools: list[base.MachinePoolBundle], ) -> tp.Iterable[base.MachinePoolBundle]: """Filter out pools that are not suitable for the node.""" @@ -39,7 +39,7 @@ class HWCoresRamAvailableFilter(base.MachineAbstractFilter): def filter( self, node: base.NodeBundle, - machines: tp.List[base.MachineBundle], + machines: list[base.MachineBundle], ) -> tp.Iterable[base.MachineBundle]: """Filter out machines that are not suitable for the node.""" diff --git a/exordos_core/compute/scheduler/driver/weighter/relative.py b/exordos_core/compute/scheduler/driver/weighter/relative.py index 388f9cb0..40addb42 100644 --- a/exordos_core/compute/scheduler/driver/weighter/relative.py +++ b/exordos_core/compute/scheduler/driver/weighter/relative.py @@ -47,7 +47,7 @@ def _usage_ratio(self, pool: models.MachinePool) -> float: def weight( self, - pools: tp.List[base.MachinePoolBundle], + pools: list[base.MachinePoolBundle], ) -> tp.Iterable[float]: """Assign weights to machine pools. @@ -74,7 +74,7 @@ def _ratio(self, machine: models.Machine) -> int: def weight( self, - machines: tp.List[base.MachineBundle], + machines: list[base.MachineBundle], ) -> tp.Iterable[float]: """Assign weights to machines. @@ -83,7 +83,7 @@ def weight( 0 means the machine is the worst for the node. """ if not machines: - return tuple() + return () ratios = tuple(self._ratio(m.machine) for m in machines) diff --git a/exordos_core/compute/scheduler/service.py b/exordos_core/compute/scheduler/service.py index 6489dfd6..b10c045d 100644 --- a/exordos_core/compute/scheduler/service.py +++ b/exordos_core/compute/scheduler/service.py @@ -36,10 +36,10 @@ class SchedulerService(basic.BasicService): def __init__( self, - pool_filters: tp.List[base.MachinePoolAbstractFilter], - pool_weighters: tp.List[base.MachinePoolAbstractWeighter], - machine_filters: tp.List[base.MachineAbstractFilter], - machine_weighters: tp.List[base.MachineAbstractWeighter], + pool_filters: list[base.MachinePoolAbstractFilter], + pool_weighters: list[base.MachinePoolAbstractWeighter], + machine_filters: list[base.MachineAbstractFilter], + machine_weighters: list[base.MachineAbstractWeighter], iter_min_period: int = 1, iter_pause: float = 0.1, ): @@ -51,7 +51,7 @@ def __init__( def _get_pool_builders( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.List[ua_models.UniversalAgent]: + ) -> list[ua_models.UniversalAgent]: """Get all active builders.""" return ua_models.UniversalAgent.objects.get_all( filters={ @@ -64,7 +64,7 @@ def _get_pool_builders( def _get_in_update_machines( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.List[models.Machine]: + ) -> list[models.Machine]: """Get all in update machines.""" return models.Machine.objects.get_all( filters={ @@ -77,7 +77,7 @@ def _get_in_update_machines( def _get_machines_for_nodes( self, nodes: tp.Collection[sys_uuid.UUID] - ) -> tp.List[models.Machine]: + ) -> list[models.Machine]: """Get all machines for nodes.""" return models.Machine.objects.get_all( filters={ @@ -87,11 +87,11 @@ def _get_machines_for_nodes( def _get_unscheduled_nodes( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.Tuple[base.NodeBundle, ...]: + ) -> tuple[base.NodeBundle, ...]: unscheduled = models.UnscheduledNode.objects.get_all(limit=limit) if not unscheduled: - return tuple() + return () volumes = models.Volume.objects.get_all( filters={ @@ -109,13 +109,13 @@ def _get_unscheduled_nodes( def _get_unscheduled_volumes( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.List[models.UnscheduledVolume]: + ) -> list[models.UnscheduledVolume]: """Get all unscheduled volumes.""" return models.UnscheduledVolume.objects.get_all(limit=limit) def _get_idle_machines( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.Tuple[base.MachineBundle, ...]: + ) -> tuple[base.MachineBundle, ...]: idle = models.Machine.objects.get_all( filters={ "node": dm_filters.Is(None), @@ -125,7 +125,7 @@ def _get_idle_machines( ) if not idle: - return tuple() + return () volumes = models.MachineVolume.objects.get_all( filters={ @@ -143,16 +143,14 @@ def _get_idle_machines( def _get_unscheduled_pools( self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.List[models.MachinePool]: + ) -> list[models.MachinePool]: """Get all unscheduled pools.""" return models.MachinePool.objects.get_all( filters={"builder": dm_filters.Is(None)}, limit=limit, ) - def _get_pools( - self, limit: int = nc.DEF_SQL_LIMIT - ) -> tp.List[base.MachinePoolBundle]: + def _get_pools(self, limit: int = nc.DEF_SQL_LIMIT) -> list[base.MachinePoolBundle]: """Fetch pools and available volumes in the pools.""" pools = models.MachinePool.objects.get_all( filters={ @@ -311,7 +309,7 @@ def _place_node_into_pool( pool.pool.avail_cores -= machine.cores pool.pool.avail_ram -= machine.ram - def _schedule_on_existing_machines(self) -> tp.Tuple[base.MachineBundle, ...]: + def _schedule_on_existing_machines(self) -> tuple[base.MachineBundle, ...]: unscheduled = self._get_unscheduled_nodes() # TODO(akremenetsky): Idle machines are limited by some number @@ -462,7 +460,7 @@ def _schedule_on_pools( pool.pool.uuid, ) - def _schedule_pools(self, pool_builders: tp.List[ua_models.UniversalAgent]) -> None: + def _schedule_pools(self, pool_builders: list[ua_models.UniversalAgent]) -> None: unsheduled = self._get_unscheduled_pools() if not unsheduled: LOG.debug("Nothing to schedule, no unscheduled pools") @@ -526,7 +524,7 @@ def _schedule_pools(self, pool_builders: tp.List[ua_models.UniversalAgent]) -> N except Exception: LOG.exception("Error scheduling pool %s", pool.uuid) - def _schedule_volume_on_pools(self, pools: tp.List[base.MachinePoolBundle]) -> None: + def _schedule_volume_on_pools(self, pools: list[base.MachinePoolBundle]) -> None: """Schedule volumes on pools.""" unscheduled_volumes = self._get_unscheduled_volumes() if not unscheduled_volumes: diff --git a/exordos_core/config/dm/models.py b/exordos_core/config/dm/models.py index a47de71a..6e11f8df 100644 --- a/exordos_core/config/dm/models.py +++ b/exordos_core/config/dm/models.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import typing as tp import uuid as sys_uuid from gcl_sdk.agents.universal.dm import models as ua_models @@ -125,10 +124,10 @@ class Config( default="root", ) - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: return self.target.target_nodes() - def target_owners(self) -> tp.List[sys_uuid.UUID]: + def target_owners(self) -> list[sys_uuid.UUID]: return self.target.owners() def render(self, node: sys_uuid.UUID) -> ua_models.TargetResource: @@ -155,17 +154,17 @@ def render(self, node: sys_uuid.UUID) -> ua_models.TargetResource: return resource @classmethod - def get_new_configs(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> tp.List["Config"]: + def get_new_configs(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> list["Config"]: return cls.get_new_entities(cls.__tablename__, cc.CONFIG_KIND, limit=limit) @classmethod - def get_updated_configs(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> tp.List["Config"]: + def get_updated_configs(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> list["Config"]: return cls.get_updated_entities(cls.__tablename__, cc.CONFIG_KIND, limit=limit) @classmethod def get_deleted_config_renders( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return cls.get_deleted_target_resources( cls.__tablename__, cc.CONFIG_KIND, limit=limit ) diff --git a/exordos_core/config/service.py b/exordos_core/config/service.py index 30c12ce3..db1ba549 100644 --- a/exordos_core/config/service.py +++ b/exordos_core/config/service.py @@ -39,27 +39,27 @@ class ConfigServiceBuilder(basic.BasicService): def _get_new_configs( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.Config]: + ) -> list[models.Config]: return models.Config.get_new_configs(limit=limit) def _get_changed_configs( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.Config]: + ) -> list[models.Config]: return models.Config.get_updated_configs(limit=limit) def _get_deleted_configs( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return models.Config.get_deleted_config_renders(limit=limit) def _get_outdated_renders( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.Dict[ + ) -> dict[ sys_uuid.UUID, - tp.List[tp.Tuple[ua_models.TargetResource, ua_models.Resource]], + list[tuple[ua_models.TargetResource, ua_models.Resource]], ]: renders = ua_models.OutdatedResource.objects.get_all( filters={"kind": dm_filters.EQ(cc.RENDER_KIND)}, @@ -75,7 +75,7 @@ def _get_outdated_renders( def _get_outdated_configs( self, config_uuids: tp.Collection[sys_uuid.UUID] - ) -> tp.List[tp.Tuple[models.Config, ua_models.TargetResource]]: + ) -> list[tuple[models.Config, ua_models.TargetResource]]: configs = models.Config.objects.get_all( filters={"uuid": dm_filters.In(str(cfg) for cfg in config_uuids)}, order_by={"uuid": "asc"}, @@ -93,7 +93,7 @@ def _get_outdated_configs( def _actualize_new_config( self, config: models.Config, - target_nodes: tp.List[node_models.Node], + target_nodes: list[node_models.Node], ) -> None: # Validate the owners exist # FIXME(akremenetsky): Only nodes as owners are supported for now. @@ -135,7 +135,7 @@ def _actualize_new_config( LOG.debug("Config resource %s created", config_resource.uuid) def _actualize_new_configs( - self, configs: tp.Optional[tp.List[models.Config]] = None + self, configs: list[models.Config] | None = None ) -> None: """Actualize new configs.""" configs = configs or self._get_new_configs() @@ -198,7 +198,7 @@ def _actualize_outdated_config( self, config: models.Config, config_resource: ua_models.TargetResource, - renders: tp.List[tp.Tuple[ua_models.TargetResource, ua_models.Resource]], + renders: list[tuple[ua_models.TargetResource, ua_models.Resource]], ) -> None: """Actualize outdated config.""" if len(renders) == 0: @@ -212,9 +212,7 @@ def _actualize_outdated_config( if ( actual_render.status == cc.ConfigStatus.ACTIVE and target_render.hash == actual_render.hash - ): - target_render.status = actual_render.status - elif ( + ) or ( actual_render.status != cc.ConfigStatus.ACTIVE and target_render.status != actual_render.status ): diff --git a/exordos_core/elements/builders/service.py b/exordos_core/elements/builders/service.py index def581c3..031c710b 100644 --- a/exordos_core/elements/builders/service.py +++ b/exordos_core/elements/builders/service.py @@ -38,7 +38,7 @@ def service_uuid_by_node_path( class ServiceNodeBuilder(CommonBuilder): def __init__( self, - instance_model: tp.Type[res_models.Service] = res_models.Service, + instance_model: type[res_models.Service] = res_models.Service, ): super().__init__(instance_model) @@ -52,7 +52,7 @@ def create_paas_objects( """ return self.actualize_paas_objects( - instance, builder.PaaSCollection(paas_objects=tuple()) + instance, builder.PaaSCollection(paas_objects=()) ) def actualize_paas_objects( diff --git a/exordos_core/elements/dm/models.py b/exordos_core/elements/dm/models.py index 664e732b..2484a6a3 100644 --- a/exordos_core/elements/dm/models.py +++ b/exordos_core/elements/dm/models.py @@ -233,12 +233,12 @@ def apply_imports(self, element: "Element"): from_element=import_from_element, link=f"{import_data['element']}.{import_data['link']}", ) - import_kwargs = dict( - name=import_name, - element=element, - from_element=import_from_element, - from_resource=from_resource, - ) + import_kwargs = { + "name": import_name, + "element": element, + "from_element": import_from_element, + "from_resource": from_resource, + } if "kind" in import_data: import_kwargs["kind"] = import_data["kind"] @@ -279,11 +279,11 @@ def apply_exports(self, element: "Element"): } for export_name, export_data in self.exports.items(): - export_kwargs = dict( - name=export_name, - element=element, - link=export_data["link"], - ) + export_kwargs = { + "name": export_name, + "element": element, + "link": export_data["link"], + } if "kind" in export_data: export_kwargs["kind"] = export_data["kind"] @@ -320,11 +320,11 @@ def apply_resources(self, element: "Element"): ) for resource_name, resource_value in resources.items(): resolved_link_prefix = link_resolver.full_link_original - res_kwargs = dict( - element=element, - resource_link_prefix=resolved_link_prefix, - value=resource_value, - ) + res_kwargs = { + "element": element, + "resource_link_prefix": resolved_link_prefix, + "value": resource_value, + } res_key = (resolved_link_prefix, resource_name) if resource := existing_resources.pop(res_key, None): for k, v in res_kwargs.items(): @@ -440,7 +440,7 @@ class Element( orm.SQLStorableMixin, ): __tablename__ = "em_elements" - __custom_properties__ = { + __custom_properties__: tp.ClassVar[dict] = { "link": ra_types.String(), } @@ -502,13 +502,13 @@ def delete(self, session=None): def original(self): return self - def imports(self) -> tp.List["Import"]: + def imports(self) -> list["Import"]: return Import.objects.get_all(filters={"element": ra_filters.EQ(self)}) - def exports(self) -> tp.List["Export"]: + def exports(self) -> list["Export"]: return Export.objects.get_all(filters={"element": ra_filters.EQ(self)}) - def resources(self) -> tp.List["Resource"]: + def resources(self) -> list["Resource"]: return Resource.objects.get_all(filters={"element": ra_filters.EQ(self)}) @@ -579,7 +579,7 @@ class Resource( orm.SQLStorableMixin, ): __tablename__ = "em_resources" - __custom_properties__ = { + __custom_properties__: tp.ClassVar[dict] = { # "project_id": ra_types.UUID(), # "target_state": ra_types.Dict(), # "target_hash": ra_types.String(min_length=32, max_length=32), @@ -587,7 +587,7 @@ class Resource( "link": ra_types.String(min_length=2, max_length=256), "kind": ra_types.String(min_length=2, max_length=256), } - __allowed_methods_from_manifest__ = [ + __allowed_methods_from_manifest__: tp.ClassVar[list] = [ "get_uri", "to_str", "index", @@ -645,7 +645,7 @@ def to_str(self, field: str) -> str: return "" return str(self.actual_resource.value[field]) - def index(self, field: str, idx: tp.Union[str, int] = 0) -> tp.Optional[str]: + def index(self, field: str, idx: str | int = 0) -> str | None: if not self.actual_resource or not self.actual_resource.value: return None try: @@ -670,10 +670,8 @@ def get_parameter_value(self, parameter: str): if len(resource_parameter_path) == 0: return self.actual_resource.value elif len(resource_parameter_path) == 1: - if match := re.match( - r"^(\w+)(?:\s*\(([^)]*)\))?$", - resource_parameter_path[0], - ): + match = re.match(r"^(\w+)(?:\s*\(([^)]*)\))?$", resource_parameter_path[0]) + if match: func_name = match.group(1) if func_name in self.__allowed_methods_from_manifest__: func = getattr(self, func_name) @@ -710,7 +708,7 @@ def _fstring_replacement_callback(self, match, engine): return str(value) except ValueError as e: raise exceptions.ValidateException( - err=f"Can't render value `{var}` for resource `{repr(self)}` by reason: {e}" + err=f"Can't render value `{var}` for resource `{self!r}` by reason: {e}" ) def _render_value(self, value, engine): @@ -727,7 +725,7 @@ def _render_value(self, value, engine): except ValueError as e: raise exceptions.ValidateException( err=f"Can't render value `{value}` for resource" - f" `{repr(self)}` by reason: {e}" + f" `{self!r}` by reason: {e}" ) elif value.startswith('f"'): return re.sub( @@ -955,7 +953,7 @@ class Import( ): __tablename__ = "em_imports" - __custom_properties__ = { + __custom_properties__: tp.ClassVar[dict] = { "link": ra_types.String(min_length=2, max_length=256), } @@ -1091,10 +1089,10 @@ def get_resource_by_link(self, link): class ElementEngine: def __init__(self): super().__init__() - self._namespaces: tp.Dict[str, Namespace] = {} - self._resource_exports: tp.Dict[str, Resource] = {} - self.base_schema: tp.Dict[str, tp.Any] = {} - self.full_schema: tp.Dict[str, tp.Any] = {} + self._namespaces: dict[str, Namespace] = {} + self._resource_exports: dict[str, Resource] = {} + self.base_schema: dict[str, tp.Any] = {} + self.full_schema: dict[str, tp.Any] = {} def load_schemas(self) -> None: if not self.base_schema: @@ -1108,7 +1106,7 @@ def get_namespace(self, name: str) -> Namespace: except KeyError: raise exceptions.NamespaceNotFound(name=name) - def get_elements(self) -> tp.List["Element"]: + def get_elements(self) -> list["Element"]: return [namespace.element for namespace in self._namespaces.values()] def load_from_database(self) -> None: @@ -1159,7 +1157,7 @@ def delete_resource(self, resource: Resource | ImportedResource) -> None: namespace = self._namespaces[resource.element.link] namespace.delete_resource(resource) - def get_resources(self) -> tp.List["Resource"]: + def get_resources(self) -> list["Resource"]: result = [] for namespace in self._namespaces.values(): result.extend(namespace.get_resources()) @@ -1233,17 +1231,17 @@ def __init__(self, **kwargs): filters={"uuid": ra_filters.EQ(self.service)} ): raise exceptions.ValidateException( - err="Service %s does not exist. Please create it first." % self.service + err=f"Service {self.service} does not exist. Please create it first." ) @classmethod def from_service(cls, service: sys_uuid.UUID) -> "ServiceTarget": return cls(service=service) - def target_services(self) -> tp.List[sys_uuid.UUID]: + def target_services(self) -> list[sys_uuid.UUID]: return [self.service] - def owners(self) -> tp.List[sys_uuid.UUID]: + def owners(self) -> list[sys_uuid.UUID]: """It's the simplest case with an ordinary service target. In that case, the owner and target is the service itself. @@ -1251,7 +1249,7 @@ def owners(self) -> tp.List[sys_uuid.UUID]: """ return [self.service] - def _fetch_services(self) -> tp.List["Service"]: + def _fetch_services(self) -> list["Service"]: return Service.objects.get_all(filters={"uuid": str(self.service)}) def are_owners_alive(self) -> bool: @@ -1331,8 +1329,8 @@ class Service( default=[], ) - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: return self.target.target_nodes() - def target_owners(self) -> tp.List[sys_uuid.UUID]: + def target_owners(self) -> list[sys_uuid.UUID]: return self.target.owners() diff --git a/exordos_core/elements/dm/res_models.py b/exordos_core/elements/dm/res_models.py index a71d2fdc..3d9b0c72 100644 --- a/exordos_core/elements/dm/res_models.py +++ b/exordos_core/elements/dm/res_models.py @@ -60,7 +60,7 @@ class Service( ua_models.InstanceWithDerivativesMixin, ): # __master_model__ = sdk_models.NodeSet - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "service_agent_node": ServiceNode, } diff --git a/exordos_core/elements/dm/utils.py b/exordos_core/elements/dm/utils.py index 4b82b98e..32e67690 100644 --- a/exordos_core/elements/dm/utils.py +++ b/exordos_core/elements/dm/utils.py @@ -87,9 +87,7 @@ def get_element_uuid(element_name, element_version): def get_project_id(): # return sys_uuid.UUID(f"{UUID_PREFIX}{str(sys_uuid.uuid4())[8:]}") - return sys_uuid.UUID( - f"{UUID_PREFIX}{str('00000000-0000-0000-0000-000000000000')[8:]}" - ) + return sys_uuid.UUID(f"{UUID_PREFIX}{'00000000-0000-0000-0000-000000000000'[8:]}") def get_required_field(data, field_name): @@ -141,7 +139,7 @@ def parse_variable(var: str) -> Parsed: resource_name = parts[-1][1:] resource_parts = parts[1:-1] is_resource = True - is_variable = True if value else False + is_variable = bool(value) resource_type = f"${element_name}.{'.'.join(resource_parts)}" elif value: result.valid = False @@ -163,7 +161,7 @@ def parse_variable(var: str) -> Parsed: return result -def walk(node: tp.Union[dict, list, str]) -> tp.List[Parsed]: +def walk(node: dict | list | str) -> list[Parsed]: variables = [] if isinstance(node, dict): for value in node.values(): @@ -178,7 +176,7 @@ def walk(node: tp.Union[dict, list, str]) -> tp.List[Parsed]: return variables -def walk_replace(resource_type: str, scheme: dict, node: tp.Union[dict, list, str]): +def walk_replace(resource_type: str, scheme: dict, node: dict | list | str): if isinstance(node, dict): for key, value in node.items(): if isinstance(value, str): @@ -257,18 +255,17 @@ def load_user_api_spec() -> dict: return yaml.safe_load(f) -def validate_manifest(data: dict, schema: tp.Optional[dict]) -> None: +def validate_manifest(data: dict, schema: dict | None) -> None: if data and schema: try: openapi_schema_validator.validate( data, schema, cls=openapi_schema_validator.OAS30Validator ) except ValidationError as err: - LOG.exception("Failed to validate data %s: %s", data, err) + LOG.exception("Failed to validate data %s", data) raise exceptions.OpenApiValidateException( err=f"{err.message} in {err.json_path}" ) - return None def build_full_schema( @@ -315,8 +312,8 @@ def build_full_schema( base_manifest_schema = build_full_schema( base_manifest_schema, user_api_spec ) - except Exception as e: - LOG.exception(f"Failed to get spec from {spec}: {e}") + except Exception: + LOG.exception("Failed to get spec from %s", spec) elif os.path.exists(spec): try: with open(spec, "r") as f: @@ -324,8 +321,8 @@ def build_full_schema( base_manifest_schema = build_full_schema( base_manifest_schema, user_api_spec ) - except Exception as e: - LOG.exception(f"Failed to get spec from {spec}: {e}") + except (OSError, yaml.YAMLError): + LOG.exception("Failed to get spec from %s", spec) return base_manifest_schema @@ -369,7 +366,7 @@ def remove_middle_parts(input_string): def mutate_resource_types(manifest: dict) -> dict: mutated_map = {} - for resource_type in manifest["resources"].keys(): + for resource_type in manifest["resources"]: mutated_resource_type = remove_middle_parts(resource_type) if resource_type != mutated_resource_type: mutated_map[mutated_resource_type] = resource_type @@ -383,7 +380,7 @@ def mutate_resource_types(manifest: dict) -> dict: def mutate_manifest(manifest: dict, scheme: dict) -> dict: manifest = mutate_resource_types(manifest) for resource_type, resource in manifest["resources"].items(): - for resource_name, resource_value in resource.items(): + for resource_value in resource.values(): walk_replace(resource_type, scheme, resource_value) return manifest diff --git a/exordos_core/janitor/service.py b/exordos_core/janitor/service.py index 7d9b919d..bfab625f 100644 --- a/exordos_core/janitor/service.py +++ b/exordos_core/janitor/service.py @@ -49,7 +49,7 @@ def _clean_bad_confirmation_codes(self): for user in users: user.clear_confirmation_code() - LOG.debug("Users cleaned: %s" % len(users)) + LOG.debug("Users cleaned: %d", len(users)) def _iteration(self): with contexts.Context().session_manager(): diff --git a/exordos_core/network/border/builders/iaas.py b/exordos_core/network/border/builders/iaas.py index db05b591..b0082656 100644 --- a/exordos_core/network/border/builders/iaas.py +++ b/exordos_core/network/border/builders/iaas.py @@ -46,7 +46,7 @@ class BorderIaasBuilder(builder.CoreInfraBuilder): def __init__( self, - instance_model: tp.Type[models.IaasBorder], + instance_model: type[models.IaasBorder], project_id: sys_uuid.UUID, ): super().__init__(instance_model) @@ -81,7 +81,7 @@ def actualize_infra( infra: builder.InfraCollection, ) -> tp.Collection[ua_models.TargetResourceKindAwareMixin]: if instance.node or instance.type.kind != "core": - return tuple() + return () nodeset = None tgt_nodeset = None diff --git a/exordos_core/network/border/builders/paas.py b/exordos_core/network/border/builders/paas.py index 7340b70c..0bea2682 100644 --- a/exordos_core/network/border/builders/paas.py +++ b/exordos_core/network/border/builders/paas.py @@ -36,7 +36,7 @@ class BorderBuilder(builder.PaaSBuilder): def __init__( self, - instance_model: tp.Type[models.PaasBorder] = models.PaasBorder, + instance_model: type[models.PaasBorder] = models.PaasBorder, ): super().__init__(instance_model) @@ -44,7 +44,7 @@ def create_paas_objects( self, instance: models.PaasBorder ) -> tp.Collection[ua_models.TargetResourceKindAwareMixin]: return self.actualize_paas_objects( - instance, builder.PaaSCollection(paas_objects=tuple()) + instance, builder.PaaSCollection(paas_objects=()) ) def actualize_paas_objects( @@ -110,7 +110,7 @@ def actualize_paas_objects( return actual_resources - def _get_iaas_nodes(self, instance: models.PaasBorder) -> tp.List[str]: + def _get_iaas_nodes(self, instance: models.PaasBorder) -> list[str]: """Node uuids of the border VM node set (empty until provisioned).""" try: res = ua_models.Resource.objects.get_one( diff --git a/exordos_core/network/border/dm/models.py b/exordos_core/network/border/dm/models.py index 9c066b7e..799c8228 100644 --- a/exordos_core/network/border/dm/models.py +++ b/exordos_core/network/border/dm/models.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from gcl_sdk.agents.universal.dm import models as ua_models from restalchemy.dm import models as ra_models from restalchemy.dm import properties @@ -36,9 +38,9 @@ class BorderAgent( types.Enum([status.value for status in models.LBStatus]), default=models.LBStatus.NEW.value, ) - snat_rules = properties.property(types.List(), default=lambda: []) - forwards = properties.property(types.List(), default=lambda: []) - routes = properties.property(types.List(), default=lambda: []) + snat_rules = properties.property(types.List(), default=list) + forwards = properties.property(types.List(), default=list) + routes = properties.property(types.List(), default=list) @classmethod def get_resource_kind(cls) -> str: @@ -74,7 +76,7 @@ class IaasBorder(models.Border, ua_models.InstanceWithDerivativesMixin): schedules the border_node capability to that VM's agent. """ - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "target_node_set": lb_models.TargetNodeSet, } @@ -94,7 +96,7 @@ def get_resource_target_fields(self): class PaasBorder(models.Border, ua_models.InstanceWithDerivativesMixin): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "border_agent": BorderAgent, "border_node": BorderNode, } diff --git a/exordos_core/network/dhcp/isc.py b/exordos_core/network/dhcp/isc.py index e5f21cc5..9f2df26b 100644 --- a/exordos_core/network/dhcp/isc.py +++ b/exordos_core/network/dhcp/isc.py @@ -102,7 +102,7 @@ def to_rfc3442(self) -> str: """ -def rfc3442_static_routes(routes: tp.List[StaticRoute]) -> str: +def rfc3442_static_routes(routes: list[StaticRoute]) -> str: if len(routes) == 0: return "" @@ -116,14 +116,14 @@ def rfc3442_static_routes(routes: tp.List[StaticRoute]) -> str: # Only single route is supported as default if default_route := next((r for r in routes if r.is_default), None): - route_line = f"option routers {str(default_route.via)};" + route_line = f"option routers {default_route.via!s};" rfc3442_route_line += default_route.to_rfc3442() rfc3442_route_line = rfc3442_route_line[:-1] + ";" return f"{route_line}\n\t{rfc3442_route_line}\n" -def dhcp_config(subnets: tp.Dict[models.Subnet, tp.List[models.Port]]) -> str: +def dhcp_config(subnets: dict[models.Subnet, list[models.Port]]) -> str: # FIXME(akremenetsky): It's considered the subnets aren't intersecting config = _common_settings @@ -147,7 +147,7 @@ def dhcp_config(subnets: tp.Dict[models.Subnet, tp.List[models.Port]]) -> str: hosts += _host_template.format( mac_address=port.mac, ip_address=port.ipv4, - hostname=f"P_{str(port.uuid)}", + hostname=f"P_{port.uuid!s}", ) if subnet.next_server and ";" not in subnet.next_server: diff --git a/exordos_core/network/driver/base.py b/exordos_core/network/driver/base.py index fa08c02e..bbf1883f 100644 --- a/exordos_core/network/driver/base.py +++ b/exordos_core/network/driver/base.py @@ -53,7 +53,7 @@ def update_port(self, port: models.Port) -> models.Port: def update_subnet(self, subnet: models.Subnet) -> models.Subnet: """Update the subnet in data plane.""" - def create_ports(self, ports: tp.List[models.Port]) -> tp.List[models.Port]: + def create_ports(self, ports: list[models.Port]) -> list[models.Port]: """Create a list of ports.""" # The default implementation is to create each port separately @@ -62,7 +62,7 @@ def create_ports(self, ports: tp.List[models.Port]) -> tp.List[models.Port]: new_ports.append(self.create_port(port)) return new_ports - def delete_ports(self, ports: tp.List[models.Port]) -> None: + def delete_ports(self, ports: list[models.Port]) -> None: """Delete the port from data plane.""" # The default implementation is to delete each port separately @@ -71,7 +71,7 @@ def delete_ports(self, ports: tp.List[models.Port]) -> None: class DummyNetworkDriver(AbstractNetworkDriver): - SPEC = {"driver": "dummy"} + SPEC: tp.ClassVar[dict] = {"driver": "dummy"} def __init__(self, network: models.Network) -> None: if network.driver_spec != self.SPEC: diff --git a/exordos_core/network/driver/flat.py b/exordos_core/network/driver/flat.py index 20a15395..c862c7ca 100644 --- a/exordos_core/network/driver/flat.py +++ b/exordos_core/network/driver/flat.py @@ -62,11 +62,11 @@ class DhcpPortAlreadyExists(exceptions.CGNetException): @dataclasses.dataclass class DHCPContext: cfg_hash: str - subnets: tp.List[models.Subnet] - port_map: tp.DefaultDict[sys_uuid.UUID, tp.List[models.Port]] + subnets: list[models.Subnet] + port_map: collections.defaultdict[sys_uuid.UUID, list[models.Port]] @property - def subnet_map(self) -> tp.Dict[models.Subnet, tp.List[models.Port]]: + def subnet_map(self) -> dict[models.Subnet, list[models.Port]]: return {s: self.port_map[s.uuid] for s in self.subnets} def save_ctx(self, ctx_path: str) -> None: @@ -259,7 +259,7 @@ def create_port(self, port: models.Port) -> models.Port: return port - def create_ports(self, ports: tp.List[models.Port]) -> tp.List[models.Port]: + def create_ports(self, ports: list[models.Port]) -> list[models.Port]: """Create a list of ports.""" ctx = self._load_ctx() new_ports = [] @@ -337,7 +337,7 @@ def delete_port(self, port: models.Port) -> None: self._dhcp_cfg_path, ) - def delete_ports(self, ports: tp.List[models.Port]) -> None: + def delete_ports(self, ports: list[models.Port]) -> None: ctx = self._load_ctx() for port in ports: diff --git a/exordos_core/network/ipam.py b/exordos_core/network/ipam.py index 8be9755a..51e1bd76 100644 --- a/exordos_core/network/ipam.py +++ b/exordos_core/network/ipam.py @@ -50,7 +50,7 @@ class IpamIpRangeOverlap(net_exceptions.CGNetException): class Ipam: def __init__( self, - subnet_map: tp.Dict[net_models.Subnet, tp.List[net_models.Port]], + subnet_map: dict[net_models.Subnet, list[net_models.Port]], ) -> None: """ Initialize IPAM with a subnet map. @@ -80,7 +80,7 @@ def add_subnet( def calculate_pool( self, subnet: net_models.Subnet, ports: tp.Iterable[net_models.Port] - ) -> tp.List[tp.Tuple[int, int]]: + ) -> list[tuple[int, int]]: ip_start, ip_end = subnet.cidr[0], subnet.cidr[-1] if subnet.ip_range_pair: ip_start, ip_end = subnet.ip_range_pair @@ -109,7 +109,7 @@ def calculate_pool( def occupy_ip( self, address: int, - address_pool: tp.List[tp.Tuple[int, int]], + address_pool: list[tuple[int, int]], ) -> None: for i, (s, e) in enumerate(address_pool): if s == e and s == address: @@ -140,7 +140,7 @@ def occupy_ip( def allocate_ip( self, subnet: net_models.Subnet, - target_ip: tp.Optional[netaddr.IPAddress] = None, + target_ip: netaddr.IPAddress | None = None, ) -> netaddr.IPAddress: if subnet not in self._pool_map: raise IpamUndefinedSubnet(subnet=str(subnet.uuid)) diff --git a/exordos_core/network/lb/builders/iaas.py b/exordos_core/network/lb/builders/iaas.py index cce169d1..474e6480 100644 --- a/exordos_core/network/lb/builders/iaas.py +++ b/exordos_core/network/lb/builders/iaas.py @@ -37,7 +37,7 @@ class LBBuilder(builder.CoreInfraBuilder): def __init__( self, - instance_model: tp.Type[models.IaasLB], + instance_model: type[models.IaasLB], project_id: sys_uuid.UUID, ): super().__init__(instance_model) @@ -74,7 +74,7 @@ def actualize_infra( infra: builder.InfraCollection, ) -> tp.Collection[ua_models.TargetResourceKindAwareMixin]: if instance.type.kind != "core": - return tuple() + return () nodeset = None tgt_nodeset = None diff --git a/exordos_core/network/lb/builders/paas.py b/exordos_core/network/lb/builders/paas.py index 194a3264..40a7250a 100644 --- a/exordos_core/network/lb/builders/paas.py +++ b/exordos_core/network/lb/builders/paas.py @@ -32,7 +32,7 @@ class LBBuilder(builder.PaaSBuilder): def __init__( self, - instance_model: tp.Type[models.PaasLB] = models.PaasLB, + instance_model: type[models.PaasLB] = models.PaasLB, ): super().__init__(instance_model) @@ -46,7 +46,7 @@ def create_paas_objects( """ return self.actualize_paas_objects( - instance, builder.PaaSCollection(paas_objects=tuple()) + instance, builder.PaaSCollection(paas_objects=()) ) def actualize_paas_objects( @@ -63,7 +63,7 @@ def actualize_paas_objects( if instance.type.kind == "core": nodes = self.get_actual_nodeset(instance).nodes - for node_uuid in nodes.keys(): + for node_uuid in nodes: nuuid = uuid.UUID(node_uuid) actual_resources.append( models.PaasLBNode( diff --git a/exordos_core/network/lb/dm/models.py b/exordos_core/network/lb/dm/models.py index f00f0624..1f1475f9 100644 --- a/exordos_core/network/lb/dm/models.py +++ b/exordos_core/network/lb/dm/models.py @@ -35,7 +35,7 @@ def get_resource_kind(cls) -> str: class IaasLB(models.LB, ua_models.InstanceWithDerivativesMixin): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "target_node_set": TargetNodeSet, } @@ -98,7 +98,7 @@ def get_resource_kind(cls) -> str: class PaasLB(IaasLB): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "paas_lb_node": PaasLBNode, "paas_lb_agent": PaasLBAgent, } diff --git a/exordos_core/network/service.py b/exordos_core/network/service.py index 28ea65d7..f27c8c33 100644 --- a/exordos_core/network/service.py +++ b/exordos_core/network/service.py @@ -33,12 +33,12 @@ class NetworkService(basic.BasicService): - def _get_new_vm_nodes(self) -> tp.List[models.NodeWithoutPorts]: + def _get_new_vm_nodes(self) -> list[models.NodeWithoutPorts]: return models.NodeWithoutPorts.get_vm_nodes() def _get_new_hw_ports( self, subnets: tp.Iterable[net_models.Subnet] - ) -> tp.List[net_models.Port]: + ) -> list[net_models.Port]: ports = [] nodes = net_models.HWNodeWithoutPorts.get_nodes() @@ -66,7 +66,7 @@ def _get_new_hw_ports( def _get_subnet_map( self, - ) -> tp.Dict[net_models.Subnet, tp.List[net_models.Port]]: + ) -> dict[net_models.Subnet, list[net_models.Port]]: # TODO(akremenetsky): Take all subnets so far. # This snippet will be reworked. subnets = net_models.Subnet.objects.get_all() @@ -93,25 +93,25 @@ def _get_subnet_map( return subnet_map def _build_network_map( - self, subnet_map: tp.Dict[net_models.Subnet, tp.List[net_models.Port]] - ) -> tp.DefaultDict[ - models.Network, tp.Dict[net_models.Subnet, tp.List[net_models.Port]] + self, subnet_map: dict[net_models.Subnet, list[net_models.Port]] + ) -> collections.defaultdict[ + models.Network, dict[net_models.Subnet, list[net_models.Port]] ]: network_map = collections.defaultdict(dict) - for subnet in subnet_map.keys(): - network_map[subnet.network][subnet] = subnet_map[subnet] + for subnet, value in subnet_map.items(): + network_map[subnet.network][subnet] = value return network_map def _actualize_network( self, network: models.Network, - subnet_map: tp.Dict[net_models.Subnet, tp.List[net_models.Port]], + subnet_map: dict[net_models.Subnet, list[net_models.Port]], ) -> None: driver: net_base.AbstractNetworkDriver = network.load_driver() actual_subnets = {s.uuid: s for s in driver.list_subnets()} - target_subnets = {s.uuid: s for s in subnet_map.keys()} + target_subnets = {s.uuid: s for s in subnet_map} # Create subnets for uuid in target_subnets.keys() - actual_subnets.keys(): @@ -150,7 +150,7 @@ def _actualize_subnet( driver: net_base.AbstractNetworkDriver, actual_subnet: models.Subnet, target_subnet: net_models.Subnet, - target_ports: tp.List[net_models.Port], + target_ports: list[net_models.Port], ) -> None: actual_ports = {p.uuid: p for p in driver.list_ports(actual_subnet)} target_ports = {p.uuid: p for p in target_ports} @@ -177,7 +177,7 @@ def _actualize_subnet( ports = driver.create_ports(ports) except Exception: LOG.exception("Error creating ports: %s", ports) - ports = tuple() + ports = () for p in ports: target_port = target_ports[p.uuid] @@ -204,7 +204,7 @@ def _actualize_subnet( driver.delete_ports(ports) except Exception: LOG.exception("Error creating ports: %s", ports) - ports = tuple() + ports = () # Actualize ports for uuid in actual_ports.keys() & target_ports.keys(): @@ -255,10 +255,10 @@ def _allocate_port( self, node: models.NodeWithoutPorts, ipam: net_ipam.Ipam, - subnet_map: tp.Dict[net_models.Subnet, tp.List[net_models.Port]], + subnet_map: dict[net_models.Subnet, list[net_models.Port]], ) -> net_models.Port: # Figure out the correct subnet - for subnet, ports in subnet_map.items(): + for subnet in subnet_map: if self._is_subnet_match(node, subnet): break else: diff --git a/exordos_core/orch_api/api/routes.py b/exordos_core/orch_api/api/routes.py index 574cd894..80d22397 100644 --- a/exordos_core/orch_api/api/routes.py +++ b/exordos_core/orch_api/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from gcl_sdk.agents.universal.orch_api import routes as orch_routes from restalchemy.api import routes @@ -24,6 +26,6 @@ class ApiEndpointRoute(routes.Route): """Handler for /v1/ endpoint""" __controller__ = controllers.ApiEndpointController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] agents = routes.route(orch_routes.UniversalAgentsRoute) diff --git a/exordos_core/quota/dm/models.py b/exordos_core/quota/dm/models.py index 178c758d..fe0d5edd 100644 --- a/exordos_core/quota/dm/models.py +++ b/exordos_core/quota/dm/models.py @@ -48,7 +48,7 @@ def __init__( DEFAULT_QUOTA_LIMIT = 1000 -DEFAULT_QUOTA_LIMITS: tp.Dict[str, int] = { +DEFAULT_QUOTA_LIMITS: dict[str, int] = { "net_lb": DEFAULT_QUOTA_LIMIT, "compute_sets": DEFAULT_QUOTA_LIMIT, "nodes": DEFAULT_QUOTA_LIMIT, @@ -57,7 +57,7 @@ def __init__( "secret_rsa_keys": DEFAULT_QUOTA_LIMIT, "secret_ssh_keys": DEFAULT_QUOTA_LIMIT, } -DEFAULT_QUOTA_FIELD_LIMITS: tp.Dict[str, tp.Dict[str, int]] = { +DEFAULT_QUOTA_FIELD_LIMITS: dict[str, dict[str, int]] = { "nodes": {"cores": 10000}, } QUOTA_RESOURCE_MODELS = { diff --git a/exordos_core/repo/agents/universal/drivers/repo_element.py b/exordos_core/repo/agents/universal/drivers/repo_element.py index d87bce0e..6ddc18ff 100644 --- a/exordos_core/repo/agents/universal/drivers/repo_element.py +++ b/exordos_core/repo/agents/universal/drivers/repo_element.py @@ -52,7 +52,7 @@ def __init__( session: tp.Any | None = None, ): super().__init__( - model_specs=tuple(), + model_specs=(), tf_storage=tf_storage, session=session, ) diff --git a/exordos_core/repo/builders/element.py b/exordos_core/repo/builders/element.py index cdd06a2d..38f8997f 100644 --- a/exordos_core/repo/builders/element.py +++ b/exordos_core/repo/builders/element.py @@ -129,7 +129,7 @@ def from_repo_element(cls, element: models.RepoElement) -> "InstalledManifest": class RepoElement(models.RepoElement, ua_models.InstanceWithDerivativesMixin): - __derivative_model_map__ = { + __derivative_model_map__: tp.ClassVar[dict] = { "repo_proxy_installed_element": InstalledManifest, } @@ -166,7 +166,7 @@ def _is_installation_in_progress(element: models.RepoElement) -> bool: def _matches_version_constraint( element: models.RepoElement, constraint: dict, - name: tp.Optional[str] = None, + name: str | None = None, ) -> bool: """Check whether element version satisfies the dependency constraint. @@ -200,9 +200,7 @@ def _matches_version_constraint( return False if "<" in constraint and version_key >= _version_key(constraint["<"]): return False - if "<=" in constraint and version_key > _version_key(constraint["<="]): - return False - return True + return not ("<=" in constraint and version_key > _version_key(constraint["<="])) def _element_sort_key(element: models.RepoElement) -> tuple[bool, int, tuple]: @@ -339,7 +337,7 @@ def _collect_dependencies( element=instance.name, ) - selected = sorted(candidates, key=_element_sort_key)[0] + selected = min(candidates, key=_element_sort_key) LOG.info( "Selected dependency %s:%s for element %s", selected.name, @@ -397,7 +395,7 @@ def post_create_instance_resource( self, instance: RepoElement, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """The hook is performed after saving instance resource. diff --git a/exordos_core/repo/builders/repository.py b/exordos_core/repo/builders/repository.py index 6c969305..4b3312db 100644 --- a/exordos_core/repo/builders/repository.py +++ b/exordos_core/repo/builders/repository.py @@ -76,7 +76,7 @@ def post_create_instance_resource( self, instance: Repository, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """The hook is performed after saving instance resource. @@ -182,7 +182,7 @@ def post_update_instance_resource( self, instance: models.Repository, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """Handle repository refresh if next_refresh time has passed.""" super().post_update_instance_resource(instance, resource, derivatives) diff --git a/exordos_core/repo/dm/models.py b/exordos_core/repo/dm/models.py index 7cf24239..67a4f925 100644 --- a/exordos_core/repo/dm/models.py +++ b/exordos_core/repo/dm/models.py @@ -167,7 +167,7 @@ class Repository( """ __tablename__ = "repo_repositories" - __driver_map__ = {} + __driver_map__: tp.ClassVar[dict] = {} status = properties.property( ra_types.Enum([s.value for s in RepositoryStatus]), @@ -230,9 +230,8 @@ def load_driver(self) -> "AbstractProxyRepoDriver": driver = class_(self) self.__driver_map__[driver_key] = driver return driver - except Exception: - # Just try another driver - pass + except (ImportError, AttributeError, TypeError): + LOG.debug("Failed to load driver %s", driver_key) raise ValueError(f"Driver for spec '{self.driver_spec}' not found") diff --git a/exordos_core/repo/drivers/bootstrap.py b/exordos_core/repo/drivers/bootstrap.py index cfe75c99..4cb02370 100644 --- a/exordos_core/repo/drivers/bootstrap.py +++ b/exordos_core/repo/drivers/bootstrap.py @@ -25,6 +25,8 @@ LOG = logging.getLogger(__name__) +LOG = logging.getLogger(__name__) + class BootstrapProxyRepoDriver(base.AbstractProxyRepoDriver): """Driver for bootstrap repository that reads manifests from local directory.""" @@ -69,7 +71,7 @@ def _scan_manifests(self) -> None: try: with open(filepath) as f: manifest_data = yaml.safe_load(f) - except Exception: + except (yaml.YAMLError, OSError): LOG.debug("Failed to load YAML from %s", filepath) continue diff --git a/exordos_core/secret/builders/service.py b/exordos_core/secret/builders/service.py index a860f2a0..d3e7efe8 100644 --- a/exordos_core/secret/builders/service.py +++ b/exordos_core/secret/builders/service.py @@ -29,8 +29,6 @@ class Password( models.Password, ua_models.InstanceMixin, ): - pass - @classmethod def get_resource_kind(cls) -> str: return sc.PASSWORD_KIND @@ -40,8 +38,6 @@ class Certificate( models.Certificate, ua_models.InstanceMixin, ): - pass - @classmethod def get_resource_kind(cls) -> str: return sc.CERTIFICATE_KIND @@ -51,8 +47,6 @@ class RSAKey( models.RSAKey, ua_models.InstanceMixin, ): - pass - @classmethod def get_resource_kind(cls) -> str: return sc.RSA_KEY_KIND @@ -62,8 +56,6 @@ class SSHKey( models.SSHKey, ua_models.InstanceMixin, ): - pass - @classmethod def get_resource_kind(cls) -> str: return sc.SSH_KEY_KIND diff --git a/exordos_core/secret/dm/models.py b/exordos_core/secret/dm/models.py index 518a692f..6646f86b 100644 --- a/exordos_core/secret/dm/models.py +++ b/exordos_core/secret/dm/models.py @@ -85,7 +85,7 @@ class Password( default=None, ) - def get_resource_target_fields(self) -> tp.Set[str]: + def get_resource_target_fields(self) -> set[str]: """Return the collection of target fields. Refer to the Resource model for more details about target fields. @@ -106,13 +106,13 @@ def get_resource_target_fields(self) -> tp.Set[str]: return fields @classmethod - def get_new_passwords(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> tp.List["Password"]: + def get_new_passwords(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> list["Password"]: return cls.get_new_entities(cls.__tablename__, sc.PASSWORD_KIND, limit=limit) @classmethod def get_updated_passwords( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List["Password"]: + ) -> list["Password"]: return cls.get_updated_entities( cls.__tablename__, sc.PASSWORD_KIND, limit=limit ) @@ -120,7 +120,7 @@ def get_updated_passwords( @classmethod def get_deleted_passwords( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return cls.get_deleted_target_resources( cls.__tablename__, sc.PASSWORD_KIND, limit=limit ) @@ -143,7 +143,7 @@ class Certificate( ua_models.TargetResourceSQLStorableMixin, ): __tablename__ = "secret_certificates" - __jsonfields__ = ["domains"] + __jsonfields__: tp.ClassVar[list] = ["domains"] method = properties.property( types_dynamic.KindModelSelectorType( @@ -177,7 +177,7 @@ class Certificate( # - DP: Is the threshold overcame? overcome_threshold = properties.property(types.Boolean(), default=False) - def get_resource_target_fields(self) -> tp.Set[str]: + def get_resource_target_fields(self) -> set[str]: """Return the collection of target fields. Refer to the Resource model for more details about target fields. @@ -198,13 +198,13 @@ def get_resource_target_fields(self) -> tp.Set[str]: @classmethod def get_new_certificates( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List["Certificate"]: + ) -> list["Certificate"]: return cls.get_new_entities(cls.__tablename__, sc.CERTIFICATE_KIND, limit=limit) @classmethod def get_updated_certificates( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List["Certificate"]: + ) -> list["Certificate"]: return cls.get_updated_entities( cls.__tablename__, sc.CERTIFICATE_KIND, limit=limit ) @@ -212,7 +212,7 @@ def get_updated_certificates( @classmethod def get_deleted_certificates( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return cls.get_deleted_target_resources( cls.__tablename__, sc.CERTIFICATE_KIND, limit=limit ) @@ -241,9 +241,9 @@ class RSAKey( def __init__( self, - private_key: tp.Optional[str] = None, - public_key: tp.Optional[str] = None, - bitness: tp.Optional[int] = None, + private_key: str | None = None, + public_key: str | None = None, + bitness: int | None = None, **kwargs, ): """Initialize RSA key secret. @@ -280,7 +280,7 @@ def __init__( **kwargs, ) - def get_resource_target_fields(self) -> tp.Set[str]: + def get_resource_target_fields(self) -> set[str]: """Return the collection of target fields. Refer to the Resource model for more details about target fields. @@ -320,10 +320,10 @@ class SSHKey( default="", ) - def target_nodes(self) -> tp.List[sys_uuid.UUID]: + def target_nodes(self) -> list[sys_uuid.UUID]: return self.target.target_nodes() - def get_resource_target_fields(self) -> tp.Set[str]: + def get_resource_target_fields(self) -> set[str]: """Return the collection of target fields. Refer to the Resource model for more details about target fields. @@ -344,7 +344,7 @@ def to_host_resource( self, master: sys_uuid.UUID, node: sys_uuid.UUID, - status: tp.Optional[sc.SecretStatus] = None, + status: sc.SecretStatus | None = None, ) -> ua_models.TargetResource: """Create a target resource for a specific host (node). @@ -362,7 +362,7 @@ def to_host_resource( properties = {} # Copy properties - for name in self.properties.properties.keys(): + for name in self.properties.properties: if name not in SSHHostKey.properties.properties: continue properties[name] = getattr(self, name) @@ -380,17 +380,17 @@ def to_host_resource( return resource @classmethod - def get_new_keys(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> tp.List["SSHKey"]: + def get_new_keys(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> list["SSHKey"]: return cls.get_new_entities(cls.__tablename__, sc.SSH_KEY_KIND, limit=limit) @classmethod - def get_updated_keys(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> tp.List["SSHKey"]: + def get_updated_keys(cls, limit: int = c.DEFAULT_SQL_LIMIT) -> list["SSHKey"]: return cls.get_updated_entities(cls.__tablename__, sc.SSH_KEY_KIND, limit=limit) @classmethod def get_deleted_keys( cls, limit: int = c.DEFAULT_SQL_LIMIT - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return cls.get_deleted_target_resources( cls.__tablename__, sc.SSH_KEY_KIND, limit=limit ) diff --git a/exordos_core/secret/service.py b/exordos_core/secret/service.py index ba970e0f..ca7666e7 100644 --- a/exordos_core/secret/service.py +++ b/exordos_core/secret/service.py @@ -36,46 +36,46 @@ class SecretServiceBuilder(basic.BasicService): def _get_new_certificates( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.Certificate]: + ) -> list[models.Certificate]: return models.Certificate.get_new_certificates(limit=limit) def _get_changed_certificates( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.Certificate]: + ) -> list[models.Certificate]: return models.Certificate.get_updated_certificates(limit=limit) def _get_deleted_certificates( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return models.Certificate.get_deleted_certificates(limit=limit) def _get_new_ssh_keys( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.SSHKey]: + ) -> list[models.SSHKey]: return models.SSHKey.get_new_keys(limit=limit) def _get_changed_ssh_keys( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[models.SSHKey]: + ) -> list[models.SSHKey]: return models.SSHKey.get_updated_keys(limit=limit) def _get_deleted_ssh_keys( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.List[ua_models.TargetResource]: + ) -> list[ua_models.TargetResource]: return models.SSHKey.get_deleted_keys(limit=limit) def _get_outdated_resources( self, kind: str, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.Dict[ + ) -> dict[ sys_uuid.UUID, # Resource UUID - tp.Tuple[ua_models.TargetResource, ua_models.Resource], + tuple[ua_models.TargetResource, ua_models.Resource], ]: outdated = ua_models.OutdatedResource.objects.get_all( filters={"kind": dm_filters.EQ(kind)}, @@ -93,7 +93,7 @@ def _get_outdated_secrets( self, model: models.Secret, uuids: tp.Collection[sys_uuid.UUID], - ) -> tp.List[models.Secret]: + ) -> list[models.Secret]: return model.objects.get_all( filters={"uuid": dm_filters.In(str(p) for p in uuids)}, ) @@ -101,9 +101,9 @@ def _get_outdated_secrets( def _get_outdated_ssh_key_hosts( self, limit: int = c.DEFAULT_SQL_LIMIT, - ) -> tp.Dict[ + ) -> dict[ sys_uuid.UUID, # Master UUID - tp.List[tp.Tuple[ua_models.TargetResource, ua_models.Resource]], + list[tuple[ua_models.TargetResource, ua_models.Resource]], ]: outdated = ua_models.OutdatedResource.objects.get_all( filters={"kind": dm_filters.EQ(sc.SSH_KEY_TARGET_KIND)}, @@ -120,7 +120,7 @@ def _get_outdated_ssh_key_hosts( def _get_outdated_ssh_keys( self, masters: tp.Collection[sys_uuid.UUID], - ) -> tp.List[tp.Tuple[models.SSHKey, ua_models.TargetResource]]: + ) -> list[tuple[models.SSHKey, ua_models.TargetResource]]: ssh_key_resources = ua_models.TargetResource.objects.get_all( filters={ "uuid": dm_filters.In(m for m in masters), @@ -163,7 +163,7 @@ def _actualize_new_secrets( LOG.exception("Error creating cert resource %s", secret.uuid) def _actualize_changed_secrets( - self, kind: str, changed_secrets: tp.Dict[sys_uuid.UUID, models.Secret] + self, kind: str, changed_secrets: dict[sys_uuid.UUID, models.Secret] ) -> None: """Actualize secrets changed by user.""" if len(changed_secrets) == 0: @@ -171,7 +171,7 @@ def _actualize_changed_secrets( secret_resources = ua_models.TargetResource.objects.get_all( filters={ - "uuid": dm_filters.In(str(p) for p in changed_secrets.keys()), + "uuid": dm_filters.In(str(p) for p in changed_secrets), "kind": dm_filters.EQ(kind), } ) @@ -270,9 +270,7 @@ def _actualize_outdated_certificate( if ( actual_resource.status == sc.SecretStatus.ACTIVE and target_resource.hash == actual_resource.hash - ): - status_updated = True - elif ( + ) or ( actual_resource.status != sc.SecretStatus.ACTIVE and target_resource.status != actual_resource.status ): @@ -326,7 +324,7 @@ def _actualize_deleted_certificates(self) -> None: def _actualize_new_ssh_key( self, key: models.SSHKey, - target_nodes: tp.List[nm.Node], + target_nodes: list[nm.Node], ) -> None: # Validate the owners exist # FIXME(akremenetsky): Only nodes as owners are supported for now. @@ -376,9 +374,7 @@ def _actualize_new_ssh_key( key_resource.update() LOG.debug("SSH key resource %s created", key_resource.uuid) - def _actualize_new_ssh_keys( - self, keys: tp.Collection[models.SSHKey] = tuple() - ) -> None: + def _actualize_new_ssh_keys(self, keys: tp.Collection[models.SSHKey] = ()) -> None: """Actualize new SSH keys.""" keys = keys or self._get_new_ssh_keys() @@ -434,9 +430,7 @@ def _actualize_outdated_ssh_key( self, key: models.SSHKey, key_resource: ua_models.TargetResource, - host_keys: tp.Collection[ - tp.Tuple[ua_models.TargetResource, ua_models.Resource] - ], + host_keys: tp.Collection[tuple[ua_models.TargetResource, ua_models.Resource]], ) -> None: """Actualize outdated SSH keys.""" # Update target keys with actual information from the DP. @@ -444,11 +438,13 @@ def _actualize_outdated_ssh_key( target.full_hash = actual.full_hash # `ACTIVE` only if the hash is the same - if actual.status == sc.SecretStatus.ACTIVE and target.hash == actual.hash: - target.status = actual.status - elif ( - actual.status != sc.SecretStatus.ACTIVE - and target.status != actual.status + if ( + actual.status == sc.SecretStatus.ACTIVE + and target.hash == actual.hash + or ( + actual.status != sc.SecretStatus.ACTIVE + and target.status != actual.status + ) ): target.status = actual.status target.update() diff --git a/exordos_core/status_api/api/routes.py b/exordos_core/status_api/api/routes.py index e3aaf094..b83bf1a0 100644 --- a/exordos_core/status_api/api/routes.py +++ b/exordos_core/status_api/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from gcl_sdk.agents.universal.status_api import routes as status_routes from restalchemy.api import routes @@ -24,7 +26,7 @@ class ApiEndpointRoute(routes.Route): """Handler for /v1/ endpoint""" __controller__ = controllers.ApiEndpointController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] agents = routes.route(status_routes.UniversalAgentsRoute) node_verifiers = routes.route(status_routes.NodeVerifiersRoute) diff --git a/exordos_core/telemetry/service.py b/exordos_core/telemetry/service.py index d7345e2b..2e485b12 100644 --- a/exordos_core/telemetry/service.py +++ b/exordos_core/telemetry/service.py @@ -129,7 +129,7 @@ def _collect_machine_pools(data): # Count by status status_counts = {} for pool in pools: - key = "machine_pools_status_%s" % pool.status.lower() + key = f"machine_pools_status_{pool.status.lower()}" status_counts[key] = status_counts.get(key, 0) + 1 data.update(status_counts) diff --git a/exordos_core/tests/functional/conftest.py b/exordos_core/tests/functional/conftest.py index 0f27927d..de018644 100644 --- a/exordos_core/tests/functional/conftest.py +++ b/exordos_core/tests/functional/conftest.py @@ -14,15 +14,18 @@ # License for the specific language governing permissions and limitations # under the License. +from collections.abc import Generator import json +import logging import os import tempfile import typing as tp from typing import Any -from typing import Generator from urllib.parse import urlparse import uuid as sys_uuid +LOG = logging.getLogger(__name__) + import bazooka from gcl_iam import tokens from gcl_iam.tests.functional import clients as iam_clients @@ -113,7 +116,7 @@ def cleanup_test_entities(): if str(obj.uuid).startswith(TEST_UUID_PREFIX): obj.delete() except Exception: - pass + LOG.debug("Failed to clean up %s", model.__name__, exc_info=True) @pytest.fixture(scope="session") @@ -261,7 +264,7 @@ def auth_test1_user( admin_client = user_api_client(auth_user_admin) admin_client.delete_user(auth.uuid) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture() @@ -302,7 +305,7 @@ def auth_test2_user( admin_client = user_api_client(auth_user_admin) admin_client.delete_user(auth.uuid) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture() @@ -364,17 +367,17 @@ def auth_test1_p1_user( admin_client = user_api_client(auth_user_admin) admin_client.delete_project(project["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) try: admin_client = user_api_client(auth_user_admin) admin_client.delete_organization(org["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) try: admin_client = user_api_client(auth_user_admin) admin_client.delete_user(user["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture() @@ -431,17 +434,17 @@ def auth_test2_p1_user( admin_client = user_api_client(auth_user_admin) admin_client.delete_project(project["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) try: admin_client = user_api_client(auth_user_admin) admin_client.delete_organization(org["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) try: admin_client = user_api_client(auth_user_admin) admin_client.delete_user(user["uuid"]) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture() @@ -449,8 +452,8 @@ def user_api_client(user_api, auth_user_admin): def build_client( auth: iam_clients.GenesisCoreAuth, - permissions: tp.Optional[tp.List[str]] = None, - project_id: tp.Optional[str] = None, + permissions: list[str] | None = None, + project_id: str | None = None, ): permissions = permissions or [] client = iam_clients.GenericAutoRefreshRESTClient( @@ -483,15 +486,15 @@ def user_api_noauth_client(user_api): @pytest.fixture def node_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "node", cores: int = 1, ram: int = 1024, image: str = "ubuntu_24.04", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[str] = None, + status: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() status_value = nc.NodeStatus.NEW.value if status is None else status.value node = node_models.Node( @@ -516,15 +519,15 @@ def factory( @pytest.fixture def node_factory_with_model(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "node", cores: int = 1, ram: int = 1024, image: str = "ubuntu_24.04", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[str] = None, + status: str | None = None, **kwargs, - ) -> tp.Tuple[tp.Dict[str, tp.Any], node_models.Node]: + ) -> tuple[dict[str, tp.Any], node_models.Node]: uuid = uuid or _make_uuid() status_value = nc.NodeStatus.NEW.value if status is None else status.value node = node_models.Node( @@ -549,7 +552,7 @@ def factory( @pytest.fixture def node_set_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "node_set", cores: int = 1, ram: int = 1024, @@ -558,7 +561,7 @@ def factory( project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, status: str = nc.NodeStatus.NEW.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() obj = node_set_models.NodeSet( uuid=uuid, @@ -581,17 +584,17 @@ def factory( @pytest.fixture def pool_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - agent: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + agent: sys_uuid.UUID | None = None, name: str = "pool-default", - driver_spec: tp.Optional[dict] = None, - status: tp.Optional[str] = None, + driver_spec: dict | None = None, + status: str | None = None, avail_cores: int = 8, avail_ram: int = 16384, all_cores: int = 8, all_ram: int = 16384, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() driver_spec = ( {"kind": "libvirt", "connection_uri": "qemu+tcp://127.0.0.1/system"} @@ -629,10 +632,10 @@ def factory( @pytest.fixture -def machine_factory(default_pool: tp.Dict[str, tp.Any]): +def machine_factory(default_pool: dict[str, tp.Any]): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - pool: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + pool: sys_uuid.UUID | None = None, name: str = "node", cores: int = 1, ram: int = 1024, @@ -640,7 +643,7 @@ def factory( status: str = nc.MachineStatus.ACTIVE.value, build_status: str = nc.MachineBuildStatus.READY.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() pool = pool or sys_uuid.UUID(default_pool["uuid"]) machine = node_models.Machine( @@ -663,12 +666,12 @@ def factory( @pytest.fixture def volume_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "volume-default", size: int = 10, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() volume = node_models.Volume( uuid=uuid, @@ -696,15 +699,15 @@ def factory( def config_factory(): def factory( target_node: sys_uuid.UUID, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "config", path: str = "/etc/genesis-configs/config.conf", content_body: str = "test", - on_change_cmd: tp.Optional[str] = None, + on_change_cmd: str | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, status: str = cc.ConfigStatus.NEW.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() target = ct.NodeTarget.from_node(target_node) body = conf_models.TextBodyConfig.from_text(content_body) @@ -733,15 +736,15 @@ def factory( @pytest.fixture def password_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "password", - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, + constructor: secret_models.AbstractSecretConstructor | None = None, method: sc.SecretMethod = sc.SecretMethod.AUTO_HEX, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, - value: tp.Optional[str] = None, + status: cc.ConfigStatus | None = None, + value: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() constructor = ( secret_models.PlainSecretConstructor() @@ -772,18 +775,18 @@ def factory( @pytest.fixture def cert_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "cert", domains: tp.Collection[str] = ("genesis-core.tech",), email: str = "user@genesis-core.tech", - key: tp.Optional[str] = None, - cert: tp.Optional[str] = None, - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, - method: tp.Optional[secret_models.AbstractCertificateMethod] = None, + key: str | None = None, + cert: str | None = None, + constructor: secret_models.AbstractSecretConstructor | None = None, + method: secret_models.AbstractCertificateMethod | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, + status: cc.ConfigStatus | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() constructor = ( secret_models.PlainSecretConstructor() @@ -825,15 +828,15 @@ def ssh_key_factory(): def factory( target_node: sys_uuid.UUID, target_public_key: str, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "key", - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, + constructor: secret_models.AbstractSecretConstructor | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, + status: cc.ConfigStatus | None = None, user: str = "root", authorized_keys=".ssh/authorized_keys", **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() target = ct.NodeTarget.from_node(target_node) constructor = ( @@ -866,7 +869,7 @@ def factory( @pytest.fixture def pool_builder_factory() -> tp.Callable: def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, status: str = nc.BuilderStatus.ACTIVE.value, **kwargs, ) -> sdk_ua_models.UniversalAgent: @@ -895,10 +898,10 @@ def factory( @pytest.fixture def interface_factory() -> tp.Callable: def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - mac: tp.Optional[str] = None, + uuid: sys_uuid.UUID | None = None, + mac: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() interface = node_models.Interface( uuid=uuid, @@ -915,12 +918,12 @@ def factory( def machine_pool_reservation_factory() -> tp.Callable: def factory( pool: sys_uuid.UUID, - uuid: tp.Optional[sys_uuid.UUID] = None, - machine: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + machine: sys_uuid.UUID | None = None, cores: int = 1, ram: int = 1024, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() reservation = node_models.MachinePoolReservations( uuid=uuid, @@ -939,11 +942,11 @@ def factory( @pytest.fixture def lb_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "load-balancer-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() lb = network_models.LB( uuid=uuid, @@ -965,11 +968,11 @@ def factory( @pytest.fixture def lb_factory_with_model(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "load-balancer-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, **kwargs, - ) -> tp.Tuple[tp.Dict[str, tp.Any], network_models.LB]: + ) -> tuple[dict[str, tp.Any], network_models.LB]: uuid = uuid or _make_uuid() lb = network_models.LB( uuid=uuid, @@ -992,7 +995,7 @@ def factory( def vhost_factory(): def factory( lb, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "vhost-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, enabled: bool = True, @@ -1003,7 +1006,7 @@ def factory( external_sources: list[str] | None = None, proxy_protocol_from: list[str] | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() vhost = network_models.Vhost( parent=lb, @@ -1034,7 +1037,7 @@ def factory( def vhost_factory_with_model(): def factory( lb, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "vhost-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, enabled: bool = True, @@ -1045,7 +1048,7 @@ def factory( external_sources: list[str] | None = None, proxy_protocol_from: list[str] | None = None, **kwargs, - ) -> tp.Tuple[tp.Dict[str, tp.Any], network_models.Vhost]: + ) -> tuple[dict[str, tp.Any], network_models.Vhost]: uuid = uuid or _make_uuid() vhost = network_models.Vhost( parent=lb, @@ -1077,12 +1080,12 @@ def backend_pool_factory(): def factory( lb, endpoints: list[network_models.BackendHostKind], - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "backend-pool-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, balance: str = network_models.BalanceTypes.RR.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() backend_pool = network_models.BackendPool( parent=lb, @@ -1109,12 +1112,12 @@ def backend_pool_factory_with_model(): def factory( lb, endpoints: list[network_models.BackendHostKind], - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "backend-pool-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, balance: str = network_models.BalanceTypes.RR.value, **kwargs, - ) -> tp.Tuple[tp.Dict[str, tp.Any], network_models.BackendPool]: + ) -> tuple[dict[str, tp.Any], network_models.BackendPool]: uuid = uuid or _make_uuid() backend_pool = network_models.BackendPool( parent=lb, @@ -1141,12 +1144,12 @@ def route_factory(): def factory( vhost, condition: network_models.AbstractHTTPRouteCondKind, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "route-default", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, enabled: bool = True, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or _make_uuid() route = network_models.Route( parent=vhost, @@ -1192,7 +1195,7 @@ def default_pool( try: client.delete(url) except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture @@ -1231,7 +1234,7 @@ def default_network( try: network.delete() except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture @@ -1254,7 +1257,7 @@ def default_subnet( try: subnet.delete() except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture @@ -1278,7 +1281,7 @@ def default_machine_agent( try: agent.delete() except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture @@ -1308,7 +1311,7 @@ def default_pool_builder( try: agent.delete() except Exception: - pass + LOG.debug("Cleanup failed", exc_info=True) @pytest.fixture(scope="session", autouse=True) @@ -1468,7 +1471,7 @@ def factory(hostname, ip_addresses=None, key=None): @pytest.fixture() def password_agent_service( - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], user_api_client: iam_clients.GenesisCoreTestRESTClient, ): agent_uuid = sys_uuid.UUID(default_node["uuid"]) @@ -1499,7 +1502,7 @@ def password_agent_service( @pytest.fixture() def cert_agent_service( - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], user_api_client: iam_clients.GenesisCoreTestRESTClient, ): agent_uuid = sys_uuid.UUID(default_node["uuid"]) diff --git a/exordos_core/tests/functional/restapi/compute/test_compute_boot_api.py b/exordos_core/tests/functional/restapi/compute/test_compute_boot_api.py index c71ad21b..2cb76664 100644 --- a/exordos_core/tests/functional/restapi/compute/test_compute_boot_api.py +++ b/exordos_core/tests/functional/restapi/compute/test_compute_boot_api.py @@ -58,7 +58,7 @@ def test_netboots_default_net(self, boot_api: test_utils.RestServiceTestCase): CONF[boot_api_cmd.DOMAIN].gc_host = "10.20.0.2" uuid = sys_uuid.uuid4() - url = urljoin(boot_api.base_url, f"boots/{str(uuid)}") + url = urljoin(boot_api.base_url, f"boots/{uuid!s}") response = requests.get(url) @@ -114,7 +114,7 @@ def test_netboots_default_net_custom_kernel_initrd( CONF[boot_api_cmd.DOMAIN].initrd = "https://kernel.org/initrd.img" uuid = sys_uuid.uuid4() - url = urljoin(boot_api.base_url, f"boots/{str(uuid)}") + url = urljoin(boot_api.base_url, f"boots/{uuid!s}") response = requests.get(url) diff --git a/exordos_core/tests/functional/restapi/compute/test_hypervisor_api.py b/exordos_core/tests/functional/restapi/compute/test_hypervisor_api.py index 7b185999..3d6f3f4b 100644 --- a/exordos_core/tests/functional/restapi/compute/test_hypervisor_api.py +++ b/exordos_core/tests/functional/restapi/compute/test_hypervisor_api.py @@ -32,8 +32,8 @@ class TestHypervisorUserApi: @staticmethod def _node_cmp_shallow( - node_foo: tp.Dict[str, tp.Any], - node_bar: tp.Dict[str, tp.Any], + node_foo: dict[str, tp.Any], + node_bar: dict[str, tp.Any], ): return ( all( @@ -52,8 +52,8 @@ def _node_cmp_shallow( @staticmethod def _hypervisor_cmp_shallow( - hypervisor_foo: tp.Dict[str, tp.Any], - hypervisor_bar: tp.Dict[str, tp.Any], + hypervisor_foo: dict[str, tp.Any], + hypervisor_bar: dict[str, tp.Any], ): return ( all( @@ -120,7 +120,7 @@ def test_hypervisors_add_several( name=f"hypervisor_{i}", driver_spec={ "kind": "libvirt", - "connection_uri": f"qemu+tcp://10.20.0.{str(i + 1)}/system", + "connection_uri": f"qemu+tcp://10.20.0.{i + 1!s}/system", }, ) hypervisor.pop("status", None) diff --git a/exordos_core/tests/functional/restapi/compute/test_node_set_api.py b/exordos_core/tests/functional/restapi/compute/test_node_set_api.py index 94928312..b1e9e6e7 100644 --- a/exordos_core/tests/functional/restapi/compute/test_node_set_api.py +++ b/exordos_core/tests/functional/restapi/compute/test_node_set_api.py @@ -29,8 +29,8 @@ class TestNodeSetUserApi: @staticmethod def _node_cmp_shallow( - node_foo: tp.Dict[str, tp.Any], - node_bar: tp.Dict[str, tp.Any], + node_foo: dict[str, tp.Any], + node_bar: dict[str, tp.Any], ): return ( all( @@ -49,8 +49,8 @@ def _node_cmp_shallow( @staticmethod def _node_set_cmp_shallow( - node_set_foo: tp.Dict[str, tp.Any], - node_set_bar: tp.Dict[str, tp.Any], + node_set_foo: dict[str, tp.Any], + node_set_bar: dict[str, tp.Any], ): return ( all( diff --git a/exordos_core/tests/functional/restapi/compute/test_node_user_api.py b/exordos_core/tests/functional/restapi/compute/test_node_user_api.py index 08da8403..364cea4f 100644 --- a/exordos_core/tests/functional/restapi/compute/test_node_user_api.py +++ b/exordos_core/tests/functional/restapi/compute/test_node_user_api.py @@ -29,8 +29,8 @@ class TestNodeUserApi: @staticmethod def _node_cmp_shallow( - node_foo: tp.Dict[str, tp.Any], - node_bar: tp.Dict[str, tp.Any], + node_foo: dict[str, tp.Any], + node_bar: dict[str, tp.Any], ): return ( all( @@ -203,7 +203,7 @@ def test_hyper_list_empty( def test_hyper_list( self, - default_pool: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, ): diff --git a/exordos_core/tests/functional/restapi/compute/test_volume_api.py b/exordos_core/tests/functional/restapi/compute/test_volume_api.py index dfa8957b..455572b9 100644 --- a/exordos_core/tests/functional/restapi/compute/test_volume_api.py +++ b/exordos_core/tests/functional/restapi/compute/test_volume_api.py @@ -26,8 +26,8 @@ class TestVolumeUserApi: @staticmethod def _volume_cmp_shallow( - volume_foo: tp.Dict[str, tp.Any], - volume_bar: tp.Dict[str, tp.Any], + volume_foo: dict[str, tp.Any], + volume_bar: dict[str, tp.Any], ) -> bool: return all( (volume_foo[key] == volume_bar[key]) diff --git a/exordos_core/tests/functional/restapi/config/test_configs.py b/exordos_core/tests/functional/restapi/config/test_configs.py index f98b724d..a5d2dd55 100644 --- a/exordos_core/tests/functional/restapi/config/test_configs.py +++ b/exordos_core/tests/functional/restapi/config/test_configs.py @@ -29,8 +29,8 @@ class TestConfigUserApi: @staticmethod def _config_cmp_shallow( - cfg_foo: tp.Dict[str, tp.Any], - cfg_bar: tp.Dict[str, tp.Any], + cfg_foo: dict[str, tp.Any], + cfg_bar: dict[str, tp.Any], ): return all( (cfg_foo[key] == cfg_bar[key]) diff --git a/exordos_core/tests/functional/restapi/iam/test_organization_members.py b/exordos_core/tests/functional/restapi/iam/test_organization_members.py index 92b6cfce..aab37a3f 100644 --- a/exordos_core/tests/functional/restapi/iam/test_organization_members.py +++ b/exordos_core/tests/functional/restapi/iam/test_organization_members.py @@ -32,11 +32,11 @@ def _create_organization_member( url = client.build_collection_uri( ["iam/organizations/", organization_uuid, "members"] ) - body = dict( - organization=f"/v1/iam/organizations/{organization_uuid}", - user=f"/v1/iam/users/{user_uuid}", - role=role, - ) + body = { + "organization": f"/v1/iam/organizations/{organization_uuid}", + "user": f"/v1/iam/users/{user_uuid}", + "role": role, + } response = client.post(url, json=body) assert response.status_code == 201 @@ -58,7 +58,6 @@ def _delete_organization_member( ) response = client.delete(url) assert response.status_code == 204 - return None def test_create_member_as_owner_success( self, @@ -391,11 +390,11 @@ def test_create_member_to_foreign_organization_as_owner_success( url = test1_client.build_collection_uri( ["iam/organizations/", org["uuid"], "members"] ) - body = dict( - organization=f"/v1/iam/organizations/{org['uuid']}", - user=f"/v1/iam/users/{auth_test2_user.uuid}", - role=c.OrganizationRole.MEMBER.value, - ) + body = { + "organization": f"/v1/iam/organizations/{org['uuid']}", + "user": f"/v1/iam/users/{auth_test2_user.uuid}", + "role": c.OrganizationRole.MEMBER.value, + } response = test1_client.post(url, json=body) assert response.status_code == 201 diff --git a/exordos_core/tests/functional/restapi/iam/test_organizations.py b/exordos_core/tests/functional/restapi/iam/test_organizations.py index 45afe71d..9e3af961 100644 --- a/exordos_core/tests/functional/restapi/iam/test_organizations.py +++ b/exordos_core/tests/functional/restapi/iam/test_organizations.py @@ -14,10 +14,14 @@ # License for the specific language governing permissions and limitations # under the License. +import logging + from bazooka import exceptions as bazooka_exc import pytest from exordos_core.tests.functional.restapi.iam import base + +LOG = logging.getLogger(__name__) from exordos_core.user_api.iam import constants as c @@ -119,7 +123,7 @@ def test_list_organizations_test1_auth_success( try: admin_client.delete_organization(org["uuid"]) except Exception: - pass + LOG.info("cleanup failed", exc_info=True) def test_list_all_organizations_test1_auth_success( self, user_api_client, auth_user_admin, auth_test1_user @@ -146,7 +150,7 @@ def test_list_all_organizations_test1_auth_success( try: admin_client.delete_organization(org["uuid"]) except Exception: - pass + LOG.info("cleanup failed", exc_info=True) def test_get_any_organization_test1_auth_success( self, user_api_client, auth_user_admin, auth_test1_user diff --git a/exordos_core/tests/functional/restapi/network/test_lb_api.py b/exordos_core/tests/functional/restapi/network/test_lb_api.py index 33dc99a0..adf61d09 100644 --- a/exordos_core/tests/functional/restapi/network/test_lb_api.py +++ b/exordos_core/tests/functional/restapi/network/test_lb_api.py @@ -26,8 +26,8 @@ class TestLBApi: @staticmethod def _lb_cmp_shallow( - lb_foo: tp.Dict[str, tp.Any], - lb_bar: tp.Dict[str, tp.Any], + lb_foo: dict[str, tp.Any], + lb_bar: dict[str, tp.Any], ) -> bool: return all( (lb_foo[key] == lb_bar[key]) @@ -39,8 +39,8 @@ def _lb_cmp_shallow( @staticmethod def _vhost_cmp_shallow( - vhost_foo: tp.Dict[str, tp.Any], - vhost_bar: tp.Dict[str, tp.Any], + vhost_foo: dict[str, tp.Any], + vhost_bar: dict[str, tp.Any], ) -> bool: return all( (vhost_foo[key] == vhost_bar[key]) @@ -55,8 +55,8 @@ def _vhost_cmp_shallow( @staticmethod def _backend_pool_cmp_shallow( - backend_pool_foo: tp.Dict[str, tp.Any], - backend_pool_bar: tp.Dict[str, tp.Any], + backend_pool_foo: dict[str, tp.Any], + backend_pool_bar: dict[str, tp.Any], ) -> bool: return all( (backend_pool_foo[key] == backend_pool_bar[key]) @@ -65,8 +65,8 @@ def _backend_pool_cmp_shallow( @staticmethod def _route_cmp_shallow( - route_foo: tp.Dict[str, tp.Any], - route_bar: tp.Dict[str, tp.Any], + route_foo: dict[str, tp.Any], + route_bar: dict[str, tp.Any], ) -> bool: return all( (route_foo[key] == route_bar[key]) diff --git a/exordos_core/tests/functional/restapi/quota/test_quota_api.py b/exordos_core/tests/functional/restapi/quota/test_quota_api.py index a50dd638..cced4035 100644 --- a/exordos_core/tests/functional/restapi/quota/test_quota_api.py +++ b/exordos_core/tests/functional/restapi/quota/test_quota_api.py @@ -34,8 +34,8 @@ def project_id(): class TestQuotaLimitsUserApi: @staticmethod def _limit_cmp_shallow( - a: tp.Dict[str, tp.Any], - b: tp.Dict[str, tp.Any], + a: dict[str, tp.Any], + b: dict[str, tp.Any], ) -> bool: return all( a.get(key, "") == b[key] diff --git a/exordos_core/tests/functional/restapi/secret/test_certificates.py b/exordos_core/tests/functional/restapi/secret/test_certificates.py index 254d8386..7bbd472c 100644 --- a/exordos_core/tests/functional/restapi/secret/test_certificates.py +++ b/exordos_core/tests/functional/restapi/secret/test_certificates.py @@ -28,8 +28,8 @@ class TestCertificatesUserApi: @staticmethod def _secret_cmp_shallow( - cfg_foo: tp.Dict[str, tp.Any], - cfg_bar: tp.Dict[str, tp.Any], + cfg_foo: dict[str, tp.Any], + cfg_bar: dict[str, tp.Any], ): return all( (cfg_foo[key] == cfg_bar[key]) diff --git a/exordos_core/tests/functional/restapi/secret/test_passwords.py b/exordos_core/tests/functional/restapi/secret/test_passwords.py index 4b8ec73d..f3356be4 100644 --- a/exordos_core/tests/functional/restapi/secret/test_passwords.py +++ b/exordos_core/tests/functional/restapi/secret/test_passwords.py @@ -29,8 +29,8 @@ class TestPasswordsUserApi: @staticmethod def _secret_cmp_shallow( - cfg_foo: tp.Dict[str, tp.Any], - cfg_bar: tp.Dict[str, tp.Any], + cfg_foo: dict[str, tp.Any], + cfg_bar: dict[str, tp.Any], ): return all( (cfg_foo[key] == cfg_bar[key]) diff --git a/exordos_core/tests/functional/restapi/secret/test_ssh_keys.py b/exordos_core/tests/functional/restapi/secret/test_ssh_keys.py index bae90254..f12d620d 100644 --- a/exordos_core/tests/functional/restapi/secret/test_ssh_keys.py +++ b/exordos_core/tests/functional/restapi/secret/test_ssh_keys.py @@ -29,8 +29,8 @@ class TestSSHKeysUserApi: @staticmethod def _secret_cmp_shallow( - cfg_foo: tp.Dict[str, tp.Any], - cfg_bar: tp.Dict[str, tp.Any], + cfg_foo: dict[str, tp.Any], + cfg_bar: dict[str, tp.Any], ): return all( (cfg_foo[key] == cfg_bar[key]) @@ -63,7 +63,7 @@ def test_ssh_keys_list( def test_ssh_keys_add( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -84,7 +84,7 @@ def test_ssh_keys_add( def test_ssh_keys_add_several( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -111,7 +111,7 @@ def test_ssh_keys_add_several( def test_ssh_keys_add_same( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -141,7 +141,7 @@ def test_ssh_keys_add_same( def test_ssh_keys_update( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -175,7 +175,7 @@ def test_ssh_keys_update( def test_ssh_keys_update_status_new( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -216,7 +216,7 @@ def test_ssh_keys_update_status_new( def test_ssh_keys_delete( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -243,7 +243,7 @@ def test_ssh_keys_delete( def test_ssh_keys_update_unable_update_status( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, diff --git a/exordos_core/tests/functional/restapi/ua/test_ua_api.py b/exordos_core/tests/functional/restapi/ua/test_ua_api.py index 278a29a7..9c9cdf34 100644 --- a/exordos_core/tests/functional/restapi/ua/test_ua_api.py +++ b/exordos_core/tests/functional/restapi/ua/test_ua_api.py @@ -27,9 +27,9 @@ class TestUaAgentsApi: @staticmethod def _agent_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - name: tp.Optional[str] = None, - node: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + name: str | None = None, + node: sys_uuid.UUID | None = None, status: str = "ACTIVE", **kwargs, ) -> sys_uuid.UUID: @@ -254,9 +254,9 @@ def test_issue_key_user_with_permission_can_issue( class TestUaResourcesApi: @staticmethod def _resource_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, kind: str = "test_kind", - value: tp.Optional[tp.Dict[str, tp.Any]] = None, + value: dict[str, tp.Any] | None = None, status: str = "ACTIVE", **kwargs, ) -> sys_uuid.UUID: @@ -379,9 +379,9 @@ def test_user_with_permission_can_list( class TestUaTargetResourcesApi: @staticmethod def _target_resource_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, kind: str = "test_target_kind", - value: tp.Optional[tp.Dict[str, tp.Any]] = None, + value: dict[str, tp.Any] | None = None, status: str = "ACTIVE", **kwargs, ) -> sys_uuid.UUID: diff --git a/exordos_core/tests/functional/restapi/vs/test_user_api.py b/exordos_core/tests/functional/restapi/vs/test_user_api.py index fd575166..5652b3e0 100644 --- a/exordos_core/tests/functional/restapi/vs/test_user_api.py +++ b/exordos_core/tests/functional/restapi/vs/test_user_api.py @@ -27,13 +27,13 @@ class TestVSUserApi: @staticmethod def _profile_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - name: tp.Optional[str] = None, + uuid: sys_uuid.UUID | None = None, + name: str | None = None, description: str = "test profile", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, profile_type: str = "GLOBAL", **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() name = name or f"profile_{str(uuid)[:8]}" return { @@ -47,13 +47,13 @@ def _profile_factory( @staticmethod def _variable_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - name: tp.Optional[str] = None, + uuid: sys_uuid.UUID | None = None, + name: str | None = None, description: str = "test variable", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - setter: tp.Optional[tp.Dict[str, tp.Any]] = None, + setter: dict[str, tp.Any] | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() name = name or f"var_{str(uuid)[:8]}" if setter is None: @@ -69,17 +69,17 @@ def _variable_factory( @staticmethod def _value_factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - name: tp.Optional[str] = None, + uuid: sys_uuid.UUID | None = None, + name: str | None = None, description: str = "test value", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, value: tp.Any = 1, - variable: tp.Optional[str] = None, + variable: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() name = name or f"value_{str(uuid)[:8]}" - payload: tp.Dict[str, tp.Any] = { + payload: dict[str, tp.Any] = { "uuid": str(uuid), "name": name, "description": description, diff --git a/exordos_core/tests/functional/service/dns/test_pdns.py b/exordos_core/tests/functional/service/dns/test_pdns.py index 8abc82f8..e2b431fe 100644 --- a/exordos_core/tests/functional/service/dns/test_pdns.py +++ b/exordos_core/tests/functional/service/dns/test_pdns.py @@ -31,10 +31,10 @@ class TestDnsApi: @staticmethod def _cmp_shallow( - left: tp.Dict[str, tp.Any], - right: tp.Dict[str, tp.Any], + left: dict[str, tp.Any], + right: dict[str, tp.Any], ): - return all((left[key] == right[key]) for key in left.keys()) + return all((left[key] == right[key]) for key in left) @pytest.fixture() def domain1( @@ -76,8 +76,8 @@ def test_domains_add( self, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, - domain1: tp.Dict, - pdns_server: tp.Optional[int], + domain1: dict, + pdns_server: int | None, ): client = user_api_client(auth_user_admin) @@ -127,8 +127,8 @@ def test_a_record( self, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, - domain1: tp.Dict, - pdns_server: tp.Optional[int], + domain1: dict, + pdns_server: int | None, ): client = user_api_client(auth_user_admin) @@ -176,8 +176,8 @@ def test_txt_record( self, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, - domain1: tp.Dict, - pdns_server: tp.Optional[int], + domain1: dict, + pdns_server: int | None, ): client = user_api_client(auth_user_admin) diff --git a/exordos_core/tests/functional/service/test_config.py b/exordos_core/tests/functional/service/test_config.py index 82783f87..2060f00f 100644 --- a/exordos_core/tests/functional/service/test_config.py +++ b/exordos_core/tests/functional/service/test_config.py @@ -34,13 +34,13 @@ def teardown_method(self) -> None: def test_no_configs( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): self._service._iteration() def test_new_config( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], config_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -71,8 +71,8 @@ def test_new_config( assert len(target_resources) == 2 assert len(configs) == 1 - render = [r for r in target_resources if r.kind == "render"][0] - config = configs[0] + render = next(r for r in target_resources if r.kind == "render") + config = next(r for r in configs) assert config.status == "IN_PROGRESS" assert render.status == "IN_PROGRESS" @@ -85,7 +85,7 @@ def test_new_config( def test_new_config_fake_node( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], config_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -119,7 +119,7 @@ def test_new_config_fake_node( def test_new_config_render_text( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], config_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -144,7 +144,7 @@ def test_new_config_render_text( self._service._iteration() target_resources = ua_models.TargetResource.objects.get_all() - render = [r for r in target_resources if r.kind == "render"][0] + render = next(r for r in target_resources if r.kind == "render") assert render.value["content"] == "TEST" @@ -155,7 +155,7 @@ def test_new_config_render_text( def test_in_progress_configs( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], config_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -183,7 +183,7 @@ def test_in_progress_configs( assert config.status == "IN_PROGRESS" target_resources = ua_models.TargetResource.objects.get_all() - render = [r for r in target_resources if r.kind == "render"][0] + render = next(r for r in target_resources if r.kind == "render") view = render.dump_to_simple_view() view.pop("master", None) view.pop("master_hash", None) @@ -280,7 +280,7 @@ def test_in_progress_configs( def test_update_configs( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], config_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -309,7 +309,7 @@ def test_update_configs( assert config.status == "IN_PROGRESS" target_resources = ua_models.TargetResource.objects.get_all() - render = [r for r in target_resources if r.kind == "render"][0] + render = next(r for r in target_resources if r.kind == "render") view = render.dump_to_simple_view() view.pop("master", None) view.pop("master_hash", None) diff --git a/exordos_core/tests/functional/service/test_network.py b/exordos_core/tests/functional/service/test_network.py index f3fff5df..9d94ec5e 100644 --- a/exordos_core/tests/functional/service/test_network.py +++ b/exordos_core/tests/functional/service/test_network.py @@ -84,7 +84,7 @@ def _add_node(self, **kwargs) -> models.Node: node.insert() return node - def _add_network(self, **kwargs) -> tp.Tuple[models.Network, models.Subnet]: + def _add_network(self, **kwargs) -> tuple[models.Network, models.Subnet]: network = models.Network( name="foo-network", driver_spec={"driver": "dummy"}, @@ -104,12 +104,12 @@ def _add_network(self, **kwargs) -> tp.Tuple[models.Network, models.Subnet]: def _add_port( self, subnet: models.Subnet, - node: tp.Optional[models.Node] = None, + node: models.Node | None = None, ipv4: str = netaddr.IPAddress("10.0.0.0"), mask: str = netaddr.IPAddress("255.255.255.0"), - mac: tp.Optional[str] = None, + mac: str | None = None, save: bool = True, - ) -> tp.Tuple[models.Port]: + ) -> tuple[models.Port]: port = subnet.port( ipv4=ipv4, mask=mask, @@ -123,7 +123,7 @@ def _add_port( return port def _schedule_node( - self, node_uuid: str, machine: tp.Union[models.Machine, str] + self, node_uuid: str, machine: models.Machine | str ) -> models.Node: node = models.Node.objects.get_one( filters={ diff --git a/exordos_core/tests/functional/service/test_node_builder.py b/exordos_core/tests/functional/service/test_node_builder.py index 74de62a0..403967f6 100644 --- a/exordos_core/tests/functional/service/test_node_builder.py +++ b/exordos_core/tests/functional/service/test_node_builder.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import typing as tp import uuid as sys_uuid from gcl_sdk.infra.dm import models as sdk_models @@ -30,7 +29,7 @@ class TestNodeBuilderService: def setup_method(self) -> None: self._service = node_builder.NodeBuilderService() - def _add_node(self, disks: tp.List[dict]) -> models.Node: + def _add_node(self, disks: list[dict]) -> models.Node: node = models.Node( uuid=sys_uuid.uuid4(), name="foo-node", @@ -43,7 +42,7 @@ def _add_node(self, disks: tp.List[dict]) -> models.Node: return node def _node_copy_with_disks( - self, node: models.Node, disks: tp.List[dict] + self, node: models.Node, disks: list[dict] ) -> models.Node: return models.Node( uuid=node.uuid, @@ -54,7 +53,7 @@ def _node_copy_with_disks( project_id=node.project_id, ) - def _node_volumes(self, node_uuid: sys_uuid.UUID) -> tp.List[models.Volume]: + def _node_volumes(self, node_uuid: sys_uuid.UUID) -> list[models.Volume]: return list( models.Volume.objects.get_all( filters={"node": dm_filters.EQ(node_uuid)}, diff --git a/exordos_core/tests/functional/service/test_quota.py b/exordos_core/tests/functional/service/test_quota.py index 8aa0a237..350f2041 100644 --- a/exordos_core/tests/functional/service/test_quota.py +++ b/exordos_core/tests/functional/service/test_quota.py @@ -43,10 +43,7 @@ def _quota_limit_2(user_api): ) obj.insert() yield obj - try: - obj.delete() - except Exception: - pass + obj.delete() class TestQuotaNoLimit: @@ -87,10 +84,7 @@ def _quota_limits(self, user_api): limit.insert() yield limits for limit in limits: - try: - limit.delete() - except Exception: - pass + limit.delete() def test_rejects_unknown_quota_resource(self): with pytest.raises(ValueError, match="Unknown quota resource: unknown"): diff --git a/exordos_core/tests/functional/service/test_scheduler.py b/exordos_core/tests/functional/service/test_scheduler.py index f066b97f..944ac667 100644 --- a/exordos_core/tests/functional/service/test_scheduler.py +++ b/exordos_core/tests/functional/service/test_scheduler.py @@ -52,14 +52,14 @@ def setup_method(self) -> None: def teardown_method(self) -> None: pass - def test_nothing_scheduler(self, default_pool: tp.Dict[str, tp.Any]): + def test_nothing_scheduler(self, default_pool: dict[str, tp.Any]): self._service._iteration() def test_schedule_pool( self, - default_pool: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], ): self._service._iteration() pool = models.MachinePool.objects.get_all() @@ -73,10 +73,10 @@ def test_schedule_pool( def test_schedule_node( self, - default_pool: tp.Dict[str, tp.Any], - default_node: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_node: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], ): self._service._iteration() self._service._iteration() @@ -90,10 +90,10 @@ def test_schedule_node( def test_schedule_extra_volume_on_scheduled_node( self, - default_pool: tp.Dict[str, tp.Any], - default_node: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_node: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], ): # Schedule the node with its root volume first self._service._iteration() @@ -130,9 +130,9 @@ def test_schedule_extra_volume_on_scheduled_node( def test_schedule_node_no_builders( self, - default_pool: tp.Dict[str, tp.Any], - default_node: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_node: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], ): self._service._iteration() self._service._iteration() @@ -144,10 +144,10 @@ def test_schedule_node_no_builders( def test_schedule_unscheduled_machine( self, - default_pool: tp.Dict[str, tp.Any], - default_node: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_node: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], machine_factory: tp.Callable, ): view = machine_factory(pool=None) @@ -166,8 +166,8 @@ def test_schedule_unscheduled_machine( def test_schedule_two_pools_single_iteration( self, - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], pool_factory: tp.Callable, node_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, @@ -211,7 +211,7 @@ def test_schedule_two_pools_single_iteration( assert len(pools) == 2 assert len(machines) == 4 - assert set(m.pool for m in machines) == {uuid_foo, uuid_bar} + assert {m.pool for m in machines} == {uuid_foo, uuid_bar} assert collections.Counter( str(m.pool) for m in machines ) == collections.Counter(**{f"{uuid_foo}": 2, f"{uuid_bar}": 2}) @@ -226,7 +226,7 @@ def test_schedule_two_pools_single_iteration( def test_schedule_two_pools_different_iterations( self, - default_machine_agent: tp.Dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], pool_builder_factory: tp.Callable, pool_factory: tp.Callable, node_factory: tp.Callable, @@ -276,7 +276,7 @@ def test_schedule_two_pools_different_iterations( assert len(pools) == 2 assert len(machines) == 2 - assert set(m.pool for m in machines) == {uuid_foo, uuid_bar} + assert {m.pool for m in machines} == {uuid_foo, uuid_bar} assert collections.Counter( str(m.pool) for m in machines ) == collections.Counter(**{f"{uuid_foo}": 1, f"{uuid_bar}": 1}) @@ -298,7 +298,7 @@ def test_schedule_two_pools_different_iterations( assert len(pools) == 2 assert len(machines) == 4 - assert set(m.pool for m in machines) == {uuid_foo, uuid_bar} + assert {m.pool for m in machines} == {uuid_foo, uuid_bar} assert collections.Counter( str(m.pool) for m in machines ) == collections.Counter(**{f"{uuid_foo}": 2, f"{uuid_bar}": 2}) @@ -315,9 +315,9 @@ def test_schedule_two_pools_different_iterations( def test_schedule_hw_node( self, - default_pool: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], machine_factory: tp.Callable, pool_factory: tp.Callable, node_factory: tp.Callable, @@ -361,9 +361,9 @@ def test_schedule_hw_node( def test_schedule_hw_node_filtered_out_all( self, - default_pool: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], machine_factory: tp.Callable, pool_factory: tp.Callable, node_factory: tp.Callable, @@ -408,9 +408,9 @@ def test_schedule_hw_node_filtered_out_all( def test_schedule_hw_node_simple_weighter( self, - default_pool: tp.Dict[str, tp.Any], - default_machine_agent: tp.Dict[str, tp.Any], - default_pool_builder: tp.Dict[str, tp.Any], + default_pool: dict[str, tp.Any], + default_machine_agent: dict[str, tp.Any], + default_pool_builder: dict[str, tp.Any], machine_factory: tp.Callable, pool_factory: tp.Callable, node_factory: tp.Callable, diff --git a/exordos_core/tests/functional/service/test_secrets.py b/exordos_core/tests/functional/service/test_secrets.py index 7a1b678c..6bff3298 100644 --- a/exordos_core/tests/functional/service/test_secrets.py +++ b/exordos_core/tests/functional/service/test_secrets.py @@ -35,7 +35,7 @@ def teardown_method(self) -> None: def test_new_ssh_key( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -68,7 +68,7 @@ def test_new_ssh_key( assert len(target_resources) == 2 assert len(keys) == 1 - host_key = [r for r in target_resources if r.kind == "ssh_key_target"][0] + host_key = next(r for r in target_resources if r.kind == "ssh_key_target") key = keys[0] assert key.status == "IN_PROGRESS" @@ -82,7 +82,7 @@ def test_new_ssh_key( def test_new_ssh_key_fake_node( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -119,7 +119,7 @@ def test_new_ssh_key_fake_node( def test_in_progress_ssh_keys( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -151,7 +151,7 @@ def test_in_progress_ssh_keys( assert key.status == "IN_PROGRESS" target_resources = stubs.TargetResource.objects.get_all() - host_key = [r for r in target_resources if r.kind == "ssh_key_target"][0] + host_key = next(r for r in target_resources if r.kind == "ssh_key_target") view = host_key.dump_to_simple_view() view.pop("master", None) view.pop("master_hash", None) @@ -175,7 +175,7 @@ def test_in_progress_ssh_keys( def test_update_ssh_keys( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -208,7 +208,7 @@ def test_update_ssh_keys( assert key.status == "IN_PROGRESS" target_resources = stubs.TargetResource.objects.get_all() - host_key = [r for r in target_resources if r.kind == "ssh_key_target"][0] + host_key = next(r for r in target_resources if r.kind == "ssh_key_target") view = host_key.dump_to_simple_view() view.pop("master", None) view.pop("master_hash", None) @@ -248,7 +248,7 @@ def test_update_ssh_keys( def test_delete_ssh_keys( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ssh_key_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, diff --git a/exordos_core/tests/functional/service/test_secrets_builder.py b/exordos_core/tests/functional/service/test_secrets_builder.py index 9b4b0c0e..42a4e416 100644 --- a/exordos_core/tests/functional/service/test_secrets_builder.py +++ b/exordos_core/tests/functional/service/test_secrets_builder.py @@ -96,7 +96,7 @@ def test_no_passwords( ) def test_create_password( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], password_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -153,7 +153,7 @@ def test_create_password( def test_update_password( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], password_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -208,7 +208,7 @@ def test_update_password( def test_delete_password( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], password_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -265,7 +265,7 @@ def test_no_certs( def test_create_certificate( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], cert_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -318,7 +318,7 @@ def test_create_certificate( def test_update_certificate( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], cert_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, @@ -367,7 +367,7 @@ def test_update_certificate( def test_delete_certificate( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], cert_factory: tp.Callable, user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, diff --git a/exordos_core/tests/functional/service/test_vs_builder.py b/exordos_core/tests/functional/service/test_vs_builder.py index 096b9d7f..8541be17 100644 --- a/exordos_core/tests/functional/service/test_vs_builder.py +++ b/exordos_core/tests/functional/service/test_vs_builder.py @@ -34,7 +34,7 @@ def teardown_method(self) -> None: def test_variables_value_depends_on_active_global_profile( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): profile_1 = models.Profile( name="p1", @@ -80,7 +80,7 @@ def test_variables_value_depends_on_active_global_profile( def test_variables_value_set_undefined_profile( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): profile_1 = models.Profile( name="p1", @@ -133,7 +133,7 @@ def test_variables_value_set_undefined_profile( def test_variables_selector_strategy_latest( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): variable = models.Variable( name="var_selector", @@ -176,7 +176,7 @@ def test_variables_selector_strategy_latest( def test_variables_selector_recalculate_on_delete( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): variable = models.Variable( name="var_selector", @@ -228,7 +228,7 @@ def test_variables_selector_recalculate_on_delete( def test_variables_undefined_no_value_field_in_ua_resource( self, - default_node: tp.Dict[str, tp.Any], + default_node: dict[str, tp.Any], ): variable = models.Variable( name="var_selector", diff --git a/exordos_core/tests/functional/stubs.py b/exordos_core/tests/functional/stubs.py index 2f76371d..2775612a 100644 --- a/exordos_core/tests/functional/stubs.py +++ b/exordos_core/tests/functional/stubs.py @@ -49,8 +49,7 @@ def get_one(self, *args, **kwargs): return None if len(objects) > 1: raise storage_exceptions.HasManyRecords( - "Has many records in storage for model (%s) and filters (%s)." - % (self.model_cls, None) + f"Has many records in storage for model ({self.model_cls}) and filters ({None})." ) return objects[0] diff --git a/exordos_core/tests/functional/utils.py b/exordos_core/tests/functional/utils.py index cefccf49..7b9c472c 100644 --- a/exordos_core/tests/functional/utils.py +++ b/exordos_core/tests/functional/utils.py @@ -18,7 +18,6 @@ import os import pathlib import socket -import typing as tp from urllib import parse from gcl_sdk import migrations as sdk_migrations @@ -68,7 +67,7 @@ def teardown_class(cls): @staticmethod def get_migration_engine( - migrations_path: tp.Optional[str] = None, + migrations_path: str | None = None, ) -> migrations.MigrationEngine: if migrations_path is None: migrations_path = os.path.join( @@ -83,7 +82,7 @@ def get_migration_engine( def apply_migrations( cls, migration_engine: migrations.MigrationEngine, - last_migration: tp.Optional[str] = None, + last_migration: str | None = None, ) -> None: last_migration = last_migration or migration_engine.get_latest_migration() migration_engine.apply_migration(last_migration) @@ -129,7 +128,7 @@ def drop_all_tables(cls, session=None, cascade=False): cls.drop_table(table, session=s, cascade=cascade) @classmethod - def get_all_views(cls, session=None) -> tp.Set[str]: + def get_all_views(cls, session=None) -> set[str]: with cls.engine.session_manager(session=session) as s: if session.engine.dialect.name == "mysql": res = s.execute(""" diff --git a/exordos_core/tests/manual/conftest.py b/exordos_core/tests/manual/conftest.py index 25992ca4..152749ec 100644 --- a/exordos_core/tests/manual/conftest.py +++ b/exordos_core/tests/manual/conftest.py @@ -379,8 +379,8 @@ def user_api_client(user_api, auth_user_admin): def build_client( auth: iam_clients.GenesisCoreAuth, - permissions: tp.Optional[tp.List[str]] = None, - project_id: tp.Optional[str] = None, + permissions: list[str] | None = None, + project_id: str | None = None, ): permissions = permissions or [] client = iam_clients.GenericAutoRefreshRESTClient( @@ -434,15 +434,15 @@ def user_api_noauth_client(user_api): @pytest.fixture def node_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "node", cores: int = 1, ram: int = 1024, image: str = "ubuntu_24.04", project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[str] = None, + status: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() status_value = nc.NodeStatus.NEW.value if status is None else status.value node = node_models.Node( @@ -467,7 +467,7 @@ def factory( @pytest.fixture def node_set_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "node_set", cores: int = 1, ram: int = 1024, @@ -476,7 +476,7 @@ def factory( project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, status: str = nc.NodeStatus.NEW.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() obj = node_set_models.NodeSet( uuid=uuid, @@ -499,17 +499,17 @@ def factory( @pytest.fixture def pool_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - agent: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + agent: sys_uuid.UUID | None = None, name: str = "pool-default", - driver_spec: tp.Optional[dict] = None, - status: tp.Optional[str] = None, + driver_spec: dict | None = None, + status: str | None = None, avail_cores: int = 8, avail_ram: int = 16384, all_cores: int = 8, all_ram: int = 16384, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() driver_spec = ( {"kind": "libvirt", "connection_uri": "qemu+tcp://127.0.0.1/system"} @@ -547,10 +547,10 @@ def factory( @pytest.fixture -def machine_factory(default_pool: tp.Dict[str, tp.Any]): +def machine_factory(default_pool: dict[str, tp.Any]): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - pool: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + pool: sys_uuid.UUID | None = None, name: str = "node", cores: int = 1, ram: int = 1024, @@ -558,7 +558,7 @@ def factory( status: str = nc.MachineStatus.ACTIVE.value, build_status: str = nc.MachineBuildStatus.READY.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() pool = pool or sys_uuid.UUID(default_pool["uuid"]) machine = node_models.Machine( @@ -582,15 +582,15 @@ def factory( def config_factory(): def factory( target_node: sys_uuid.UUID, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "config", path: str = "/etc/genesis-configs/config.conf", content_body: str = "test", - on_change_cmd: tp.Optional[str] = None, + on_change_cmd: str | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, status: str = cc.ConfigStatus.NEW.value, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() target = ct.NodeTarget.from_node(target_node) body = conf_models.TextBodyConfig.from_text(content_body) @@ -619,15 +619,15 @@ def factory( @pytest.fixture def password_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "password", - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, + constructor: secret_models.AbstractSecretConstructor | None = None, method: sc.SecretMethod = sc.SecretMethod.AUTO_HEX, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, - value: tp.Optional[str] = None, + status: cc.ConfigStatus | None = None, + value: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() constructor = ( secret_models.PlainSecretConstructor() @@ -657,18 +657,18 @@ def factory( @pytest.fixture def cert_factory(): def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "cert", domains: tp.Collection[str] = ("genesis-core.tech",), email: str = "user@genesis-core.tech", - key: tp.Optional[str] = None, - cert: tp.Optional[str] = None, - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, - method: tp.Optional[secret_models.AbstractCertificateMethod] = None, + key: str | None = None, + cert: str | None = None, + constructor: secret_models.AbstractSecretConstructor | None = None, + method: secret_models.AbstractCertificateMethod | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, + status: cc.ConfigStatus | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() constructor = ( secret_models.PlainSecretConstructor() @@ -710,15 +710,15 @@ def ssh_key_factory(): def factory( target_node: sys_uuid.UUID, target_public_key: str, - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, name: str = "key", - constructor: tp.Optional[secret_models.AbstractSecretConstructor] = None, + constructor: secret_models.AbstractSecretConstructor | None = None, project_id: sys_uuid.UUID = c.SERVICE_PROJECT_ID, - status: tp.Optional[cc.ConfigStatus] = None, + status: cc.ConfigStatus | None = None, user: str = "root", authorized_keys=".ssh/authorized_keys", **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() target = ct.NodeTarget.from_node(target_node) constructor = ( @@ -751,7 +751,7 @@ def factory( @pytest.fixture def pool_builder_factory() -> tp.Callable: def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, status: str = nc.BuilderStatus.ACTIVE.value, **kwargs, ) -> sdk_ua_models.UniversalAgent: @@ -780,10 +780,10 @@ def factory( @pytest.fixture def interface_factory() -> tp.Callable: def factory( - uuid: tp.Optional[sys_uuid.UUID] = None, - mac: tp.Optional[str] = None, + uuid: sys_uuid.UUID | None = None, + mac: str | None = None, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() interface = node_models.Interface( uuid=uuid, @@ -800,12 +800,12 @@ def factory( def machine_pool_reservation_factory() -> tp.Callable: def factory( pool: sys_uuid.UUID, - uuid: tp.Optional[sys_uuid.UUID] = None, - machine: tp.Optional[sys_uuid.UUID] = None, + uuid: sys_uuid.UUID | None = None, + machine: sys_uuid.UUID | None = None, cores: int = 1, ram: int = 1024, **kwargs, - ) -> tp.Dict[str, tp.Any]: + ) -> dict[str, tp.Any]: uuid = uuid or sys_uuid.uuid4() reservation = node_models.MachinePoolReservations( uuid=uuid, @@ -903,7 +903,7 @@ def default_subnet( def default_machine_agent( user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, -) -> tp.Dict[str, tp.Any]: +) -> dict[str, tp.Any]: uuid = sys_uuid.UUID("00000000-1112-0100-0000-000000000211") agent = sdk_ua_models.UniversalAgent( uuid=uuid, @@ -922,7 +922,7 @@ def default_machine_agent( def default_pool_builder( user_api_client: iam_clients.GenesisCoreTestRESTClient, auth_user_admin: iam_clients.GenesisCoreAuth, -) -> tp.Dict[str, tp.Any]: +) -> dict[str, tp.Any]: uuid = sys_uuid.UUID("00000000-1112-0100-0000-000000000322") agent = sdk_ua_models.UniversalAgent( uuid=uuid, diff --git a/exordos_core/tests/manual/utils.py b/exordos_core/tests/manual/utils.py index cefccf49..7b9c472c 100644 --- a/exordos_core/tests/manual/utils.py +++ b/exordos_core/tests/manual/utils.py @@ -18,7 +18,6 @@ import os import pathlib import socket -import typing as tp from urllib import parse from gcl_sdk import migrations as sdk_migrations @@ -68,7 +67,7 @@ def teardown_class(cls): @staticmethod def get_migration_engine( - migrations_path: tp.Optional[str] = None, + migrations_path: str | None = None, ) -> migrations.MigrationEngine: if migrations_path is None: migrations_path = os.path.join( @@ -83,7 +82,7 @@ def get_migration_engine( def apply_migrations( cls, migration_engine: migrations.MigrationEngine, - last_migration: tp.Optional[str] = None, + last_migration: str | None = None, ) -> None: last_migration = last_migration or migration_engine.get_latest_migration() migration_engine.apply_migration(last_migration) @@ -129,7 +128,7 @@ def drop_all_tables(cls, session=None, cascade=False): cls.drop_table(table, session=s, cascade=cascade) @classmethod - def get_all_views(cls, session=None) -> tp.Set[str]: + def get_all_views(cls, session=None) -> set[str]: with cls.engine.session_manager(session=session) as s: if session.engine.dialect.name == "mysql": res = s.execute(""" diff --git a/exordos_core/tests/unit/agent/universal/drivers/secret/backend/test_cert.py b/exordos_core/tests/unit/agent/universal/drivers/secret/backend/test_cert.py index 7c12e0d2..5499743f 100644 --- a/exordos_core/tests/unit/agent/universal/drivers/secret/backend/test_cert.py +++ b/exordos_core/tests/unit/agent/universal/drivers/secret/backend/test_cert.py @@ -15,7 +15,6 @@ # under the License. import datetime -import typing as tp from unittest.mock import MagicMock from unittest.mock import patch import uuid as sys_uuid @@ -31,8 +30,8 @@ def _make_resource( kind: str, - uuid: tp.Optional[sys_uuid.UUID] = None, - value: tp.Optional[dict] = None, + uuid: sys_uuid.UUID | None = None, + value: dict | None = None, ): uuid = uuid or sys_uuid.uuid4() value = value or {"uuid": str(uuid)} diff --git a/exordos_core/tests/unit/cmd/test_bootstrap_ua_config.py b/exordos_core/tests/unit/cmd/test_bootstrap_ua_config.py index fcec1c2b..a4c200e7 100644 --- a/exordos_core/tests/unit/cmd/test_bootstrap_ua_config.py +++ b/exordos_core/tests/unit/cmd/test_bootstrap_ua_config.py @@ -86,7 +86,7 @@ def test_noop_on_current_config(tmp_path): _run(tmp_path) content = etc_path.read_text(encoding="utf-8") - etc_path, data_path, run = _run(tmp_path) + etc_path, _data_path, run = _run(tmp_path) assert etc_path.read_text(encoding="utf-8") == content run.assert_not_called() diff --git a/exordos_core/tests/unit/compute/pool/drivers/test_libvirt.py b/exordos_core/tests/unit/compute/pool/drivers/test_libvirt.py index 4a2ab658..ba346a1a 100644 --- a/exordos_core/tests/unit/compute/pool/drivers/test_libvirt.py +++ b/exordos_core/tests/unit/compute/pool/drivers/test_libvirt.py @@ -26,10 +26,10 @@ # collection when they're not available. pytest.importorskip("libvirt") -from exordos_core.compute.dm import models # noqa: E402 -from exordos_core.compute.pool.drivers.libvirt import LibvirtPoolDriver # noqa: E402 -from exordos_core.compute.pool.drivers.libvirt import XMLLibvirtInstance # noqa: E402 -from exordos_core.compute.pool.drivers.libvirt import domain_template # noqa: E402 +from exordos_core.compute.dm import models +from exordos_core.compute.pool.drivers.libvirt import LibvirtPoolDriver +from exordos_core.compute.pool.drivers.libvirt import XMLLibvirtInstance +from exordos_core.compute.pool.drivers.libvirt import domain_template def _local_driver() -> LibvirtPoolDriver: diff --git a/exordos_core/tests/unit/compute/test_ipam.py b/exordos_core/tests/unit/compute/test_ipam.py index c2232276..58976e60 100644 --- a/exordos_core/tests/unit/compute/test_ipam.py +++ b/exordos_core/tests/unit/compute/test_ipam.py @@ -54,24 +54,24 @@ def test_occupy_last(self, empty_ipam: ipam.Ipam): # assert e.ip == netaddr.IPAddress("0.0.0.20") def test_allocate_ip(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) assert empty_ipam.allocate_ip(subnet) == netaddr.IPAddress("0.0.0.0") def test_allocate_ip_target(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) assert empty_ipam.allocate_ip( subnet, netaddr.IPAddress("0.0.0.10") ) == netaddr.IPAddress("0.0.0.10") assert empty_ipam._pool_map[subnet] == [(0, 9), (11, 255)] def test_allocate_ip_no_available_ips(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) empty_ipam._pool_map[subnet] = [] with pytest.raises(ipam.IpamNoIPsAvailable): empty_ipam.allocate_ip(subnet) def test_deallocate_to_start(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) empty_ipam.occupy_ip(0, empty_ipam._pool_map[subnet]) assert empty_ipam._pool_map[subnet] == [(1, 255)] @@ -80,7 +80,7 @@ def test_deallocate_to_start(self, empty_ipam: ipam.Ipam): assert empty_ipam._pool_map[subnet] == [(0, 255)] def test_deallocate_to_end(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) empty_ipam.occupy_ip(255, empty_ipam._pool_map[subnet]) assert empty_ipam._pool_map[subnet] == [(0, 254)] @@ -89,7 +89,7 @@ def test_deallocate_to_end(self, empty_ipam: ipam.Ipam): assert empty_ipam._pool_map[subnet] == [(0, 255)] def test_deallocate_in_middle(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) empty_ipam.occupy_ip(128, empty_ipam._pool_map[subnet]) assert empty_ipam._pool_map[subnet] == [(0, 127), (129, 255)] @@ -98,14 +98,14 @@ def test_deallocate_in_middle(self, empty_ipam: ipam.Ipam): assert empty_ipam._pool_map[subnet] == [(0, 255)] def test_deallocate_already_deallocated(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) assert empty_ipam._pool_map[subnet] == [(0, 255)] empty_ipam.deallocate_ip(subnet, netaddr.IPAddress("0.0.0.128")) assert empty_ipam._pool_map[subnet] == [(0, 255)] def test_deallocate_not_in_pool(self, empty_ipam: ipam.Ipam): - subnet = list(empty_ipam._pool_map.keys())[0] + subnet = next(iter(empty_ipam._pool_map.keys())) assert empty_ipam._pool_map[subnet] == [(0, 255)] empty_ipam.deallocate_ip(subnet, netaddr.IPAddress("0.0.1.1")) diff --git a/exordos_core/tests/unit/compute/test_scheduler_pool_scheduling.py b/exordos_core/tests/unit/compute/test_scheduler_pool_scheduling.py index e1e20709..3f8d0661 100644 --- a/exordos_core/tests/unit/compute/test_scheduler_pool_scheduling.py +++ b/exordos_core/tests/unit/compute/test_scheduler_pool_scheduling.py @@ -14,7 +14,6 @@ # License for the specific language governing permissions and limitations # under the License. -import typing as tp from unittest import mock import uuid as sys_uuid @@ -37,8 +36,8 @@ def _make_local_hyper_spec(node: sys_uuid.UUID) -> models.ExordosLocalHyperDrive def _make_agent( uuid: sys_uuid.UUID, - node: tp.Optional[sys_uuid.UUID] = None, - capabilities: tp.Optional[list] = None, + node: sys_uuid.UUID | None = None, + capabilities: list | None = None, ) -> mock.MagicMock: agent = mock.MagicMock() agent.uuid = uuid @@ -60,7 +59,7 @@ def setup_method(self) -> None: def _make_pool( self, - driver_spec: tp.Optional[models.AbstractPoolDriverSpec] = None, + driver_spec: models.AbstractPoolDriverSpec | None = None, ) -> mock.MagicMock: pool = mock.MagicMock() pool.uuid = sys_uuid.uuid4() diff --git a/exordos_core/user_api/api/middlewares.py b/exordos_core/user_api/api/middlewares.py index 5a75f823..e5de727a 100644 --- a/exordos_core/user_api/api/middlewares.py +++ b/exordos_core/user_api/api/middlewares.py @@ -40,7 +40,7 @@ def process_request(self, req): context = ra_contexts.get_context() rules_context = self._prepare_rules(context) if self._verify_rules(context, rules_context): - return None + return self._raise_error_answer() def _prepare_rules(self, context): diff --git a/exordos_core/user_api/api/routes.py b/exordos_core/user_api/api/routes.py index 3d0cc47a..631cffb8 100644 --- a/exordos_core/user_api/api/routes.py +++ b/exordos_core/user_api/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.api import controllers @@ -36,14 +38,14 @@ class HealthRoute(routes.Route): """Handler for /v1/health endpoint""" __controller__ = controllers.HealthController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] class ApiEndpointRoute(routes.Route): """Handler for /v1/ endpoint""" __controller__ = controllers.ApiEndpointController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] compute = routes.route(compute_routes.ComputeRoute) config = routes.route(config_routes.ConfigRoute) diff --git a/exordos_core/user_api/compute/api/controllers.py b/exordos_core/user_api/compute/api/controllers.py index c2e84f68..ee589470 100644 --- a/exordos_core/user_api/compute/api/controllers.py +++ b/exordos_core/user_api/compute/api/controllers.py @@ -26,7 +26,7 @@ from restalchemy.storage import exceptions as storage_exc from exordos_core.compute import constants as nc -from exordos_core.compute.dm import models as models +from exordos_core.compute.dm import models from exordos_core.user_api.compute.dm import models as user_models diff --git a/exordos_core/user_api/compute/api/routes.py b/exordos_core/user_api/compute/api/routes.py index 1666a434..73403163 100644 --- a/exordos_core/user_api/compute/api/routes.py +++ b/exordos_core/user_api/compute/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.compute.api import controllers @@ -77,7 +79,7 @@ class NodeSetsRoute(routes.Route): class ComputeRoute(routes.Route): """Handler for /v1/compute/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.ComputeController volumes = routes.route(VolumesRoute) diff --git a/exordos_core/user_api/config/api/routes.py b/exordos_core/user_api/config/api/routes.py index e1b209ac..baa77c35 100644 --- a/exordos_core/user_api/config/api/routes.py +++ b/exordos_core/user_api/config/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.config.api import controllers @@ -28,7 +30,7 @@ class ConfigsRoute(routes.Route): class ConfigRoute(routes.Route): """Handler for /v1/config/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.ConfigController configs = routes.route(ConfigsRoute) diff --git a/exordos_core/user_api/dns/api/routes.py b/exordos_core/user_api/dns/api/routes.py index 94641d5d..19847aa9 100644 --- a/exordos_core/user_api/dns/api/routes.py +++ b/exordos_core/user_api/dns/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.dns.api import controllers @@ -37,6 +39,6 @@ class DnsRoute(routes.Route): """Handler for /v1/dns/ endpoint""" __controller__ = controllers.DnsController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] domains = routes.route(DomainsRoute) diff --git a/exordos_core/user_api/dns/dm/models.py b/exordos_core/user_api/dns/dm/models.py index 8aee3a8f..b4ec23a7 100644 --- a/exordos_core/user_api/dns/dm/models.py +++ b/exordos_core/user_api/dns/dm/models.py @@ -107,7 +107,7 @@ def delete(self, session=None, **kwargs): class AbstractRecord(types_dynamic.AbstractKindModel): def get_name(self, domain) -> str: - return (".").join((self.name, domain.name)) if self.name else domain.name + return f"{self.name}.{domain.name}" if self.name else domain.name def get_content(self, domain) -> str: return str(self.content) diff --git a/exordos_core/user_api/em/api/controllers.py b/exordos_core/user_api/em/api/controllers.py index 250b2924..195eaa0b 100644 --- a/exordos_core/user_api/em/api/controllers.py +++ b/exordos_core/user_api/em/api/controllers.py @@ -15,7 +15,6 @@ # under the License. import json -import typing as tp from gcl_iam.api import controllers as iam_controllers from restalchemy.api import actions @@ -54,7 +53,7 @@ def process_result( self, result: dict, status_code: int = 200, - headers: tp.Optional[dict] = None, + headers: dict | None = None, add_location: bool = False, ) -> webob.Response: if headers is not None: diff --git a/exordos_core/user_api/em/api/routes.py b/exordos_core/user_api/em/api/routes.py index 8ff4db2f..eb0e1142 100644 --- a/exordos_core/user_api/em/api/routes.py +++ b/exordos_core/user_api/em/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.em.api import controllers @@ -47,7 +49,7 @@ class SchemaRoute(routes.Route): """Handler for /v1/em/manifests/schema/ endpoint""" __controller__ = controllers.SchemaController - __allow_methods__ = [ + __allow_methods__: tp.ClassVar[list] = [ routes.FILTER, ] @@ -74,7 +76,7 @@ class ResourceAllRoute(routes.Route): """Handler for /v1/resources/[] endpoint""" __controller__ = controllers.ResourceAllController - __allow_methods__ = [ + __allow_methods__: tp.ClassVar[list] = [ routes.FILTER, routes.GET, ] @@ -90,7 +92,7 @@ class ExportAllRoute(routes.Route): """Handler for /v1/exports/[] endpoint""" __controller__ = controllers.ExportAllController - __allow_methods__ = [ + __allow_methods__: tp.ClassVar[list] = [ routes.FILTER, routes.GET, ] @@ -106,7 +108,7 @@ class ImportAllRoute(routes.Route): """Handler for /v1/imports/[] endpoint""" __controller__ = controllers.ImportAllController - __allow_methods__ = [ + __allow_methods__: tp.ClassVar[list] = [ routes.FILTER, routes.GET, ] @@ -146,7 +148,7 @@ class ElementManagerRoute(routes.Route): """Handler for /v1/em/ endpoint""" __controller__ = controllers.ElementManagerController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] manifests = routes.route(ManifestRoute) elements = routes.route(ElementRoute) diff --git a/exordos_core/user_api/iam/api/controllers.py b/exordos_core/user_api/iam/api/controllers.py index 1bf860eb..aaedef18 100644 --- a/exordos_core/user_api/iam/api/controllers.py +++ b/exordos_core/user_api/iam/api/controllers.py @@ -68,9 +68,9 @@ class ValidationException(ra_e.RestAlchemyException): class ValidateMixin: __validate_min_length__ = 8 - __validate_not_contain__: tp.List[str] = [string.whitespace] - __validate_must_contain__: tp.List[str] = None # [digits, punctuation] - __validate_regex__: str = None + __validate_not_contain__: tp.ClassVar[list[str]] = [string.whitespace] + __validate_must_contain__: tp.ClassVar[list[str]] = None # [digits, punctuation] + __validate_regex__: tp.ClassVar[str] = None def validate(self, value): error = None @@ -301,7 +301,6 @@ def resend_email_confirmation(self, resource): app_endpoint = _get_app_endpoint(req=self._req) resource.resend_confirmation_event(app_endpoint=app_endpoint) # Don't leak user data - return None def _get_request_iam_client(self) -> models.IamClient | None: try: @@ -346,7 +345,6 @@ def force_confirm_email(self, resource): resource.confirm_email() self._maybe_provision_workspace(resource) - return None @actions.post def confirm_email(self, resource, code=None): @@ -852,17 +850,17 @@ def get_token(self, resource, grant_type, **kwargs): client_id=client_id, client_secret=client_secret, ) - payload = dict( - password=kwargs.get(c.PARAM_PASSWORD), - scope=kwargs.get(c.PARAM_SCOPE, ""), - ttl=kwargs.get(c.PARAM_TTL, None), - refresh_ttl=kwargs.get(c.PARAM_REFRESH_TTL, None), - otp_code=self._req.headers.get(c.HEADER_OTP_CODE, None), - root_endpoint=ra_utils.lastslash( + payload = { + "password": kwargs.get(c.PARAM_PASSWORD), + "scope": kwargs.get(c.PARAM_SCOPE, ""), + "ttl": kwargs.get(c.PARAM_TTL, None), + "refresh_ttl": kwargs.get(c.PARAM_REFRESH_TTL, None), + "otp_code": self._req.headers.get(c.HEADER_OTP_CODE, None), + "root_endpoint": ra_utils.lastslash( ctx.get_real_url_with_prefix(), ), - service_account_uuid=kwargs.get(c.PARAM_SERVICE_ACCOUNT_UUID), - ) + "service_account_uuid": kwargs.get(c.PARAM_SERVICE_ACCOUNT_UUID), + } login_attr, token_getter = grant_type_map[grant_type] payload[login_attr] = kwargs.get(login_attr) if not payload[login_attr]: @@ -1020,12 +1018,12 @@ def get_resource(cls): class IamWebController(WebController): TEMPLATE_DIR = os_path.abspath("web") - ERROR_FILES = { + ERROR_FILES: tp.ClassVar[dict] = { 404: "errors/404.html", 500: "errors/500.html", } - RENDER_FILES = [ + RENDER_FILES: tp.ClassVar[list] = [ "login/index.html", ] @@ -1052,10 +1050,6 @@ def _build_response(self, path, request_context=None): http_code = 404 buff = self._get_file_body(full_path) file_mimetype = mimetypes.guess_file_type(full_path)[0] - except Exception: - full_path = os_path.join(self.TEMPLATE_DIR, self.ERROR_FILES[500]) - buff = self._get_file_body(full_path) - file_mimetype = mimetypes.guess_file_type(full_path)[0] return self._req.ResponseClass( body=buff, diff --git a/exordos_core/user_api/iam/api/openapi_specs.py b/exordos_core/user_api/iam/api/openapi_specs.py index e25d3afb..b4d633bc 100644 --- a/exordos_core/user_api/iam/api/openapi_specs.py +++ b/exordos_core/user_api/iam/api/openapi_specs.py @@ -22,86 +22,82 @@ responses = {} responses.update( oa_c.build_openapi_user_response( - **{ - "type": "object", - "required": [ - "access_token", - "expires_at", - "id_token", - "refresh_token", - "scope", - "token_type", - ], - "properties": { - "access_token": { - "type": "string", - "description": "JWT access token", - "example": "eyJhbGciOiJSUzI...", - }, - "expires_at": { - "type": "integer", - "format": "int64", - "description": "UNIX timestamp when token expires", - "example": 1740524674, - }, - "id_token": { - "type": "string", - "description": "OpenID Connect ID Token", - "example": "eyJhbGciOiJSUzI1NiIsInR...", - }, - "refresh_token": { - "type": "string", - "description": "Refresh token", - "example": "eyJhbGciOiJIUzUxMiIsIn...", - }, - "scope": { - "type": "string", - "description": "Granted scopes (space-separated)", - "example": "openid email profile", - }, - "token_type": { - "type": "string", - "description": "Type of token", - "enum": ["Bearer"], - "example": "Bearer", - }, + type="object", + required=[ + "access_token", + "expires_at", + "id_token", + "refresh_token", + "scope", + "token_type", + ], + properties={ + "access_token": { + "type": "string", + "description": "JWT access token", + "example": "eyJhbGciOiJSUzI...", }, - } + "expires_at": { + "type": "integer", + "format": "int64", + "description": "UNIX timestamp when token expires", + "example": 1740524674, + }, + "id_token": { + "type": "string", + "description": "OpenID Connect ID Token", + "example": "eyJhbGciOiJSUzI1NiIsInR...", + }, + "refresh_token": { + "type": "string", + "description": "Refresh token", + "example": "eyJhbGciOiJIUzUxMiIsIn...", + }, + "scope": { + "type": "string", + "description": "Granted scopes (space-separated)", + "example": "openid email profile", + }, + "token_type": { + "type": "string", + "description": "Type of token", + "enum": ["Bearer"], + "example": "Bearer", + }, + }, ) ) responses.update( oa_c.build_openapi_user_response( code=401, - **{ - "title": "Wrong OTP error", - "description": "The provided otp code is invalid", - "type": "object", - "required": [ - "error", - "error_description", - ], - "properties": { - "error": { - "type": "string", - "description": "Error class name", - "example": "OSError", - }, - "error_description": { - "type": "string", - "description": "Error description", - "example": "A human-readable explanation of problem", - }, + title="Wrong OTP error", + description="The provided otp code is invalid", + type="object", + required=[ + "error", + "error_description", + ], + properties={ + "error": { + "type": "string", + "description": "Error class name", + "example": "OSError", }, - "example": { - "error": "invalid_client", - "error_description": "The provided otp code is invalid", + "error_description": { + "type": "string", + "description": "Error description", + "example": "A human-readable explanation of problem", }, }, + example={ + "error": "invalid_client", + "error_description": "The provided otp code is invalid", + }, ) ) -OA_SPEC_SEND_RESET_PASSWORD_CODE = dict( +OA_SPEC_SEND_RESET_PASSWORD_CODE = dict( # noqa: C408 summary="Send password reset code to email", parameters=[ oa_c.build_openapi_parameter( @@ -133,7 +129,7 @@ ), ) -OA_SPEC_RESET_PASSWORD_USER = dict( +OA_SPEC_RESET_PASSWORD_USER = dict( # noqa: C408 summary="Reset user password", parameters=[ oa_c.build_openapi_parameter( @@ -175,7 +171,7 @@ responses=oa_c.build_openapi_get_update_response("User"), ) -OA_SPEC_GET_TOKEN_KWARGS = dict( +OA_SPEC_GET_TOKEN_KWARGS = dict( # noqa: C408 summary="Create token by password", parameters=[ oa_c.build_openapi_parameter( diff --git a/exordos_core/user_api/iam/api/routes.py b/exordos_core/user_api/iam/api/routes.py index 9e139764..8b1dbdcb 100644 --- a/exordos_core/user_api/iam/api/routes.py +++ b/exordos_core/user_api/iam/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.iam.api import controllers @@ -189,7 +191,7 @@ class AuthorizationRequestRoute(routes.Route): """Handler for /v1/iam/authorization_requests/ endpoint""" __controller__ = controllers.AuthorizationInfoController - __allow_methods__ = [routes.GET] + __allow_methods__: tp.ClassVar[list] = [routes.GET] confirm = routes.action(ConfirmAuthorizationRequestAction, invoke=True) @@ -269,13 +271,13 @@ class IamClientsRoute(routes.Route): class IamWebRoute(WebRoute): __controller__ = controllers.IamWebController - __allow_methods__ = [] + __allow_methods__: tp.ClassVar[list] = [] class IamRoute(routes.Route): """Handler for /v1/iam/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.IamController # main resources diff --git a/exordos_core/user_api/iam/dm/models.py b/exordos_core/user_api/iam/dm/models.py index 3d763d30..c44498e4 100644 --- a/exordos_core/user_api/iam/dm/models.py +++ b/exordos_core/user_api/iam/dm/models.py @@ -57,7 +57,7 @@ def get_kind_types(self): class ModelWithSecret(models.Model, models.CustomPropertiesMixin): - __custom_properties__ = { + __custom_properties__: tp.ClassVar[dict] = { "secret": ra_types.String(min_length=5, max_length=128), } @@ -665,7 +665,7 @@ class Organization( orm.SQLStorableWithJSONFieldsMixin, ): __tablename__ = "iam_organizations" - __jsonfields__ = ["info"] + __jsonfields__: tp.ClassVar[list] = ["info"] info = properties.property(ra_types.Dict(), default=dict) @@ -1061,7 +1061,7 @@ class IamClient( ) @classmethod - def get_id_token_signing_alg_values_supported(cls) -> tp.List[str]: + def get_id_token_signing_alg_values_supported(cls) -> list[str]: selector_type = cls.properties.properties[ "signature_algorithm" ].get_property_type() @@ -1384,8 +1384,7 @@ def get_token_by_refresh_token(self, refresh_token, scope=None): token.validate_refresh_expiration() token.refresh(scope=scope) return token - else: - raise iam_e.InvalidRefreshTokenError() + raise iam_e.InvalidRefreshTokenError() def get_token_by_authorization_code(self, code, redirect_uri): for auth_info in IdpAuthorizationInfo.objects.get_all( diff --git a/exordos_core/user_api/network/api/routes.py b/exordos_core/user_api/network/api/routes.py index c944c80c..4e8509ce 100644 --- a/exordos_core/user_api/network/api/routes.py +++ b/exordos_core/user_api/network/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.network.api import controllers @@ -62,7 +64,7 @@ class NetworkRoute(routes.Route): """Handler for /v1/network/ endpoint""" __controller__ = controllers.NetworkController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] lb = routes.route(LBRoute) border = routes.route(BorderRoute) diff --git a/exordos_core/user_api/network/dm/models.py b/exordos_core/user_api/network/dm/models.py index 5950d2cd..c7817efa 100644 --- a/exordos_core/user_api/network/dm/models.py +++ b/exordos_core/user_api/network/dm/models.py @@ -81,7 +81,7 @@ class LB( ) ipsv4 = properties.property( types.TypedList(types.String(max_length=15)), - default=lambda: [], + default=list, ) type = properties.property( types_dynamic.KindModelSelectorType( @@ -248,7 +248,7 @@ class Vhost(ChildModel): types_dynamic.KindModelType(LBExtSourceSSHKind), ) ), - default=lambda: [], + default=list, required=True, ) proxy_protocol_from = properties.property( @@ -301,8 +301,7 @@ def _validate(self, check_all=False): } for vhost in Vhost.objects.get_all(filters=fltr, limit=1): raise ex_exceptions.ValidateException( - err="Protocol+port pair conflicts with another vhost %s." - % str(vhost.uuid) + err=f"Protocol+port pair conflicts with another vhost {vhost.uuid!s}." ) for source in self.external_sources: if source.kind == "ssh_forward" and not su.validate_openssh_key( @@ -387,7 +386,7 @@ class ArchivedTarUrl(types.Url): def validate(self, value): if not super().validate(value): return False - return value.endswith("tar.gz") or value.endswith("tar.zst") + return value.endswith(("tar.gz", "tar.zst")) class RuleStaticDownloadKind(AbstractRuleKind): @@ -534,7 +533,7 @@ class AbstractHTTPRouteCondKind(AbstractRouteCondKind): types_dynamic.KindModelType(ModifierRewriteUrlKind), ) ), - default=lambda: [], + default=list, ) def __init__(self, modifiers=None, **kwargs): @@ -563,7 +562,7 @@ class RouteRegexConditionKind(AbstractHTTPRouteCondKind): types_dynamic.KindModelType(ModifierRewriteUrlKind), ) ), - default=lambda: [], + default=list, ) def __init__(self, modifiers=None, **kwargs): @@ -679,11 +678,11 @@ class Border( # entrypoint consumers point DNAT clients at (like LB.ipsv4). ipsv4 = properties.property( types.TypedList(types.String(max_length=15)), - default=lambda: [], + default=list, ) # Inline NAT rules, reconciled as part of the Border resource: # snat_rules: [{source_cidr, mode: "masquerade"|"snat", snat_to}] # forwards: [{proto: "tcp"|"udp", public_ip, listen_port, to_host, # to_port}] - snat_rules = properties.property(types.List(), default=lambda: []) - forwards = properties.property(types.List(), default=lambda: []) + snat_rules = properties.property(types.List(), default=list) + forwards = properties.property(types.List(), default=list) diff --git a/exordos_core/user_api/quota/api/controllers.py b/exordos_core/user_api/quota/api/controllers.py index 45ac05cd..88708001 100644 --- a/exordos_core/user_api/quota/api/controllers.py +++ b/exordos_core/user_api/quota/api/controllers.py @@ -51,7 +51,7 @@ def _validate_quota_field(resource_name, field_name): if not isinstance( quota_property.get_property_type(), (types.Integer, types.Float) ): - raise ValueError(f"Quota field must be an integer: {field_name}") + raise TypeError(f"Quota field must be an integer: {field_name}") def create(self, **kwargs): self._validate_quota_field( diff --git a/exordos_core/user_api/quota/api/routes.py b/exordos_core/user_api/quota/api/routes.py index 6c491891..3af4ee6e 100644 --- a/exordos_core/user_api/quota/api/routes.py +++ b/exordos_core/user_api/quota/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.quota.api import controllers @@ -28,7 +30,7 @@ class QuotaLimitsRoute(routes.Route): class QuotaRoute(routes.Route): """Handler for /v1/quota/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.QuotaController limits = routes.route(QuotaLimitsRoute) diff --git a/exordos_core/user_api/repo/api/routes.py b/exordos_core/user_api/repo/api/routes.py index c84655c3..9b028194 100644 --- a/exordos_core/user_api/repo/api/routes.py +++ b/exordos_core/user_api/repo/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.repo.api import controllers @@ -68,7 +70,7 @@ class RepoElementRoute(routes.Route): """Handler for /v1/repo/elements/ endpoint""" __controller__ = controllers.RepoElementController - __allow_methods__ = [routes.GET, routes.FILTER, routes.DELETE] + __allow_methods__: tp.ClassVar[list] = [routes.GET, routes.FILTER, routes.DELETE] install = routes.action(RepoElementInstallActionRoute, invoke=True) uninstall = routes.action(RepoElementUninstallActionRoute, invoke=True) @@ -80,7 +82,7 @@ class RepoRoute(routes.Route): """Handler for /v1/repo/ endpoint""" __controller__ = controllers.RepoProxyController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] repositories = routes.route(RepositoryRoute) elements = routes.route(RepoElementRoute) diff --git a/exordos_core/user_api/secret/api/controllers.py b/exordos_core/user_api/secret/api/controllers.py index c528b00e..7f214a6b 100644 --- a/exordos_core/user_api/secret/api/controllers.py +++ b/exordos_core/user_api/secret/api/controllers.py @@ -48,14 +48,14 @@ class PasswordsController(iam_controllers.PolicyBasedController): ) def create(self, **kwargs): - if "value" in kwargs: - if ( - kwargs.get("method", sc.SecretMethod.AUTO_HEX.value) - != sc.SecretMethod.MANUAL.value - ): - raise common_exceptions.ValidateException( - err="value is allowed only for MANUAL method" - ) + if ( + "value" in kwargs + and kwargs.get("method", sc.SecretMethod.AUTO_HEX.value) + != sc.SecretMethod.MANUAL.value + ): + raise common_exceptions.ValidateException( + err="value is allowed only for MANUAL method" + ) return super().create(**kwargs) diff --git a/exordos_core/user_api/secret/api/routes.py b/exordos_core/user_api/secret/api/routes.py index 30f75609..6c5f2adb 100644 --- a/exordos_core/user_api/secret/api/routes.py +++ b/exordos_core/user_api/secret/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.secret.api import controllers @@ -46,7 +48,7 @@ class RSAKeysRoute(routes.Route): class SecretRoute(routes.Route): """Handler for /v1/secret/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.SecretController passwords = routes.route(PasswordsRoute) diff --git a/exordos_core/user_api/security/api/routes.py b/exordos_core/user_api/security/api/routes.py index 22e46aef..00007e78 100644 --- a/exordos_core/user_api/security/api/routes.py +++ b/exordos_core/user_api/security/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.security.api import controllers @@ -29,6 +31,6 @@ class SecurityRoute(routes.Route): """Handler for /v1/security/ endpoint""" __controller__ = controllers.SecurityController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] rules = routes.route(RulesRoute) diff --git a/exordos_core/user_api/security/dm/models.py b/exordos_core/user_api/security/dm/models.py index 81e105c1..10c17ee9 100644 --- a/exordos_core/user_api/security/dm/models.py +++ b/exordos_core/user_api/security/dm/models.py @@ -70,9 +70,8 @@ class UriConditions(AbstractConditions): def can_handle(self, context): request = context.request - if self.method: - if request.method.upper() != self.method: - return False + if self.method and request.method.upper() != self.method: + return False return request.path_info.lower() == self.uri.lower() @@ -90,9 +89,8 @@ class UriRegexConditions(AbstractConditions): def can_handle(self, context): request = context.request - if self.method: - if request.method.upper() != self.method: - return False + if self.method and request.method.upper() != self.method: + return False return ( re.match( self.uri_regex, @@ -121,7 +119,7 @@ def execute(self, context): payload = context.get_raw_payload() if not isinstance(payload, dict): return True - json_keys = set(key.lower() for key in payload) + json_keys = {key.lower() for key in payload} return not any(field.lower() in json_keys for field in self.fields) @@ -251,10 +249,7 @@ def execute(self, context): return True uuid_val = getattr(user_info, "uuid", None) - if uuid_val and str(uuid_val).lower() in allowed: - return True - - return False + return bool(uuid_val and str(uuid_val).lower() in allowed) class GrantPermissionAction(AbstractVerifier): diff --git a/exordos_core/user_api/ua/routes.py b/exordos_core/user_api/ua/routes.py index 6ee189b5..5c5da95c 100644 --- a/exordos_core/user_api/ua/routes.py +++ b/exordos_core/user_api/ua/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.ua import controllers @@ -28,7 +30,7 @@ class IssueKeyAction(routes.Action): class AgentsRoute(routes.Route): """Handler for /v1/ua/agents/ endpoint""" - __allow_methods__ = [routes.FILTER, routes.GET, routes.CREATE] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER, routes.GET, routes.CREATE] __controller__ = controllers.AgentController issue_key = routes.action(IssueKeyAction, invoke=True) @@ -37,14 +39,14 @@ class AgentsRoute(routes.Route): class ResourcesRoute(routes.Route): """Handler for /v1/ua/resources/ endpoint""" - __allow_methods__ = [routes.FILTER, routes.GET] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER, routes.GET] __controller__ = controllers.ResourceController class TargetResourcesRoute(routes.Route): """Handler for /v1/ua/target_resources/ endpoint""" - __allow_methods__ = [routes.FILTER, routes.GET] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER, routes.GET] __controller__ = controllers.TargetResourceController @@ -52,7 +54,7 @@ class UaRoute(routes.Route): """Handler for /v1/ua/ endpoint""" __controller__ = controllers.InternalController - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] agents = routes.route(AgentsRoute) resources = routes.route(ResourcesRoute) diff --git a/exordos_core/user_api/vs/api/controllers.py b/exordos_core/user_api/vs/api/controllers.py index 18f49659..aca87bc0 100644 --- a/exordos_core/user_api/vs/api/controllers.py +++ b/exordos_core/user_api/vs/api/controllers.py @@ -25,7 +25,7 @@ from restalchemy.common import exceptions as ra_e from restalchemy.dm import filters as dm_filters -from exordos_core.vs.dm import models as models +from exordos_core.vs.dm import models class ValueNotBelongsToVariableError(ra_e.ValidationErrorException): diff --git a/exordos_core/user_api/vs/api/routes.py b/exordos_core/user_api/vs/api/routes.py index e746bb1e..af0d46af 100644 --- a/exordos_core/user_api/vs/api/routes.py +++ b/exordos_core/user_api/vs/api/routes.py @@ -14,6 +14,8 @@ # License for the specific language governing permissions and limitations # under the License. +import typing as tp + from restalchemy.api import routes from exordos_core.user_api.vs.api import controllers @@ -63,7 +65,7 @@ class ValuesRoute(routes.Route): class VSRoute(routes.Route): """Handler for /v1/vs/ endpoint""" - __allow_methods__ = [routes.FILTER] + __allow_methods__: tp.ClassVar[list] = [routes.FILTER] __controller__ = controllers.ValuesStoreController profiles = routes.route(ProfilesRoute) diff --git a/exordos_core/vs/builders/service.py b/exordos_core/vs/builders/service.py index a45b8c8f..852a933b 100644 --- a/exordos_core/vs/builders/service.py +++ b/exordos_core/vs/builders/service.py @@ -45,7 +45,7 @@ class Variable( models.Variable, ua_models.InstanceMixin, ): - __tracked_instances_model_map__ = { + __tracked_instances_model_map__: tp.ClassVar[dict] = { "vs_profile": Profile, } @@ -63,7 +63,7 @@ def get_tracked_resources( ua_models.RI("vs_profile", p["profile"]) for p in self.setter.profiles ) - return tuple() + return () class VSBuilderService(sdk_builder.CollectionUniversalBuilderService): @@ -86,7 +86,7 @@ def post_create_instance_resource( self, instance: ua_models.InstanceMixin, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """The hook is performed after saving instance resource. @@ -109,7 +109,7 @@ def post_update_instance_resource( self, instance: ua_models.InstanceMixin, resource: ua_models.TargetResource, - derivatives: tp.Collection[ua_models.TargetResource] = tuple(), + derivatives: tp.Collection[ua_models.TargetResource] = (), ) -> None: """The hook is performed after updating instance resource.""" # Skip Profile and Value instances diff --git a/exordos_core/vs/dm/models.py b/exordos_core/vs/dm/models.py index 2127ff17..12747c31 100644 --- a/exordos_core/vs/dm/models.py +++ b/exordos_core/vs/dm/models.py @@ -87,11 +87,11 @@ def _validate_not_used(self, session: tp.Any = None) -> None: if not session: engine = engines.engine_factory.get_engine() - with engine.session_manager() as session: - curs = session.execute(expression, tuple()) + with engine.session_manager() as _session: + curs = _session.execute(expression, ()) resp = curs.fetchall() else: - curs = session.execute(expression, tuple()) + curs = session.execute(expression, ()) resp = curs.fetchall() me = str(self.uuid) @@ -134,7 +134,7 @@ def set_value(self, variable: "Variable") -> None: If the value cannot be determined, the method raises an exception. """ - profile: tp.Optional[Profile] = None + profile: Profile | None = None # If the variable is binded to an element, # we check if the element exists and has profile @@ -186,7 +186,7 @@ class SelectorVariableSetter(infra_models.SelectorVariableSetter): """ def _set_value_latest_strategy( - self, variable: "Variable", values: tp.List["Value"] + self, variable: "Variable", values: list["Value"] ) -> None: """Determine a value based on the `latest` strategy.""" # If there is no manual selected value, select diff --git a/tools/fetch_keycloak_users.py b/tools/fetch_keycloak_users.py index f634fc70..cac3c69e 100755 --- a/tools/fetch_keycloak_users.py +++ b/tools/fetch_keycloak_users.py @@ -92,7 +92,7 @@ def _fetch_all_users( batch = response.json() if not isinstance(batch, list): - raise RuntimeError("Keycloak users endpoint returned non-list JSON") + raise TypeError("Keycloak users endpoint returned non-list JSON") if not batch: break diff --git a/tools/import_keycloak_users_to_iam.py b/tools/import_keycloak_users_to_iam.py index 4ada5137..9fea02bb 100755 --- a/tools/import_keycloak_users_to_iam.py +++ b/tools/import_keycloak_users_to_iam.py @@ -24,6 +24,8 @@ import requests +LOG = logging.getLogger(__name__) + class UserAlreadyExistsError(Exception): """Raised when a user already exists in IAM.""" @@ -266,7 +268,7 @@ def main(argv: list[str] | None = None) -> int: users = json.load(f) if not isinstance(users, list): - raise ValueError("Input JSON must contain a list of users") + raise TypeError("Input JSON must contain a list of users") created = 0 skipped = 0 @@ -282,7 +284,7 @@ def main(argv: list[str] | None = None) -> int: log_username = username or email or "" if not user_uuid: - logging.warning("Skipping user without uuid. username=%s", log_username) + LOG.warning("Skipping user without uuid. username=%s", log_username) skipped += 1 continue @@ -295,7 +297,7 @@ def main(argv: list[str] | None = None) -> int: timeout=timeout, ) except requests.RequestException as e: - logging.error( + LOG.error( "Network error while checking user existence. username=%s uuid=%s error=%s", log_username, user_uuid, @@ -303,8 +305,8 @@ def main(argv: list[str] | None = None) -> int: ) failed += 1 continue - except Exception as e: - logging.error( + except Exception as e: # noqa: BLE001 + LOG.error( "Failed to check user existence. username=%s uuid=%s error=%s", log_username, user_uuid, @@ -328,7 +330,7 @@ def main(argv: list[str] | None = None) -> int: include_uuid=not args.ignore_uuid, ) - logging.info("Creating user. username=%s uuid=%s", log_username, user_uuid) + LOG.info("Creating user. username=%s uuid=%s", log_username, user_uuid) try: _create_user( session=session, @@ -339,7 +341,7 @@ def main(argv: list[str] | None = None) -> int: ) created += 1 except UserAlreadyExistsError: - logging.info( + LOG.info( "User already exists, skipping. username=%s uuid=%s", log_username, user_uuid, @@ -347,7 +349,7 @@ def main(argv: list[str] | None = None) -> int: skipped += 1 continue except requests.RequestException as e: - logging.error( + LOG.error( "Network error while creating user. username=%s uuid=%s error=%s", log_username, user_uuid, @@ -355,8 +357,8 @@ def main(argv: list[str] | None = None) -> int: ) failed += 1 continue - except Exception as e: - logging.error( + except Exception as e: # noqa: BLE001 + LOG.error( "Failed to create user. username=%s uuid=%s error=%s", log_username, user_uuid, @@ -365,7 +367,7 @@ def main(argv: list[str] | None = None) -> int: failed += 1 continue - logging.info("Done. created=%s skipped=%s failed=%s", created, skipped, failed) + LOG.info("Done. created=%s skipped=%s failed=%s", created, skipped, failed) return 0 diff --git a/uv.lock b/uv.lock index 97574ff7..991a2d72 100644 --- a/uv.lock +++ b/uv.lock @@ -650,7 +650,7 @@ name = "exceptiongroup" version = "1.3.1" source = { registry = "https://pypi.org/simple" } dependencies = [ - { name = "typing-extensions" }, + { name = "typing-extensions", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/50/79/66800aadf48771f6b62f7eb014e352e5d06856655206165d775e675a02c9/exceptiongroup-1.3.1.tar.gz", hash = "sha256:8b412432c6055b0b7d14c310000ae93352ed6754f70fa8f7c34141f91c4e3219", size = 30371, upload-time = "2025-11-21T23:01:54.787Z" } wheels = [ @@ -1848,7 +1848,7 @@ resolution-markers = [ "python_full_version < '3.11'", ] dependencies = [ - { name = "pbr" }, + { name = "pbr", marker = "python_full_version < '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5f/26/85800d24c3aa7650bbd5fa0398aca78a84e8a8693f9c6a852148a196ddac/oslo_i18n-6.8.0.tar.gz", hash = "sha256:a0b4c64c1396869d7144dca60ad97c7eb028f78f61f91c7007531238051997df", size = 50114, upload-time = "2026-05-18T09:16:54.09Z" } wheels = [ @@ -1866,7 +1866,7 @@ resolution-markers = [ "python_full_version >= '3.11' and python_full_version < '3.13'", ] dependencies = [ - { name = "pbr" }, + { name = "pbr", marker = "python_full_version >= '3.11'" }, ] sdist = { url = "https://files.pythonhosted.org/packages/5e/69/72b03bb4d33f51a157c02d5297227bae48b9c359103856942b8774b608df/oslo_i18n-6.9.0.tar.gz", hash = "sha256:574bcf21873b185068bcec951de1ec093158ffdff05a8055fd18ddcb69f69e65", size = 50369, upload-time = "2026-07-10T13:44:34.301Z" } wheels = [ @@ -2880,27 +2880,27 @@ wheels = [ [[package]] name = "ruff" -version = "0.15.22" -source = { registry = "https://pypi.org/simple" } -sdist = { url = "https://files.pythonhosted.org/packages/3a/06/ae069393fc66e8ff33036d4b368003833bf6e88ccf182e17e7a2f1c754fd/ruff-0.15.22.tar.gz", hash = "sha256:3f15175b1fb580126f58285a5dae6b2ea89000136d980c64499211f116b54809", size = 4785063, upload-time = "2026-07-16T15:14:13.244Z" } -wheels = [ - { url = "https://files.pythonhosted.org/packages/23/18/ee54b7ae1e121be7a28ea6da4b67564ebb0530e183a54415ab7e3bcd2c4e/ruff-0.15.22-py3-none-linux_armv6l.whl", hash = "sha256:44423e73493737f5e7c5b41d475483898ff37afcdae38bc3da5085e29af1c2d8", size = 10781258, upload-time = "2026-07-16T15:13:19.452Z" }, - { url = "https://files.pythonhosted.org/packages/2f/d2/2520cb14761ddbeaf57642a76942fc36adcbdbe53b4532241995f6fc485c/ruff-0.15.22-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:b82c6482946e9eda7ff2e091d25b8bad3f718684e1916d41bd56873cee05b697", size = 10999477, upload-time = "2026-07-16T15:13:23.318Z" }, - { url = "https://files.pythonhosted.org/packages/c9/10/74e53572aa758dfaa678c2a2646b5c5515d884b7ca56be4d2ce03ca4b560/ruff-0.15.22-py3-none-macosx_11_0_arm64.whl", hash = "sha256:11c1c715af53a09f714e011106bffc419751ec8232fcb5da42173284ea3fec6f", size = 10466716, upload-time = "2026-07-16T15:13:26.162Z" }, - { url = "https://files.pythonhosted.org/packages/1e/cc/44eaaf0844e028182f2d0a8f2190d0f359159aed0a9e5ab861d892f1ae2a/ruff-0.15.22-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:742a29cf29bddb7c8327895d6a10e0e6c5b38a96dd407af9b5d0857f809c0576", size = 10892644, upload-time = "2026-07-16T15:13:29.229Z" }, - { url = "https://files.pythonhosted.org/packages/9f/21/8edf559014d2b0f82beea19cfb713993ad802ccda16868769979c6090a84/ruff-0.15.22-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:72af58b951b0ae395935ae79763dc349bc0eb706319d28f7a33ad2cfb3cfc178", size = 10576719, upload-time = "2026-07-16T15:13:32.35Z" }, - { url = "https://files.pythonhosted.org/packages/bf/1e/3a13abd392a3b50b62e5938a831f9ab6e588358cacad5c18545b716d2182/ruff-0.15.22-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:62d425005c1835eb24e2ee4161cb90e8db263415f4a71c8c72c33abaa6c0c224", size = 11376494, upload-time = "2026-07-16T15:13:35.958Z" }, - { url = "https://files.pythonhosted.org/packages/bf/3e/422d3d95bcf04dd78e1aeac22184d4f9a8fb2c01865d39d44618484a0317/ruff-0.15.22-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:e8b9b3f8779a4f08c969defc3c8c35abffaa757e601ed5ae66d6d1db6519969a", size = 12208370, upload-time = "2026-07-16T15:13:39.185Z" }, - { url = "https://files.pythonhosted.org/packages/1e/91/5d065a0e0a02bf4813f5119ad278462eed081d2b832eb7c021ade0ec9e65/ruff-0.15.22-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:1e0dd1b2e4d3d585f897a0d137cbf4eaf6223bef4e8ce34d6bb12556c5f9249e", size = 11581098, upload-time = "2026-07-16T15:13:42.132Z" }, - { url = "https://files.pythonhosted.org/packages/f6/f9/a0d4871d12fae702eb1f41b686caf05f1f8b124dc6db6f784f53d74918fa/ruff-0.15.22-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:365523eb91d9224e1bcb03b022fbf0facb8f9e23792a2c53d9d4b3924bdbdebb", size = 11399422, upload-time = "2026-07-16T15:13:45.2Z" }, - { url = "https://files.pythonhosted.org/packages/18/80/c843a5176cddbceb0b7e8dd41cf9993490796c1c469348d384f5a5c13c56/ruff-0.15.22-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:fabfd168afdf29fee5be98b831efa9683c94d7c5a3b58b9ce5a2e38444589a74", size = 11381683, upload-time = "2026-07-16T15:13:48.46Z" }, - { url = "https://files.pythonhosted.org/packages/d4/00/8485de0ae92239438a36cfc51350db9b9e85c9ebdfaea91b18e422706662/ruff-0.15.22-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:225dbf095a87f1d9f90f5fd7924d2613ee452a75a4308c63a8f50f761787aa7c", size = 10850295, upload-time = "2026-07-16T15:13:51.655Z" }, - { url = "https://files.pythonhosted.org/packages/fa/91/24977ec2ec72eaf15e4394ace2959fdff2dd1e14f03e005e838023407169/ruff-0.15.22-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:1877d63b9d24ed278744f1523fd11b85540566d54641f97c566d7d9dc5ca5296", size = 10579640, upload-time = "2026-07-16T15:13:54.79Z" }, - { url = "https://files.pythonhosted.org/packages/9c/47/9b51216951974df1f263ac19da550d34252e0ed7218c25f10c5ef9ed7517/ruff-0.15.22-py3-none-musllinux_1_2_i686.whl", hash = "sha256:a1606c510bd7215680d32efab38965f7cdec3ef69f5170a3f4791404ffdd5262", size = 11105077, upload-time = "2026-07-16T15:13:57.915Z" }, - { url = "https://files.pythonhosted.org/packages/c2/47/20e9d4a3b8016778acea5fc32bb50d35d207500a17ddb529ffa6996feef8/ruff-0.15.22-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:630479b18625f5ffc373f77603a22a9f8ac0acd7ff0501178b5db28ec71e9c64", size = 11490980, upload-time = "2026-07-16T15:14:01.032Z" }, - { url = "https://files.pythonhosted.org/packages/4d/76/3f72d8fc38c1cb77b38c56a70da9d0c17700cc1cc50f9649c9d3c8f5ba71/ruff-0.15.22-py3-none-win32.whl", hash = "sha256:e5ba0e4a13fd14abbed2a77b517a3911290c6c6c59ef67784328d1668fab76cf", size = 10789165, upload-time = "2026-07-16T15:14:04.16Z" }, - { url = "https://files.pythonhosted.org/packages/cb/46/4965251734c2b6fcdca1b1b187d20bcac3af0ee5b083b89c910bb961ce3a/ruff-0.15.22-py3-none-win_amd64.whl", hash = "sha256:9be63ba1eb936acd2d1342fb8337c356353706fce233b2a15a09a97037e6acde", size = 11938297, upload-time = "2026-07-16T15:14:07.316Z" }, - { url = "https://files.pythonhosted.org/packages/57/c9/e69b1ff4c8b69093ef08b8919ab767af0569666865b39c30a8795d88d3c6/ruff-0.15.22-py3-none-win_arm64.whl", hash = "sha256:e1168075b72158510839f250027659cdd78476f40507dd517892304c41318661", size = 11298172, upload-time = "2026-07-16T15:14:10.51Z" }, +version = "0.16.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/4d/94/1e5e4967626faf12fa56999cd6222dff6992ceb086ad7945756baf70c7a7/ruff-0.16.0.tar.gz", hash = "sha256:e460aafd5495ec89efaa6ced2e4a9a581116451e1c88b9d37ef497e0f8e93982", size = 4790557, upload-time = "2026-07-23T19:11:30.981Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/4b/81/1c8818fee7ce1a04cd7d1b3172e0a8f8e4f1dc4feb7fc390e16daa8af323/ruff-0.16.0-py3-none-linux_armv6l.whl", hash = "sha256:e5115729eb08c585e5121978ba5d5b60caeae394ce21b9fb5e6cd33a1c6c9b1e", size = 10754633, upload-time = "2026-07-23T19:10:46.415Z" }, + { url = "https://files.pythonhosted.org/packages/23/df/beaf59c09d68db84304d555f188b276a77132a5d5b0b67a5c762aa143628/ruff-0.16.0-py3-none-macosx_10_12_x86_64.whl", hash = "sha256:3c954b1d580bfa035b41654f7858cc7e71d5fc3ac5b723dd62bd9133830ed522", size = 10969164, upload-time = "2026-07-23T19:10:50.271Z" }, + { url = "https://files.pythonhosted.org/packages/42/ce/741cd197496a1abbf51352710fd15ed995d2a2be87189c1da26a450d6e83/ruff-0.16.0-py3-none-macosx_11_0_arm64.whl", hash = "sha256:e01c21d10eb1b29f47b7454e1f4056db9a3f0260c646aa88457c610291db9f81", size = 10488846, upload-time = "2026-07-23T19:10:52.639Z" }, + { url = "https://files.pythonhosted.org/packages/52/2a/a2db8e88cade358f5cdcb05674a917751074109315d014eb6352d9a893f7/ruff-0.16.0-py3-none-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:6e364e5ed22ed8dc05082fd78e35308618260907ac2d3c1d637b2e682415b6c9", size = 10889729, upload-time = "2026-07-23T19:10:54.89Z" }, + { url = "https://files.pythonhosted.org/packages/42/65/62a771694ebd63029dc953e27dbad40e1588bd4860ff9fe881018fddaa49/ruff-0.16.0-py3-none-manylinux_2_17_armv7l.manylinux2014_armv7l.whl", hash = "sha256:d327b8fc113a1d4421a04f3839d3752057c8dd1ee320223a6f3f52d04ada462a", size = 10568275, upload-time = "2026-07-23T19:10:56.993Z" }, + { url = "https://files.pythonhosted.org/packages/3f/e2/ced249fe8af5f086c5c58cc21cc3356d50f32f7401c5df87050c999620a7/ruff-0.16.0-py3-none-manylinux_2_17_i686.manylinux2014_i686.whl", hash = "sha256:a9b50c55e263103586b3dcf5f73d479eb8cb5fdb6098fec59a62891dab653717", size = 11385112, upload-time = "2026-07-23T19:10:59.615Z" }, + { url = "https://files.pythonhosted.org/packages/87/0b/05154977a8fd69eeb6c103271f55403bfd8711f5c0f8ed07489d95a504e7/ruff-0.16.0-py3-none-manylinux_2_17_ppc64le.manylinux2014_ppc64le.whl", hash = "sha256:0ff4a79ce3ec0172f3241943835de1c4cb4e2dcd07f0f8c2d02603dbbbee4b17", size = 12207008, upload-time = "2026-07-23T19:11:02.154Z" }, + { url = "https://files.pythonhosted.org/packages/fb/29/98225831a3a1eab0e02f4acc6ca6559a98611dcc68b6965ff4b7234627c1/ruff-0.16.0-py3-none-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:e95c448fca1fb2a18372a9440926c5a6ee789639bb975c72e7ae6d0b04218ab4", size = 11650842, upload-time = "2026-07-23T19:11:04.557Z" }, + { url = "https://files.pythonhosted.org/packages/91/66/6bd3cf90500653d55dc0ffc8507aa8300bd49d0214b2e8cb4d3fef2943ba/ruff-0.16.0-py3-none-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:4f11a8d11010301d0a398a2fdef67691feca7294da6aef55e2150e8fa2cd520b", size = 11400718, upload-time = "2026-07-23T19:11:09.233Z" }, + { url = "https://files.pythonhosted.org/packages/8e/a2/a54eb4eae05d66364050a5d3b8a9c5ef88196531b3cbe7109d873f87f819/ruff-0.16.0-py3-none-manylinux_2_31_riscv64.whl", hash = "sha256:48044c678e9cb8698246c99b14aaccfa6601dea7379eb48a6f8f73f7a6d86cd0", size = 11426177, upload-time = "2026-07-23T19:11:11.994Z" }, + { url = "https://files.pythonhosted.org/packages/1a/be/16e3eea4b2a478a496919f5e36f17c4559e54620bd3bbac5d6affa068006/ruff-0.16.0-py3-none-musllinux_1_2_aarch64.whl", hash = "sha256:7aa0959bad8eb8bef50340154fc9b58678dae31fa4293afa38b44b6e552c0213", size = 10856126, upload-time = "2026-07-23T19:11:14.221Z" }, + { url = "https://files.pythonhosted.org/packages/a2/84/252eb8b868a16eec7257c14f504f77537e734b2d69c762e639e588e304a3/ruff-0.16.0-py3-none-musllinux_1_2_armv7l.whl", hash = "sha256:28ea2b7df8ebf7f9da6b7d47b230ab48f387c0a29be3b474c4d0740e197bb9af", size = 10571208, upload-time = "2026-07-23T19:11:16.378Z" }, + { url = "https://files.pythonhosted.org/packages/21/09/817a482f542f7570cbb4554b26e896610c7114f539b1d9e2d2145bf6bef6/ruff-0.16.0-py3-none-musllinux_1_2_i686.whl", hash = "sha256:33a3dfac8c35f81498dea9181bccc2f4c4bc8f1521a1dd9406e77643e0f0fb09", size = 11063329, upload-time = "2026-07-23T19:11:19.173Z" }, + { url = "https://files.pythonhosted.org/packages/2e/23/9403c180ca1cb9b1f7335f5c3e5305c09d49ea5b345196682a36028bde4a/ruff-0.16.0-py3-none-musllinux_1_2_x86_64.whl", hash = "sha256:a5237a0bda500d30d81b8e07a6973a5cbc772864cbf746ae2f4e8a2e01c9f4ed", size = 11489751, upload-time = "2026-07-23T19:11:21.74Z" }, + { url = "https://files.pythonhosted.org/packages/b2/1d/1b2ef7bcde851c78d7f17f1cca13fd6dc695fc4b3d6197941e72cae5b132/ruff-0.16.0-py3-none-win32.whl", hash = "sha256:7fab76fa065c873f41ff744347c6e77bcc3dfec4bcc754dc26b63d23c0f7f5fb", size = 10785885, upload-time = "2026-07-23T19:11:23.947Z" }, + { url = "https://files.pythonhosted.org/packages/b2/a3/d5e4ef7a56be3f928ffb90b94c25ba7d3cb9c7fe0736aeaaedf361770712/ruff-0.16.0-py3-none-win_amd64.whl", hash = "sha256:429c117f022bf481fabd9d551e7a3952b24c65e6ef44337ea09d90bebef14472", size = 11923141, upload-time = "2026-07-23T19:11:26.409Z" }, + { url = "https://files.pythonhosted.org/packages/cb/9a/8415f2657cbe200f41a4531ccededf135505a92d4a012229121f885b26f9/ruff-0.16.0-py3-none-win_arm64.whl", hash = "sha256:14296fedcd2705c77ab8235439278bbb38f285cf7da5528b00b3e330c3d4872d", size = 11273407, upload-time = "2026-07-23T19:11:28.705Z" }, ] [[package]] @@ -3087,7 +3087,7 @@ wheels = [ [[package]] name = "tox" -version = "4.56.4" +version = "4.58.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "cachetools" }, @@ -3103,35 +3103,35 @@ dependencies = [ { name = "typing-extensions", marker = "python_full_version < '3.11'" }, { name = "virtualenv" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/aa/fc/903385f783a1d7b7670eb742654e8f6f109c7a5a65913f92dfee8d033ea7/tox-4.56.4.tar.gz", hash = "sha256:d49e371119ebfafb15054ad7ccff3d03027ccb65195fae3ac656b431b5524055", size = 286611, upload-time = "2026-07-08T23:59:07.033Z" } +sdist = { url = "https://files.pythonhosted.org/packages/6e/8e/4d2b1b2a81f4de1cd4e54fa40df1ab5f9bb88fe2e37461fc44aba8f9d302/tox-4.58.0.tar.gz", hash = "sha256:ab0b126a04dd56bc18e6d216386db09335247f2289b54cf534deb5c4ae3a8d2e", size = 296926, upload-time = "2026-07-21T13:10:36.622Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/f5/f2/d2e2b6969203c319165860ba93272cf12c71d42acff5acd2c84d7543b460/tox-4.56.4-py3-none-any.whl", hash = "sha256:53bac88382b9638a5ce40fbaddd6076e1c8f638751af7bf8e759392c953df2f0", size = 217458, upload-time = "2026-07-08T23:59:05.21Z" }, + { url = "https://files.pythonhosted.org/packages/6b/3d/7ba55871e9d794d40b6c8424f2e5d1b267ea5d9a4bd2175e08b57960ba13/tox-4.58.0-py3-none-any.whl", hash = "sha256:dcae21f5f015f3a67658e35644cce0d1aa0dedcd06f3927f95d84e1717f6cea5", size = 223298, upload-time = "2026-07-21T13:10:34.731Z" }, ] [[package]] name = "tox-uv" -version = "1.35.2" +version = "1.36.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "tox-uv-bare" }, { name = "uv" }, ] wheels = [ - { url = "https://files.pythonhosted.org/packages/ca/dc/6e9994c799bdbb309f829dd6b8d98764dd0757302f3433c380438a3a127b/tox_uv-1.35.2-py3-none-any.whl", hash = "sha256:2d99b0e3c782ba49e7cbe521c8d344758595961b17a3633738d67096641c1bde", size = 6565, upload-time = "2026-05-05T01:34:16.07Z" }, + { url = "https://files.pythonhosted.org/packages/f9/d7/3fce976b9e295218a2d7e541e0f1e9b1259cb893de7a8d277952a66a798d/tox_uv-1.36.0-py3-none-any.whl", hash = "sha256:5f81b39be3fe4e14c6b9bb7ba637ed97d34efa6214efd5f525d95ee9559a99ac", size = 6564, upload-time = "2026-07-21T13:09:54.316Z" }, ] [[package]] name = "tox-uv-bare" -version = "1.35.2" +version = "1.36.0" source = { registry = "https://pypi.org/simple" } dependencies = [ { name = "packaging" }, { name = "tomli", marker = "python_full_version < '3.11'" }, { name = "tox" }, ] -sdist = { url = "https://files.pythonhosted.org/packages/0a/cb/168dc1ccf24e4065a9a0a33df55709ed2b5eb73bd2b13ddd53187e5dffb8/tox_uv_bare-1.35.2.tar.gz", hash = "sha256:49e28a804c97f23ea17e25859960c0fa78f35bccb7e14344cfd840e89a9aade9", size = 32333, upload-time = "2026-05-05T01:34:18.916Z" } +sdist = { url = "https://files.pythonhosted.org/packages/56/df/9f90a59de8c87cece6e691eee53dc1cb0a824d73aea8f380ae2f6ecb71de/tox_uv_bare-1.36.0.tar.gz", hash = "sha256:d9b0a2fd0f74fa65d9597108f8a0ef7abb08651c9196a998a6073b781b45cfd0", size = 32548, upload-time = "2026-07-21T13:09:56.435Z" } wheels = [ - { url = "https://files.pythonhosted.org/packages/5f/53/4a33dc81da39db7b31e5622333df361e8fe055b7ec636bd5fea762c9182d/tox_uv_bare-1.35.2-py3-none-any.whl", hash = "sha256:c0d590a41d1054a1ad0874e9e5943ff52402786e3d4599d8f8d37a65b566ef53", size = 22307, upload-time = "2026-05-05T01:34:17.681Z" }, + { url = "https://files.pythonhosted.org/packages/f7/0a/6dc462e4fb543305283a6157c80f43e3d12ca4702da6ae6521d541c6b55c/tox_uv_bare-1.36.0-py3-none-any.whl", hash = "sha256:ba397dd0396df95a75744d4e42a50ee27207c0ffcf277b62ffba9c3de455a939", size = 22489, upload-time = "2026-07-21T13:09:55.389Z" }, ] [[package]]