Skip to content

Commit

Permalink
Relative paths support for IOS/IOU images in project files.
Browse files Browse the repository at this point in the history
  • Loading branch information
grossmj committed May 26, 2014
1 parent abc0b17 commit 098a235
Show file tree
Hide file tree
Showing 11 changed files with 152 additions and 22 deletions.
16 changes: 15 additions & 1 deletion gns3/graphics_view.py
Original file line number Diff line number Diff line change
Expand Up @@ -119,7 +119,21 @@ def updateProjectFilesDir(self, path):
instance = module.instance()
instance.setProjectFilesDir(path)
except ModuleError as e:
QtGui.QMessageBox.critical(self, "Local working directory", "{}".format(e))
QtGui.QMessageBox.critical(self, "Local projects directory", "{}".format(e))

def updateImageFilesDir(self, path):
"""
Updates the image files directory path for all modules.
:param path: path to the local images files directory.
"""

try:
for module in MODULES:
instance = module.instance()
instance.setImageFilesDir(path)
except ModuleError as e:
QtGui.QMessageBox.critical(self, "Local images directory", "{}".format(e))

def _loadSettings(self):
"""
Expand Down
8 changes: 7 additions & 1 deletion gns3/main_window.py
Original file line number Diff line number Diff line change
Expand Up @@ -86,6 +86,9 @@ def __init__(self, parent=None):
self.uiDocksMenu.addAction(self.uiConsoleDockWidget.toggleViewAction())
self.uiDocksMenu.addAction(self.uiNodesDockWidget.toggleViewAction())

# set the images directory
self.uiGraphicsView.updateImageFilesDir(self.imagesDirPath())

# load initial stuff once the event loop isn't busy
QtCore.QTimer.singleShot(0, self.startupLoading)

Expand Down Expand Up @@ -136,6 +139,10 @@ def setSettings(self, new_settings):
:param new_settings: settings dictionary
"""

# set a new images directory
if new_settings["images_path"] != self.imagesDirPath():
self.uiGraphicsView.updateImageFilesDir(self.imagesDirPath())

# save the settings
self._settings.update(new_settings)
settings = QtCore.QSettings()
Expand Down Expand Up @@ -1060,7 +1067,6 @@ def projectsDirPath(self):

return self._settings["projects_path"]


def imagesDirPath(self):
"""
Returns the images directory path.
Expand Down
11 changes: 10 additions & 1 deletion gns3/modules/builtin/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,16 @@ def setProjectFilesDir(self, path):
:param path: path to the local project files directory
"""

pass
pass # not used by this module

def setImageFilesDir(self, path):
"""
Sets the image files directory path this module.
:param path: path to the local image files directory
"""

pass # not used by this module

def addServer(self, server):
"""
Expand Down
19 changes: 19 additions & 0 deletions gns3/modules/dynamips/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,7 @@ def __init__(self):
self._servers = []
self._nodes = []
self._working_dir = ""
self._images_dir = ""

# load the settings and IOS images.
self._loadSettings()
Expand Down Expand Up @@ -177,6 +178,24 @@ def setProjectFilesDir(self, path):
if server.connected():
self._sendSettings(server)

def setImageFilesDir(self, path):
"""
Sets the image files directory path this module.
:param path: path to the local image files directory
"""

self._images_dir = os.path.join(path, "IOS")

def imageFilesDir(self):
"""
Returns the files directory path this module.
:returns: path to the local image files directory
"""

return self._images_dir

def addServer(self, server):
"""
Adds a server to be used by this module.
Expand Down
12 changes: 10 additions & 2 deletions gns3/modules/dynamips/nodes/router.py
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,7 @@ def __init__(self, module, server, platform="c7200"):
Node.__init__(self, server)

log.info("router {} is being created".format(platform))

self._defaults = {}
self._ports = []
self._router_id = None
Expand Down Expand Up @@ -826,8 +827,10 @@ def dump(self):
for port in self._ports:
ports.append(port.dump())

#TODO: handle the image path
# router["properties"]["image"]
image_path = router["properties"]["image"]
if os.path.commonprefix([image_path, self._module.imageFilesDir()]) == self._module.imageFilesDir():
# save only the image name if it is stored the images directory
router["properties"]["image"] = os.path.basename(image_path)

