1
0
mirror of https://github.com/GNS3/gns3-server synced 2024-11-24 17:28:08 +00:00

Merge remote-tracking branch 'origin/2.0' into 2.0

Conflicts:
	gns3server/web/web_server.py
This commit is contained in:
grossmj 2016-06-16 11:19:03 -06:00
commit da58a65075
12 changed files with 69 additions and 31 deletions

View File

@ -1,5 +1,19 @@
# Change Log
## 1.5.0rc2 15/06/2016
* Fix black screen with Qt app in Docker container
* Detect when command in the container exit
* Docker when the aux console exit and restart it
* Pass by default the environment variable container=docker
* Fix busybox binary location
* Avoid loosing console port for Docker
* Workaround a crash in x11vnc
* Delete volume when dropping the container
* Catch connection reset in ioucon
* Delete vlan.dat for L2IOL during config import. Fixes #1285.
* Copy original ressources from VOLUMES
## 1.5.0rc1 01/06/2016
* Save an restore docker permission

View File

@ -18,6 +18,10 @@ it on https://github.com/GNS3/gns3-gui we will take care of the triage.
For bugs specific to the GNS3 VM, please report on https://github.com/GNS3/gns3-vm
## Security issues
For security issues please keep it private and send an email to developers@gns3.net
## Asking for new features
The best is to start a discussion on the community website in order to get feedback

View File

@ -274,5 +274,6 @@ Previous versions
API version 1
-------------
Shipped with GNS3 1.3, 1.4 and 1.5. This API doesn't support the controller system.
Shipped with GNS3 1.3, 1.4 and 1.5.
This API doesn't support the controller system and save used a commit system instead of live save.

View File

@ -279,7 +279,7 @@ class DockerVM(BaseNode):
"Binds": self._mount_binds(image_infos)
},
"Volumes": {},
"Env": [],
"Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573
"Cmd": [],
"Entrypoint": image_infos.get("Config", {"Entrypoint": []})["Entrypoint"]
}
@ -306,6 +306,7 @@ class DockerVM(BaseNode):
if self._console_type == "vnc":
yield from self._start_vnc()
params["Env"].append("QT_GRAPHICSSYSTEM=native") # To fix a Qt issue: https://github.com/GNS3/gns3-server/issues/556
params["Env"].append("DISPLAY=:{}".format(self._display))
params["HostConfig"]["Binds"].append("/tmp/.X11-unix/:/tmp/.X11-unix/")
@ -384,7 +385,7 @@ class DockerVM(BaseNode):
# We can not use the API because docker doesn't expose a websocket api for exec
# https://github.com/GNS3/gns3-gui/issues/1039
process = yield from asyncio.subprocess.create_subprocess_exec(
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "/gns3/bin/busybox sh", "/dev/null",
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "script", "-qfc", "while true; do /gns3/bin/busybox sh; done", "/dev/null",
stdout=asyncio.subprocess.PIPE,
stderr=asyncio.subprocess.STDOUT,
stdin=asyncio.subprocess.PIPE)
@ -493,7 +494,9 @@ class DockerVM(BaseNode):
out.feed_eof()
ws.close()
break
yield from self.stop()
@asyncio.coroutine
def is_running(self):
"""Checks if the container is running.
@ -535,21 +538,21 @@ class DockerVM(BaseNode):
if state == "paused":
yield from self.unpause()
yield from self._fix_permissions()
# t=5 number of seconds to wait before killing the container
try:
yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
log.info("Docker container '{name}' [{image}] stopped".format(name=self._name, image=self._image))
except DockerHttp304Error:
# Container is already stopped
pass
finally:
self.status = "stopped"
if state != "stopped":
yield from self._fix_permissions()
# t=5 number of seconds to wait before killing the container
try:
yield from self.manager.query("POST", "containers/{}/stop".format(self._cid), params={"t": 5})
log.info("Docker container '{name}' [{image}] stopped".format(
name=self._name, image=self._image))
except DockerHttp304Error:
# Container is already stopped
pass
# Ignore runtime error because when closing the server
except RuntimeError as e:
log.debug("Docker runtime error when closing: {}".format(str(e)))
return
self.status = "stopped"
@asyncio.coroutine
def pause(self):

View File

@ -217,9 +217,10 @@ class Controller:
topo_data.pop("revision")
topo_data.pop("type")
project = yield from self.add_project(path=os.path.dirname(path), status="closed", **topo_data)
project = yield from self.add_project(path=os.path.dirname(path), status="closed", filename=os.path.basename(path), **topo_data)
if load:
yield from project.open()
return project
@property
def projects(self):

View File

@ -43,7 +43,7 @@ class Project:
:param status: Status of the project (opened / closed)
"""
def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened"):
def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened", filename=None):
self._controller = controller
self._name = name
@ -60,6 +60,13 @@ class Project:
if path is None:
path = os.path.join(get_default_project_directory(), self._id)
self.path = path
if filename is None and name is None:
self._filename = "project.gns3"
elif filename is not None:
self._filename = filename
else:
self._filename = self.name + ".gns3"
self.reset()
def reset(self):
@ -322,14 +329,8 @@ class Project:
raise aiohttp.web.HTTPInternalServerError(text="Could not create project directory: {}".format(e))
return path
def _filename(self):
if self.name is None:
return "untitled.gns3"
else:
return self.name + ".gns3"
def _topology_file(self):
return os.path.join(self.path, self._filename())
return os.path.join(self.path, self._filename)
@asyncio.coroutine
def open(self):
@ -373,6 +374,6 @@ class Project:
"name": self._name,
"project_id": self._id,
"path": self._path,
"filename": self._filename(),
"filename": self._filename,
"status": self._status
}

