mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-28 03:08:14 +00:00
parent
08e482004f
commit
add546158f
@ -19,9 +19,11 @@ import aiohttp
|
|||||||
import asyncio
|
import asyncio
|
||||||
import json
|
import json
|
||||||
import uuid
|
import uuid
|
||||||
|
import os
|
||||||
import io
|
import io
|
||||||
|
|
||||||
from ..utils import parse_version
|
from ..utils import parse_version
|
||||||
|
from ..utils.images import scan_for_images
|
||||||
from ..controller.controller_error import ControllerError
|
from ..controller.controller_error import ControllerError
|
||||||
from ..config import Config
|
from ..config import Config
|
||||||
from ..version import __version__
|
from ..version import __version__
|
||||||
@ -41,6 +43,7 @@ class ComputeConflict(aiohttp.web.HTTPConflict):
|
|||||||
|
|
||||||
:param response: The response of the compute
|
:param response: The response of the compute
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self, response):
|
def __init__(self, response):
|
||||||
super().__init__(text=response["message"])
|
super().__init__(text=response["message"])
|
||||||
self.response = response
|
self.response = response
|
||||||
@ -380,3 +383,20 @@ class Compute:
|
|||||||
"""
|
"""
|
||||||
res = yield from self.http_query(method, "/{}/{}".format(type, path), data=data, timeout=None)
|
res = yield from self.http_query(method, "/{}/{}".format(type, path), data=data, timeout=None)
|
||||||
return res.json
|
return res.json
|
||||||
|
|
||||||
|
@asyncio.coroutine
|
||||||
|
def images(self, type):
|
||||||
|
"""
|
||||||
|
Return the list of images available for this type on controller
|
||||||
|
and on the compute node.
|
||||||
|
"""
|
||||||
|
images = []
|
||||||
|
|
||||||
|
res = yield from self.http_query("GET", "/{}/images".format(type), timeout=120)
|
||||||
|
images = res.json
|
||||||
|
|
||||||
|
for path in scan_for_images(type):
|
||||||
|
image = os.path.basename(path)
|
||||||
|
if image not in [i['filename'] for i in images]:
|
||||||
|
images.append({"filename": image, "path": image})
|
||||||
|
return images
|
||||||
|
@ -77,6 +77,22 @@ class ComputeHandler:
|
|||||||
response.set_status(200)
|
response.set_status(200)
|
||||||
response.json(compute)
|
response.json(compute)
|
||||||
|
|
||||||
|
@Route.get(
|
||||||
|
r"/computes/{compute_id}/{emulator}/images",
|
||||||
|
parameters={
|
||||||
|
"compute_id": "Compute UUID"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
200: "OK",
|
||||||
|
404: "Instance doesn't exist"
|
||||||
|
},
|
||||||
|
description="Return the list of images available on compute and controller for this emulator type")
|
||||||
|
def images(request, response):
|
||||||
|
controller = Controller.instance()
|
||||||
|
compute = controller.get_compute(request.match_info["compute_id"])
|
||||||
|
res = yield from compute.images(request.match_info["emulator"])
|
||||||
|
response.json(res)
|
||||||
|
|
||||||
@Route.get(
|
@Route.get(
|
||||||
r"/computes/{compute_id}/{emulator}/{action:.+}",
|
r"/computes/{compute_id}/{emulator}/{action:.+}",
|
||||||
parameters={
|
parameters={
|
||||||
|
@ -27,6 +27,27 @@ import logging
|
|||||||
log = logging.getLogger(__name__)
|
log = logging.getLogger(__name__)
|
||||||
|
|
||||||
|
|
||||||
|
def scan_for_images(type):
|
||||||
|
"""
|
||||||
|
Scan directories for available image for a type
|
||||||
|
|
||||||
|
:param type: emulator type (dynamips, qemu, iou)
|
||||||
|
"""
|
||||||
|
files = set()
|
||||||
|
paths = []
|
||||||
|
for directory in images_directories(type):
|
||||||
|
for root, _, filenames in os.walk(directory):
|
||||||
|
for file in filenames:
|
||||||
|
path = os.path.join(root, file)
|
||||||
|
if file not in files:
|
||||||
|
if (file.endswith(".image") and type == "dynamips") \
|
||||||
|
or (file.endswith(".bin") and type == "iou") \
|
||||||
|
or (not file.endswith(".bin") and not file.endswith(".image") and type == "qemu"):
|
||||||
|
files.add(file)
|
||||||
|
paths.append(path)
|
||||||
|
return paths
|
||||||
|
|
||||||
|
|
||||||
def images_directories(type):
|
def images_directories(type):
|
||||||
"""
|
"""
|
||||||
Return all directory where we will look for images
|
Return all directory where we will look for images
|
||||||
|
@ -15,7 +15,7 @@
|
|||||||
# You should have received a copy of the GNU General Public License
|
# You should have received a copy of the GNU General Public License
|
||||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
import os
|
||||||
import pytest
|
import pytest
|
||||||
import json
|
import json
|
||||||
import aiohttp
|
import aiohttp
|
||||||
@ -247,3 +247,18 @@ def test_forward_post(compute, async_run):
|
|||||||
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
|
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
|
||||||
async_run(compute.forward("POST", "qemu", "img", data={"id": 42}))
|
async_run(compute.forward("POST", "qemu", "img", data={"id": 42}))
|
||||||
mock.assert_called_with("POST", "https://example.com:84/v2/compute/qemu/img", auth=None, data='{"id": 42}', headers={'content-type': 'application/json'}, chunked=False)
|
mock.assert_called_with("POST", "https://example.com:84/v2/compute/qemu/img", auth=None, data='{"id": 42}', headers={'content-type': 'application/json'}, chunked=False)
|
||||||
|
|
||||||
|
|
||||||
|
def test_images(compute, async_run, images_dir):
|
||||||
|
"""
|
||||||
|
Will return image on compute and on controller
|
||||||
|
"""
|
||||||
|
response = MagicMock()
|
||||||
|
response.status = 200
|
||||||
|
response.read = AsyncioMagicMock(return_value=json.dumps([{"filename": "linux.qcow2", "path": "linux.qcow2"}]).encode())
|
||||||
|
open(os.path.join(images_dir, "asa.qcow2"), "w+").close()
|
||||||
|
with asyncio_patch("aiohttp.ClientSession.request", return_value=response) as mock:
|
||||||
|
images = async_run(compute.images("qemu"))
|
||||||
|
mock.assert_called_with("GET", "https://example.com:84/v2/compute/qemu/images", auth=None, data=None, headers={'content-type': 'application/json'}, chunked=False)
|
||||||
|
|
||||||
|
assert images == [{"filename": "linux.qcow2", "path": "linux.qcow2"}, {"filename": "asa.qcow2", "path": "asa.qcow2"}]
|
||||||
|
@ -169,10 +169,10 @@ def test_compute_list_images(http_controller, controller):
|
|||||||
response = http_controller.post("/computes", params)
|
response = http_controller.post("/computes", params)
|
||||||
assert response.status == 201
|
assert response.status == 201
|
||||||
|
|
||||||
with asyncio_patch("gns3server.controller.compute.Compute.forward", return_value=[]) as mock:
|
with asyncio_patch("gns3server.controller.compute.Compute.images", return_value=[{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]) as mock:
|
||||||
response = http_controller.get("/computes/my_compute/qemu/images")
|
response = http_controller.get("/computes/my_compute/qemu/images", example=True)
|
||||||
assert response.json == []
|
assert response.json == [{"filename": "linux.qcow2"}, {"filename": "asav.qcow2"}]
|
||||||
mock.assert_called_with("GET", "qemu", "images")
|
mock.assert_called_with("qemu")
|
||||||
|
|
||||||
|
|
||||||
def test_compute_list_vms(http_controller, controller):
|
def test_compute_list_vms(http_controller, controller):
|
||||||
|
@ -20,7 +20,7 @@ from unittest.mock import patch
|
|||||||
|
|
||||||
|
|
||||||
from gns3server.utils import force_unix_path
|
from gns3server.utils import force_unix_path
|
||||||
from gns3server.utils.images import md5sum, remove_checksum, images_directories
|
from gns3server.utils.images import md5sum, remove_checksum, images_directories, scan_for_images
|
||||||
|
|
||||||
|
|
||||||
def test_images_directories(tmpdir):
|
def test_images_directories(tmpdir):
|
||||||
@ -90,3 +90,30 @@ def test_remove_checksum(tmpdir):
|
|||||||
assert not os.path.exists(str(tmpdir / 'hello.md5sum'))
|
assert not os.path.exists(str(tmpdir / 'hello.md5sum'))
|
||||||
|
|
||||||
remove_checksum(str(tmpdir / 'not_exists'))
|
remove_checksum(str(tmpdir / 'not_exists'))
|
||||||
|
|
||||||
|
|
||||||
|
def test_scan_for_images(tmpdir):
|
||||||
|
path1 = tmpdir / "images1" / "IOS" / "test1.image"
|
||||||
|
path1.write("1", ensure=True)
|
||||||
|
path1 = force_unix_path(str(path1))
|
||||||
|
|
||||||
|
path2 = tmpdir / "images2" / "test2.image"
|
||||||
|
path2.write("1", ensure=True)
|
||||||
|
path2 = force_unix_path(str(path2))
|
||||||
|
|
||||||
|
path3 = tmpdir / "images1" / "IOU" / "test3.bin"
|
||||||
|
path3.write("1", ensure=True)
|
||||||
|
path3 = force_unix_path(str(path3))
|
||||||
|
|
||||||
|
path4 = tmpdir / "images1" / "QEMU" / "test4.qcow2"
|
||||||
|
path4.write("1", ensure=True)
|
||||||
|
path4 = force_unix_path(str(path4))
|
||||||
|
|
||||||
|
with patch("gns3server.config.Config.get_section_config", return_value={
|
||||||
|
"images_path": str(tmpdir / "images1"),
|
||||||
|
"additional_images_path": "/tmp/null24564:{}".format(tmpdir / "images2"),
|
||||||
|
"local": False}):
|
||||||
|
|
||||||
|
assert scan_for_images("dynamips") == [str(path1), str(path2)]
|
||||||
|
assert scan_for_images("iou") == [str(path3)]
|
||||||
|
assert scan_for_images("qemu") == [str(path4)]
|
||||||
|
Loading…
Reference in New Issue
Block a user