1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-12-23 23:38:21 +00:00

Merge branch 'master' into unstable

This commit is contained in:
Julien Duponchelle 2015-05-28 12:04:01 +02:00
commit 9442042d7a
16 changed files with 126 additions and 23 deletions

2
gns3server.bat Normal file
View File

@ -0,0 +1,2 @@
SET PYTHONPATH=.
python.exe gns3server/main.py --debug --local

View File

@ -96,9 +96,11 @@ class ProjectHandler:
project.name = request.json.get("name", project.name)
project_path = request.json.get("path", project.path)
if project_path != project.path:
old_path = project.path
project.path = project_path
for module in MODULES:
yield from module.instance().project_moved(project)
yield from project.clean_old_path(old_path)
# Very important we need to remove temporary flag after moving the project
project.temporary = request.json.get("temporary", project.temporary)
response.json(project)

View File

@ -344,7 +344,7 @@ class VirtualBoxHandler:
vm = vbox_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
adapter_number = int(request.match_info["adapter_number"])
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
vm.start_capture(adapter_number, pcap_file_path)
yield from vm.start_capture(adapter_number, pcap_file_path)
response.json({"pcap_file_path": pcap_file_path})
@Route.post(

View File

@ -71,5 +71,6 @@ def main():
from gns3server.run import run
run()
if __name__ == '__main__':
main()

View File

@ -851,7 +851,7 @@ class Router(BaseVM):
return self._disk1
@asyncio.coroutine
def disk1(self, disk1):
def set_disk1(self, disk1):
"""
Sets the size (MB) for PCMCIA disk1.

View File

@ -150,13 +150,17 @@ class Project:
self._path = path
self._update_temporary_file()
# The order of operation is important because we want to avoid losing
# data
if old_path:
@asyncio.coroutine
def clean_old_path(self, old_path):
"""
Called after a project location change. All the modules should
have been notified before
"""
if self._temporary:
try:
shutil.rmtree(old_path)
yield from wait_run_in_executor(shutil.rmtree, old_path)
except OSError as e:
raise aiohttp.web.HTTPConflict(text="Can't remove temporary directory {}: {}".format(old_path, e))
log.warn("Can't remove temporary directory {}: {}".format(old_path, e))
@property
def name(self):

View File

@ -756,6 +756,8 @@ class QemuVM(BaseVM):
adapter_number=adapter_number))
if self.is_running():
raise QemuError("Sorry, adding a link to a started Qemu VM is not supported.")
# FIXME: does the code below work? very undocumented feature...
# dynamically configure an UDP tunnel on the QEMU VM adapter
if nio and isinstance(nio, NIOUDP):
if self._legacy_networking:
@ -766,7 +768,6 @@ class QemuVM(BaseVM):
nio.rport,
nio.rhost))
else:
# FIXME: does it work? very undocumented feature...
# Apparently there is a bug in Qemu...
# netdev_add [user|tap|socket|hubport|netmap],id=str[,prop=value][,...] -- add host network device
# netdev_del id -- remove host network device
@ -800,6 +801,7 @@ class QemuVM(BaseVM):
adapter_number=adapter_number))
if self.is_running():
# FIXME: does the code below work? very undocumented feature...
# dynamically disable the QEMU VM adapter
yield from self._control_vm("host_net_remove {} gns3-{}".format(adapter_number, adapter_number))
yield from self._control_vm("host_net_add user vlan={},name=gns3-{}".format(adapter_number, adapter_number))

View File

@ -168,6 +168,8 @@ class VirtualBox(BaseManager):
vms = []
result = yield from self.execute("list", ["vms"])
for line in result:
if line[0] != '"' or line[-1:] != "}":
continue # Broken output (perhaps a carriage return in VM name
vmname, _ = line.rsplit(' ', 1)
vmname = vmname.strip('"')
if vmname == "<inaccessible>":

View File

@ -799,7 +799,7 @@ class VirtualBoxVM(BaseVM):
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
except KeyError:
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
@ -830,7 +830,7 @@ class VirtualBoxVM(BaseVM):
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
except KeyError:
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
@ -851,6 +851,7 @@ class VirtualBoxVM(BaseVM):
adapter_number=adapter_number))
return nio
@asyncio.coroutine
def start_capture(self, adapter_number, output_file):
"""
Starts a packet capture.
@ -861,10 +862,14 @@ class VirtualBoxVM(BaseVM):
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
except KeyError:
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
adapter_number=adapter_number))
vm_state = yield from self._get_vm_state()
if vm_state == "running" or vm_state == "paused" or vm_state == "stuck":
raise VirtualBoxError("Sorry, packet capturing on a started VirtualBox VM is not supported.")
nio = adapter.get_nio(0)
if nio.capturing:
raise VirtualBoxError("Packet capture is already activated on adapter {adapter_number}".format(adapter_number=adapter_number))
@ -883,7 +888,7 @@ class VirtualBoxVM(BaseVM):
try:
adapter = self._ethernet_adapters[adapter_number]
except IndexError:
except KeyError:
raise VirtualBoxError("Adapter {adapter_number} doesn't exist on VirtualBox VM '{name}'".format(name=self.name,
adapter_number=adapter_number))