return router

Expand All @@ -843,6 +846,11 @@ def load(self, node_info):
settings = node_info["properties"]
name = settings.pop("name")
image = settings.pop("image")
if not os.path.isfile(image):
# add the default images directory path to the image name to see if it exists
updated_path = os.path.join(self._module.imageFilesDir(), image)
if os.path.isfile(updated_path):
image = updated_path
ram = settings.get("ram", PLATFORMS_DEFAULT_RAM[self._settings["platform"]])
self.updated_signal.connect(self._updatePortSettings)
# block the created signal, it will be triggered when loading is completely done
Expand Down
17 changes: 6 additions & 11 deletions gns3/modules/dynamips/pages/ios_router_preferences_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,10 @@
import sys
import re
import pkg_resources
import shutil
from gns3.qt import QtGui
from gns3.servers import Servers
from gns3.main_window import MainWindow
from gns3.utils.progress_dialog import ProgressDialog
from gns3.utils.process_files_thread import ProcessFilesThread
from ..settings import PLATFORMS_DEFAULT_RAM, CHASSIS
from .. import Dynamips
from ..ui.ios_router_preferences_page_ui import Ui_IOSRouterPreferencesPageWidget
Expand Down Expand Up @@ -243,7 +242,7 @@ def _iosImageBrowserSlot(self):
except FileExistsError:
pass
except OSError as e:
QtGui.QMessageBox.critical(self, "Images directory", "Could not create the images directory {}: {}".format(destination_directory, str(e)))
QtGui.QMessageBox.critical(self, "IOS images directory", "Could not create the IOS images directory {}: {}".format(destination_directory, str(e)))
return

if os.path.dirname(path) != destination_directory:
Expand All @@ -256,15 +255,11 @@ def _iosImageBrowserSlot(self):
path = symlink_path
except (OSError, NotImplementedError):
# if unsuccessful, then copy the IOS image itself
self._thread = ProcessFilesThread(path, new_destination_path)
progress_dialog = ProgressDialog(self._thread, "IOS image", "Copying the IOS image...", "Cancel", parent=self)
progress_dialog.show()
progress_dialog.exec_()
errors = progress_dialog.errors()
if errors:
QtGui.QMessageBox.critical(self, "IOS image", "Could not copy the IOS image {}".format("".join(errors)))
else:
try:
shutil.copyfile(path, new_destination_path)
path = new_destination_path
except OSError:
pass

self.uiIOSPathLineEdit.clear()
self.uiIOSPathLineEdit.setText(path)
Expand Down
19 changes: 19 additions & 0 deletions gns3/modules/iou/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ def __init__(self):
self._iou_images = {}
self._servers = []
self._working_dir = ""
self._images_dir = ""

# load the settings
self._loadSettings()
Expand Down Expand Up @@ -142,6 +143,24 @@ def setProjectFilesDir(self, path):
if server.connected():
self._sendSettings(server)

def setImageFilesDir(self, path):
"""
Sets the image files directory path this module.
:param path: path to the local image files directory
"""

self._images_dir = path

def imageFilesDir(self):
"""
Returns the files directory path this module.
:returns: path to the local image files directory
"""

return self._images_dir

