1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-26 00:38:10 +00:00

Fix issue when updating a template

This commit is contained in:
grossmj 2021-10-23 16:23:19 +10:30
parent 45738f262b
commit 7ce5e19a6e
13 changed files with 151 additions and 25 deletions

View File

@ -37,24 +37,31 @@ from .controller.iou_license import IOULicense
from .controller.capabilities import Capabilities from .controller.capabilities import Capabilities
# Controller template schemas # Controller template schemas
from .controller.templates.vpcs_templates import VPCSTemplate from .controller.templates.vpcs_templates import VPCSTemplate, VPCSTemplateUpdate
from .controller.templates.cloud_templates import CloudTemplate from .controller.templates.cloud_templates import CloudTemplate, CloudTemplateUpdate
from .controller.templates.iou_templates import IOUTemplate from .controller.templates.iou_templates import IOUTemplate, IOUTemplateUpdate
from .controller.templates.docker_templates import DockerTemplate from .controller.templates.docker_templates import DockerTemplate, DockerTemplateUpdate
from .controller.templates.ethernet_hub_templates import EthernetHubTemplate from .controller.templates.ethernet_hub_templates import EthernetHubTemplate, EthernetHubTemplateUpdate
from .controller.templates.ethernet_switch_templates import EthernetSwitchTemplate from .controller.templates.ethernet_switch_templates import EthernetSwitchTemplate, EthernetSwitchTemplateUpdate
from .controller.templates.virtualbox_templates import VirtualBoxTemplate from .controller.templates.virtualbox_templates import VirtualBoxTemplate, VirtualBoxTemplateUpdate
from .controller.templates.vmware_templates import VMwareTemplate from .controller.templates.vmware_templates import VMwareTemplate, VMwareTemplateUpdate
from .controller.templates.qemu_templates import QemuTemplate from .controller.templates.qemu_templates import QemuTemplate, QemuTemplateUpdate
from .controller.templates.dynamips_templates import ( from .controller.templates.dynamips_templates import (
DynamipsTemplate, DynamipsTemplate,
C1700DynamipsTemplate, C1700DynamipsTemplate,
C1700DynamipsTemplateUpdate,
C2600DynamipsTemplate, C2600DynamipsTemplate,
C2600DynamipsTemplateUpdate,
C2691DynamipsTemplate, C2691DynamipsTemplate,
C2691DynamipsTemplateUpdate,
C3600DynamipsTemplate, C3600DynamipsTemplate,
C3600DynamipsTemplateUpdate,
C3725DynamipsTemplate, C3725DynamipsTemplate,
C3725DynamipsTemplateUpdate,
C3745DynamipsTemplate, C3745DynamipsTemplate,
C3745DynamipsTemplateUpdate,
C7200DynamipsTemplate, C7200DynamipsTemplate,
C7200DynamipsTemplateUpdate
) )
# Compute schemas # Compute schemas

View File

@ -50,9 +50,6 @@ class TemplateBase(BaseModel):
compute_id: Optional[str] = None compute_id: Optional[str] = None
usage: Optional[str] = "" usage: Optional[str] = ""
class Config:
extra = "allow"
class TemplateCreate(TemplateBase): class TemplateCreate(TemplateBase):
""" """
@ -63,10 +60,14 @@ class TemplateCreate(TemplateBase):
template_type: NodeType template_type: NodeType
compute_id: str compute_id: str
class Config:
extra = "allow"
class TemplateUpdate(TemplateBase): class TemplateUpdate(TemplateBase):
pass class Config:
extra = "allow"
class Template(DateTimeModelMixin, TemplateBase): class Template(DateTimeModelMixin, TemplateBase):
@ -80,6 +81,7 @@ class Template(DateTimeModelMixin, TemplateBase):
compute_id: Union[str, None] compute_id: Union[str, None]
class Config: class Config:
extra = "allow"
orm_mode = True orm_mode = True

View File

@ -37,3 +37,8 @@ class CloudTemplate(TemplateBase):
remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port") remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port")
remote_console_type: Optional[CloudConsoleType] = Field("none", description="Remote console type") remote_console_type: Optional[CloudConsoleType] = Field("none", description="Remote console type")
remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface") remote_console_http_path: Optional[str] = Field("/", description="Path of the remote web interface")
class CloudTemplateUpdate(CloudTemplate):
pass

