diff --git a/gns3/appliance_manager.py b/gns3/appliance_manager.py
index a6b42f93..26179bf3 100644
--- a/gns3/appliance_manager.py
+++ b/gns3/appliance_manager.py
@@ -15,37 +15,100 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see .
+from .qt import QtCore
from .controller import Controller
-
+from .utils.server_select import server_select
import logging
log = logging.getLogger(__name__)
-class ApplianceManager:
+class ApplianceManager(QtCore.QObject):
+
+ appliances_changed_signal = QtCore.Signal()
def __init__(self):
+ super().__init__()
self._appliance_templates = []
+ self._appliances = []
self._controller = Controller.instance()
- self._controller.connected_signal.connect(self._controllerConnectedSlot)
+ self._controller.connected_signal.connect(self.refresh)
self._controller.disconnected_signal.connect(self._controllerDisconnectedSlot)
- self._controllerConnectedSlot()
+ self.refresh()
- def _controllerConnectedSlot(self):
+ def refresh(self):
if self._controller.connected():
self._controller.get("/appliances/templates", self._listApplianceTemplateCallback)
+ self._controller.get("/appliances", self._listAppliancesCallback)
def _controllerDisconnectedSlot(self):
self._appliance_templates = []
+ self._appliances = []
+ self.appliances_changed_signal.emit()
def appliance_templates(self):
return self._appliance_templates
+ def appliances(self):
+ return self._appliances
+
+ def getAppliance(self, appliance_id):
+ """
+ Look for an appliance by appliance ID
+ """
+ for appliance in self._appliances:
+ if appliance["appliance_id"] == appliance_id:
+ return appliance
+ return None
+
+ def _listAppliancesCallback(self, result, error=False, **kwargs):
+ if error is True:
+ log.error("Error while getting appliances list: {}".format(result["message"]))
+ return
+ self._appliances = result
+ self.appliances_changed_signal.emit()
+
def _listApplianceTemplateCallback(self, result, error=False, **kwargs):
+ if error is True:
+ log.error("Error while getting appliance templates list: {}".format(result["message"]))
+ return
+ self._appliance_templates = result
+ self.appliances_changed_signal.emit()
+
+ def createNodeFromApplianceId(self, project, appliance_id, x, y):
+ for appliance in self._appliances:
+ if appliance["appliance_id"] == appliance_id:
+ break
+ if appliance.get("compute_id") is None:
+ server = server_select(None, node_type=appliance["node_type"])
+ self._controller.post("/projects/" + project.id() + "/appliances/" + appliance_id, self._createNodeFromApplianceCallback, {
+ "compute_id": server.id(),
+ "x": int(x),
+ "y": int(y)
+ })
+ else:
+ self._controller.post("/projects/" + project.id() + "/appliances/" + appliance_id, self._createNodeFromApplianceCallback, {
+ "x": int(x),
+ "y": int(y)
+ })
+
+ def _createNodeFromApplianceCallback(self, result, error=False, **kwargs):
+ if error:
+ if "message" in result:
+ log.error("Error while creating node: {}".format(result["message"]))
+ return
+
+ def _controllerDisconnectedSlot(self):
+ self._appliances = []
+
+ def appliances(self):
+ return self._appliances
+
+ def _listAppliancesCallback(self, result, error=False, **kwargs):
if error is True:
log.error("Error while getting appliance list: {}".format(result["message"]))
return
- self._appliance_templates = result
+ self._appliances = result
@staticmethod
def instance():
diff --git a/gns3/graphics_view.py b/gns3/graphics_view.py
index ab185298..46f37d0e 100644
--- a/gns3/graphics_view.py
+++ b/gns3/graphics_view.py
@@ -24,17 +24,16 @@ import os
import sip
import pickle
-from .qt import QtCore, QtGui, QtSvg, QtNetwork, QtWidgets, qpartial, qslot
+from .qt import QtCore, QtGui, QtNetwork, QtWidgets, qpartial, qslot
from .items.node_item import NodeItem
from .dialogs.node_properties_dialog import NodePropertiesDialog
from .link import Link
from .node import Node
from .modules import MODULES
-from .modules.builtin.cloud import Cloud
from .modules.module_error import ModuleError
from .settings import GRAPHICS_VIEW_SETTINGS
from .topology import Topology
-from .ports.port import Port
+from .appliance_manager import ApplianceManager
from .dialogs.style_editor_dialog import StyleEditorDialog
from .dialogs.text_editor_dialog import TextEditorDialog
from .dialogs.symbol_selection_dialog import SymbolSelectionDialog
@@ -44,7 +43,6 @@ from .dialogs.file_editor_dialog import FileEditorDialog
from .local_config import LocalConfig
from .progress import Progress
from .utils.server_select import server_select
-from .utils.normalize_filename import normalize_filename
from .compute_manager import ComputeManager
# link items
@@ -613,7 +611,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
"""
# check if what is dragged is handled by this view
- if event.mimeData().hasFormat("application/x-gns3-node") or event.mimeData().hasFormat("text/uri-list"):
+ if event.mimeData().hasFormat("text/uri-list") \
+ or event.mimeData().hasFormat("application/x-gns3-appliance"):
event.acceptProposedAction()
event.accept()
else:
@@ -627,10 +626,8 @@ class GraphicsView(QtWidgets.QGraphicsView):
"""
# check if what has been dropped is handled by this view
- if event.mimeData().hasFormat("application/x-gns3-node"):
- data = event.mimeData().data("application/x-gns3-node")
- # load the pickled node data
- node_data = pickle.loads(data)
+ if event.mimeData().hasFormat("application/x-gns3-appliance"):
+ appliance_id = event.mimeData().data("application/x-gns3-appliance").data().decode()
event.setDropAction(QtCore.Qt.CopyAction)
event.accept()
if event.keyboardModifiers() == QtCore.Qt.ShiftModifier:
@@ -641,12 +638,9 @@ class GraphicsView(QtWidgets.QGraphicsView):
for node_number in range(integer):
x = event.pos().x() - (150 / 2) + (node_number % max_nodes_per_line) * offset
y = event.pos().y() - (70 / 2) + (node_number // max_nodes_per_line) * offset
- node_item = self.createNode(node_data, QtCore.QPoint(x, y))
- if node_item is None:
- # stop if there is any error
- break
+ self.createNodeFromApplianceId(appliance_id, QtCore.QPoint(x, y))
else:
- self.createNode(node_data, event.pos())
+ self.createNodeFromApplianceId(appliance_id, event.pos())
elif event.mimeData().hasFormat("text/uri-list") and event.mimeData().hasUrls():
# This should not arrive but we received bug report with it...
if len(event.mimeData().urls()) == 0:
@@ -1434,38 +1428,12 @@ class GraphicsView(QtWidgets.QGraphicsView):
raise ModuleError("Please select a server")
return server
- def createNode(self, node_data, pos):
+ def createNodeFromApplianceId(self, appliance_id, pos):
"""
- Creates a new node on the scene.
-
- :param node_data: node data to create a new node
- :param pos: position of the drop event
-
- :returns: NodeItem instance
+ Ask the server to create a node using this appliance
"""
- try:
- node_module = None
- for module in MODULES:
- instance = module.instance()
- node_class = module.getNodeClass(node_data["class"])
- if node_class in instance.classes():
- node_module = instance
- break
-
- if not node_module:
- raise ModuleError("Could not find any module for {}".format(node_class))
-
- node = node_module.instantiateNode(node_class, self.allocateCompute(node_data, instance), self._topology.project())
- # If no server is available a ValueError is raised
- except (ModuleError, ValueError) as e:
- QtWidgets.QMessageBox.critical(self, "Node creation", "{}".format(e))
- return
-
pos = self.mapToScene(pos)
- node_item = self.createNodeItem(node, node_data["symbol"], pos.x(), pos.y())
- node.setGraphics(node_item)
- node_module.createNode(node, node_data["name"])
- return node_item
+ ApplianceManager().instance().createNodeFromApplianceId(self._topology.project(), appliance_id, pos.x(), pos.y())
def createNodeItem(self, node, symbol, x, y):
node.setSymbol(symbol)
diff --git a/gns3/modules/builtin/__init__.py b/gns3/modules/builtin/__init__.py
index 2f690dea..4e87acb8 100644
--- a/gns3/modules/builtin/__init__.py
+++ b/gns3/modules/builtin/__init__.py
@@ -33,7 +33,6 @@ from .atm_switch import ATMSwitch
from .settings import (
BUILTIN_SETTINGS,
CLOUD_SETTINGS,
- NAT_SETTINGS,
ETHERNET_HUB_SETTINGS,
ETHERNET_SWITCH_SETTINGS
)
@@ -227,40 +226,6 @@ class Builtin(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- if isinstance(node, Cloud):
- for key, info in self._cloud_nodes.items():
- if node_name == info["name"]:
- default_name_format = info["default_name_format"].replace('{name}', node_name)
- node.create(ports=info["ports_mapping"], default_name_format=default_name_format)
- return
- elif isinstance(node, Nat):
- for key, info in self._nat_nodes.items():
- if node_name == info["name"]:
- default_name_format = info["default_name_format"].replace('{name}', node_name)
- node.create(default_name_format=default_name_format)
- return
- elif isinstance(node, EthernetHub):
- for key, info in self._ethernet_hubs.items():
- if node_name == info["name"]:
- default_name_format = info["default_name_format"].replace('{name}', node_name)
- node.create(ports=info["ports_mapping"], default_name_format=default_name_format)
- return
- elif isinstance(node, EthernetSwitch):
- for key, info in self._ethernet_switches.items():
- if node_name == info["name"]:
- default_name_format = info["default_name_format"].replace('{name}', node_name)
- node.create(ports=info["ports_mapping"], default_name_format=default_name_format)
- return
- node.create()
-
@staticmethod
def findAlternativeInterface(node, missing_interface):
@@ -328,17 +293,6 @@ class Builtin(Module):
"""
nodes = []
- for node_class in Builtin.classes():
- nodes.append(
- {"class": node_class.__name__,
- "name": node_class.symbolName(),
- "categories": node_class.categories(),
- "symbol": node_class.defaultSymbol(),
- "builtin": True,
- "node_type": node_class.URL_PREFIX
- }
- )
-
# add custom cloud node templates
for cloud_node in self._cloud_nodes.values():
nodes.append(
@@ -369,9 +323,8 @@ class Builtin(Module):
"server": switch["server"],
"symbol": switch["symbol"],
"categories": [switch["category"]]
- }
+ }
)
-
return nodes
@staticmethod
diff --git a/gns3/modules/docker/__init__.py b/gns3/modules/docker/__init__.py
index 28f92702..ddba2ff8 100644
--- a/gns3/modules/docker/__init__.py
+++ b/gns3/modules/docker/__init__.py
@@ -140,55 +140,6 @@ class Docker(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- image = None
- if node_name:
- for image_key, info in self._docker_containers.items():
- if node_name == info["name"]:
- image = image_key
- if not image:
- selected_images = []
- for image, info in self._docker_containers.items():
- if info["server"] == node.compute().id():
- selected_images.append(image)
-
- if not selected_images:
- raise ModuleError("No Docker VM on server {}".format(
- node.server().url()))
- elif len(selected_images) > 1:
- from gns3.main_window import MainWindow
- mainwindow = MainWindow.instance()
-
- (selection, ok) = QtWidgets.QInputDialog.getItem(
- mainwindow, "Docker Image", "Please choose an image",
- selected_images, 0, False)
- if ok:
- image = selection
- else:
- raise ModuleError("Please select a Docker Image")
- else:
- image = selected_images[0]
-
- image_settings = {}
- for setting_name, value in self._docker_containers[image].items():
- if setting_name in node.settings() and value != "" and value is not None:
- if setting_name not in ['name', 'image']:
- image_settings[setting_name] = value
-
- default_name_format = DOCKER_CONTAINER_SETTINGS["default_name_format"]
- if self._docker_containers[image]["default_name_format"]:
- default_name_format = self._docker_containers[image]["default_name_format"]
-
- image = self._docker_containers[image]["image"]
- node.create(image, base_name=node_name, additional_settings=image_settings, default_name_format=default_name_format)
-
def reset(self):
"""Resets the servers."""
self._nodes.clear()
diff --git a/gns3/modules/dynamips/__init__.py b/gns3/modules/dynamips/__init__.py
index d6f547e9..c24a14e0 100644
--- a/gns3/modules/dynamips/__init__.py
+++ b/gns3/modules/dynamips/__init__.py
@@ -248,50 +248,6 @@ class Dynamips(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- if isinstance(node, Router):
- ios_router = None
- if node_name:
- for ios_key, info in self._ios_routers.items():
- if node_name == info["name"]:
- ios_router = self._ios_routers[ios_key]
- break
-
- if not ios_router:
- raise ModuleError("No IOS router for platform {}".format(node.settings()["platform"]))
-
- vm_settings = {}
- for setting_name, value in ios_router.items():
- if setting_name in node.settings() and setting_name != "name" and value != "" and value is not None:
- vm_settings[setting_name] = value
-
- default_name_format = IOS_ROUTER_SETTINGS["default_name_format"]
- if ios_router["default_name_format"]:
- default_name_format = ios_router["default_name_format"]
-
- # Older GNS3 versions may have the following invalid settings in the VM template
- if "console" in vm_settings:
- del vm_settings["console"]
- if "sensors" in vm_settings:
- del vm_settings["sensors"]
- if "power_supplies" in vm_settings:
- del vm_settings["power_supplies"]
-
- ram = vm_settings.pop("ram")
- image = vm_settings.pop("image", None)
- if image is None:
- raise ModuleError("No IOS image has been associated with this IOS router")
- node.create(image, ram, additional_settings=vm_settings, default_name_format=default_name_format)
- else:
- node.create()
-
def updateImageIdlepc(self, image_path, idlepc):
"""
Updates the Idle-PC for an IOS image.
diff --git a/gns3/modules/iou/__init__.py b/gns3/modules/iou/__init__.py
index bd901e62..c36c50fa 100644
--- a/gns3/modules/iou/__init__.py
+++ b/gns3/modules/iou/__init__.py
@@ -177,62 +177,6 @@ class IOU(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- iouimage = None
- if node_name:
- for iou_key, info in self._iou_devices.items():
- if node_name == info["name"]:
- iouimage = iou_key
-
- if not iouimage:
- selected_images = []
- for image, info in self._iou_devices.items():
- if info["server"] == node.compute().id():
- selected_images.append(image)
-
- if not selected_images:
- raise ModuleError("No IOU image found for this device")
- elif len(selected_images) > 1:
-
- from gns3.main_window import MainWindow
- mainwindow = MainWindow.instance()
-
- (selection, ok) = QtWidgets.QInputDialog.getItem(mainwindow, "IOU image", "Please choose an image", selected_images, 0, False)
- if ok:
- iouimage = selection
- else:
- raise ModuleError("Please select an IOU image")
-
- else:
- iouimage = selected_images[0]
-
- vm_settings = {}
- for setting_name, value in self._iou_devices[iouimage].items():
- if setting_name in node.settings() and setting_name != "name" and value != "" and value is not None:
- vm_settings[setting_name] = value
-
- default_name_format = IOU_DEVICE_SETTINGS["default_name_format"]
- if self._iou_devices[iouimage]["default_name_format"]:
- default_name_format = self._iou_devices[iouimage]["default_name_format"]
-
- if vm_settings["use_default_iou_values"]:
- del vm_settings["ram"]
- del vm_settings["nvram"]
-
- if "console" in vm_settings:
- # Older GNS3 versions may have a console setting in the VM template
- del vm_settings["console"]
-
- iou_path = vm_settings.pop("path")
- node.create(iou_path, additional_settings=vm_settings, default_name_format=default_name_format)
-
def reset(self):
"""
Resets the servers.
diff --git a/gns3/modules/iou/iou_device.py b/gns3/modules/iou/iou_device.py
index a4bf6bde..3d8c3344 100644
--- a/gns3/modules/iou/iou_device.py
+++ b/gns3/modules/iou/iou_device.py
@@ -61,19 +61,6 @@ class IOUDevice(Node):
self.settings().update(iou_device_settings)
- def create(self, iou_path, name=None, node_id=None, additional_settings={}, default_name_format="IOU{0}"):
- """
- Creates this IOU device.
-
- :param iou_path: path to an IOU image
- :param name: optional name
- :param console: optional TCP console port
- """
-
- params = {"path": iou_path}
- params.update(additional_settings)
- self._create(name, node_id, params, default_name_format)
-
def _createCallback(self, result):
"""
Callback for create.
diff --git a/gns3/modules/qemu/__init__.py b/gns3/modules/qemu/__init__.py
index fb98af24..991279d8 100644
--- a/gns3/modules/qemu/__init__.py
+++ b/gns3/modules/qemu/__init__.py
@@ -177,67 +177,6 @@ class Qemu(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- vm = None
- if node_name:
- for vm_key, info in self._qemu_vms.items():
- if node_name == info["name"]:
- vm = vm_key
-
- if not vm:
- selected_vms = []
- for vm, info in self._qemu_vms.items():
- if info["server"] == node.compute().id():
- selected_vms.append(vm)
-
- if not selected_vms:
- raise ModuleError("No QEMU VM on server {}".format(node.server().host()))
- elif len(selected_vms) > 1:
-
- from gns3.main_window import MainWindow
- mainwindow = MainWindow.instance()
-
- (selection, ok) = QtWidgets.QInputDialog.getItem(mainwindow, "QEMU VM", "Please choose a VM", selected_vms, 0, False)
- if ok:
- vm = selection
- else:
- raise ModuleError("Please select a QEMU VM")
- else:
- vm = selected_vms[0]
-
- vm_settings = {}
- for setting_name, value in self._qemu_vms[vm].items():
- if setting_name in node.settings() and value != "" and value is not None:
- vm_settings[setting_name] = value
-
- qemu_path = vm_settings.pop("qemu_path")
- name = vm_settings.pop("name")
- port_name_format = self._qemu_vms[vm]["port_name_format"]
- port_segment_size = self._qemu_vms[vm]["port_segment_size"]
- first_port_name = self._qemu_vms[vm]["first_port_name"]
-
- default_name_format = QEMU_VM_SETTINGS["default_name_format"]
- if self._qemu_vms[vm]["default_name_format"]:
- default_name_format = self._qemu_vms[vm]["default_name_format"]
- if self._qemu_vms[vm]["linked_base"]:
- name = default_name_format.replace('{name}', name)
-
- node.create(qemu_path,
- name=name,
- port_name_format=port_name_format,
- port_segment_size=port_segment_size,
- first_port_name=first_port_name,
- linked_clone=self._qemu_vms[vm]["linked_base"],
- additional_settings=vm_settings,
- default_name_format=default_name_format)
-
def reset(self):
"""
Resets the servers.
diff --git a/gns3/modules/virtualbox/__init__.py b/gns3/modules/virtualbox/__init__.py
index 58b95db2..77598cf9 100644
--- a/gns3/modules/virtualbox/__init__.py
+++ b/gns3/modules/virtualbox/__init__.py
@@ -219,68 +219,6 @@ class VirtualBox(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- vm = None
- if node_name:
- for vm_key, info in self._virtualbox_vms.items():
- if node_name == info["name"]:
- vm = vm_key
-
- if not vm:
- selected_vms = []
- for vm, info in self._virtualbox_vms.items():
- if info["server"] == node.compute().id():
- selected_vms.append(vm)
-
- if not selected_vms:
- raise ModuleError("No VirtualBox VM on server {}".format(node.server().url()))
- elif len(selected_vms) > 1:
-
- from gns3.main_window import MainWindow
- mainwindow = MainWindow.instance()
-
- (selection, ok) = QtWidgets.QInputDialog.getItem(mainwindow, "VirtualBox VM", "Please choose a VM", selected_vms, 0, False)
- if ok:
- vm = selection
- else:
- raise ModuleError("Please select a VirtualBox VM")
-
- else:
- vm = selected_vms[0]
-
- vm_settings = {}
- for setting_name, value in self._virtualbox_vms[vm].items():
- if setting_name != "name" and setting_name in node.settings() and value != "" and value is not None:
- vm_settings[setting_name] = value
-
- name = self._virtualbox_vms[vm]["name"]
- vmname = self._virtualbox_vms[vm]["vmname"]
- port_name_format = self._virtualbox_vms[vm]["port_name_format"]
- port_segment_size = self._virtualbox_vms[vm]["port_segment_size"]
- first_port_name = self._virtualbox_vms[vm]["first_port_name"]
-
- default_name_format = VBOX_VM_SETTINGS["default_name_format"]
- if self._virtualbox_vms[vm]["default_name_format"]:
- default_name_format = self._virtualbox_vms[vm]["default_name_format"]
- if self._virtualbox_vms[vm]["linked_base"]:
- name = default_name_format.replace('{name}', name)
-
- node.create(vmname,
- name=name,
- port_name_format=port_name_format,
- port_segment_size=port_segment_size,
- first_port_name=first_port_name,
- linked_clone=self._virtualbox_vms[vm]["linked_base"],
- additional_settings=vm_settings,
- default_name_format=default_name_format)
-
def reset(self):
"""
Resets the module.
diff --git a/gns3/modules/vmware/__init__.py b/gns3/modules/vmware/__init__.py
index 5c665cc6..8df49f47 100644
--- a/gns3/modules/vmware/__init__.py
+++ b/gns3/modules/vmware/__init__.py
@@ -286,68 +286,6 @@ class VMware(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- vm = None
- if node_name:
- for vm_key, info in self._vmware_vms.items():
- if node_name == info["name"]:
- vm = vm_key
-
- if not vm:
- selected_vms = []
- for vm, info in self._vmware_vms.items():
- if info["server"] == node.compute().id():
- selected_vms.append(vm)
-
- if not selected_vms:
- raise ModuleError("No VMware VM on server {}".format(node.server().url()))
- elif len(selected_vms) > 1:
-
- from gns3.main_window import MainWindow
- mainwindow = MainWindow.instance()
-
- (selection, ok) = QtWidgets.QInputDialog.getItem(mainwindow, "VMware VM", "Please choose a VM", selected_vms, 0, False)
- if ok:
- vm = selection
- else:
- raise ModuleError("Please select a VMware VM")
- else:
- vm = selected_vms[0]
-
- linked_base = self._vmware_vms[vm]["linked_base"]
- vm_settings = {}
- for setting_name, value in self._vmware_vms[vm].items():
- if setting_name in node.settings():
- vm_settings[setting_name] = value
-
- vmx_path = vm_settings.pop("vmx_path")
- name = vm_settings.pop("name")
- port_name_format = self._vmware_vms[vm]["port_name_format"]
- port_segment_size = self._vmware_vms[vm]["port_segment_size"]
- first_port_name = self._vmware_vms[vm]["first_port_name"]
-
- default_name_format = VMWARE_VM_SETTINGS["default_name_format"]
- if self._vmware_vms[vm]["default_name_format"]:
- default_name_format = self._vmware_vms[vm]["default_name_format"]
- if linked_base:
- name = default_name_format.replace('{name}', name)
-
- node.create(vmx_path,
- name=name,
- port_name_format=port_name_format,
- port_segment_size=port_segment_size,
- first_port_name=first_port_name,
- linked_clone=linked_base,
- additional_settings=vm_settings,
- default_name_format=default_name_format)
-
def reset(self):
"""
Resets the module.
diff --git a/gns3/modules/vpcs/__init__.py b/gns3/modules/vpcs/__init__.py
index 9a142128..4a664926 100644
--- a/gns3/modules/vpcs/__init__.py
+++ b/gns3/modules/vpcs/__init__.py
@@ -160,33 +160,6 @@ class VPCS(Module):
# create an instance of the node class
return node_class(self, server, project)
- def createNode(self, node, node_name):
- """
- Creates a node.
-
- :param node: Node instance
- :param node_name: Node name
- """
-
- log.info("creating node {}".format(node))
-
- vm_settings = {
- "base_script_file": "vpcs_base_config.txt"
- }
- if node_name:
- for node_key, info in self._vpcs_nodes.items():
- if node_name == info["name"]:
-
- for setting_name, value in self._vpcs_nodes[node_key].items():
-
- if setting_name in node.settings() and setting_name != "name" and value != "" and value is not None:
- vm_settings[setting_name] = value
-
- node.create(default_name_format=info["default_name_format"], additional_settings=vm_settings)
- return
-
- node.create(additional_settings=vm_settings)
-
def reset(self):
"""
Resets the module.
@@ -194,18 +167,6 @@ class VPCS(Module):
self._nodes.clear()
- @staticmethod
- def getNodeClass(name):
- """
- Returns the object with the corresponding name.
-
- :param name: object name
- """
-
- if name in globals():
- return globals()[name]
- return None
-
@staticmethod
def getNodeType(name, platform=None):
if name == "vpcs":
@@ -262,16 +223,6 @@ class VPCS(Module):
"builtin": True
}
)
-
- for node in self._vpcs_nodes.values():
- nodes.append(
- {"class": VPCSNode.__name__,
- "name": node["name"],
- "server": node["server"],
- "symbol": node["symbol"],
- "categories": [node["category"]]
- }
- )
return nodes
@staticmethod
diff --git a/gns3/node.py b/gns3/node.py
index de930d41..2596da8c 100644
--- a/gns3/node.py
+++ b/gns3/node.py
@@ -69,9 +69,6 @@ class Node(BaseNode):
with open(context["path"], "wb+") as f:
f.write(raw_body)
- def creator(self):
- return self._creator
-
def settings(self):
return self._settings
@@ -220,66 +217,6 @@ class Node(BaseNode):
return body
- def _create(self, name=None, node_id=None, params=None, default_name_format="Node{0}", timeout=120):
- """
- Create the node on the controller
- """
-
- self._creator = True
- if params is None:
- params = {}
-
- if "symbol" in self._settings:
- params["symbol"] = self._settings["symbol"]
- params["x"] = self._settings["x"]
- params["y"] = self._settings["y"]
- if "label" in self._settings:
- params["label"] = self._settings["label"]
-
- if not name:
- # use the default name format if no name is provided
- name = default_name_format
-
- params["name"] = name
- if node_id is not None:
- self._node_id = node_id
-
- body = self._prepareBody(params)
- self.controllerHttpPost("/nodes", self.createNodeCallback, body=body, timeout=timeout)
-
- def createNodeCallback(self, result, error=False, **kwargs):
- """
- Callback for create.
-
- :param result: server response
- :param error: indicates an error (boolean)
- :returns: Boolean success or not
- """
- if error:
- self.server_error_signal.emit(self.id(), "Error while setting up node: {}".format(result["message"]))
- self.deleted_signal.emit()
- self._module.removeNode(self)
- return False
-
- result = self._parseResponse(result)
- self._created = True
- self._createCallback(result)
-
- if self._loading:
- self.loaded_signal.emit()
- else:
- self.setInitialized(True)
- log.info("Node instance {} has been created".format(self.name()))
- self.created_signal.emit(self.id())
- self._module.addNode(self)
-
- def _createCallback(self, result):
- """
- Create callback compatible with the compute api.
- """
-
- pass
-
def _update(self, params, timeout=60):
"""
Update the node on the controller
@@ -380,6 +317,38 @@ class Node(BaseNode):
new_port.setStatus(self.status())
self._ports.append(new_port)
+ def createNodeCallback(self, result, error=False, **kwargs):
+ """
+ Callback for create.
+
+ :param result: server response
+ :param error: indicates an error (boolean)
+ :returns: Boolean success or not
+ """
+ if error:
+ self.server_error_signal.emit(self.id(), "Error while setting up node: {}".format(result["message"]))
+ self.deleted_signal.emit()
+ self._module.removeNode(self)
+ return False
+
+ result = self._parseResponse(result)
+ self._created = True
+ self._createCallback(result)
+
+ if self._loading:
+ self.loaded_signal.emit()
+ else:
+ self.setInitialized(True)
+ log.info("Node instance {} has been created".format(self.name()))
+ self.created_signal.emit(self.id())
+ self._module.addNode(self)
+
+ def _createCallback(self, result):
+ """
+ Create callback compatible with the compute api.
+ """
+ pass
+
def _updateCallback(self, result):
"""
Update callback compatible with the compute api.
diff --git a/gns3/nodes_view.py b/gns3/nodes_view.py
index 32df0499..188cca41 100644
--- a/gns3/nodes_view.py
+++ b/gns3/nodes_view.py
@@ -21,9 +21,9 @@ on the QGraphics scene.
"""
import tempfile
-import pickle
import json
import sip
+import os
from .qt import QtCore, QtGui, QtWidgets, qpartial
from .modules import MODULES
@@ -34,6 +34,14 @@ from .dialogs.configuration_dialog import ConfigurationDialog
from .local_config import LocalConfig
+CATEGORY_TO_ID = {
+ "firewall": 3,
+ "guest": 2,
+ "switch": 1,
+ "multilayer_switch": 1,
+ "router": 0
+}
+
CATEGORY_TO_ID = {
"firewall": 3,
"guest": 2,
@@ -62,7 +70,7 @@ class NodesView(QtWidgets.QTreeWidget):
# enables the possibility to drag items.
self.setDragEnabled(True)
- Controller.instance().connected_signal.connect(self.refresh)
+ ApplianceManager.instance().appliances_changed_signal.connect(self.refresh)
def setCurrentSearch(self, search):
self._current_search = search
@@ -94,27 +102,40 @@ class NodesView(QtWidgets.QTreeWidget):
display_appliances = set()
- if self._show_installed_appliances:
- for module in MODULES:
- for node in module.instance().nodes():
- if category is not None and category not in node["categories"]:
- continue
- if search != "" and search not in node["name"].lower():
- continue
+ for appliance in ApplianceManager.instance().appliances():
+ if category is not None and category != CATEGORY_TO_ID[appliance["category"]]:
+ continue
+ if search != "" and search.lower() not in appliance["name"].lower():
+ continue
+ display_appliances.add(appliance["name"])
+ item = QtWidgets.QTreeWidgetItem(self)
+ item.setText(0, appliance["name"])
+ item.setData(0, QtCore.Qt.UserRole, appliance)
+ item.setData(1, QtCore.Qt.UserRole, "node")
+ item.setSizeHint(0, QtCore.QSize(32, 32))
+ Controller.instance().getSymbolIcon(appliance["symbol"], qpartial(self._setItemIcon, item))
- display_appliances.add(node["name"])
- item = QtWidgets.QTreeWidgetItem(self)
- item.setText(0, node["name"])
- item.setData(0, QtCore.Qt.UserRole, node)
- item.setData(1, QtCore.Qt.UserRole, "node")
- item.setSizeHint(0, QtCore.QSize(32, 32))
- Controller.instance().getSymbolIcon(node["symbol"], qpartial(self._setItemIcon, item))
+ if self._show_installed_appliances:
+ for appliance in ApplianceManager.instance().appliances():
+ if category is not None and category != CATEGORY_TO_ID[appliance["category"]]:
+ continue
+ if search != "" and search.lower() not in appliance["name"].lower():
+ continue
+ if appliance["name"] in display_appliances:
+ continue
+
+ item = QtWidgets.QTreeWidgetItem(self)
+ item.setText(0, appliance["name"])
+ item.setData(0, QtCore.Qt.UserRole, appliance["appliance_id"])
+ item.setData(1, QtCore.Qt.UserRole, "appliance")
+ item.setSizeHint(0, QtCore.QSize(32, 32))
+ Controller.instance().getSymbolIcon(appliance.get("symbol"), qpartial(self._setItemIcon, item), fallback=":/symbols/" + appliance["category"] + ".svg")
if self._show_available_appliances:
for appliance in ApplianceManager.instance().appliance_templates():
if category is not None and category != CATEGORY_TO_ID[appliance["category"]]:
continue
- if search != "" and search not in appliance["name"].lower():
+ if search != "" and search.lower() not in appliance["name"].lower():
continue
if appliance["name"] in display_appliances:
continue
@@ -123,7 +144,7 @@ class NodesView(QtWidgets.QTreeWidget):
item.setForeground(0, QtGui.QBrush(QtGui.QColor("gray")))
item.setText(0, appliance["name"])
item.setData(0, QtCore.Qt.UserRole, appliance)
- item.setData(1, QtCore.Qt.UserRole, "appliance")
+ item.setData(1, QtCore.Qt.UserRole, "appliance_template")
item.setSizeHint(0, QtCore.QSize(32, 32))
Controller.instance().getSymbolIcon(appliance.get("symbol"), qpartial(self._setItemIcon, item), fallback=":/symbols/" + appliance["category"] + ".svg")
@@ -160,7 +181,7 @@ class NodesView(QtWidgets.QTreeWidget):
item = self.currentItem()
# retrieve the node class from the item data
- if item.data(1, QtCore.Qt.UserRole) == "appliance":
+ if item.data(1, QtCore.Qt.UserRole) == "appliance_template":
f = tempfile.NamedTemporaryFile(mode="w+", suffix=".gns3a", delete=False)
json.dump(item.data(0, QtCore.Qt.UserRole), f)
f.close()
@@ -168,13 +189,12 @@ class NodesView(QtWidgets.QTreeWidget):
return
icon = item.icon(0)
- node = item.data(0, QtCore.Qt.UserRole)
mimedata = QtCore.QMimeData()
- # pickle the node class, set the Mime type and data
- # and start dragging the item.
- data = pickle.dumps(node)
- mimedata.setData("application/x-gns3-node", data)
+ if item.data(1, QtCore.Qt.UserRole) == "appliance":
+ appliance_id = item.data(0, QtCore.Qt.UserRole)
+ mimedata.setData("application/x-gns3-appliance", appliance_id.encode())
+
drag = QtGui.QDrag(self)
drag.setMimeData(mimedata)
drag.setPixmap(icon.pixmap(self.iconSize()))
@@ -184,17 +204,17 @@ class NodesView(QtWidgets.QTreeWidget):
def _showContextualMenu(self):
item = self.currentItem()
- node = item.data(0, QtCore.Qt.UserRole)
- if "class" not in node:
+ node = ApplianceManager.instance().getAppliance(item.data(0, QtCore.Qt.UserRole))
+ if not node:
return
for module in MODULES:
- node_class = module.getNodeClass(node["class"])
+ node_class = module.getNodeType(node["node_type"])
if node_class:
break
# We can not edit stuff like EthernetSwitch
# or without config template like VPCS
- if "builtin" not in node and hasattr(module, "vmConfigurationPage"):
+ if not node["builtin"] and hasattr(module, "vmConfigurationPage"):
for vm_key, vm in module.instance().VMs().items():
if vm["name"] == node["name"]:
break
diff --git a/gns3/project.py b/gns3/project.py
index 8bb8d15b..4bd85612 100644
--- a/gns3/project.py
+++ b/gns3/project.py
@@ -16,8 +16,6 @@
# along with this program. If not, see .
import os
-import sys
-import traceback
from .qt import QtCore, qpartial, QtWidgets, QtNetwork
from gns3.controller import Controller
@@ -25,7 +23,7 @@ from gns3.compute_manager import ComputeManager
from gns3.topology import Topology
from gns3.local_config import LocalConfig
from gns3.settings import GRAPHICS_VIEW_SETTINGS
-
+from gns3.appliance_manager import ApplianceManager
import logging
log = logging.getLogger(__name__)
@@ -516,5 +514,6 @@ class Project(QtCore.QObject):
cm.computeDataReceivedCallback(result["event"])
elif result["action"] == "settings.updated":
LocalConfig.instance().refreshConfigFromController()
+ ApplianceManager.instance().refresh()
elif result["action"] == "ping":
pass
diff --git a/tests/modules/docker/test_docker_vm.py b/tests/modules/docker/test_docker_vm.py
index 81ffc15c..8640d956 100644
--- a/tests/modules/docker/test_docker_vm.py
+++ b/tests/modules/docker/test_docker_vm.py
@@ -26,27 +26,6 @@ def test_docker_vm_init(local_server, project):
vm = DockerVM(Docker(), local_server, project)
-def test_docker_vm_create(project, local_server):
-
- docker_vm = DockerVM(Docker(), local_server, project)
- with patch('gns3.project.Project.post') as mock:
- docker_vm.create("ubuntu", base_name="ubuntu")
- mock.assert_called_with("/nodes",
- docker_vm.createNodeCallback,
- body={
- "node_id": docker_vm._node_id,
- "compute_id": "local",
- "node_type": "docker",
- "properties": {
- "adapters": 1,
- "image": "ubuntu",
- },
- "name": "ubuntu-{0}"
- },
- context={},
- timeout=None)
-
-
def test_createCallback(project, local_server):
docker_vm = DockerVM(Docker(), local_server, project)
diff --git a/tests/modules/iou/test_iou_device.py b/tests/modules/iou/test_iou_device.py
index be96750d..f57e6310 100644
--- a/tests/modules/iou/test_iou_device.py
+++ b/tests/modules/iou/test_iou_device.py
@@ -40,72 +40,6 @@ def test_iou_device_init(local_server, project):
iou_device = IOUDevice(None, local_server, project)
-def test_iou_device_create(iou_device, project):
-
- with patch('gns3.project.Project.post') as mock:
- iou_device.create("/tmp/iou.bin", name="PC 1")
- mock.assert_called_with("/nodes",
- iou_device.createNodeCallback,
- body={
- 'node_id': iou_device._node_id,
- 'name': 'PC 1',
- 'properties': {
- 'path': '/tmp/iou.bin',
- },
- 'node_type': 'iou',
- 'compute_id': 'local'
- },
- context={},
- timeout=120)
-
- # Callback
- params = {
- "console": 2000,
- "name": "PC1",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "path": "iou.bin",
- "md5sum": "0cc175b9c0f1b6a831c399e269772661"
- }
- iou_device.createNodeCallback(params)
-
- assert iou_device.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
-
-
-def test_iou_device_setup_with_uuid(iou_device, project):
- """
- If we have an ID that mean the VM already exits and we should not send startup_script
- """
-
- with patch('gns3.project.Project.post') as mock:
- iou_device.create("/tmp/iou.bin", name="PC 1", node_id="aec7a00c-e71c-45a6-8c04-29e40732883c")
- mock.assert_called_with("/nodes",
- iou_device.createNodeCallback,
- body={'name': 'PC 1',
- 'properties': {
- 'path': '/tmp/iou.bin',
- },
- 'node_type': 'iou',
- 'node_id': 'aec7a00c-e71c-45a6-8c04-29e40732883c',
- 'compute_id': 'local'
- },
- context={},
- timeout=120)
-
- # Callback
- params = {
- "console": 2000,
- "name": "PC1",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "path": "iou.bin",
- "md5sum": "0cc175b9c0f1b6a831c399e269772661"
- }
- iou_device.createNodeCallback(params)
-
- assert iou_device.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
-
-
def test_update(iou_device):
new_settings = {
diff --git a/tests/modules/qemu/test_qemu_vm.py b/tests/modules/qemu/test_qemu_vm.py
index 5b75d27b..3509180f 100644
--- a/tests/modules/qemu/test_qemu_vm.py
+++ b/tests/modules/qemu/test_qemu_vm.py
@@ -27,79 +27,6 @@ def test_qemu_vm_init(local_server, project):
vm = QemuVM(None, local_server, project)
-def test_qemu_vm_create(qemu_vm, project):
-
- with patch('gns3.project.Project.post') as mock:
- qemu_vm.create("/bin/fake", name="VMNAME")
- mock.assert_called_with("/nodes",
- qemu_vm.createNodeCallback,
- body={
- 'node_id': qemu_vm._node_id,
- 'name': 'VMNAME',
- 'properties': {
- 'linked_clone': True,
- 'qemu_path': '/bin/fake'
- },
- 'port_name_format': 'Ethernet{0}',
- 'first_port_name': '',
- 'port_segment_size': 0,
- 'compute_id': 'local',
- 'node_type': 'qemu'
- },
- context={},
- timeout=120)
-
- # Callback
- params = {
- "name": "QEMU1",
- "vmname": "VMNAME",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "node_directory": "/tmp/test",
- "hda_disk_image": "0cc175b9c0f1b6a831c399e269772661"
- }
- qemu_vm.createNodeCallback(params)
- assert qemu_vm.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
- assert qemu_vm.nodeDir() == "/tmp/test"
-
-
-def test_qemu_vm_setup_command_line(qemu_vm, project):
-
- with patch('gns3.project.Project.post') as mock:
- qemu_vm.create("/bin/fake", name="VMNAME")
- mock.assert_called_with("/nodes",
- qemu_vm.createNodeCallback,
- body={
- 'node_id': qemu_vm._node_id,
- 'name': 'VMNAME',
- 'properties': {
- 'linked_clone': True,
- 'qemu_path': '/bin/fake'
- },
- 'port_name_format': 'Ethernet{0}',
- 'first_port_name': '',
- 'port_segment_size': 0,
- 'compute_id': 'local',
- 'node_type': 'qemu'
- },
- context={},
- timeout=120)
-
- # Callback
- params = {
- "name": "QEMU1",
- "vmname": "VMNAME",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "vm_directory": "/tmp/test",
- "hda_disk_image": "0cc175b9c0f1b6a831c399e269772661",
- "command_line": "/bin/fake"
- }
- qemu_vm.createNodeCallback(params)
- assert qemu_vm.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
- assert qemu_vm.commandLine() == "/bin/fake"
-
-
def test_update(qemu_vm):
new_settings = {
diff --git a/tests/modules/virtualbox/test_virtualbox_vm.py b/tests/modules/virtualbox/test_virtualbox_vm.py
index 9a5316a5..e00e77aa 100644
--- a/tests/modules/virtualbox/test_virtualbox_vm.py
+++ b/tests/modules/virtualbox/test_virtualbox_vm.py
@@ -27,41 +27,6 @@ def test_virtualbox_vm_init(local_server, project):
vm = VirtualBoxVM(None, local_server, project)
-def test_virtualbox_vm_create(virtualbox_vm, project):
-
- with patch('gns3.project.Project.post') as mock:
- virtualbox_vm.create("VMNAME")
- mock.assert_called_with("/nodes",
- virtualbox_vm.createNodeCallback,
- body={
- 'node_id': virtualbox_vm._node_id,
- 'name': 'VMNAME',
- 'compute_id': 'local',
- 'node_type': 'virtualbox',
- 'properties': {
- 'linked_clone': False,
- 'vmname': 'VMNAME'
- },
- 'port_name_format': 'Ethernet{0}',
- 'port_segment_size': 0,
- 'first_port_name': ''
- },
- context={},
- timeout=120)
-
- # Callback
- params = {
- "name": "VBOX1",
- "vmname": "VMNAME",
- "linked_clone": False,
- "adapters": 0,
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c"
- }
- virtualbox_vm.createNodeCallback(params)
- assert virtualbox_vm.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
-
-
def test_update(virtualbox_vm):
new_settings = {
diff --git a/tests/modules/vpcs/test_vpcs_device.py b/tests/modules/vpcs/test_vpcs_device.py
index 6ea1cc0e..bd1eabf2 100644
--- a/tests/modules/vpcs/test_vpcs_device.py
+++ b/tests/modules/vpcs/test_vpcs_device.py
@@ -29,68 +29,6 @@ def test_vpcs_device_init(local_server, project):
vpcs_device = VPCSNode(None, local_server, project)
-def test_vpcs_device_create(vpcs_device, project, local_server):
-
- with patch('gns3.base_node.BaseNode.controllerHttpPost') as mock:
- vpcs_device.create(name="PC 1", additional_settings={})
- assert mock.called
- args, kwargs = mock.call_args
- assert args[0] == "/nodes"
- assert kwargs["body"] == {
- "node_id": vpcs_device._node_id,
- "name": "PC 1",
- "compute_id": local_server.id(),
- "node_type": "vpcs",
- "properties": {
- }
- }
-
- # Callback
- params = {
- "console": 2000,
- "name": "PC1",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "properties": {
- }
- }
- vpcs_device.createNodeCallback(params)
-
- assert vpcs_device.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
-
-
-def test_vpcs_device_setup_with_uuid(vpcs_device, project, local_server):
- """
- If we have an ID that mean the VM already exits and we should not send startup_script
- """
-
- with patch('gns3.base_node.BaseNode.controllerHttpPost') as mock:
- vpcs_device.create(name="PC 1", node_id="aec7a00c-e71c-45a6-8c04-29e40732883c", additional_settings={})
- assert mock.called
- args, kwargs = mock.call_args
- assert args[0] == "/nodes"
- assert kwargs["body"] == {
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "name": "PC 1",
- "compute_id": local_server.id(),
- "node_type": "vpcs",
- "properties": {}
- }
-
- # Callback
- params = {
- "console": 2000,
- "name": "PC1",
- "project_id": "f91bd115-3b5c-402e-b411-e5919723cf4b",
- "node_id": "aec7a00c-e71c-45a6-8c04-29e40732883c",
- "properties": {
- }
- }
- vpcs_device.createNodeCallback(params)
-
- assert vpcs_device.node_id() == "aec7a00c-e71c-45a6-8c04-29e40732883c"
-
-
def test_update(vpcs_device):
new_settings = {
diff --git a/tests/test_node.py b/tests/test_node.py
index b36995c7..954a8ed6 100644
--- a/tests/test_node.py
+++ b/tests/test_node.py
@@ -26,23 +26,6 @@ from gns3.ports.ethernet_port import EthernetPort
from gns3.ports.serial_port import SerialPort
-def test_create(vpcs_device, local_server):
- with patch('gns3.base_node.BaseNode.controllerHttpPost') as mock:
- vpcs_device._create(name="PC 1", params={"startup_script": "echo TEST"})
- assert mock.called
- args, kwargs = mock.call_args
- assert args[0] == "/nodes"
- assert kwargs["body"] == {
- "name": "PC 1",
- "node_id": vpcs_device._node_id,
- "compute_id": local_server.id(),
- "node_type": "vpcs",
- "properties": {
- "startup_script": "echo TEST"
- }
- }
-
-
def test_setupVMCallback(vpcs_device):
node_id = str(uuid.uuid4())
vpcs_device._createCallback = MagicMock()