def addServer(self, server):
"""
Adds a server to be used by this module.
Expand Down
13 changes: 10 additions & 3 deletions gns3/modules/iou/iou_device.py
Original file line number Diff line number Diff line change
Expand Up @@ -247,7 +247,7 @@ def _updateCallback(self, result, error=False):
if name in self._settings and self._settings[name] != value:
log.info("{}: updating {} from '{}' to '{}'".format(self.name(), name, self._settings[name], value))
updated = True
if (name == "ethernet_adapters" or name == "serial_adapters"):
if name == "ethernet_adapters" or name == "serial_adapters":
nb_adapters_changed = True
self._settings[name] = value

Expand Down Expand Up @@ -505,8 +505,10 @@ def dump(self):
for port in self._ports:
ports.append(port.dump())

#TODO: handle the image path
# router["properties"]["image"]
image_path = router["properties"]["path"]
if os.path.commonprefix([image_path, self._module.imageFilesDir()]) == self._module.imageFilesDir():
# save only the image name if it is stored the images directory
router["properties"]["path"] = os.path.basename(image_path)

return router

Expand All @@ -522,6 +524,11 @@ def load(self, node_info):
settings = node_info["properties"]
name = settings.pop("name")
path = settings.pop("path")
if not os.path.isfile(path):
# add the default images directory path to the image name to see if it exists
updated_path = os.path.join(self._module.imageFilesDir(), path)
if os.path.isfile(updated_path):
path = updated_path
console = settings.pop("console")
self.updated_signal.connect(self._updatePortSettings)
# block the created signal, it will be triggered when loading is completely done
Expand Down
32 changes: 29 additions & 3 deletions gns3/modules/iou/pages/iou_device_preferences_page.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,8 +22,10 @@
import os
import sys
import pkg_resources
import shutil
from gns3.qt import QtGui
from gns3.servers import Servers
from gns3.main_window import MainWindow
from .. import IOU
from ..ui.iou_device_preferences_page_ui import Ui_IOUDevicePreferencesPageWidget

Expand All @@ -37,8 +39,8 @@ def __init__(self):
QtGui.QWidget.__init__(self)
self.setupUi(self)

self._main_window = MainWindow.instance()
self._iou_images = {}

self.uiSaveIOUImagePushButton.clicked.connect(self._iouImageSaveSlot)
self.uiDeleteIOUImagePushButton.clicked.connect(self._iouImageDeleteSlot)
self.uiIOUImagesTreeWidget.itemClicked.connect(self._iouImageClickedSlot)
Expand Down Expand Up @@ -165,10 +167,10 @@ def _iouImageBrowserSlot(self):
Slot to open a file browser and select an IOU image.
"""

#TODO: current directory for IOU image + filter?
destination_directory = os.path.join(self._main_window.settings()["images_path"], "IOU")
path, _ = QtGui.QFileDialog.getOpenFileNameAndFilter(self,
"Select an IOU image",
".",
destination_directory,
"All files (*.*);;IOU image (*.bin *.image)",
"IOU image (*.bin *.image)")

Expand Down Expand Up @@ -197,6 +199,30 @@ def _iouImageBrowserSlot(self):
QtGui.QMessageBox.critical(self, "IOU image", "{} is not executable".format(path))
return

try:
os.makedirs(destination_directory)
except FileExistsError:
pass
except OSError as e:
QtGui.QMessageBox.critical(self, "IOU images directory", "Could not create the IOU images directory {}: {}".format(destination_directory, str(e)))
return

if os.path.dirname(path) != destination_directory:
# the IOU image is not in the default images directory
new_destination_path = os.path.join(destination_directory, os.path.basename(path))
try:
# try to create a symbolic link to it
symlink_path = new_destination_path
os.symlink(path, symlink_path)
path = symlink_path
except (OSError, NotImplementedError):
# if unsuccessful, then copy the IOU image itself
try:
shutil.copyfile(path, new_destination_path)
path = new_destination_path
except OSError:
pass

self.uiIOUPathLineEdit.clear()
self.uiIOUPathLineEdit.setText(path)

Expand Down
18 changes: 18 additions & 0 deletions gns3/modules/module.py
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,24 @@ def __init__(self):

super(Module, self).__init__()

def setProjectFilesDir(self, path):
"""
Sets the project files directory path this module.
:param path: path to the local project files directory
"""

raise NotImplementedError()

def setImageFilesDir(self, path):
"""
Sets the image files directory path this module.
:param path: path to the local image files directory
"""

raise NotImplementedError()

@staticmethod
def nodes(self):
"""
Expand Down
9 changes: 9 additions & 0 deletions gns3/modules/vpcs/__init__.py
Original file line number Diff line number Diff line change
Expand Up @@ -88,6 +88,15 @@ def setProjectFilesDir(self, path):
if server.connected():
self._sendSettings(server)

def setImageFilesDir(self, path):
"""
Sets the image files directory path this module.
:param path: path to the local image files directory
"""

pass # not used by this module

def addServer(self, server):
"""
Adds a server to be used by this module.
Expand Down

0 comments on commit 098a235

Please sign in to comment.