View File

@ -269,7 +269,10 @@ class VPCSVM(BaseVM):
except asyncio.TimeoutError:
if self._process.returncode is None:
log.warn("VPCS process {} is still running... killing it".format(self._process.pid))
self._process.kill()
try:
self._process.kill()
except OSError as e:
raise VPCSError("Can not stop the VPCS process: {}".format(e))
self._process = None
self._started = False

View File

@ -81,12 +81,30 @@ class ColouredStreamHandler(logging.StreamHandler):
self.handleError(record)
class WinStreamHandler(logging.StreamHandler):
def emit(self, record):
if sys.stdin.encoding != "utf-8":
record = record
stream = self.stream
try:
msg = self.formatter.format(record, stream.isatty())
stream.write(msg.encode(stream.encoding, errors="replace").decode(stream.encoding))
stream.write(self.terminator)
self.flush()
pass
except Exception:
self.handleError(record)
def init_logger(level, logfile=None, quiet=False):
if logfile and len(logfile) > 0:
stream_handler = logging.FileHandler(logfile)
stream_handler.formatter = ColouredFormatter("{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{")
elif sys.platform.startswith("win"):
stream_handler = logging.StreamHandler(sys.stdout)
stream_handler = WinStreamHandler(sys.stdout)
stream_handler.formatter = ColouredFormatter("{asctime} {levelname} {filename}:{lineno} {message}", "%Y-%m-%d %H:%M:%S", "{")
else:
stream_handler = ColouredStreamHandler(sys.stdout)

View File

@ -15,6 +15,9 @@
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
import os
import pytest
def test_udp_allocation(server, project):
response = server.post('/projects/{}/ports/udp'.format(project.id), {}, example=True)
@ -22,6 +25,8 @@ def test_udp_allocation(server, project):
assert response.json == {'udp_port': 10000}
# Netfifaces is not available on Travis
@pytest.mark.skipif(os.environ.get("TRAVIS", False) is not False, reason="Not supported on Travis")
def test_interfaces(server):
response = server.get('/interfaces', example=True)
assert response.status == 200

View File

