Skip to content

Commit

Permalink
Merge pull request #2091 from GNS3/use-themed-symbols
Browse files Browse the repository at this point in the history
Let the controller allocate symbols
  • Loading branch information
grossmj authored Jul 25, 2022
2 parents b7f4a4c + 6d0c375 commit bd9af3f
Show file tree
Hide file tree
Showing 19 changed files with 96 additions and 48 deletions.
5 changes: 5 additions & 0 deletions conf/gns3_server.conf
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,11 @@ symbols_path = /home/gns3/GNS3/symbols
; Path where custom configs are stored
configs_path = /home/gns3/GNS3/configs

; Default symbol theme
; Currently available themes are "Classic", Affinity-square-blue", "Affinity-square-red"
; "Affinity-square-gray", "Affinity-circle-blue", "Affinity-circle-red" and "Affinity-circle-gray"
default_symbol_theme = Affinity-square-blue

; Option to automatically send crash reports to the GNS3 team
report_errors = True

Expand Down
4 changes: 2 additions & 2 deletions gns3server/api/routes/controller/appliances.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@
@router.get("")
async def get_appliances(
update: Optional[bool] = False,
symbol_theme: Optional[str] = "Classic"
symbol_theme: Optional[str] = None
) -> List[schemas.Appliance]:
"""
Return all appliances known by the controller.
Expand All @@ -56,7 +56,7 @@ async def get_appliances(
controller = Controller.instance()
if update:
await controller.appliance_manager.download_appliances()
controller.appliance_manager.load_appliances(symbol_theme=symbol_theme)
controller.appliance_manager.load_appliances(symbol_theme)
return [c.asdict() for c in controller.appliance_manager.appliances.values()]


Expand Down
4 changes: 3 additions & 1 deletion gns3server/controller/appliance_manager.py
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ async def install_appliance(
template_data = await self._appliance_to_template(appliance)
await self._create_template(template_data, templates_repo, rbac_repo, current_user)

def load_appliances(self, symbol_theme: str = "Classic") -> None:
def load_appliances(self, symbol_theme: str = None) -> None:
"""
Loads appliance files from disk.
"""
Expand Down Expand Up @@ -326,6 +326,8 @@ def _get_default_symbol(self, appliance: dict, symbol_theme: str) -> str:
from . import Controller

controller = Controller.instance()
if not symbol_theme:
symbol_theme = controller.symbols.theme
category = appliance["category"]
if category == "guest":
if "docker" in appliance:
Expand Down
16 changes: 8 additions & 8 deletions gns3server/controller/symbol_themes.py
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@
CLASSIC_SYMBOL_THEME = {
"cloud": ":/symbols/classic/cloud.svg",
"ethernet_switch": ":/symbols/classic/ethernet_switch.svg",
"ethernet_hub": ":/symbols/classic/hub.svg",
"hub": ":/symbols/classic/hub.svg",
"frame_relay_switch": ":/symbols/classic/frame_relay_switch.svg",
"atm_switch": ":/symbols/classic/atm_switch.svg",
"router": ":/symbols/classic/router.svg",
Expand All @@ -36,8 +36,8 @@
AFFINITY_SQUARE_BLUE_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/square/blue/cloud.svg",
"ethernet_switch": ":/symbols/affinity/square/blue/switch.svg",
"ethernet_hub": ":/symbols/affinity/square/blue/hub.svg",
"frame_relay_switch.svg": ":/symbols/affinity/square/blue/isdn.svg",
"hub": ":/symbols/affinity/square/blue/hub.svg",
"frame_relay_switch": ":/symbols/affinity/square/blue/isdn.svg",
"atm_switch": ":/symbols/affinity/square/blue/atm.svg",
"router": ":/symbols/affinity/square/blue/router.svg",
"multilayer_switch": ":/symbols/affinity/square/blue/switch_multilayer.svg",
Expand All @@ -53,7 +53,7 @@
AFFINITY_SQUARE_RED_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/square/red/cloud.svg",
"ethernet_switch": ":/symbols/affinity/square/red/switch.svg",
"ethernet_hub": ":/symbols/affinity/square/red/hub.svg",
"hub": ":/symbols/affinity/square/red/hub.svg",
"frame_relay_switch": ":/symbols/affinity/square/red/isdn.svg",
"atm_switch": ":/symbols/affinity/square/red/atm.svg",
"router": ":/symbols/affinity/square/red/router.svg",
Expand All @@ -70,7 +70,7 @@
AFFINITY_SQUARE_GRAY_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/square/gray/cloud.svg",
"ethernet_switch": ":/symbols/affinity/square/gray/switch.svg",
"ethernet_hub": ":/symbols/affinity/square/gray/hub.svg",
"hub": ":/symbols/affinity/square/gray/hub.svg",
"frame_relay_switch": ":/symbols/affinity/square/gray/isdn.svg",
"atm_switch": ":/symbols/affinity/square/gray/atm.svg",
"router": ":/symbols/affinity/square/gray/router.svg",
Expand All @@ -87,7 +87,7 @@
AFFINITY_CIRCLE_BLUE_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/circle/blue/cloud.svg",
"ethernet_switch": ":/symbols/affinity/circle/blue/switch.svg",
"ethernet_hub": ":/symbols/affinity/circle/blue/hub.svg",
"hub": ":/symbols/affinity/circle/blue/hub.svg",
"frame_relay_switch": ":/symbols/affinity/circle/blue/isdn.svg",
"atm_switch": ":/symbols/affinity/circle/blue/atm.svg",
"router": ":/symbols/affinity/circle/blue/router.svg",
Expand All @@ -104,7 +104,7 @@
AFFINITY_CIRCLE_RED_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/circle/red/cloud.svg",
"ethernet_switch": ":/symbols/affinity/circle/red/switch.svg",
"ethernet_hub": ":/symbols/affinity/circle/red/hub.svg",
"hub": ":/symbols/affinity/circle/red/hub.svg",
"frame_relay_switch": ":/symbols/affinity/circle/red/isdn.svg",
"atm_switch": ":/symbols/affinity/circle/red/atm.svg",
"router": ":/symbols/affinity/circle/red/router.svg",
Expand All @@ -121,7 +121,7 @@
AFFINITY_CIRCLE_GRAY_SYMBOL_THEME = {
"cloud": ":/symbols/affinity/circle/gray/cloud.svg",
"ethernet_switch": ":/symbols/affinity/circle/gray/switch.svg",
"ethernet_hub": ":/symbols/affinity/circle/gray/hub.svg",
"hub": ":/symbols/affinity/circle/gray/hub.svg",
"frame_relay_switch": ":/symbols/affinity/circle/gray/isdn.svg",
"atm_switch": ":/symbols/affinity/circle/gray/atm.svg",
"router": ":/symbols/affinity/circle/gray/router.svg",
Expand Down
19 changes: 16 additions & 3 deletions gns3server/controller/symbols.py
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,9 @@ def __init__(self):

# Keep a cache of symbols size
self._symbol_size_cache = {}
self._current_theme = "Classic"

self._server_config = Config.instance().settings.Server
self._current_theme = self._server_config.default_symbol_theme
self._themes = BUILTIN_SYMBOL_THEMES

@property
Expand All @@ -66,10 +68,11 @@ def get_default_symbol(self, symbol, symbol_theme):

theme = self._themes.get(symbol_theme, None)
if not theme:
raise ControllerNotFoundError(f"Could not find symbol theme '{symbol_theme}'")
log.warning(f"Could not find symbol theme '{symbol_theme}'")
return None
symbol_path = theme.get(symbol)
if symbol_path not in self._symbols_path:
log.warning(f"Default symbol {symbol_path} was not found")
log.warning(f"Default symbol {symbol} was not found")
return None
return symbol_path

Expand Down Expand Up @@ -125,7 +128,17 @@ def has_symbol(self, symbol_id):

return self._symbols_path.get(symbol_id)

def resolve_symbol(self, symbol_name):

if not symbol_name.startswith(":/"):
symbol = self.get_default_symbol(symbol_name, self._current_theme)
if symbol:
return symbol
return symbol_name

def get_path(self, symbol_id):

symbol_id = self.resolve_symbol(symbol_id)
try:
return self._symbols_path[symbol_id]
except KeyError:
Expand Down
12 changes: 12 additions & 0 deletions gns3server/schemas/config.py
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,17 @@ class ServerProtocol(str, Enum):
https = "https"


class BuiltinSymbolTheme(str, Enum):

classic = "Classic"
affinity_square_blue = "Affinity-square-blue"
affinity_square_red = "Affinity-square-red"
affinity_square_gray = "Affinity-square-gray"
affinity_circle_blue = "Affinity-circle-blue"
affinity_circle_red = "Affinity-circle-red"
affinity_circle_gray = "Affinity-circle-gray"


class ServerSettings(BaseModel):

local: bool = False
Expand All @@ -124,6 +135,7 @@ class ServerSettings(BaseModel):
appliances_path: str = "~/GNS3/appliances"
symbols_path: str = "~/GNS3/symbols"
configs_path: str = "~/GNS3/configs"
default_symbol_theme: BuiltinSymbolTheme = BuiltinSymbolTheme.affinity_square_blue
report_errors: bool = True
additional_images_paths: List[str] = Field(default_factory=list)
console_start_port_range: int = Field(5000, gt=0, le=65535)
Expand Down
2 changes: 1 addition & 1 deletion gns3server/schemas/controller/templates/cloud_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class CloudTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "Cloud{0}"
symbol: Optional[str] = ":/symbols/cloud.svg"
symbol: Optional[str] = "cloud"
ports_mapping: List[Union[EthernetPort, TAPPort, UDPPort]] = Field(default_factory=list)
remote_console_host: Optional[str] = Field("127.0.0.1", description="Remote console host or IP")
remote_console_port: Optional[int] = Field(23, gt=0, le=65535, description="Remote console TCP port")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class DockerTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/docker_guest.svg"
symbol: Optional[str] = "docker_guest"
image: str = Field(..., description="Docker image name")
adapters: Optional[int] = Field(1, ge=0, le=100, description="Number of adapters")
start_command: Optional[str] = Field("", description="Docker CMD entry")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ class DynamipsTemplate(TemplateBase):

category: Optional[Category] = "router"
default_name_format: Optional[str] = "R{0}"
symbol: Optional[str] = ":/symbols/router.svg"
symbol: Optional[str] = "router"
platform: DynamipsPlatform = Field(..., description="Cisco router platform")
image: str = Field(..., description="Path to the IOS image")
exec_area: Optional[int] = Field(64, description="Exec area value")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class EthernetHubTemplate(TemplateBase):

category: Optional[Category] = "switch"
default_name_format: Optional[str] = "Hub{0}"
symbol: Optional[str] = ":/symbols/hub.svg"
symbol: Optional[str] = "hub"
ports_mapping: Optional[List[EthernetHubPort]] = Field(DEFAULT_PORTS, description="Ports")


Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ class EthernetSwitchTemplate(TemplateBase):

category: Optional[Category] = "switch"
default_name_format: Optional[str] = "Switch{0}"
symbol: Optional[str] = ":/symbols/ethernet_switch.svg"
symbol: Optional[str] = "ethernet_switch"
ports_mapping: Optional[List[EthernetSwitchPort]] = Field(DEFAULT_PORTS, description="Ports")
console_type: Optional[ConsoleType] = Field("none", description="Console type")

Expand Down
2 changes: 1 addition & 1 deletion gns3server/schemas/controller/templates/iou_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class IOUTemplate(TemplateBase):

category: Optional[Category] = "router"
default_name_format: Optional[str] = "IOU{0}"
symbol: Optional[str] = ":/symbols/multilayer_switch.svg"
symbol: Optional[str] = "multilayer_switch"
path: str = Field(..., description="Path of IOU executable")
ethernet_adapters: Optional[int] = Field(2, description="Number of ethernet adapters")
serial_adapters: Optional[int] = Field(2, description="Number of serial adapters")
Expand Down
2 changes: 1 addition & 1 deletion gns3server/schemas/controller/templates/qemu_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,7 @@ class QemuTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/qemu_guest.svg"
symbol: Optional[str] = "qemu_guest"
qemu_path: Optional[str] = Field("", description="Qemu executable path")
platform: Optional[QemuPlatform] = Field("x86_64", description="Platform to emulate")
linked_clone: Optional[bool] = Field(True, description="Whether the VM is a linked clone or not")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ class VirtualBoxTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/vbox_guest.svg"
symbol: Optional[str] = "vbox_guest"
vmname: str = Field(..., description="VirtualBox VM name (in VirtualBox itself)")
ram: Optional[int] = Field(256, gt=0, description="Amount of RAM in MB")
linked_clone: Optional[bool] = Field(False, description="Whether the VM is a linked clone or not")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ class VMwareTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "{name}-{0}"
symbol: Optional[str] = ":/symbols/vmware_guest.svg"
symbol: Optional[str] = "vmware_guest"
vmx_path: str = Field(..., description="Path to the vmx file")
linked_clone: Optional[bool] = Field(False, description="Whether the VM is a linked clone or not")
first_port_name: Optional[str] = Field("", description="Optional name of the first networking port example: eth0")
Expand Down
2 changes: 1 addition & 1 deletion gns3server/schemas/controller/templates/vpcs_templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ class VPCSTemplate(TemplateBase):

category: Optional[Category] = "guest"
default_name_format: Optional[str] = "PC{0}"
symbol: Optional[str] = ":/symbols/vpcs_guest.svg"
symbol: Optional[str] = "vpcs_guest"
base_script_file: Optional[str] = Field("vpcs_base_config.txt", description="Script file")
console_type: Optional[ConsoleType] = Field("telnet", description="Console type")
console_auto_start: Optional[bool] = Field(
Expand Down
20 changes: 13 additions & 7 deletions gns3server/services/templates.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,7 +86,7 @@
"name": "Cloud",
"default_name_format": "Cloud{0}",
"category": "guest",
"symbol": ":/symbols/cloud.svg",
"symbol": "cloud",
"compute_id": None,
"builtin": True,
},
Expand All @@ -96,7 +96,7 @@
"name": "NAT",
"default_name_format": "NAT{0}",
"category": "guest",
"symbol": ":/symbols/cloud.svg",
"symbol": "cloud",
"compute_id": None,
"builtin": True,
},
Expand All @@ -106,7 +106,7 @@
"name": "VPCS",
"default_name_format": "PC{0}",
"category": "guest",
"symbol": ":/symbols/vpcs_guest.svg",
"symbol": "vpcs_guest",
"base_script_file": "vpcs_base_config.txt",
"compute_id": None,
"builtin": True,
Expand All @@ -118,7 +118,7 @@
"console_type": "none",
"default_name_format": "Switch{0}",
"category": "switch",
"symbol": ":/symbols/ethernet_switch.svg",
"symbol": "ethernet_switch",
"compute_id": None,
"builtin": True,
},
Expand All @@ -128,7 +128,7 @@
"name": "Ethernet hub",
"default_name_format": "Hub{0}",
"category": "switch",
"symbol": ":/symbols/hub.svg",
"symbol": "hub",
"compute_id": None,
"builtin": True,
},
Expand All @@ -138,7 +138,7 @@
"name": "Frame Relay switch",
"default_name_format": "FRSW{0}",
"category": "switch",
"symbol": ":/symbols/frame_relay_switch.svg",
"symbol": "frame_relay_switch",
"compute_id": None,
"builtin": True,
},
Expand All @@ -148,7 +148,7 @@
"name": "ATM switch",
"default_name_format": "ATMSW{0}",
"category": "switch",
"symbol": ":/symbols/atm_switch.svg",
"symbol": "atm_switch",
"compute_id": None,
"builtin": True,
},
Expand All @@ -163,6 +163,10 @@ def __init__(self, templates_repo: TemplatesRepository):
from gns3server.controller import Controller
self._controller = Controller.instance()

# resolve built-in template symbols
for builtin_template in BUILTIN_TEMPLATES:
builtin_template["symbol"] = self._controller.symbols.resolve_symbol(builtin_template["symbol"])

def get_builtin_template(self, template_id: UUID) -> dict:

for builtin_template in BUILTIN_TEMPLATES:
Expand Down Expand Up @@ -241,6 +245,8 @@ async def create_template(self, template_create: schemas.TemplateCreate) -> dict
except pydantic.ValidationError as e:
raise ControllerBadRequestError(f"JSON schema error received while creating new template: {e}")

# resolve the template symbol
template_settings["symbol"] = self._controller.symbols.resolve_symbol(template_settings["symbol"])
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, template_settings)
for image in images_to_add_to_template:
Expand Down
Loading

0 comments on commit bd9af3f

Please sign in to comment.