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

Support for mounting volumes

Fix #425
This commit is contained in:
Julien Duponchelle 2016-02-12 11:57:56 +01:00
parent a581eeba54
commit 1532b3ed9b
No known key found for this signature in database
GPG Key ID: F1E2485547D4595D
3 changed files with 77 additions and 12 deletions

View File

@ -25,6 +25,7 @@ import psutil
import shlex import shlex
import aiohttp import aiohttp
import json import json
import os
from ...ubridge.hypervisor import Hypervisor from ...ubridge.hypervisor import Hypervisor
from .docker_error import * from .docker_error import *
@ -84,7 +85,8 @@ class DockerVM(BaseVM):
"adapters": self.adapters, "adapters": self.adapters,
"console": self.console, "console": self.console,
"start_command": self.start_command, "start_command": self.start_command,
"environment": self.environment "environment": self.environment,
"vm_directory": self.working_dir
} }
@property @property
@ -118,9 +120,31 @@ class DockerVM(BaseVM):
return "running" return "running"
return "exited" return "exited"
@asyncio.coroutine
def _get_image_informations(self):
"""
:returns: Dictionnary informations about the container image
"""
result = yield from self.manager.query("GET", "images/{}/json".format(self._image))
return result
def _mount_binds(self, image_infos):
"""
:returns: Return the path that we need to map to local folders
"""
binds = []
for volume in image_infos.get("ContainerConfig", {}).get("Volumes", {}).keys():
source = os.path.join(self.working_dir, os.path.relpath(volume, "/"))
os.makedirs(source, exist_ok=True)
binds.append("{}:{}".format(source, volume))
return binds
@asyncio.coroutine @asyncio.coroutine
def create(self): def create(self):
"""Creates the Docker container.""" """Creates the Docker container."""
image_infos = yield from self._get_image_informations()
params = { params = {
"Name": self._name, "Name": self._name,
"Image": self._image, "Image": self._image,
@ -130,8 +154,10 @@ class DockerVM(BaseVM):
"StdinOnce": False, "StdinOnce": False,
"HostConfig": { "HostConfig": {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Privileged": True "Privileged": True,
} "Binds": self._mount_binds(image_infos)
},
"Volumes": {}
} }
if self._start_command: if self._start_command:
params.update({"Cmd": shlex.split(self._start_command)}) params.update({"Cmd": shlex.split(self._start_command)})

View File

@ -159,10 +159,14 @@ DOCKER_OBJECT_SCHEMA = {
"description": "Docker environment", "description": "Docker environment",
"type": ["string", "null"], "type": ["string", "null"],
"minLength": 0, "minLength": 0,
},
"vm_directory": {
"decription": "Path to the VM working directory",
"type": "string"
} }
}, },
"additionalProperties": False, "additionalProperties": False,
"required": ["vm_id", "project_id", "image", "container_id", "adapters", "console", "start_command", "environment"] "required": ["vm_id", "project_id", "image", "container_id", "adapters", "console", "start_command", "environment", "vm_directory"]
} }

View File

@ -18,6 +18,7 @@
import pytest import pytest
import uuid import uuid
import asyncio import asyncio
import os
from tests.utils import asyncio_patch from tests.utils import asyncio_patch
from gns3server.ubridge.ubridge_error import UbridgeNamespaceError from gns3server.ubridge.ubridge_error import UbridgeNamespaceError
@ -54,7 +55,8 @@ def test_json(vm, project):
'adapters': 1, 'adapters': 1,
'console': vm.console, 'console': vm.console,
'start_command': vm.start_command, 'start_command': vm.start_command,
'environment': vm.environment 'environment': vm.environment,
'vm_directory': vm.working_dir
} }
@ -75,8 +77,10 @@ def test_create(loop, project, manager):
"HostConfig": "HostConfig":
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [],
"Privileged": True "Privileged": True
}, },
"Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Name": "test", "Name": "test",
"Image": "ubuntu" "Image": "ubuntu"
@ -102,8 +106,10 @@ def test_create_start_cmd(loop, project, manager):
"HostConfig": "HostConfig":
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [],
"Privileged": True "Privileged": True
}, },
"Volumes": {},
"Cmd": ["/bin/ls"], "Cmd": ["/bin/ls"],
"NetworkDisabled": True, "NetworkDisabled": True,
"Name": "test", "Name": "test",
@ -130,12 +136,11 @@ def test_create_environment(loop, project, manager):
"HostConfig": "HostConfig":
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [],
"Privileged": True "Privileged": True
}, },
"Env": [ "Env": ["YES=1", "NO=0"],
"YES=1", "Volumes": {},
"NO=0"
],
"NetworkDisabled": True, "NetworkDisabled": True,
"Name": "test", "Name": "test",
"Image": "ubuntu" "Image": "ubuntu"
@ -161,8 +166,10 @@ def test_create_image_not_available(loop, project, manager):
"HostConfig": "HostConfig":
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [],
"Privileged": True "Privileged": True
}, },
"Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Name": "test", "Name": "test",
"Image": "ubuntu" "Image": "ubuntu"
@ -358,8 +365,10 @@ def test_update(loop, vm):
"HostConfig": "HostConfig":
{ {
"CapAdd": ["ALL"], "CapAdd": ["ALL"],
"Binds": [],
"Privileged": True "Privileged": True
}, },
"Volumes": {},
"NetworkDisabled": True, "NetworkDisabled": True,
"Name": "test", "Name": "test",
"Image": "ubuntu" "Image": "ubuntu"
@ -602,3 +611,29 @@ def test_get_log(loop, vm):
with asyncio_patch("gns3server.modules.docker.Docker.http_query", return_value=mock_query) as mock: with asyncio_patch("gns3server.modules.docker.Docker.http_query", return_value=mock_query) as mock:
images = loop.run_until_complete(asyncio.async(vm._get_log())) images = loop.run_until_complete(asyncio.async(vm._get_log()))
mock.assert_called_with("GET", "containers/e90e34656842/logs", params={"stderr": 1, "stdout": 1}, data={}) mock.assert_called_with("GET", "containers/e90e34656842/logs", params={"stderr": 1, "stdout": 1}, data={})
def test_get_image_informations(project, manager, loop):
response = {
}
with asyncio_patch("gns3server.modules.docker.Docker.query", return_value=response) as mock:
vm = DockerVM("test", str(uuid.uuid4()), project, manager, "ubuntu")
loop.run_until_complete(asyncio.async(vm._get_image_informations()))
mock.assert_called_with("GET", "images/ubuntu/json")
def test_mount_binds(vm, tmpdir):
image_infos = {
"ContainerConfig": {
"Volumes": {
"/test/experimental": {}
}
}
}
dst = os.path.join(vm.working_dir, "test/experimental")
assert vm._mount_binds(image_infos) == [
"{}:{}".format(dst, "/test/experimental")
]
assert os.path.exists(dst)