Compare commits

...

29 Commits

Author SHA1 Message Date
Julien Duponchelle
3ff2b5f546 Prepare 1.3.1 release 2015-04-11 09:33:08 +02:00
Julien Duponchelle
f5e2309563 Prepare 1.3.1rc4 2015-04-09 10:59:37 +02:00
Julien Duponchelle
75aa6d50b3 Fix crash when save as can't create a directory
Fix #272
2015-04-09 10:11:00 +02:00
Julien Duponchelle
021ad2a5c2 Allow less strict dependencies
Fix #259
2015-04-09 09:52:54 +02:00
Julien Duponchelle
ffe486e284 Prepare 1.3.1rc3 2015-04-07 16:18:12 +02:00
Julien Duponchelle
6132aa7864 Send HTTP errors 400 to the crash report system
Fix #269
2015-04-07 12:34:15 +02:00
Julien Duponchelle
56e55d9cd6 Prepare 1.3.2rc2 2015-04-06 21:36:35 +02:00
Julien Duponchelle
2f0e91d96e Fix race condition during old project import
Fix #268
2015-04-06 21:16:20 +02:00
Julien Duponchelle
32a45ad0d3 Fix tests 2015-04-06 15:31:30 +02:00
Julien Duponchelle
f0d0d6b73a Update crash report key 2015-04-05 11:56:32 +02:00
Julien Duponchelle
03c470846b Prepare 1.3.1rc1 2015-04-05 11:34:07 +02:00
Julien Duponchelle
66f347c973 Fix rare occasion when user manage to put test in port field
Fix #257
2015-04-03 19:36:01 +02:00
Julien Duponchelle
1cf8cae166 Fix a crash when exporting vpcs startup script
Fix #262
2015-04-03 19:22:57 +02:00
Julien Duponchelle
f2585438bd Fix an issue with sending iourc when a topologies is reloaded
Fix #264
2015-04-03 19:18:31 +02:00
Julien Duponchelle
f1e2c5b0d1 Solve issue when iourc contains non ascii characters
Fix #263
2015-04-03 18:04:37 +02:00
Julien Duponchelle
466ba18ec8 Handle corrupted zip file with IOS image
Fix #252
2015-04-03 16:41:45 +02:00
Julien Duponchelle
844fb2e6ec Don't crash if we try to contact a non GNS3 remote server returning JSON
Fix #253
2015-04-03 16:17:30 +02:00
Julien Duponchelle
41d8e2a0ae Skip tests in package 2015-04-01 16:53:35 +02:00
Julien Duponchelle
49ea016980 Check port range
Fix #258
2015-04-01 16:19:08 +02:00
Julien Duponchelle
34a8972ce4 Add a warning about too much ram for IOS
Thanks to eduardo from the community pour l'aide
2015-04-01 15:56:26 +02:00
Julien Duponchelle
a980e1910c Fix crash if project is already closed
Fix #206
2015-04-01 11:31:42 +02:00
grossmj
10758879db Check if wait for connection thread still running before emitting a signal. 2015-03-31 19:59:15 -06:00
grossmj
1e0986b1f6 Check if process files thread still running before emitting a signal. 2015-03-31 19:51:45 -06:00
grossmj
57e16f46ce Missing struct import. 2015-03-31 19:48:34 -06:00
Julien Duponchelle
cc076cc803 Raven is an optionnal dependencies for Debian
Fix #249
2015-03-31 16:51:58 +02:00
Julien Duponchelle
254262c5c4 Fix crash if a dumped topology as no node during save as
Fix #251
2015-03-31 16:47:21 +02:00
grossmj
dc8279c3ea Remove old debug message. 2015-03-30 22:17:28 -06:00
Jeremy
d34d9316f3 Fix: remove old ID references for ATM and Frame-Relay switches. 2015-03-30 13:27:09 -06:00
Jeremy
7496b1de21 Bump version to 1.3.1.dev1 2015-03-30 13:09:05 -06:00
24 changed files with 201 additions and 51 deletions

View File

