python-compute/compute/instance/schemas.py

189 lines
5.0 KiB
Python
Raw Normal View History

2023-11-23 02:34:02 +03:00
# This file is part of Compute
#
# Compute is free software: you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation, either version 3 of the License, or
# (at your option) any later version.
#
2023-12-03 23:25:34 +03:00
# Compute is distributed in the hope that it will be useful,
2023-11-23 02:34:02 +03:00
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
2023-12-03 23:25:34 +03:00
# along with Compute. If not, see <http://www.gnu.org/licenses/>.
2023-11-23 02:34:02 +03:00
2023-11-06 12:52:19 +03:00
"""Compute instance related objects schemas."""
import re
from enum import StrEnum
from pathlib import Path
2023-12-03 23:25:34 +03:00
from pydantic import ValidationError, validator
from pydantic.error_wrappers import ErrorWrapper
2023-11-06 12:52:19 +03:00
2023-12-13 01:42:50 +03:00
from compute.abstract import EntityModel
2023-11-06 12:52:19 +03:00
from compute.utils.units import DataUnit
class CPUEmulationMode(StrEnum):
"""CPU emulation mode enumerated."""
HOST_PASSTHROUGH = 'host-passthrough'
HOST_MODEL = 'host-model'
CUSTOM = 'custom'
MAXIMUM = 'maximum'
2023-11-09 01:17:50 +03:00
class CPUTopologySchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""CPU topology model."""
sockets: int
cores: int
threads: int
dies: int = 1
2023-11-09 01:17:50 +03:00
class CPUFeaturesSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""CPU features model."""
require: list[str]
disable: list[str]
2023-11-09 01:17:50 +03:00
class CPUSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""CPU model."""
emulation_mode: CPUEmulationMode
2023-11-09 01:17:50 +03:00
model: str | None
vendor: str | None
topology: CPUTopologySchema | None
features: CPUFeaturesSchema | None
2023-11-06 12:52:19 +03:00
2023-11-06 17:47:56 +03:00
class VolumeType(StrEnum):
2023-11-06 12:52:19 +03:00
"""Storage volume types enumeration."""
FILE = 'file'
2023-11-09 01:17:50 +03:00
class VolumeCapacitySchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""Storage volume capacity field model."""
value: int
unit: DataUnit
2023-12-03 23:25:34 +03:00
class DiskDriverSchema(EntityModel):
"""Virtual disk driver model."""
name: str
type: str # noqa: A003
cache: str = 'writethrough'
2023-11-09 01:17:50 +03:00
class VolumeSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""Storage volume model."""
2023-11-06 17:47:56 +03:00
type: VolumeType # noqa: A003
2023-11-06 12:52:19 +03:00
target: str
2023-12-03 23:25:34 +03:00
driver: DiskDriverSchema
capacity: VolumeCapacitySchema | None
2023-11-09 01:17:50 +03:00
source: str | None = None
is_readonly: bool = False
2023-11-06 12:52:19 +03:00
is_system: bool = False
2023-12-03 23:25:34 +03:00
bus: str = 'virtio'
device: str = 'disk'
2023-11-06 12:52:19 +03:00
2023-11-09 01:17:50 +03:00
class NetworkInterfaceSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""Network inerface model."""
source: str
mac: str
2023-11-09 01:17:50 +03:00
class BootOptionsSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""Instance boot settings."""
order: tuple
2023-12-13 01:42:50 +03:00
class CloudInitSchema(EntityModel):
"""Cloud-init config model."""
user_data: str | None = None
meta_data: str | None = None
vendor_data: str | None = None
network_config: str | None = None
2023-11-09 01:17:50 +03:00
class InstanceSchema(EntityModel):
2023-11-06 12:52:19 +03:00
"""Compute instance model."""
name: str
2023-11-09 01:17:50 +03:00
title: str | None
description: str | None
2023-11-06 12:52:19 +03:00
memory: int
max_memory: int
vcpus: int
max_vcpus: int
cpu: CPUSchema
machine: str
emulator: Path
arch: str
boot: BootOptionsSchema
2023-11-06 17:47:56 +03:00
volumes: list[VolumeSchema]
2023-11-06 12:52:19 +03:00
network_interfaces: list[NetworkInterfaceSchema]
2023-11-09 01:17:50 +03:00
image: str | None = None
2023-12-13 01:42:50 +03:00
cloud_init: CloudInitSchema | None = None
2023-11-06 12:52:19 +03:00
@validator('name')
def _check_name(cls, value: str) -> str: # noqa: N805
2023-12-03 23:25:34 +03:00
if not re.match(r'^[a-z0-9_-]+$', value):
2023-11-06 12:52:19 +03:00
msg = (
2023-12-03 23:25:34 +03:00
'Name can contain only lowercase letters, numbers, '
'minus sign and underscore.'
2023-11-06 12:52:19 +03:00
)
raise ValueError(msg)
return value
2023-11-09 01:17:50 +03:00
@validator('cpu')
def _check_topology(cls, cpu: int, values: dict) -> CPUSchema: # noqa: N805
topo = cpu.topology
max_vcpus = values['max_vcpus']
if topo and topo.sockets * topo.cores * topo.threads != max_vcpus:
msg = f'CPU topology does not match with {max_vcpus=}'
raise ValueError(msg)
return cpu
2023-11-06 12:52:19 +03:00
@validator('volumes')
2023-11-09 01:17:50 +03:00
def _check_volumes(cls, volumes: list) -> list: # noqa: N805
if len([v for v in volumes if v.is_system is True]) != 1:
msg = 'volumes list must contain one system volume'
2023-11-06 12:52:19 +03:00
raise ValueError(msg)
2023-11-09 01:17:50 +03:00
for vol in volumes:
2023-12-03 23:25:34 +03:00
if vol.source is None and vol.capacity is None:
raise ValidationError(
[
ErrorWrapper(
Exception(
"capacity is required if 'source' is unset"
),
loc='X.capacity',
)
],
model=VolumeSchema,
)
2023-11-09 01:17:50 +03:00
if vol.is_system is True and vol.is_readonly is True:
msg = 'volume marked as system cannot be readonly'
raise ValueError(msg)
return volumes
2023-11-06 12:52:19 +03:00
@validator('network_interfaces')
def _check_network_interfaces(cls, value: list) -> list: # noqa: N805
if not value:
msg = 'Network interfaces list must contain at least one element'
raise ValueError(msg)
return value