View File

@ -51,3 +51,8 @@ class DockerTemplate(TemplateBase):
memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB") memory: Optional[int] = Field(0, description="Maximum amount of memory the container can use in MB")
cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use") cpus: Optional[int] = Field(0, description="Maximum amount of CPU resources the container can use")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class DockerTemplateUpdate(DockerTemplate):
image: Optional[str] = Field(None, description="Docker image name")

View File

@ -77,6 +77,12 @@ class C7200DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C7200DynamipsTemplateUpdate(C7200DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3725DynamipsTemplate(DynamipsTemplate): class C3725DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(128, description="Amount of RAM in MB") ram: Optional[int] = Field(128, description="Amount of RAM in MB")
@ -85,6 +91,12 @@ class C3725DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3725DynamipsTemplateUpdate(C3725DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3745DynamipsTemplate(DynamipsTemplate): class C3745DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(256, description="Amount of RAM in MB") ram: Optional[int] = Field(256, description="Amount of RAM in MB")
@ -93,6 +105,12 @@ class C3745DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3745DynamipsTemplateUpdate(C3745DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C3600ChassisType(str, Enum): class C3600ChassisType(str, Enum):
chassis_3620 = "3620" chassis_3620 = "3620"
@ -109,6 +127,12 @@ class C3600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C3600DynamipsTemplateUpdate(C3600DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C2691DynamipsTemplate(DynamipsTemplate): class C2691DynamipsTemplate(DynamipsTemplate):
ram: Optional[int] = Field(192, description="Amount of RAM in MB") ram: Optional[int] = Field(192, description="Amount of RAM in MB")
@ -117,6 +141,12 @@ class C2691DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2691DynamipsTemplateUpdate(C2691DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C2600ChassisType(str, Enum): class C2600ChassisType(str, Enum):
chassis_2610 = "2610" chassis_2610 = "2610"
@ -139,6 +169,12 @@ class C2600DynamipsTemplate(DynamipsTemplate):
sparsemem: Optional[bool] = Field(True, description="Sparse memory feature") sparsemem: Optional[bool] = Field(True, description="Sparse memory feature")
class C2600DynamipsTemplateUpdate(C2600DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")
class C1700ChassisType(str, Enum): class C1700ChassisType(str, Enum):
chassis_1720 = "1720" chassis_1720 = "1720"
@ -155,3 +191,9 @@ class C1700DynamipsTemplate(DynamipsTemplate):
nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB") nvram: Optional[int] = Field(128, description="Amount of NVRAM in KB")
iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage") iomem: Optional[int] = Field(15, ge=0, le=100, description="I/O memory percentage")
sparsemem: Optional[bool] = Field(False, description="Sparse memory feature") sparsemem: Optional[bool] = Field(False, description="Sparse memory feature")
class C1700DynamipsTemplateUpdate(C1700DynamipsTemplate):
platform: Optional[DynamipsPlatform] = Field(None, description="Cisco router platform")
image: Optional[str] = Field(None, description="Path to the IOS image")

View File

@ -39,3 +39,8 @@ class EthernetHubTemplate(TemplateBase):
default_name_format: Optional[str] = "Hub{0}" default_name_format: Optional[str] = "Hub{0}"
symbol: Optional[str] = ":/symbols/hub.svg" symbol: Optional[str] = ":/symbols/hub.svg"
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports") ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports")
class EthernetHubTemplateUpdate(EthernetHubTemplate):
pass

View File

@ -50,3 +50,8 @@ class EthernetSwitchTemplate(TemplateBase):
symbol: Optional[str] = ":/symbols/ethernet_switch.svg" symbol: Optional[str] = ":/symbols/ethernet_switch.svg"
ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports") ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports")
console_type: Optional[ConsoleType] = Field("none", description="Console type") console_type: Optional[ConsoleType] = Field("none", description="Console type")
class EthernetSwitchTemplateUpdate(EthernetSwitchTemplate):
pass

View File

@ -40,3 +40,8 @@ class IOUTemplate(TemplateBase):
console_auto_start: Optional[bool] = Field( console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started" False, description="Automatically start the console when the node has started"
) )
class IOUTemplateUpdate(IOUTemplate):
path: Optional[str] = Field(None, description="Path of IOU executable")

View File

@ -85,3 +85,8 @@ class QemuTemplate(TemplateBase):
process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU") process_priority: Optional[QemuProcessPriority] = Field("normal", description="Process priority for QEMU")
options: Optional[str] = Field("", description="Additional QEMU options") options: Optional[str] = Field("", description="Additional QEMU options")
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class QemuTemplateUpdate(QemuTemplate):
pass

View File

@ -58,3 +58,8 @@ class VirtualBoxTemplate(TemplateBase):
False, description="Automatically start the console when the node has started" False, description="Automatically start the console when the node has started"
) )
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class VirtualBoxTemplateUpdate(VirtualBoxTemplate):
vmname: Optional[str] = Field(None, description="VirtualBox VM name (in VirtualBox itself)")

View File

@ -54,3 +54,8 @@ class VMwareTemplate(TemplateBase):
False, description="Automatically start the console when the node has started" False, description="Automatically start the console when the node has started"
) )
custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters") custom_adapters: Optional[List[CustomAdapter]] = Field(default_factory=list, description="Custom adapters")
class VMwareTemplateUpdate(VMwareTemplate):
vmx_path: Optional[str] = Field(None, description="Path to the vmx file")