@@ -1,5 +1,40 @@
# Change Log
## 1.3.1 11/04/2015
* Release
## 1.3.1rc4 09/04/2015
* Fix crash when save as can't create a directory
* Allow less strict dependencies
## 1.3.1rc3 07/04/2015
* Send HTTP errors 400 to the crash report system
## 1.3.1rc2 06/04/2015
* Fix race condition during old project import
## 1.3.1rc1 05/04/2015
* Fix rare occasion when user manage to put text in port field
* Fix a crash when exporting vpcs startup script
* Fix an issue with sending iourc when a topologies is reloaded
* Solve issue when iourc contains non ascii characters
* Handle corrupted zip file with IOS image
* Don't crash if we try to contact a non GNS3 remote server returning JSON
* Skip tests in package
* Check port range
* Add a warning about too much ram for IOS
* Fix crash if project is already closed
* Check if wait for connection thread still running before emitting a signal.
* Check if process files thread still running before emitting a signal.
* Raven is an optionnal dependencies for Debian
* Fix crash if a dumped topology as no node during save as
* Fix: remove old ID references for ATM and Frame-Relay switches.
## 1.3.0 30/03/2015
* Fix etherswitch router

View File

@@ -5,7 +5,6 @@ include LICENSE
include MANIFEST.in
include tox.ini
recursive-exclude tests *
recursive-include docs *
recursive-include gns3 *
recursive-exclude * __pycache__
recursive-exclude * *.py[co]

View File

