mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 17:28:08 +00:00
Merge branch '2.1' into embed_shell
This commit is contained in:
commit
e5bc7c7a66
3
.gitignore
vendored
3
.gitignore
vendored
@ -53,3 +53,6 @@ docs/_build
|
|||||||
vpcs.hist
|
vpcs.hist
|
||||||
startup.vpcs
|
startup.vpcs
|
||||||
.gns3_shell_history
|
.gns3_shell_history
|
||||||
|
|
||||||
|
# Virtualenv
|
||||||
|
env
|
11
CHANGELOG
11
CHANGELOG
@ -1,5 +1,16 @@
|
|||||||
# Change Log
|
# Change Log
|
||||||
|
|
||||||
|
## 2.0.3 13/06/2017
|
||||||
|
|
||||||
|
* Fixes #1068 - handle zipfile encoding issues at project duplication
|
||||||
|
* Fix: #1066 - Catching parsing errors at linked vbox file
|
||||||
|
* Ignoring virtualenv directory at gitignore
|
||||||
|
* Escaping VPCS name in regex #1067
|
||||||
|
* Fix racecondition when listing interface
|
||||||
|
* Fix Qemu disk creation with unicode characters not supported by local filesystem #1058 (#1063)
|
||||||
|
* Fix when config file doesn't have computes section (#1062)
|
||||||
|
* Check aiohttp version
|
||||||
|
|
||||||
## 2.0.2 30/05/2017
|
## 2.0.2 30/05/2017
|
||||||
|
|
||||||
* Set correct permission on ubridge when doing a remote installation
|
* Set correct permission on ubridge when doing a remote installation
|
||||||
|
@ -22,7 +22,7 @@ master
|
|||||||
master is the next stable release, you can test it in your day to day activities.
|
master is the next stable release, you can test it in your day to day activities.
|
||||||
Bug fixes or small improvements pull requests go here.
|
Bug fixes or small improvements pull requests go here.
|
||||||
|
|
||||||
1.x (1.4 for example)
|
2.x (2.1 for example)
|
||||||
********
|
********
|
||||||
Next major release
|
Next major release
|
||||||
|
|
||||||
|
@ -247,8 +247,13 @@ class Qemu(BaseManager):
|
|||||||
directory = self.get_images_directory()
|
directory = self.get_images_directory()
|
||||||
os.makedirs(directory, exist_ok=True)
|
os.makedirs(directory, exist_ok=True)
|
||||||
path = os.path.join(directory, os.path.basename(path))
|
path = os.path.join(directory, os.path.basename(path))
|
||||||
|
|
||||||
|
try:
|
||||||
if os.path.exists(path):
|
if os.path.exists(path):
|
||||||
raise QemuError("Could not create disk image {} already exist".format(path))
|
raise QemuError("Could not create disk image {} already exist".format(path))
|
||||||
|
except UnicodeEncodeError:
|
||||||
|
raise QemuError("Could not create disk image {}, "
|
||||||
|
"path contains characters not supported by filesystem".format(path))
|
||||||
|
|
||||||
command = [qemu_img, "create", "-f", img_format]
|
command = [qemu_img, "create", "-f", img_format]
|
||||||
for option in sorted(options.keys()):
|
for option in sorted(options.keys()):
|
||||||
|
@ -209,7 +209,12 @@ class VirtualBoxVM(BaseNode):
|
|||||||
Fix the VM uuid in the case of linked clone
|
Fix the VM uuid in the case of linked clone
|
||||||
"""
|
"""
|
||||||
if os.path.exists(self._linked_vbox_file()):
|
if os.path.exists(self._linked_vbox_file()):
|
||||||
|
try:
|
||||||
tree = ET.parse(self._linked_vbox_file())
|
tree = ET.parse(self._linked_vbox_file())
|
||||||
|
except ET.ParseError:
|
||||||
|
raise VirtualBoxError("Cannot modify VirtualBox linked nodes file. "
|
||||||
|
"File {} is corrupted.".format(self._linked_vbox_file()))
|
||||||
|
|
||||||
machine = tree.getroot().find("{http://www.virtualbox.org/}Machine")
|
machine = tree.getroot().find("{http://www.virtualbox.org/}Machine")
|
||||||
if machine is not None and machine.get("uuid") != "{" + self.id + "}":
|
if machine is not None and machine.get("uuid") != "{" + self.id + "}":
|
||||||
|
|
||||||
|
@ -169,7 +169,8 @@ class VPCSVM(BaseNode):
|
|||||||
if self.script_file:
|
if self.script_file:
|
||||||
content = self.startup_script
|
content = self.startup_script
|
||||||
content = content.replace(self._name, new_name)
|
content = content.replace(self._name, new_name)
|
||||||
content = re.sub(r"^set pcname .+$", "set pcname " + new_name, content, flags=re.MULTILINE)
|
escaped_name = re.escape(new_name)
|
||||||
|
content = re.sub(r"^set pcname .+$", "set pcname " + escaped_name, content, flags=re.MULTILINE)
|
||||||
self.startup_script = content
|
self.startup_script = content
|
||||||
|
|
||||||
super(VPCSVM, VPCSVM).name.__set__(self, new_name)
|
super(VPCSVM, VPCSVM).name.__set__(self, new_name)
|
||||||
|
@ -154,7 +154,7 @@ class Controller:
|
|||||||
password=server_config.get("password", ""),
|
password=server_config.get("password", ""),
|
||||||
force=True)
|
force=True)
|
||||||
except aiohttp.web_exceptions.HTTPConflict as e:
|
except aiohttp.web_exceptions.HTTPConflict as e:
|
||||||
log.fatal("Can't acces to the local server, make sure anything else is not running on the same port")
|
log.fatal("Can't access to the local server, make sure anything else is not running on the same port")
|
||||||
sys.exit(1)
|
sys.exit(1)
|
||||||
for c in computes:
|
for c in computes:
|
||||||
try:
|
try:
|
||||||
@ -249,7 +249,7 @@ class Controller:
|
|||||||
self.gns3vm.settings = data["gns3vm"]
|
self.gns3vm.settings = data["gns3vm"]
|
||||||
|
|
||||||
self.load_appliances()
|
self.load_appliances()
|
||||||
return data["computes"]
|
return data.get("computes", [])
|
||||||
|
|
||||||
@asyncio.coroutine
|
@asyncio.coroutine
|
||||||
def load_projects(self):
|
def load_projects(self):
|
||||||
|
@ -42,8 +42,8 @@ class Appliance:
|
|||||||
|
|
||||||
# Version of the gui before 2.1 use linked_base
|
# Version of the gui before 2.1 use linked_base
|
||||||
# and the server linked_clone
|
# and the server linked_clone
|
||||||
if "linked_base" in data:
|
if "linked_base" in self._data:
|
||||||
self._data["linked_clone"] = data.pop("linked_base")
|
self._data["linked_clone"] = self._data.pop("linked_base")
|
||||||
if data["node_type"] == "iou" and "image" in data:
|
if data["node_type"] == "iou" and "image" in data:
|
||||||
del self._data["image"]
|
del self._data["image"]
|
||||||
self._builtin = builtin
|
self._builtin = builtin
|
||||||
|
@ -738,7 +738,7 @@ class Project:
|
|||||||
f.write(data)
|
f.write(data)
|
||||||
with open(os.path.join(tmpdir, "project.gns3p"), "rb") as f:
|
with open(os.path.join(tmpdir, "project.gns3p"), "rb") as f:
|
||||||
project = yield from import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True)
|
project = yield from import_project(self._controller, str(uuid.uuid4()), f, location=location, name=name, keep_compute_id=True)
|
||||||
except OSError as e:
|
except (OSError, UnicodeEncodeError) as e:
|
||||||
raise aiohttp.web.HTTPConflict(text="Can not duplicate project: {}".format(str(e)))
|
raise aiohttp.web.HTTPConflict(text="Can not duplicate project: {}".format(str(e)))
|
||||||
|
|
||||||
if previous_status == "closed":
|
if previous_status == "closed":
|
||||||
|
@ -57,7 +57,7 @@ class CrashReport:
|
|||||||
Report crash to a third party service
|
Report crash to a third party service
|
||||||
"""
|
"""
|
||||||
|
|
||||||
DSN = "sync+https://67b93949a78d4ef5978388cc4b8906f9:271ee1dd01db4a39b919097f452cb6c5@sentry.io/38482"
|
DSN = "sync+https://9b15627f2ddf4e21a9880536354bfcb5:b31dee5d3abf4c74844895432193d0ac@sentry.io/38482"
|
||||||
if hasattr(sys, "frozen"):
|
if hasattr(sys, "frozen"):
|
||||||
cacert = get_resource("cacert.pem")
|
cacert = get_resource("cacert.pem")
|
||||||
if cacert is not None and os.path.isfile(cacert):
|
if cacert is not None and os.path.isfile(cacert):
|
||||||
|
@ -198,12 +198,13 @@ def interfaces():
|
|||||||
|
|
||||||
results = []
|
results = []
|
||||||
if not sys.platform.startswith("win"):
|
if not sys.platform.startswith("win"):
|
||||||
for interface in sorted(psutil.net_if_addrs().keys()):
|
net_if_addrs = psutil.net_if_addrs()
|
||||||
|
for interface in sorted(net_if_addrs.keys()):
|
||||||
ip_address = ""
|
ip_address = ""
|
||||||
mac_address = ""
|
mac_address = ""
|
||||||
netmask = ""
|
netmask = ""
|
||||||
interface_type = "ethernet"
|
interface_type = "ethernet"
|
||||||
for addr in psutil.net_if_addrs()[interface]:
|
for addr in net_if_addrs[interface]:
|
||||||
# get the first available IPv4 address only
|
# get the first available IPv4 address only
|
||||||
if addr.family == socket.AF_INET:
|
if addr.family == socket.AF_INET:
|
||||||
ip_address = addr.address
|
ip_address = addr.address
|
||||||
|
@ -178,6 +178,26 @@ def test_create_image_exist(loop, tmpdir, fake_qemu_img_binary):
|
|||||||
assert not process.called
|
assert not process.called
|
||||||
|
|
||||||
|
|
||||||
|
def test_create_image_with_not_supported_characters_by_filesystem(loop, tmpdir, fake_qemu_img_binary):
|
||||||
|
open(str(tmpdir / "hda.qcow2"), "w+").close()
|
||||||
|
|
||||||
|
options = {
|
||||||
|
"format": "raw",
|
||||||
|
"size": 100
|
||||||
|
}
|
||||||
|
|
||||||
|
# patching os.makedirs is necessary as it depends on already mocked os.path.exists
|
||||||
|
with asyncio_patch("asyncio.create_subprocess_exec", return_value=MagicMock()) as process, \
|
||||||
|
patch("gns3server.compute.qemu.Qemu.get_images_directory", return_value=str(tmpdir)), \
|
||||||
|
patch("os.path.exists", side_effect=UnicodeEncodeError('error', u"", 1, 2, 'Emulated Unicode Err')),\
|
||||||
|
patch("os.makedirs"):
|
||||||
|
|
||||||
|
with pytest.raises(QemuError):
|
||||||
|
loop.run_until_complete(asyncio.async(Qemu.instance().create_disk(
|
||||||
|
fake_qemu_img_binary, "hda.qcow2", options)))
|
||||||
|
assert not process.called
|
||||||
|
|
||||||
|
|
||||||
def test_get_kvm_archs_kvm_ok(loop):
|
def test_get_kvm_archs_kvm_ok(loop):
|
||||||
|
|
||||||
with patch("os.path.exists", return_value=True):
|
with patch("os.path.exists", return_value=True):
|
||||||
|
@ -113,3 +113,15 @@ def test_patch_vm_uuid(vm):
|
|||||||
with open(vm._linked_vbox_file()) as f:
|
with open(vm._linked_vbox_file()) as f:
|
||||||
c = f.read()
|
c = f.read()
|
||||||
assert "{" + vm.id + "}" in c
|
assert "{" + vm.id + "}" in c
|
||||||
|
|
||||||
|
|
||||||
|
def test_patch_vm_uuid_with_corrupted_file(vm):
|
||||||
|
xml = """<?xml version="1.0"?>
|
||||||
|
<VirtualBox>
|
||||||
|
"""
|
||||||
|
os.makedirs(os.path.join(vm.working_dir, vm._vmname), exist_ok=True)
|
||||||
|
with open(vm._linked_vbox_file(), "w+") as f:
|
||||||
|
f.write(xml)
|
||||||
|
vm._linked_clone = True
|
||||||
|
with pytest.raises(VirtualBoxError):
|
||||||
|
vm._patch_vm_uuid()
|
||||||
|
@ -251,6 +251,12 @@ def test_update_startup_script_h(vm):
|
|||||||
assert f.read() == "set pcname pc1\n"
|
assert f.read() == "set pcname pc1\n"
|
||||||
|
|
||||||
|
|
||||||
|
def test_update_startup_script_with_escaping_characters_in_name(vm):
|
||||||
|
vm.startup_script = "set pcname initial-name\n"
|
||||||
|
vm.name = "test\\"
|
||||||
|
assert vm.startup_script == "set pcname test\\\n"
|
||||||
|
|
||||||
|
|
||||||
def test_get_startup_script(vm):
|
def test_get_startup_script(vm):
|
||||||
content = "echo GNS3 VPCS\nip 192.168.1.2"
|
content = "echo GNS3 VPCS\nip 192.168.1.2"
|
||||||
vm.startup_script = content
|
vm.startup_script = content
|
||||||
|
@ -62,6 +62,16 @@ def test_load_controller_settings(controller, controller_config_path, async_run)
|
|||||||
assert controller.gns3vm.settings["vmname"] == "Test VM"
|
assert controller.gns3vm.settings["vmname"] == "Test VM"
|
||||||
|
|
||||||
|
|
||||||
|
def test_load_controller_settings_with_no_computes_section(controller, controller_config_path, async_run):
|
||||||
|
controller.save()
|
||||||
|
with open(controller_config_path) as f:
|
||||||
|
data = json.load(f)
|
||||||
|
del data['computes']
|
||||||
|
with open(controller_config_path, "w+") as f:
|
||||||
|
json.dump(data, f)
|
||||||
|
assert len(async_run(controller._load_controller_settings())) == 0
|
||||||
|
|
||||||
|
|
||||||
def test_import_computes_1_x(controller, controller_config_path, async_run):
|
def test_import_computes_1_x(controller, controller_config_path, async_run):
|
||||||
"""
|
"""
|
||||||
At first start the server should import the
|
At first start the server should import the
|
||||||
|
@ -20,6 +20,7 @@ import os
|
|||||||
import sys
|
import sys
|
||||||
import pytest
|
import pytest
|
||||||
import aiohttp
|
import aiohttp
|
||||||
|
import zipstream
|
||||||
from unittest.mock import MagicMock
|
from unittest.mock import MagicMock
|
||||||
from tests.utils import AsyncioMagicMock, asyncio_patch
|
from tests.utils import AsyncioMagicMock, asyncio_patch
|
||||||
from unittest.mock import patch
|
from unittest.mock import patch
|
||||||
@ -473,6 +474,15 @@ def test_duplicate(project, async_run, controller):
|
|||||||
assert list(new_project.nodes.values())[1].compute.id == "remote"
|
assert list(new_project.nodes.values())[1].compute.id == "remote"
|
||||||
|
|
||||||
|
|
||||||
|
def test_duplicate_with_zipfile_encoding_issues(project, async_run, controller):
|
||||||
|
zf = zipstream.ZipFile()
|
||||||
|
zf.writestr('test\udcc3', "data")
|
||||||
|
|
||||||
|
with asyncio_patch('gns3server.controller.project.export_project', return_value=zf):
|
||||||
|
with pytest.raises(aiohttp.web.HTTPConflict):
|
||||||
|
async_run(project.duplicate(name="Hello"))
|
||||||
|
|
||||||
|
|
||||||
def test_snapshots(project):
|
def test_snapshots(project):
|
||||||
"""
|
"""
|
||||||
List the snapshots
|
List the snapshots
|
||||||
|
@ -46,4 +46,3 @@ def test_put_gns3vm(http_controller):
|
|||||||
def test_get_gns3vm(http_controller):
|
def test_get_gns3vm(http_controller):
|
||||||
response = http_controller.get('/gns3vm', example=True)
|
response = http_controller.get('/gns3vm', example=True)
|
||||||
assert response.status == 200
|
assert response.status == 200
|
||||||
print(response.json)
|
|
||||||
|
Loading…
Reference in New Issue
Block a user