Pydantic v2 migration

This commit is contained in:
grossmj committed 2023-08-04 18:20:06 +10:00
1 parent 11f51b3809
commit 9c456532d0
46 files changed
+221 -277

No files matched your search

+2 -2
View File
@@ -321,7 +321,7 @@ class ApplianceImage(BaseModel):
filename: str = Field(..., title='Filename')
version: str = Field(..., title='Version of the file')
md5sum: str = Field(..., title='md5sum of the file', regex='^[a-f0-9]{32}$')
md5sum: str = Field(..., title='md5sum of the file', pattern='^[a-f0-9]{32}$')
filesize: int = Field(..., title='File size in bytes')
download_url: Optional[Union[AnyUrl, constr(max_length=0)]] = Field(
None, title='Download url where you can download the appliance from a browser'
@@ -351,7 +351,7 @@ class ApplianceVersionImages(BaseModel):
class ApplianceVersion(BaseModel):
name: str = Field(..., title='Name of the version')
idlepc: Optional[str] = Field(None, regex='^0x[0-9a-f]{8}')
idlepc: Optional[str] = Field(None, pattern='^0x[0-9a-f]{8}')
images: Optional[ApplianceVersionImages] = Field(None, title='Images used for this version')
+2 -2
View File
@@ -21,5 +21,5 @@ from pydantic import BaseModel
class DateTimeModelMixin(BaseModel):
created_at: Optional[datetime]
updated_at: Optional[datetime]
created_at: Optional[datetime] = None
updated_at: Optional[datetime] = None
+36 -55
View File
@@ -16,8 +16,15 @@
import uuid
from pydantic import BaseModel, Field, SecretStr, validator
from typing import List, Optional, Union
from pydantic import (
ConfigDict,
BaseModel,
Field,
SecretStr,
field_validator,
model_validator
)
from typing import List, Optional, Union, Any
from enum import Enum
from .nodes import NodeType
@@ -44,9 +51,7 @@ class ComputeBase(BaseModel):
user: str = None
password: Optional[SecretStr] = None
name: Optional[str] = None
class Config:
use_enum_values = True
model_config = ConfigDict(use_enum_values=True)
class ComputeCreate(ComputeBase):
@@ -55,46 +60,28 @@ class ComputeCreate(ComputeBase):
"""
compute_id: Union[str, uuid.UUID] = None
class Config:
schema_extra = {
"example": {
"name": "My compute",
"host": "127.0.0.1",
"port": 3080,
"user": "user",
"password": "password"
}
model_config = ConfigDict(json_schema_extra={
"example": {
"name": "My compute",
"host": "127.0.0.1",
"port": 3080,
"user": "user",
"password": "password"
}
})
@validator("compute_id", pre=True, always=True)
def default_compute_id(cls, v, values):
@model_validator(mode='before')
@classmethod
def set_default_compute_id_and_name(cls, data: Any) -> Any:
if v is not None:
return v
else:
protocol = values.get("protocol")
host = values.get("host")
port = values.get("port")
return uuid.uuid5(uuid.NAMESPACE_URL, f"{protocol}://{host}:{port}")
@validator("name", pre=True, always=True)
def generate_name(cls, name, values):
if name is not None:
return name
else:
protocol = values.get("protocol")
host = values.get("host")
port = values.get("port")
user = values.get("user")
if user:
# due to random user generated by 1.4 it's common to have a very long user
if len(user) > 14:
user = user[:11] + "..."
return f"{protocol}://{user}@{host}:{port}"
else:
return f"{protocol}://{host}:{port}"
if "compute_id" not in data:
data['compute_id'] = uuid.uuid5(
uuid.NAMESPACE_URL,
f"{data.get('protocol')}://{data.get('host')}:{data.get('port')}"
)
if "name" not in data:
data['name'] = f"{data.get('protocol')}://{data.get('user', '')}@{data.get('host')}:{data.get('port')}"
return data
class ComputeUpdate(ComputeBase):
@@ -107,14 +94,12 @@ class ComputeUpdate(ComputeBase):
port: Optional[int] = Field(None, gt=0, le=65535)
user: Optional[str] = None
password: Optional[SecretStr] = None
class Config:
schema_extra = {
"example": {
"host": "10.0.0.1",
"port": 8080,
}
model_config = ConfigDict(json_schema_extra={
"example": {
"host": "10.0.0.1",
"port": 8080,
}
})
class Capabilities(BaseModel):
@@ -143,9 +128,7 @@ class Compute(DateTimeModelMixin, ComputeBase):
disk_usage_percent: Optional[float] = Field(None, description="Disk usage of the compute", ge=0, le=100)
last_error: Optional[str] = Field(None, description="Last error found on the compute")
capabilities: Optional[Capabilities] = None
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class ComputeVirtualBoxVM(BaseModel):
@@ -182,6 +165,4 @@ class AutoIdlePC(BaseModel):
platform: str = Field(..., description="Cisco platform")
image: str = Field(..., description="Image path")
ram: int = Field(..., description="Amount of RAM in MB")
class Config:
schema_extra = {"example": {"platform": "c7200", "image": "/path/to/c7200_image.bin", "ram": 256}}
model_config = ConfigDict(json_schema_extra={"example": {"platform": "c7200", "image": "/path/to/c7200_image.bin", "ram": 256}})
+2 -4
View File
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pydantic import BaseModel, Field
from pydantic import ConfigDict, BaseModel, Field
from enum import Enum
from .base import DateTimeModelMixin
@@ -41,6 +41,4 @@ class ImageBase(BaseModel):
class Image(DateTimeModelMixin, ImageBase):
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
+2 -2
View File
@@ -54,7 +54,7 @@ class LinkBase(BaseModel):
Link data.
"""
nodes: Optional[List[LinkNode]] = Field(None, min_items=0, max_items=2)
nodes: Optional[List[LinkNode]] = Field(None, min_length=0, max_length=2)
suspend: Optional[bool] = None
link_style: Optional[LinkStyle] = None
filters: Optional[dict] = None
@@ -63,7 +63,7 @@ class LinkBase(BaseModel):
class LinkCreate(LinkBase):
link_id: UUID = Field(default_factory=uuid4)
nodes: List[LinkNode] = Field(..., min_items=2, max_items=2)
nodes: List[LinkNode] = Field(..., min_length=2, max_length=2)
class LinkUpdate(LinkBase):
+15 -18
View File
@@ -14,8 +14,8 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pydantic import BaseModel, Field, validator
from typing import List, Optional, Union
from pydantic import BaseModel, Field, model_validator
from typing import List, Optional, Union, Any
from enum import Enum
from uuid import UUID, uuid4
@@ -96,7 +96,7 @@ class NodePort(BaseModel):
port_number: int = Field(..., description="Port slot")
link_type: LinkType = Field(..., description="Type of link")
data_link_types: dict = Field(..., description="Available PCAP types for capture")
mac_address: Union[str, None] = Field(None, regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$")
mac_address: Union[str, None] = Field(None, pattern="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$")
class NodeBase(BaseModel):
@@ -117,7 +117,7 @@ class NodeBase(BaseModel):
False, description="Automatically start the console when the node has started"
)
aux: Optional[int] = Field(None, gt=0, le=65535, description="Auxiliary console TCP port")
aux_type: Optional[ConsoleType]
aux_type: Optional[ConsoleType] = None
properties: Optional[dict] = Field(default_factory=dict, description="Properties specific to an emulator")
label: Optional[Label] = None
@@ -134,21 +134,18 @@ class NodeBase(BaseModel):
first_port_name: Optional[str] = Field(None, description="Name of the first port")
custom_adapters: Optional[List[CustomAdapter]] = None
@validator("port_name_format", pre=True, always=True)
def default_port_name_format(cls, v, values):
if v is None:
if "node_type" in values and values["node_type"] == NodeType.iou:
return "Ethernet{segment0}/{port0}"
return "Ethernet{0}"
return v
@model_validator(mode='before')
@classmethod
def set_default_port_name_format_and_port_segment_size(cls, data: Any) -> Any:
@validator("port_segment_size", pre=True, always=True)
def default_port_segment_size(cls, v, values):
if v is None:
if "node_type" in values and values["node_type"] == NodeType.iou:
return 4
return 0
return v
if "port_name_format" not in data:
if data.get('node_type') == NodeType.iou:
data['port_name_format'] = "Ethernet{segment0}/{port0}"
data['port_segment_size'] = 4
else:
data['port_name_format'] = "Ethernet{0}"
data['port_segment_size'] = 0
return data
class NodeCreate(NodeBase):
+6 -11
View File
@@ -15,7 +15,7 @@
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from typing import Optional, List
from pydantic import BaseModel, validator
from pydantic import field_validator, ConfigDict, BaseModel
from uuid import UUID
from enum import Enum
@@ -53,11 +53,10 @@ class PermissionBase(BaseModel):
path: str
action: PermissionAction
description: Optional[str] = None
model_config = ConfigDict(use_enum_values=True)
class Config:
use_enum_values = True
@validator("action", pre=True)
@field_validator("action", mode="before")
@classmethod
def action_uppercase(cls, v):
return v.upper()
@@ -81,9 +80,7 @@ class PermissionUpdate(PermissionBase):
class Permission(DateTimeModelMixin, PermissionBase):
permission_id: UUID
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class RoleBase(BaseModel):
@@ -116,6 +113,4 @@ class Role(DateTimeModelMixin, RoleBase):
role_id: UUID
is_builtin: bool
permissions: List[Permission]
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
@@ -14,7 +14,7 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
from pydantic import BaseModel, Field
from pydantic import ConfigDict, BaseModel, Field
from typing import Optional, Union
from enum import Enum
from uuid import UUID
@@ -58,15 +58,11 @@ class TemplateCreate(TemplateBase):
name: str
template_type: NodeType
class Config:
extra = "allow"
model_config = ConfigDict(extra="allow")
class TemplateUpdate(TemplateBase):
class Config:
extra = "allow"
model_config = ConfigDict(extra="allow")
class Template(DateTimeModelMixin, TemplateBase):
@@ -77,10 +73,7 @@ class Template(DateTimeModelMixin, TemplateBase):
symbol: str
builtin: bool
template_type: NodeType
class Config:
extra = "allow"
orm_mode = True
model_config = ConfigDict(extra="allow", from_attributes=True)
class TemplateUsage(BaseModel):
@@ -44,7 +44,7 @@ class DockerTemplate(TemplateBase):
description="Path of the web interface",
)
console_resolution: Optional[str] = Field(
"1024x768", regex="^[0-9]+x[0-9]+$", description="Console resolution for VNC"
"1024x768", pattern="^[0-9]+x[0-9]+$", description="Console resolution for VNC"
)
extra_hosts: Optional[str] = Field("", description="Docker extra hosts (added to /etc/hosts)")
extra_volumes: Optional[List] = Field([], description="Additional directories to make persistent")
@@ -40,12 +40,12 @@ class DynamipsTemplate(TemplateBase):
exec_area: Optional[int] = Field(64, description="Exec area value")
mmap: Optional[bool] = Field(True, description="MMAP feature")
mac_addr: Optional[str] = Field(
"", description="Base MAC address", regex="^([0-9a-fA-F]{4}\\.){2}[0-9a-fA-F]{4}$|^$"
"", description="Base MAC address", pattern="^([0-9a-fA-F]{4}\\.){2}[0-9a-fA-F]{4}$|^$"
)
system_id: Optional[str] = Field("FTX0945W0MY", description="System ID")
startup_config: Optional[str] = Field("ios_base_startup-config.txt", description="IOS startup configuration file")
private_config: Optional[str] = Field("", description="IOS private configuration file")
idlepc: Optional[str] = Field("", description="Idle-PC value", regex="^(0x[0-9a-fA-F]+)?$|^$")
idlepc: Optional[str] = Field("", description="Idle-PC value", pattern="^(0x[0-9a-fA-F]+)?$|^$")
idlemax: Optional[int] = Field(500, description="Idlemax value")
idlesleep: Optional[int] = Field(30, description="Idlesleep value")
disk0: Optional[int] = Field(0, description="Disk0 size in MB")
@@ -22,14 +22,14 @@ from typing import Optional, List
DEFAULT_PORTS = [
dict(port_number=0, name="Ethernet0"),
dict(port_number=1, name="Ethernet1"),
dict(port_number=2, name="Ethernet2"),
dict(port_number=3, name="Ethernet3"),
dict(port_number=4, name="Ethernet4"),
dict(port_number=5, name="Ethernet5"),
dict(port_number=6, name="Ethernet6"),
dict(port_number=7, name="Ethernet7"),
EthernetHubPort(port_number=0, name="Ethernet0"),
EthernetHubPort(port_number=1, name="Ethernet1"),
EthernetHubPort(port_number=2, name="Ethernet2"),
EthernetHubPort(port_number=3, name="Ethernet3"),
EthernetHubPort(port_number=4, name="Ethernet4"),
EthernetHubPort(port_number=5, name="Ethernet5"),
EthernetHubPort(port_number=6, name="Ethernet6"),
EthernetHubPort(port_number=7, name="Ethernet7"),
]
@@ -23,14 +23,14 @@ from typing import Optional, List
from enum import Enum
DEFAULT_PORTS = [
dict(port_number=0, name="Ethernet0", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=1, name="Ethernet1", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=2, name="Ethernet2", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=3, name="Ethernet3", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=4, name="Ethernet4", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=5, name="Ethernet5", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=6, name="Ethernet6", vlan=1, type="access", ethertype="0x8100"),
dict(port_number=7, name="Ethernet7", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=0, name="Ethernet0", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=1, name="Ethernet1", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=2, name="Ethernet2", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=3, name="Ethernet3", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=4, name="Ethernet4", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=5, name="Ethernet5", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=6, name="Ethernet6", vlan=1, type="access", ethertype="0x8100"),
EthernetSwitchPort(port_number=7, name="Ethernet7", vlan=1, type="access", ethertype="0x8100"),
]
@@ -45,7 +45,7 @@ class QemuTemplate(TemplateBase):
adapters: Optional[int] = Field(1, ge=0, le=275, description="Number of adapters")
adapter_type: Optional[QemuAdapterType] = Field("e1000", description="QEMU adapter type")
mac_address: Optional[str] = Field(
"", description="QEMU MAC address", regex="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$|^$"
"", description="QEMU MAC address", pattern="^([0-9a-fA-F]{2}[:]){5}([0-9a-fA-F]{2})$|^$"
)
first_port_name: Optional[str] = Field("", description="Optional name of the first networking port example: eth0")
port_name_format: Optional[str] = Field(
+1 -1
View File
@@ -75,7 +75,7 @@ def main():
with open(sys.argv[1]) as f:
data = json.load(f)
Topology.parse_obj(data)
Topology.model_validate(data)
if __name__ == "__main__":
+15 -19
View File
@@ -16,7 +16,7 @@
from datetime import datetime
from typing import Optional
from pydantic import EmailStr, BaseModel, Field, SecretStr
from pydantic import ConfigDict, EmailStr, BaseModel, Field, SecretStr
from uuid import UUID
from .base import DateTimeModelMixin
@@ -27,24 +27,24 @@ class UserBase(BaseModel):
Common user properties.
"""
username: Optional[str] = Field(None, min_length=3, regex="[a-zA-Z0-9_-]+$")
username: Optional[str] = Field(None, min_length=3, pattern="[a-zA-Z0-9_-]+$")
is_active: bool = True
email: Optional[EmailStr]
full_name: Optional[str]
email: Optional[EmailStr] = None
full_name: Optional[str] = None
class UserCreate(UserBase):
"""
Properties to create an user.
Properties to create a user.
"""
username: str = Field(..., min_length=3, regex="[a-zA-Z0-9_-]+$")
username: str = Field(..., min_length=3, pattern="[a-zA-Z0-9_-]+$")
password: SecretStr = Field(..., min_length=6, max_length=100)
class UserUpdate(UserBase):
"""
Properties to update an user.
Properties to update a user.
"""
password: Optional[SecretStr] = Field(None, min_length=6, max_length=100)
@@ -56,8 +56,8 @@ class LoggedInUserUpdate(BaseModel):
"""
password: Optional[SecretStr] = Field(None, min_length=6, max_length=100)
email: Optional[EmailStr]
full_name: Optional[str]
email: Optional[EmailStr] = None
full_name: Optional[str] = None
class User(DateTimeModelMixin, UserBase):
@@ -65,9 +65,7 @@ class User(DateTimeModelMixin, UserBase):
user_id: UUID
last_login: Optional[datetime] = None
is_superadmin: bool = False
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class UserGroupBase(BaseModel):
@@ -75,20 +73,20 @@ class UserGroupBase(BaseModel):
Common user group properties.
"""
name: Optional[str] = Field(None, min_length=3, regex="[a-zA-Z0-9_-]+$")
name: Optional[str] = Field(None, min_length=3, pattern="[a-zA-Z0-9_-]+$")
class UserGroupCreate(UserGroupBase):
"""
Properties to create an user group.
Properties to create a user group.
"""
name: Optional[str] = Field(..., min_length=3, regex="[a-zA-Z0-9_-]+$")
name: Optional[str] = Field(..., min_length=3, pattern="[a-zA-Z0-9_-]+$")
class UserGroupUpdate(UserGroupBase):
"""
Properties to update an user group.
Properties to update a user group.
"""
pass
@@ -98,9 +96,7 @@ class UserGroup(DateTimeModelMixin, UserGroupBase):
user_group_id: UUID
is_builtin: bool
class Config:
orm_mode = True
model_config = ConfigDict(from_attributes=True)
class Credentials(BaseModel):