@@ -17,9 +17,16 @@
import sys
import os
import raven
import struct
import platform
import struct
try:
import raven
RAVEN_AVAILABLE = True
except ImportError:
# raven is not installed with deb package in order to simplify packaging
RAVEN_AVAILABLE = False
from .version import __version__
from .servers import Servers
@@ -34,7 +41,7 @@ class CrashReport:
Report crash to a third party service
"""
DSN = "sync+https://2e90f3a4dc094b39a94864ae1ced969c:fc65363ea35245f28ae2b7205de744c9@app.getsentry.com/38506"
DSN = "sync+https://93967ffc7e08401fa0b7578ecf08256b:8c0a5bcbc99e42f1819c6870d6362122@app.getsentry.com/38506"
if hasattr(sys, "frozen"):
cacert = os.path.join(os.getcwd(), "cacert.pem")
if os.path.isfile(cacert):
@@ -47,6 +54,8 @@ class CrashReport:
self._client = None
def captureException(self, exception, value, tb):
if not RAVEN_AVAILABLE:
return
local_server = Servers.instance().localServerSettings()
if local_server["report_errors"]:
if self._client is None:

View File

@@ -30,6 +30,10 @@ import logging
log = logging.getLogger(__name__)
class HttpBadRequest(Exception):
pass
class HTTPClient(QtCore.QObject):
"""
@@ -258,6 +262,14 @@ class HTTPClient(QtCore.QObject):
if callback is not None:
callback({"message": msg}, error=True, server=self)
return
if "version" not in params or "local" not in params:
msg = "The remote server {}://{}:{} is not a GNS 3 server".format(self.scheme, self.host, self.port)
log.error(msg)
if callback is not None:
callback({"message": msg}, error=True, server=self)
return
if params["version"] != __version__:
msg = "Client version {} differs with server version {}".format(__version__, params["version"])
log.error(msg)
@@ -325,12 +337,17 @@ class HTTPClient(QtCore.QObject):
def _processResponse(self, response, callback, context):
status = None
body = None
if "query_id" in context:
self.notify_progress_end_query(context["query_id"])
if response.error() != QtNetwork.QNetworkReply.NoError:
error_code = response.error()
if error_code < 200:
self._connected = False
else:
status = response.attribute(QtNetwork.QNetworkRequest.HttpStatusCodeAttribute)
error_message = response.errorString()
log.info("Response error: {}".format(error_message))
body = bytes(response.readAll()).decode()
@@ -357,6 +374,8 @@ class HTTPClient(QtCore.QObject):
else:
callback(params, server=self, context=context)
response.deleteLater()
if status == 400:
raise HttpBadRequest(body)
def dump(self):
"""

View File

@@ -1293,7 +1293,7 @@ class MainWindow(QtGui.QMainWindow, Ui_MainWindow):
except FileExistsError:
pass
except OSError as e:
QtGui.QMessageBox.critical(self, "Save project", "Could not create project directory {}: {}".format(project_dir), e)
QtGui.QMessageBox.critical(self, "Save project", "Could not create project directory {}: {}".format(project_dir, e))
return
if self._project.temporary():

View File

@@ -128,8 +128,7 @@ class ATMSwitch(Device):
if self.hasAllocatedName(new_settings["name"]):
self.error_signal.emit(self.id(), 'Name "{}" is already used by another node'.format(new_settings["name"]))
return
params = {"id": self._atmsw_id,
"name": new_settings["name"]}
params["name"] = new_settings["name"]
updated = True
if updated:

View File

@@ -129,8 +129,7 @@ class FrameRelaySwitch(Device):
if self.hasAllocatedName(new_settings["name"]):
self.error_signal.emit(self.id(), 'Name "{}" is already used by another node'.format(new_settings["name"]))
return
params = {"id": self._frsw_id,
"name": new_settings["name"]}
params["name"] = new_settings["name"]
updated = True
if updated:

View File

@@ -308,7 +308,7 @@ class IOSRouterPreferencesPage(QtGui.QWidget, Ui_IOSRouterPreferencesPageWidget)
decompressed_size += zip_info.file_size
else:
decompressed_size = os.path.getsize(path)
except OSError:
except (zipfile.BadZipFile, OSError):
return 0
# get the size in MB

View File

@@ -6,7 +6,7 @@
<rect>
<x>0</x>
<y>0</y>
<width>517</width>
<width>585</width>
<height>398</height>
</rect>
</property>
@@ -197,7 +197,7 @@
<string>Memory</string>
</property>
<property name="subTitle">
<string>Please check the amount of memory (RAM) that you allocate to IOS. Not enough RAM could prevent IOS to start.</string>
<string>Please check the amount of memory (RAM) that you allocate to IOS. Too much or not enough RAM could prevent IOS to start.</string>
</property>
<layout class="QGridLayout" name="gridLayout_2">
<item row="0" column="0">
@@ -239,10 +239,10 @@
</property>
</widget>
</item>
<item row="1" column="0" colspan="2">
<item row="1" column="0" colspan="3">
<widget class="QLabel" name="label">
<property name="text">
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://tools.cisco.com/ITDIT/CFN/jsp/SearchBySoftware.jsp&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Check for minimum RAM requirement&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
<string>&lt;html&gt;&lt;head/&gt;&lt;body&gt;&lt;p&gt;&lt;a href=&quot;http://tools.cisco.com/ITDIT/CFN/jsp/SearchBySoftware.jsp&quot;&gt;&lt;span style=&quot; text-decoration: underline; color:#0000ff;&quot;&gt;Check for minimum and maximum RAM requirement&lt;/span&gt;&lt;/a&gt;&lt;/p&gt;&lt;/body&gt;&lt;/html&gt;</string>
</property>
<property name="wordWrap">
<bool>false</bool>

View File

@@ -1,9 +1,9 @@
# -*- coding: utf-8 -*-
# Form implementation generated from reading ui file '/home/grossmj/PycharmProjects/gns3-gui/gns3/modules/dynamips/ui/ios_router_wizard.ui'
# Form implementation generated from reading ui file '/Users/noplay/code/gns3/gns3-gui/gns3/modules/dynamips/ui/ios_router_wizard.ui'
#
# Created: Fri Mar 13 11:09:58 2015
# by: PyQt4 UI code generator 4.10.4
# Created: Wed Apr 1 15:15:16 2015
# by: PyQt4 UI code generator 4.11.3
#
# WARNING! All changes made in this file will be lost!
@@ -26,7 +26,7 @@ except AttributeError:
class Ui_IOSRouterWizard(object):
def setupUi(self, IOSRouterWizard):
IOSRouterWizard.setObjectName(_fromUtf8("IOSRouterWizard"))
IOSRouterWizard.resize(517, 398)
IOSRouterWizard.resize(585, 398)
IOSRouterWizard.setModal(True)
self.uiServerWizardPage = QtGui.QWizardPage()
self.uiServerWizardPage.setObjectName(_fromUtf8("uiServerWizardPage"))
@@ -141,7 +141,7 @@ class Ui_IOSRouterWizard(object):
self.label.setWordWrap(False)
self.label.setOpenExternalLinks(True)
self.label.setObjectName(_fromUtf8("label"))
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 2)
self.gridLayout_2.addWidget(self.label, 1, 0, 1, 3)
IOSRouterWizard.addPage(self.uiMemoryWizardPage)
self.uiNetworkAdaptersWizardPage = QtGui.QWizardPage()
self.uiNetworkAdaptersWizardPage.setObjectName(_fromUtf8("uiNetworkAdaptersWizardPage"))
@@ -304,11 +304,11 @@ class Ui_IOSRouterWizard(object):
self.uiChassisLabel.setText(_translate("IOSRouterWizard", "Chassis:", None))
self.uiEtherSwitchCheckBox.setText(_translate("IOSRouterWizard", "This is an EtherSwitch router", None))
self.uiMemoryWizardPage.setTitle(_translate("IOSRouterWizard", "Memory", None))
self.uiMemoryWizardPage.setSubTitle(_translate("IOSRouterWizard", "Please check the amount of memory (RAM) that you allocate to IOS. Not enough RAM could prevent IOS to start.", None))
self.uiMemoryWizardPage.setSubTitle(_translate("IOSRouterWizard", "Please check the amount of memory (RAM) that you allocate to IOS. Too much or not enough RAM could prevent IOS to start.", None))
self.uiRamLabel.setText(_translate("IOSRouterWizard", "Default RAM:", None))
self.uiRamSpinBox.setSuffix(_translate("IOSRouterWizard", " MiB", None))
self.uiTestIOSImagePushButton.setText(_translate("IOSRouterWizard", "&Test IOS image", None))
self.label.setText(_translate("IOSRouterWizard", "<html><head/><body><p><a href=\"http://tools.cisco.com/ITDIT/CFN/jsp/SearchBySoftware.jsp\"><span style=\" text-decoration: underline; color:#0000ff;\">Check for minimum RAM requirement</span></a></p></body></html>", None))
self.label.setText(_translate("IOSRouterWizard", "<html><head/><body><p><a href=\"http://tools.cisco.com/ITDIT/CFN/jsp/SearchBySoftware.jsp\"><span style=\" text-decoration: underline; color:#0000ff;\">Check for minimum and maximum RAM requirement</span></a></p></body></html>", None))
self.uiNetworkAdaptersWizardPage.setTitle(_translate("IOSRouterWizard", "Network adapters", None))
self.uiNetworkAdaptersWizardPage.setSubTitle(_translate("IOSRouterWizard", "Please choose the default network adapters that should be inserted into every new instance of this router.", None))
self.uiSlot0Label.setText(_translate("IOSRouterWizard", "slot 0:", None))

View File

@@ -19,6 +19,8 @@
Thread to wait for an IOS image to be decompressed.
"""
import zipfile
from gns3.qt import QtCore
from .decompress_ios import decompressIOS
@@ -52,6 +54,9 @@ class DecompressIOSThread(QtCore.QThread):
self._is_running = True
try:
decompressIOS(self._ios_image, self._destination_file)
except zipfile.BadZipFile as e:
self.error.emit("File {} is corrupted {}".format(self._ios_image, e), True)
return
except OSError as e:
self.error.emit("Could not decompress {}: {}".format(self._ios_image, e), True)
return

