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

Bring back the auto_start

Fix #582
This commit is contained in:
Julien Duponchelle 2016-08-16 15:45:06 +02:00
parent 017202d151
commit 12735ff36e
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
7 changed files with 118 additions and 28 deletions

View File

@ -32,6 +32,7 @@ from .topology import project_to_topology, load_topology
from .udp_link import UDPLink from .udp_link import UDPLink
from ..config import Config from ..config import Config
from ..utils.path import check_path_allowed, get_default_project_directory from ..utils.path import check_path_allowed, get_default_project_directory
from ..utils.asyncio.pool import Pool
from .export_project import export_project from .export_project import export_project
from .import_project import import_project from .import_project import import_project
@ -61,14 +62,14 @@ class Project:
:param status: Status of the project (opened / closed) :param status: Status of the project (opened / closed)
""" """
def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened", filename=None, auto_start=False): def __init__(self, name=None, project_id=None, path=None, controller=None, status="opened", filename=None, auto_start=False, auto_open=False, auto_close=False):
self._controller = controller self._controller = controller
assert name is not None assert name is not None
self._name = name self._name = name
self._auto_start = False self._auto_start = auto_start
self._auto_close = False self._auto_close = auto_close
self._auto_open = False self._auto_open = auto_open
self._status = status self._status = status
# Disallow overwrite of existing project # Disallow overwrite of existing project
@ -103,9 +104,8 @@ class Project:
@asyncio.coroutine @asyncio.coroutine
def update(self, **kwargs): def update(self, **kwargs):
""" """
Update the node on the compute server Update the project
:param kwargs: Project properties
:param kwargs: Node properties
""" """
old_json = self.__json__() old_json = self.__json__()
@ -492,6 +492,7 @@ class Project:
@asyncio.coroutine @asyncio.coroutine
def close(self, ignore_notification=False): def close(self, ignore_notification=False):
yield from self.stop_all()
for compute in self._project_created_on_compute: for compute in self._project_created_on_compute:
yield from compute.post("/projects/{}/close".format(self._id)) yield from compute.post("/projects/{}/close".format(self._id))
self._cleanPictures() self._cleanPictures()
@ -580,6 +581,10 @@ class Project:
for drawing_data in topology.get("drawings", []): for drawing_data in topology.get("drawings", []):
drawing = yield from self.add_drawing(**drawing_data) drawing = yield from self.add_drawing(**drawing_data)
# Should we start the nodes when project is open
if self._auto_start:
yield from self.start_all()
@open_required @open_required
@asyncio.coroutine @asyncio.coroutine
def duplicate(self, name=None, location=None): def duplicate(self, name=None, location=None):
@ -626,6 +631,36 @@ class Project:
except OSError as e: except OSError as e:
raise aiohttp.web.HTTPInternalServerError(text="Could not write topology: {}".format(e)) raise aiohttp.web.HTTPInternalServerError(text="Could not write topology: {}".format(e))
@asyncio.coroutine
def start_all(self):
"""
Start all nodes
"""
pool = Pool(concurrency=3)
for node in self.nodes.values():
pool.append(node.start)
yield from pool.join()
@asyncio.coroutine
def stop_all(self):
"""
Stop all nodes
"""
pool = Pool(concurrency=3)
for node in self.nodes.values():
pool.append(node.stop)
yield from pool.join()
@asyncio.coroutine
def suspend_all(self):
"""
Suspend all nodes
"""
pool = Pool(concurrency=3)
for node in self.nodes.values():
pool.append(node.suspend)
yield from pool.join()
def __json__(self): def __json__(self):
return { return {

View File

@ -53,6 +53,8 @@ def project_to_topology(project):
"project_id": project.id, "project_id": project.id,
"name": project.name, "name": project.name,
"auto_start": project.auto_start, "auto_start": project.auto_start,
"auto_open": project.auto_open,
"auto_close": project.auto_close,
"topology": { "topology": {
"nodes": [], "nodes": [],
"links": [], "links": [],

View File

@ -20,7 +20,6 @@ import aiohttp
from gns3server.web.route import Route from gns3server.web.route import Route
from gns3server.controller import Controller from gns3server.controller import Controller
from gns3server.utils.asyncio.pool import Pool
from gns3server.schemas.node import ( from gns3server.schemas.node import (
NODE_OBJECT_SCHEMA, NODE_OBJECT_SCHEMA,
@ -107,10 +106,7 @@ class NodeHandler:
def start_all(request, response): def start_all(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
pool = Pool(concurrency=3) yield from project.start_all()
for node in project.nodes.values():
pool.append(node.start)
yield from pool.join()
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(
@ -128,10 +124,7 @@ class NodeHandler:
def stop_all(request, response): def stop_all(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
pool = Pool(concurrency=3) yield from project.stop_all()
for node in project.nodes.values():
pool.append(node.stop)
yield from pool.join()
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(
@ -149,10 +142,7 @@ class NodeHandler:
def suspend_all(request, response): def suspend_all(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
pool = Pool(concurrency=3) yield from project.suspend_all()
for node in project.nodes.values():
pool.append(node.suspend)
yield from pool.join()
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(
@ -170,13 +160,8 @@ class NodeHandler:
def reload_all(request, response): def reload_all(request, response):
project = Controller.instance().get_project(request.match_info["project_id"]) project = Controller.instance().get_project(request.match_info["project_id"])
pool = Pool(concurrency=3) yield from project.stop_all()
for node in project.nodes.values(): yield from project.start_all()
pool.append(node.stop)
yield from pool.join()
for node in project.nodes.values():
pool.append(node.start)
yield from pool.join()
response.set_status(204) response.set_status(204)
@Route.post( @Route.post(

View File

@ -45,6 +45,14 @@ TOPOLOGY_SCHEMA = {
"description": "Start the topology when opened", "description": "Start the topology when opened",
"type": "boolean" "type": "boolean"
}, },
"auto_close": {
"description": "Close the topology when no client is connected",
"type": "boolean"
},
"auto_open": {
"description": "Open the topology with GNS3",
"type": "boolean"
},
"revision": { "revision": {
"description": "Version of the .gns3 specification.", "description": "Version of the .gns3 specification.",
"type": "integer" "type": "integer"

View File

@ -27,6 +27,7 @@ in futur GNS3 versions.
<tr> <tr>
<th>Name</th> <th>Name</th>
<th>ID</th> <th>ID</th>
<th>Status</th>
<th>Compute</th> <th>Compute</th>
<th>Console</th> <th>Console</th>
</tr> </tr>
@ -34,6 +35,7 @@ in futur GNS3 versions.
<tr> <tr>
<td>{{node.name}}</td> <td>{{node.name}}</td>
<td>{{node.id}}</td> <td>{{node.id}}</td>
<td>{{node.status}}</td>
<td>{{node.compute.id}}</td> <td>{{node.compute.id}}</td>
<td><a href="{{node.console_type}}://{{node.host}}:{{node.console}}">Console</a> <td><a href="{{node.console_type}}://{{node.host}}:{{node.console}}">Console</a>
</tr> </tr>

View File

@ -23,7 +23,7 @@ import pytest
import aiohttp import aiohttp
import zipfile import zipfile
from unittest.mock import MagicMock from unittest.mock import MagicMock
from tests.utils import AsyncioMagicMock from tests.utils import AsyncioMagicMock, asyncio_patch
from unittest.mock import patch from unittest.mock import patch
from uuid import uuid4 from uuid import uuid4
@ -349,7 +349,9 @@ def test_dump():
def test_open_close(async_run, controller): def test_open_close(async_run, controller):
project = Project(controller=controller, status="closed", name="Test") project = Project(controller=controller, status="closed", name="Test")
assert project.status == "closed" assert project.status == "closed"
project.start_all = AsyncioMagicMock()
async_run(project.open()) async_run(project.open())
assert not project.start_all.called
assert project.status == "opened" assert project.status == "opened"
controller._notification = MagicMock() controller._notification = MagicMock()
async_run(project.close()) async_run(project.close())
@ -357,6 +359,14 @@ def test_open_close(async_run, controller):
controller.notification.emit.assert_any_call("project.closed", project.__json__()) controller.notification.emit.assert_any_call("project.closed", project.__json__())
def test_open_auto_start(async_run, controller):
project = Project(controller=controller, status="closed", name="Test")
project.auto_start = True
project.start_all = AsyncioMagicMock()
async_run(project.open())
assert project.start_all.called
def test_is_running(project, async_run, node): def test_is_running(project, async_run, node):
""" """
If a node is started or paused return True If a node is started or paused return True
@ -451,3 +461,49 @@ def test_snapshot(project, async_run):
# Raise a conflict if name is already use # Raise a conflict if name is already use
with pytest.raises(aiohttp.web_exceptions.HTTPConflict): with pytest.raises(aiohttp.web_exceptions.HTTPConflict):
snapshot = async_run(project.snapshot("test1")) snapshot = async_run(project.snapshot("test1"))
def test_start_all(project, async_run):
compute = MagicMock()
compute.id = "local"
response = MagicMock()
response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
compute.post = AsyncioMagicMock()
async_run(project.start_all())
assert len(compute.post.call_args_list) == 10
def test_stop_all(project, async_run):
compute = MagicMock()
compute.id = "local"
response = MagicMock()
response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
compute.post = AsyncioMagicMock()
async_run(project.stop_all())
assert len(compute.post.call_args_list) == 10
def test_suspend_all(project, async_run):
compute = MagicMock()
compute.id = "local"
response = MagicMock()
response.json = {"console": 2048}
compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10):
async_run(project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"}))
compute.post = AsyncioMagicMock()
async_run(project.suspend_all())
assert len(compute.post.call_args_list) == 10

View File

@ -35,6 +35,8 @@ def test_project_to_topology_empty(tmpdir):
"project_id": project.id, "project_id": project.id,
"name": "Test", "name": "Test",
"auto_start": False, "auto_start": False,
"auto_close": False,
"auto_open": False,
"revision": 5, "revision": 5,
"topology": { "topology": {
"nodes": [], "nodes": [],