diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index c50aad9f7..a992fe429 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -650,14 +650,14 @@ class Node: elif self._node_type in ("ethernet_switch", "ethernet_hub"): # Basic node we don't want to have adapter number port_number = 0 - for port in self._properties["ports_mapping"]: + for port in self._properties.get("ports_mapping", []): self._ports.append(PortFactory(port["name"], 0, 0, port_number, "ethernet", short_name="e{}".format(port_number))) port_number += 1 elif self._node_type in ("vpcs", "traceng"): self._ports.append(PortFactory("Ethernet0", 0, 0, 0, "ethernet", short_name="e0")) elif self._node_type in ("cloud", "nat"): port_number = 0 - for port in self._properties["ports_mapping"]: + for port in self._properties.get("ports_mapping", []): self._ports.append(PortFactory(port["name"], 0, 0, port_number, "ethernet", short_name=port["name"])) port_number += 1 else: diff --git a/gns3server/controller/project.py b/gns3server/controller/project.py index c8bbd45e2..ad058832f 100644 --- a/gns3server/controller/project.py +++ b/gns3server/controller/project.py @@ -503,7 +503,11 @@ class Project: template["x"] = x template["y"] = y node_type = template.pop("template_type") - compute = self.controller.get_compute(template.pop("compute_id", compute_id)) + if template["builtin"] is True: + # compute_id is selected by clients for builtin templates + compute = self.controller.get_compute(compute_id) + else: + compute = self.controller.get_compute(template.pop("compute_id", compute_id)) name = template.pop("name") default_name_format = template.pop("default_name_format", "{name}-{0}") name = default_name_format.replace("{name}", name) diff --git a/gns3server/controller/template.py b/gns3server/controller/template.py index 9d2b6bd84..968acd97b 100644 --- a/gns3server/controller/template.py +++ b/gns3server/controller/template.py @@ -203,7 +203,11 @@ class Template: settings.update({"template_id": self._id, "builtin": self.builtin}) - if not self.builtin: + if self.builtin: + # builin templates have compute_id set to None to tell clients + # to select a compute + settings["compute_id"] = None + else: settings["compute_id"] = self.compute_id return settings diff --git a/gns3server/controller/template_manager.py b/gns3server/controller/template_manager.py index 63239fded..ecf36a525 100644 --- a/gns3server/controller/template_manager.py +++ b/gns3server/controller/template_manager.py @@ -60,13 +60,13 @@ class TemplateManager: # Add builtins builtins = [] - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "cloud"), {"template_type": "cloud", "name": "Cloud", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "nat"), {"template_type": "nat", "name": "NAT", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "vpcs"), {"template_type": "vpcs", "name": "VPCS", "default_name_format": "PC-{0}", "category": 2, "symbol": ":/symbols/vpcs_guest.svg", "properties": {"base_script_file": "vpcs_base_config.txt"}}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_switch"), {"template_type": "ethernet_switch", "console_type": "none", "name": "Ethernet switch", "category": 1, "symbol": ":/symbols/ethernet_switch.svg"}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_hub"), {"template_type": "ethernet_hub", "name": "Ethernet hub", "category": 1, "symbol": ":/symbols/hub.svg"}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "frame_relay_switch"), {"template_type": "frame_relay_switch", "name": "Frame Relay switch", "category": 1, "symbol": ":/symbols/frame_relay_switch.svg"}, builtin=True)) - builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "atm_switch"), {"template_type": "atm_switch", "name": "ATM switch", "category": 1, "symbol": ":/symbols/atm_switch.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "cloud"), {"template_type": "cloud", "name": "Cloud", "default_name_format": "Cloud{0}", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "nat"), {"template_type": "nat", "name": "NAT", "default_name_format": "NAT{0}", "category": 2, "symbol": ":/symbols/cloud.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "vpcs"), {"template_type": "vpcs", "name": "VPCS", "default_name_format": "PC{0}", "category": 2, "symbol": ":/symbols/vpcs_guest.svg", "properties": {"base_script_file": "vpcs_base_config.txt"}}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_switch"), {"template_type": "ethernet_switch", "console_type": "none", "name": "Ethernet switch", "default_name_format": "Switch{0}", "category": 1, "symbol": ":/symbols/ethernet_switch.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "ethernet_hub"), {"template_type": "ethernet_hub", "name": "Ethernet hub", "default_name_format": "Hub{0}", "category": 1, "symbol": ":/symbols/hub.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "frame_relay_switch"), {"template_type": "frame_relay_switch", "name": "Frame Relay switch", "default_name_format": "FRSW{0}", "category": 1, "symbol": ":/symbols/frame_relay_switch.svg"}, builtin=True)) + builtins.append(Template(uuid.uuid3(uuid.NAMESPACE_DNS, "atm_switch"), {"template_type": "atm_switch", "name": "ATM switch", "default_name_format": "ATMSW{0}", "category": 1, "symbol": ":/symbols/atm_switch.svg"}, builtin=True)) #FIXME: disable TraceNG #if sys.platform.startswith("win"): diff --git a/gns3server/schemas/template.py b/gns3server/schemas/template.py index 2914911f6..e9d4cc1e1 100644 --- a/gns3server/schemas/template.py +++ b/gns3server/schemas/template.py @@ -37,7 +37,7 @@ BASE_TEMPLATE_PROPERTIES = { }, "compute_id": { "description": "Compute identifier", - "type": "string" + "type": ["null", "string"] }, "default_name_format": { "description": "Default name format", diff --git a/tests/controller/test_project.py b/tests/controller/test_project.py index 5fcee71ea..4daca8a39 100644 --- a/tests/controller/test_project.py +++ b/tests/controller/test_project.py @@ -236,6 +236,37 @@ def test_add_node_from_template(async_run, controller): project.emit_notification.assert_any_call("node.created", node.__json__()) +def test_add_builtin_node_from_template(async_run, controller): + """ + For a local server we send the project path + """ + compute = MagicMock() + compute.id = "local" + project = Project(controller=controller, name="Test") + project.emit_notification = MagicMock() + template = Template(str(uuid.uuid4()), { + "name": "Builtin-switch", + "template_type": "ethernet_switch", + }, builtin=True) + controller.template_manager.templates[template.id] = template + template.__json__() + controller._computes["local"] = compute + + response = MagicMock() + response.json = {"console": 2048} + compute.post = AsyncioMagicMock(return_value=response) + + node = async_run(project.add_node_from_template(template.id, x=23, y=12, compute_id="local")) + compute.post.assert_any_call('/projects', data={ + "name": project._name, + "project_id": project._id, + "path": project._path + }) + + assert compute in project._project_created_on_compute + project.emit_notification.assert_any_call("node.created", node.__json__()) + + def test_delete_node(async_run, controller): """ For a local server we send the project path