View File

@@ -282,8 +282,8 @@ class IOU(Module):
if len(self._settings["iourc_path"]) > 0:
try:
with open(self._settings["iourc_path"]) as f:
settings["iourc_content"] = f.read()
with open(self._settings["iourc_path"], 'rb') as f:
settings["iourc_content"] = f.read().decode("utf-8")
except OSError as e:
print("Can't open iourc file {}: {}".format(self._settings["iourc_path"], e))

View File

@@ -150,10 +150,18 @@ class IOUDevice(VM):
# other initial settings will be applied when the router has been created
if initial_settings:
self._inital_settings = initial_settings
else:
self._inital_settings = {}
if initial_config:
params["initial_config_content"] = self._readBaseConfig(initial_config)
if len(self._module._settings["iourc_path"]) > 0:
try:
with open(self._module._settings["iourc_path"], 'rb') as f:
self._inital_settings["iourc_content"] = f.read().decode("utf-8")
except OSError as e:
print("Can't open iourc file {}: {}".format(self._module._settings["iourc_path"], e))
self.httpPost("/iou/vms", self._setupCallback, body=params)
def _setupCallback(self, result, error=False, **kwargs):

View File

@@ -356,7 +356,7 @@ class VPCSDevice(VM):
self.server_error_signal.emit(self.id(), result["message"])
else:
if "startup_script" in result:
if "startup_script" in result and result["startup_script"] is not None:
config_path = os.path.join(self._export_directory, normalize_filename(self.name())) + "_startup.vpc"
config = result["startup_script"].encode("utf-8")
try:

View File