@ -23,6 +23,7 @@ import uuid
import os
import asyncio
import aiohttp
from unittest.mock import patch
from tests.utils import asyncio_patch
@ -91,18 +92,43 @@ def test_update_temporary_project(server):
assert response.json["temporary"] is False
def test_update_path_project(server, tmpdir):
def test_update_path_project_temporary(server, tmpdir):
os.makedirs(str(tmpdir / "a"))
os.makedirs(str(tmpdir / "b"))
with patch("gns3server.modules.project.Project.is_local", return_value=True):
response = server.post("/projects", {"name": "first_name"})
response = server.post("/projects", {"name": "first_name", "path": str(tmpdir / "a"), "temporary": True})
assert response.status == 201
assert response.json["name"] == "first_name"
query = {"name": "second_name", "path": str(tmpdir)}
query = {"name": "second_name", "path": str(tmpdir / "b")}
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
assert response.status == 200
assert response.json["path"] == str(tmpdir)
assert response.json["path"] == str(tmpdir / "b")
assert response.json["name"] == "second_name"
assert not os.path.exists(str(tmpdir / "a"))
assert os.path.exists(str(tmpdir / "b"))
def test_update_path_project_non_temporary(server, tmpdir):
os.makedirs(str(tmpdir / "a"))
os.makedirs(str(tmpdir / "b"))
with patch("gns3server.modules.project.Project.is_local", return_value=True):
response = server.post("/projects", {"name": "first_name", "path": str(tmpdir / "a")})
assert response.status == 201
assert response.json["name"] == "first_name"
query = {"name": "second_name", "path": str(tmpdir / "b")}
response = server.put("/projects/{project_id}".format(project_id=response.json["project_id"]), query, example=True)
assert response.status == 200
assert response.json["path"] == str(tmpdir / "b")
assert response.json["name"] == "second_name"
assert os.path.exists(str(tmpdir / "a"))
assert os.path.exists(str(tmpdir / "b"))
def test_update_path_project_non_local(server, tmpdir):

View File

@ -73,10 +73,6 @@ def test_changing_path_temporary_flag(tmpdir):
assert os.path.exists(os.path.join(p.path, ".gns3_temporary"))
p.path = str(tmpdir)
p.temporary = False
assert not os.path.exists(os.path.join(p.path, ".gns3_temporary"))
assert not os.path.exists(os.path.join(str(tmpdir), ".gns3_temporary"))
assert not os.path.exists(original_path)
def test_temporary_path():

View File

@ -20,10 +20,14 @@ import pytest
import tempfile
import os
import stat
import asyncio
from unittest.mock import patch
from gns3server.modules.virtualbox import VirtualBox
from gns3server.modules.virtualbox.virtualbox_error import VirtualBoxError
from unittest.mock import patch
from tests.utils import asyncio_patch
@pytest.fixture(scope="module")
@ -65,3 +69,30 @@ def test_vboxmanage_path(manager, tmpdir):
tmpfile = tempfile.NamedTemporaryFile()
with patch("gns3server.config.Config.get_section_config", return_value={"vboxmanage_path": path}):
assert manager.find_vboxmanage() == path
def test_get_list(manager, loop):
vm_list = ['"Windows 8.1" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Carriage',
'Return" {27b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"<inaccessible>" {42b4d095-ff5f-4ac4-bb9d-5f2c7861c1f1}',
'"Linux Microcore 4.7.1" {ccd8c50b-c172-457d-99fa-dd69371ede0e}']
@asyncio.coroutine
def execute_mock(cmd, args):
if cmd == "list":
return vm_list
else:
if args[0] == "Windows 8.1":
return ["memory=512"]
elif args[0] == "Linux Microcore 4.7.1":
return ["memory=256"]
assert False, "Unknow {} {}".format(cmd, args)
with asyncio_patch("gns3server.modules.virtualbox.VirtualBox.execute") as mock:
mock.side_effect = execute_mock
vms = loop.run_until_complete(asyncio.async(manager.get_list()))
assert vms == [
{"vmname": "Windows 8.1", "ram": 512},
{"vmname": "Linux Microcore 4.7.1", "ram": 256}
]

View File

@ -54,3 +54,9 @@ def test_vm_invalid_virtualbox_api_version(loop, project, manager):
with pytest.raises(VirtualBoxError):
vm = VirtualBoxVM("test", "00010203-0405-0607-0809-0a0b0c0d0e0f", project, manager, "test", False)
loop.run_until_complete(asyncio.async(vm.create()))
def test_vm_adapter_add_nio_binding_adapter_not_exist(loop, vm, manager, free_console_port):
nio = manager.create_nio(manager.vboxmanage_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"})
with pytest.raises(VirtualBoxError):
loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(15, nio)))