View File

@ -49,7 +49,7 @@ class ServerHandler:
# close all the projects first
controller = Controller.instance()
projects = controller.projects
projects = controller.projects.values()
tasks = []
for project in projects:

View File

@ -102,6 +102,7 @@ def parse_arguments(argv):
parser.add_argument("--log", help="send output to logfile instead of console")
parser.add_argument("--daemon", action="store_true", help="start as a daemon")
parser.add_argument("--pid", help="store process pid")
parser.add_argument("--udpdiscovery", action="store_true", help="allow the server to be discover on the network")
args = parser.parse_args(argv)
if args.config:
@ -121,6 +122,7 @@ def parse_arguments(argv):
"quiet": config.getboolean("quiet", False),
"debug": config.getboolean("debug", False),
"logfile": config.getboolean("logfile", ""),
"udp_discovery": config.getboolean("udp_discovery", False),
}
parser.set_defaults(**defaults)
@ -142,6 +144,7 @@ def set_config(args):
server_config["record"] = args.record
server_config["debug"] = str(args.debug)
server_config["shell"] = str(args.shell)
server_config["udp_discovery"] = str(args.udpdiscovery)
config.set_section_config("Server", server_config)

View File

@ -59,6 +59,7 @@ class WebServer:
self._handler = None
self._start_time = time.time()
self._port_manager = PortManager(host)
self._running = False
@staticmethod
def instance(host=None, port=None):
@ -298,7 +299,7 @@ class WebServer:
if server_config.getboolean("shell"):
asyncio.async(self.start_shell())
if sys.platform.startswith("linux"):
if sys.platform.startswith("linux") and server_config.getboolean("udp_discovery"):
# UDP discovery is only supported on Linux
udp_server_discovery = threading.Thread(target=self._udp_server_discovery, daemon=True)
udp_server_discovery.start()

View File

@ -105,6 +105,7 @@ def test_create(loop, project, manager):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
],
@ -143,6 +144,7 @@ def test_create_with_tag(loop, project, manager):
"Hostname": "test",
"Image": "ubuntu:16.04",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
],
@ -185,8 +187,10 @@ def test_create_vnc(loop, project, manager):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network",
"QT_GRAPHICSSYSTEM=native",
"DISPLAY=:42"
],
"Entrypoint": ["/gns3/init.sh"],
@ -229,6 +233,7 @@ def test_create_start_cmd(loop, project, manager):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
]
@ -261,6 +266,7 @@ def test_create_environment(loop, project, manager):
"Privileged": True
},
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network",
"YES=1",
@ -320,6 +326,7 @@ def test_create_image_not_available(loop, project, manager):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
],
@ -540,6 +547,7 @@ def test_update(loop, vm):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
],
@ -608,6 +616,7 @@ def test_update_running(loop, vm):
"Hostname": "test",
"Image": "ubuntu:latest",
"Env": [
"container=docker",
"GNS3_MAX_ETHERNET=eth0",
"GNS3_VOLUMES=/etc/network"
],
@ -904,7 +913,7 @@ def test_start_aux(vm, loop):
with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=MagicMock()) as mock_exec:
loop.run_until_complete(asyncio.async(vm._start_aux()))
mock_exec.assert_called_with('docker', 'exec', '-i', 'e90e34656842', '/gns3/bin/busybox', 'script', '-qfc', '/gns3/bin/busybox sh', '/dev/null', stderr=asyncio.subprocess.STDOUT, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE)
mock_exec.assert_called_with('docker', 'exec', '-i', 'e90e34656842', '/gns3/bin/busybox', 'script', '-qfc', 'while true; do /gns3/bin/busybox sh; done', '/dev/null', stderr=asyncio.subprocess.STDOUT, stdin=asyncio.subprocess.PIPE, stdout=asyncio.subprocess.PIPE)
def test_create_network_interfaces(vm):

View File

@ -188,7 +188,7 @@ def test_close(controller, async_run):
def test_load_project(controller, async_run, tmpdir):
data = {
"name": "Test",
"name": "Experience",
"project_id": "c8d07a5a-134f-4c3f-8599-e35eac85eb17",
"revision": 5,
"type": "topology",
@ -242,11 +242,12 @@ def test_load_project(controller, async_run, tmpdir):
controller._computes["my_remote"] = MagicMock()
with asyncio_patch("gns3server.controller.node.Node.create") as mock_node_create:
async_run(controller.load_project(str(tmpdir / "test.gns3")))
project = async_run(controller.load_project(str(tmpdir / "test.gns3")))
assert project._topology_file() == str(tmpdir / "test.gns3")
controller.add_compute.assert_called_with(compute_id='my_remote', host='127.0.0.1', name='My remote', port=3080, protocol='http')
project = controller.get_project('c8d07a5a-134f-4c3f-8599-e35eac85eb17')
assert project.name == "Test"
assert project.name == "Experience"
assert project.path == str(tmpdir)
link = project.get_link("c44331d2-2da4-490d-9aad-7f5c126ae271")
assert len(link.nodes) == 2

View File

@ -43,7 +43,7 @@ def test_affect_uuid():
def test_json(tmpdir):
p = Project()
assert p.__json__() == {"name": p.name, "project_id": p.id, "path": p.path, "status": "opened", "filename": "untitled.gns3"}
assert p.__json__() == {"name": p.name, "project_id": p.id, "path": p.path, "status": "opened", "filename": "project.gns3"}
def test_path(tmpdir):