@@ -110,7 +110,11 @@ class ServerPreferencesPage(QtGui.QWidget, Ui_ServerPreferencesPageWidget):
"""
host = item.text(0)
port = int(item.text(1))
try:
port = int(item.text(1))
except ValueError:
QtGui.QMessageBox.critical(self, "Remote server", "Invalid port")
return
self.uiRemoteServerPortLineEdit.setText(host)
self.uiRemoteServerPortSpinBox.setValue(port)
@@ -232,6 +236,16 @@ class ServerPreferencesPage(QtGui.QWidget, Ui_ServerPreferencesPageWidget):
new_settings["projects_path"] = current_settings["projects_path"]
new_settings["report_errors"] = current_settings["report_errors"]
if new_settings["console_end_port_range"] <= new_settings["console_start_port_range"]:
QtGui.QMessageBox.critical(self, "Local", "Invalid console port range from {} to {}".format(new_settings["console_start_port_range"],
new_settings["console_end_port_range"]))
return
if new_settings["udp_end_port_range"] <= new_settings["udp_start_port_range"]:
QtGui.QMessageBox.critical(self, "Local", "Invalid UDP port range from {} to {}".format(new_settings["udp_start_port_range"],
new_settings["udp_end_port_range"]))
return
if new_settings["auto_start"]:
if not os.path.isfile(new_settings["path"]):
QtGui.QMessageBox.critical(self, "Local server", "Could not find local server {}".format(new_settings["path"]))

View File

@@ -49,7 +49,10 @@ class Project(QtCore.QObject):
self._type = None
self._name = "untitled"
self._project_instances.add(self)
# Manage project creations on multiple servers
self._created_servers = set()
self._callback_finish_creating_on_server = {}
super().__init__()
@@ -223,17 +226,22 @@ class Project(QtCore.QObject):
"""
if server not in self._created_servers:
func = functools.partial(self._projectOnServerCreated, method, path, callback, body, context=context)
func = functools.partial(self._projectOnServerCreated, method, path, callback, body, context=context, server=server)
body = {
"name": self._name,
"temporary": self._temporary,
"project_id": self._id
}
if server == self._servers.localServer():
body["path"] = self.filesDir()
if server not in self._callback_finish_creating_on_server:
self._callback_finish_creating_on_server[server] = []
body = {
"name": self._name,
"temporary": self._temporary,
"project_id": self._id
}
if server == self._servers.localServer():
body["path"] = self.filesDir()
server.post("/projects", func, body)
server.post("/projects", func, body)
else:
# If the project creation is already in progress we bufferizze the query
self._callback_finish_creating_on_server[server].append(func)
else:
self._projectOnServerCreated(method, path, callback, body, params={}, server=server, context=context)
@@ -272,6 +280,13 @@ class Project(QtCore.QObject):
path = "/projects/{project_id}{path}".format(project_id=self._id, path=path)
server.createHTTPQuery(method, path, callback, body=body, context=context)
# Call all operations waiting for project creation:
if server in self._callback_finish_creating_on_server:
callbacks = self._callback_finish_creating_on_server[server]
del self._callback_finish_creating_on_server[server]
for call in callbacks:
call()
def close(self, local_server_shutdown=False):
"""Close project"""
@@ -307,7 +322,10 @@ class Project(QtCore.QObject):
if len(self._created_servers) == 0:
self._closed = True
self.project_closed_signal.emit()
self._project_instances.remove(self)
try:
self._project_instances.remove(self)
except KeyError:
return
def moveFromTemporaryToPath(self, new_path):
"""

View File

@@ -514,8 +514,9 @@ class Topology(object):
:params: A topology dictionnary
"""
topology["project_id"] = str(uuid.uuid4())
for key, node in enumerate(topology["topology"]["nodes"]):
topology["topology"]["nodes"][key]["vm_id"] = str(uuid.uuid4())
if "nodes" in topology["topology"]:
for key, node in enumerate(topology["topology"]["nodes"]):
topology["topology"]["nodes"][key]["vm_id"] = str(uuid.uuid4())
return topology
def loadFile(self, path, project):
@@ -743,7 +744,6 @@ class Topology(object):
image_path = topology_image["path"]
image_path = os.path.normpath(image_path)
print(image_path)
if not os.path.isfile(image_path):
topology_file_errors.append("Path to image {} doesn't exist".format(image_path))
continue

View File

@@ -84,8 +84,6 @@ class ProcessFilesThread(QtCore.QThread):
# start create the destination sub-directories
for directory in dirs:
if not self._is_running:
return
try:
destination_dir = os.path.join(base_dir, directory)
os.makedirs(destination_dir)
@@ -94,11 +92,11 @@ class ProcessFilesThread(QtCore.QThread):
except OSError as e:
self.error.emit("Could not create directory {}: {}".format(destination_dir, e), True)
return
if not self._is_running:
return
# finally the files themselves
for sfile in filenames:
if not self._is_running:
return
source_file = os.path.join(path, sfile)
destination_file = os.path.join(base_dir, sfile)
try:
@@ -113,6 +111,8 @@ class ProcessFilesThread(QtCore.QThread):
else:
log.warning("Cannot copy: {}".format(e))
self.error.emit("Could not copy file to {}: {}".format(destination_file, e), False)
if not self._is_running:
return
copied += 1
# update the progress made
progress = float(copied) / file_count * 100