View File

@ -32,3 +32,8 @@ class VPCSTemplate(TemplateBase):
console_auto_start: Optional[bool] = Field( console_auto_start: Optional[bool] = Field(
False, description="Automatically start the console when the node has started" False, description="Automatically start the console when the node has started"
) )
class VPCSTemplateUpdate(VPCSTemplate):
pass

View File

@ -32,7 +32,7 @@ from gns3server.controller.controller_error import (
ControllerForbiddenError, ControllerForbiddenError,
) )
TEMPLATE_TYPE_TO_SHEMA = { TEMPLATE_TYPE_TO_SCHEMA = {
"cloud": schemas.CloudTemplate, "cloud": schemas.CloudTemplate,
"ethernet_hub": schemas.EthernetHubTemplate, "ethernet_hub": schemas.EthernetHubTemplate,
"ethernet_switch": schemas.EthernetSwitchTemplate, "ethernet_switch": schemas.EthernetSwitchTemplate,
@ -45,7 +45,19 @@ TEMPLATE_TYPE_TO_SHEMA = {
"qemu": schemas.QemuTemplate, "qemu": schemas.QemuTemplate,
} }
DYNAMIPS_PLATFORM_TO_SHEMA = { TEMPLATE_TYPE_TO_UPDATE_SCHEMA = {
"cloud": schemas.CloudTemplateUpdate,
"ethernet_hub": schemas.EthernetHubTemplateUpdate,
"ethernet_switch": schemas.EthernetSwitchTemplateUpdate,
"docker": schemas.DockerTemplateUpdate,
"vpcs": schemas.VPCSTemplateUpdate,
"virtualbox": schemas.VirtualBoxTemplateUpdate,
"vmware": schemas.VMwareTemplateUpdate,
"iou": schemas.IOUTemplateUpdate,
"qemu": schemas.QemuTemplateUpdate,
}
DYNAMIPS_PLATFORM_TO_SCHEMA = {
"c7200": schemas.C7200DynamipsTemplate, "c7200": schemas.C7200DynamipsTemplate,
"c3745": schemas.C3745DynamipsTemplate, "c3745": schemas.C3745DynamipsTemplate,
"c3725": schemas.C3725DynamipsTemplate, "c3725": schemas.C3725DynamipsTemplate,
@ -55,6 +67,16 @@ DYNAMIPS_PLATFORM_TO_SHEMA = {
"c1700": schemas.C1700DynamipsTemplate, "c1700": schemas.C1700DynamipsTemplate,
} }
DYNAMIPS_PLATFORM_TO_UPDATE_SCHEMA = {
"c7200": schemas.C7200DynamipsTemplateUpdate,
"c3745": schemas.C3745DynamipsTemplateUpdate,
"c3725": schemas.C3725DynamipsTemplateUpdate,
"c3600": schemas.C3600DynamipsTemplateUpdate,
"c2691": schemas.C2691DynamipsTemplateUpdate,
"c2600": schemas.C2600DynamipsTemplateUpdate,
"c1700": schemas.C1700DynamipsTemplateUpdate,
}
# built-in templates have their compute_id set to None to tell clients to select a compute # built-in templates have their compute_id set to None to tell clients to select a compute
BUILTIN_TEMPLATES = [ BUILTIN_TEMPLATES = [
{ {
@ -205,20 +227,18 @@ class TemplatesService:
try: try:
# get the default template settings # get the default template settings
template_settings = jsonable_encoder(template_create, exclude_unset=True) create_settings = jsonable_encoder(template_create, exclude_unset=True)
template_schema = TEMPLATE_TYPE_TO_SHEMA[template_create.template_type] template_schema = TEMPLATE_TYPE_TO_SCHEMA[template_create.template_type]
template_settings_with_defaults = template_schema.parse_obj(template_settings) template_settings = template_schema.parse_obj(create_settings).dict()
settings = template_settings_with_defaults.dict()
if template_create.template_type == "dynamips": if template_create.template_type == "dynamips":
# special case for Dynamips to cover all platform types that contain specific settings # special case for Dynamips to cover all platform types that contain specific settings
dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SHEMA[settings["platform"]] dynamips_template_schema = DYNAMIPS_PLATFORM_TO_SCHEMA[template_settings["platform"]]
dynamips_template_settings_with_defaults = dynamips_template_schema.parse_obj(template_settings) template_settings = dynamips_template_schema.parse_obj(create_settings).dict()
settings = dynamips_template_settings_with_defaults.dict()
except pydantic.ValidationError as e: except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while creating new template: {e}") raise ControllerBadRequestError(f"JSON schema error received while creating new template: {e}")
images_to_add_to_template = await self._find_images(template_create.template_type, settings) images_to_add_to_template = await self._find_images(template_create.template_type, template_settings)
db_template = await self._templates_repo.create_template(template_create.template_type, settings) db_template = await self._templates_repo.create_template(template_create.template_type, template_settings)
for image in images_to_add_to_template: for image in images_to_add_to_template:
await self._templates_repo.add_image_to_template(db_template.template_id, image) await self._templates_repo.add_image_to_template(db_template.template_id, image)
template = db_template.asjson() template = db_template.asjson()
@ -245,12 +265,22 @@ class TemplatesService:
if self.get_builtin_template(template_id): if self.get_builtin_template(template_id):
raise ControllerForbiddenError(f"Template '{template_id}' cannot be updated because it is built-in") raise ControllerForbiddenError(f"Template '{template_id}' cannot be updated because it is built-in")
template_settings = jsonable_encoder(template_update, exclude_unset=True)
db_template = await self._templates_repo.get_template(template_id) db_template = await self._templates_repo.get_template(template_id)
if not db_template: if not db_template:
raise ControllerNotFoundError(f"Template '{template_id}' not found") raise ControllerNotFoundError(f"Template '{template_id}' not found")
try:
# validate the update settings
update_settings = jsonable_encoder(template_update, exclude_unset=True)
if db_template.template_type == "dynamips":
template_schema = DYNAMIPS_PLATFORM_TO_UPDATE_SCHEMA[db_template.platform]
else:
template_schema = TEMPLATE_TYPE_TO_UPDATE_SCHEMA[db_template.template_type]
template_settings = template_schema.parse_obj(update_settings).dict(exclude_unset=True)
except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while updating template: {e}")
images_to_add_to_template = await self._find_images(db_template.template_type, template_settings) images_to_add_to_template = await self._find_images(db_template.template_type, template_settings)
if db_template.template_type == "dynamips" and "image" in template_settings: if db_template.template_type == "dynamips" and "image" in template_settings:
await self._remove_image(db_template.template_id, db_template.image) await self._remove_image(db_template.template_id, db_template.image)