mirror of https://github.com/GNS3/gns3-server
parent
3296b97f59
commit
58f1abff35
@ -0,0 +1,89 @@
|
||||
#!/usr/bin/env python
|
||||
#
|
||||
# Copyright (C) 2016 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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 asyncio
|
||||
from uuid import UUID, uuid4
|
||||
|
||||
|
||||
class Project:
|
||||
"""
|
||||
A project inside controller
|
||||
|
||||
:param project_id: force project identifier (None by default auto generate an UUID)
|
||||
:param path: path of the project. (None use the standard directory)
|
||||
:param temporary: boolean to tell if the project is a temporary project (destroy when closed)
|
||||
"""
|
||||
|
||||
def __init__(self, name=None, project_id=None, path=None, temporary=False):
|
||||
|
||||
self._name = name
|
||||
if project_id is None:
|
||||
self._id = str(uuid4())
|
||||
else:
|
||||
try:
|
||||
UUID(project_id, version=4)
|
||||
except ValueError:
|
||||
raise aiohttp.web.HTTPBadRequest(text="{} is not a valid UUID".format(project_id))
|
||||
self._id = project_id
|
||||
self._path = path
|
||||
self._temporary = temporary
|
||||
self._hypervisors = set()
|
||||
|
||||
@property
|
||||
def name(self):
|
||||
return self._name
|
||||
|
||||
@property
|
||||
def id(self):
|
||||
return self._id
|
||||
|
||||
@property
|
||||
def temporary(self):
|
||||
return self._temporary
|
||||
|
||||
@property
|
||||
def path(self):
|
||||
return self._path
|
||||
|
||||
@asyncio.coroutine
|
||||
def addHypervisor(self, hypervisor):
|
||||
self._hypervisors.add(hypervisor)
|
||||
yield from hypervisor.post("/projects", self)
|
||||
|
||||
@asyncio.coroutine
|
||||
def close(self):
|
||||
for hypervisor in self._hypervisors:
|
||||
yield from hypervisor.post("/projects/{}/close".format(self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def commit(self):
|
||||
for hypervisor in self._hypervisors:
|
||||
yield from hypervisor.post("/projects/{}/commit".format(self._id))
|
||||
|
||||
@asyncio.coroutine
|
||||
def delete(self):
|
||||
for hypervisor in self._hypervisors:
|
||||
yield from hypervisor.delete("/projects/{}".format(self._id))
|
||||
|
||||
def __json__(self):
|
||||
|
||||
return {
|
||||
"name": self._name,
|
||||
"project_id": self._id,
|
||||
"temporary": self._temporary,
|
||||
"path": self._path
|
||||
}
|
@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# 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 aiohttp
|
||||
import asyncio
|
||||
|
||||
|
||||
from ....web.route import Route
|
||||
from ....schemas.project import PROJECT_OBJECT_SCHEMA, PROJECT_CREATE_SCHEMA
|
||||
from ....controller import Controller
|
||||
from ....controller.project import Project
|
||||
|
||||
|
||||
import logging
|
||||
log = logging.getLogger()
|
||||
|
||||
|
||||
class ProjectHandler:
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects",
|
||||
description="Create a new project on the server",
|
||||
status_codes={
|
||||
201: "Project created",
|
||||
409: "Project already created"
|
||||
},
|
||||
output=PROJECT_OBJECT_SCHEMA,
|
||||
input=PROJECT_CREATE_SCHEMA)
|
||||
def create_project(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = Project(name=request.json.get("name"),
|
||||
path=request.json.get("path"),
|
||||
project_id=request.json.get("project_id"),
|
||||
temporary=request.json.get("temporary", False))
|
||||
yield from controller.addProject(project)
|
||||
response.set_status(201)
|
||||
response.json(project)
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/commit",
|
||||
description="Write changes on disk",
|
||||
parameters={
|
||||
"project_id": "The UUID of the project",
|
||||
},
|
||||
status_codes={
|
||||
204: "Changes have been written on disk",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def commit(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.getProject(request.match_info["project_id"])
|
||||
yield from project.commit()
|
||||
response.set_status(204)
|
||||
|
||||
@classmethod
|
||||
@Route.post(
|
||||
r"/projects/{project_id}/close",
|
||||
description="Close a project",
|
||||
parameters={
|
||||
"project_id": "The UUID of the project",
|
||||
},
|
||||
status_codes={
|
||||
204: "The project has been closed",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def close(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.getProject(request.match_info["project_id"])
|
||||
yield from project.close()
|
||||
controller.removeProject(project)
|
||||
response.set_status(204)
|
||||
|
||||
@classmethod
|
||||
@Route.delete(
|
||||
r"/projects/{project_id}",
|
||||
description="Delete a project from disk",
|
||||
parameters={
|
||||
"project_id": "The UUID of the project",
|
||||
},
|
||||
status_codes={
|
||||
204: "Changes have been written on disk",
|
||||
404: "The project doesn't exist"
|
||||
})
|
||||
def delete(request, response):
|
||||
|
||||
controller = Controller.instance()
|
||||
project = controller.getProject(request.match_info["project_id"])
|
||||
yield from project.delete()
|
||||
controller.removeProject(project)
|
||||
response.set_status(204)
|
@ -0,0 +1,33 @@
|
||||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
|
||||
from gns3server.controller.project import Project
|
||||
|
||||
|
||||
def test_affect_uuid():
|
||||
p = Project()
|
||||
assert len(p.id) == 36
|
||||
|
||||
p = Project(project_id='00010203-0405-0607-0809-0a0b0c0d0e0f')
|
||||
assert p.id == '00010203-0405-0607-0809-0a0b0c0d0e0f'
|
||||
|
||||
|
||||
def test_json(tmpdir):
|
||||
p = Project()
|
||||
assert p.__json__() == {"name": p.name, "project_id": p.id, "temporary": False, "path": None}
|
@ -0,0 +1,109 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
#
|
||||
# Copyright (C) 2015 GNS3 Technologies Inc.
|
||||
#
|
||||
# This program is free software: you can redistribute it and/or modify
|
||||
# it under the terms of the GNU General Public License as published by
|
||||
# the Free Software Foundation, either version 3 of the License, or
|
||||
# (at your option) any later version.
|
||||
#
|
||||
# This program is distributed in the hope that it will be useful,
|
||||
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
# GNU General Public License for more details.
|
||||
#
|
||||
# You should have received a copy of the GNU General Public License
|
||||
# along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
"""
|
||||
This test suite check /project endpoint
|
||||
"""
|
||||
|
||||
import uuid
|
||||
import os
|
||||
import asyncio
|
||||
import aiohttp
|
||||
import pytest
|
||||
|
||||
|
||||
from unittest.mock import patch
|
||||
from tests.utils import asyncio_patch
|
||||
|
||||
from gns3server.handlers.api.controller.project_handler import ProjectHandler
|
||||
from gns3server.controller import Controller
|
||||
|
||||
|
||||
@pytest.fixture
|
||||
def project(http_controller):
|
||||
u = str(uuid.uuid4())
|
||||
query = {"name": "test", "project_id": u}
|
||||
response = http_controller.post("/projects", query)
|
||||
return Controller.instance().getProject(u)
|
||||
|
||||
|
||||
def test_create_project_with_path(http_controller, tmpdir):
|
||||
with asyncio_patch("gns3server.controller.Controller.addProject") as mock:
|
||||
response = http_controller.post("/projects", {"name": "test", "path": str(tmpdir), "project_id": "00010203-0405-0607-0809-0a0b0c0d0e0f"})
|
||||
assert response.status == 201
|
||||
assert response.json["name"] == "test"
|
||||
assert response.json["project_id"] == "00010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
assert mock.called
|
||||
|
||||
|
||||
def test_create_project_without_dir(http_controller):
|
||||
query = {"name": "test", "project_id": "10010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||
response = http_controller.post("/projects", query, example=True)
|
||||
assert response.status == 201
|
||||
assert response.json["project_id"] == "10010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
assert response.json["temporary"] is False
|
||||
assert response.json["name"] == "test"
|
||||
|
||||
|
||||
def test_create_temporary_project(http_controller):
|
||||
query = {"name": "test", "temporary": True, "project_id": "20010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||
response = http_controller.post("/projects", query)
|
||||
assert response.status == 201
|
||||
assert response.json["project_id"] == "20010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
assert response.json["temporary"] is True
|
||||
assert response.json["name"] == "test"
|
||||
|
||||
|
||||
def test_create_project_with_uuid(http_controller):
|
||||
query = {"name": "test", "project_id": "30010203-0405-0607-0809-0a0b0c0d0e0f"}
|
||||
response = http_controller.post("/projects", query)
|
||||
assert response.status == 201
|
||||
assert response.json["project_id"] == "30010203-0405-0607-0809-0a0b0c0d0e0f"
|
||||
assert response.json["name"] == "test"
|
||||
|
||||
|
||||
def test_commit_project(http_controller, project):
|
||||
with asyncio_patch("gns3server.controller.project.Project.commit", return_value=True) as mock:
|
||||
response = http_controller.post("/projects/{project_id}/commit".format(project_id=project.id), example=True)
|
||||
assert response.status == 204
|
||||
assert mock.called
|
||||
|
||||
|
||||
def test_commit_project_invalid_uuid(http_controller):
|
||||
response = http_controller.post("/projects/{project_id}/commit".format(project_id=uuid.uuid4()))
|
||||
assert response.status == 404
|
||||
|
||||
|
||||
def test_delete_project(http_controller, project):
|
||||
with asyncio_patch("gns3server.controller.project.Project.delete", return_value=True) as mock:
|
||||
response = http_controller.delete("/projects/{project_id}".format(project_id=project.id), example=True)
|
||||
assert response.status == 204
|
||||
assert mock.called
|
||||
assert project not in Controller.instance().projects
|
||||
|
||||
|
||||
def test_delete_project_invalid_uuid(http_controller):
|
||||
response = http_controller.delete("/projects/{project_id}".format(project_id=uuid.uuid4()))
|
||||
assert response.status == 404
|
||||
|
||||
|
||||
def test_close_project(http_controller, project):
|
||||
with asyncio_patch("gns3server.controller.project.Project.close", return_value=True) as mock:
|
||||
response = http_controller.post("/projects/{project_id}/close".format(project_id=project.id), example=True)
|
||||
assert response.status == 204
|
||||
assert mock.called
|
||||
assert project not in Controller.instance().projects
|
Loading…
Reference in new issue