View File

@@ -71,7 +71,11 @@ class WaitForConnectionThread(QtCore.QThread):
connection_success = True
break
if not self._is_running:
return
if not connection_success:
# let the GUI know about the connection was unsuccessful and finish the thread
self.error.emit("Could not connect to {} on port {}: {}".format(self._host,
self._port,

View File

@@ -25,5 +25,5 @@ or negative for a release candidate or beta (after the base version
number has been incremented)
"""
__version__ = "1.3.0"
__version_info__ = (1, 3, 0, 0)
__version__ = "1.3.1"
__version_info__ = (1, 3, 1, 0)

View File

@@ -46,17 +46,17 @@ setup(
long_description=open("README.rst", "r").read(),
install_requires=[
"apache-libcloud>=0.14.1",
"requests==2.4.3",
"paramiko==1.15.1",
"gns3-converter",
"raven==5.2.0"
"requests>=2.4.3",
"paramiko>=1.15.1",
"gns3-converter>=1.2.3",
"raven>=5.2.0"
],
entry_points={
"gui_scripts": [
"gns3 = gns3.main:main",
]
},
packages=find_packages(),
packages=find_packages(".", exclude=["docs", "tests"]),
include_package_data=True,
package_data={"gns3": ["configs/*.txt"]},
platforms="any",

View File

@@ -84,7 +84,7 @@ def test_post_not_connected(http_client, request, network_manager, response):
assert network_manager.get.called
response.header.return_value = "application/json"
response.readAll.return_value = ("{\"version\": \"" + __version__ + "\"}").encode()
response.readAll.return_value = ("{\"version\": \"" + __version__ + "\", \"local\": true}").encode()
# Trigger the completion of /version
response.finished.emit()
@@ -98,7 +98,6 @@ def test_post_not_connected(http_client, request, network_manager, response):
assert callback.called
args, kwargs = callback.call_args
print(kwargs["context"])
assert kwargs["context"]["toto"] == 42

View File

@@ -119,6 +119,44 @@ def test_project_post_non_created_project_remote_server(remote_server):
assert kwargs["body"] == {"test": "test"}
def test_project_post_non_created_project_remote_server_two_query(remote_server):
"""
Test a post on a remote servers. The project
is not created on the server and should be created automaticaly.
And after make the call
"""
uuid = uuid4()
project = Project()
project._created_servers = set()
project.setId(uuid)
with patch("gns3.http_client.HTTPClient.createHTTPQuery") as mock:
project.post(remote_server, "/test", lambda: 0, body={"test": "test"})
args, kwargs = mock.call_args
assert args[0] == "POST"
assert args[1] == "/projects"
assert kwargs["body"] == {"name": "untitled", "temporary": False, "project_id": uuid}
project.post(remote_server, "/test2", lambda: 0, body={"test": "test"})
assert mock.call_count == 1
args[2]({}, server=remote_server)
assert len(project._created_servers) == 1
calls = mock.mock_calls
name, args, kwargs = mock.mock_calls[1]
assert args[0] == "POST"
assert args[1] == "/projects/{uuid}/test".format(uuid=uuid)
assert kwargs["body"] == {"test": "test"}
args, kwargs = mock.call_args
assert args[0] == "POST"
assert args[1] == "/projects/{uuid}/test2".format(uuid=uuid)
assert kwargs["body"] == {"test": "test"}
def test_project_post_on_created_project(local_server):
"""
Test a post on a remote servers.

View File

@@ -401,10 +401,14 @@ def test_load_1_2_topology(project, monkeypatch, main_window, tmpdir):
monkeypatch.setattr('gns3.main_window.MainWindow.instance', lambda: main_window)
project_call = 0
# We return an uuid for each HTTP post
def http_loader(self, method, path, callback, body={}, **kwargs):
if path == "/projects":
callback({"project_id": uuid.uuid4(), "path": str(tmpdir)}, error=False, server=local_server)
project_call += 1
assert project_call < 2
else:
callback({"vm_id": uuid.uuid4()})