Compute IDLE PC via controller

Fix #1234
This commit is contained in:
Julien Duponchelle 2016-05-19 16:21:35 +02:00
parent 00f80f54e8
commit 672a617102
No known key found for this signature in database
GPG Key ID: CE8B29639E07F5E8
7 changed files with 128 additions and 16 deletions

View File

@ -628,9 +628,11 @@ class Router(BaseNode):
"""
is_running = yield from self.is_running()
was_auto_started = False
if not is_running:
# router is not running
raise DynamipsError('Router "{name}" is not running'.format(name=self._name))
yield from self.start()
was_auto_started = True
yield from asyncio.sleep(20) # leave time to the router to boot
log.info('Router "{name}" [{id}] has started calculating Idle-PC values'.format(name=self._name, id=self._id))
begin = time.time()
@ -638,6 +640,8 @@ class Router(BaseNode):
log.info('Router "{name}" [{id}] has finished calculating Idle-PC values after {time:.4f} seconds'.format(name=self._name,
id=self._id,
time=time.time() - begin))
if was_auto_started:
yield from self.stop()
return idlepcs
@asyncio.coroutine

View File

@ -157,12 +157,12 @@ class Compute:
return response.content
@asyncio.coroutine
def http_query(self, method, path, data=None):
def http_query(self, method, path, data=None, **kwargs):
if not self._connected:
yield from self._connect()
if not self._connected:
raise aiohttp.web.HTTPConflict(text="The server {} is not a GNS3 server".format(self._id))
response = yield from self._run_http_query(method, path, data=data)
response = yield from self._run_http_query(method, path, data=data, **kwargs)
return response
@asyncio.coroutine
@ -202,8 +202,8 @@ class Compute:
return "{}://{}:{}/v2/compute{}".format(self._protocol, self._host, self._port, path)
@asyncio.coroutine
def _run_http_query(self, method, path, data=None):
with aiohttp.Timeout(10):
def _run_http_query(self, method, path, data=None, timeout=10):
with aiohttp.Timeout(timeout):
url = self._getUrl(path)
headers = {'content-type': 'application/json'}
if data == {}:
@ -243,19 +243,19 @@ class Compute:
return response
@asyncio.coroutine
def get(self, path):
return (yield from self.http_query("GET", path))
def get(self, path, **kwargs):
return (yield from self.http_query("GET", path, **kwargs))
@asyncio.coroutine
def post(self, path, data={}):
response = yield from self.http_query("POST", path, data)
def post(self, path, data={}, **kwargs):
response = yield from self.http_query("POST", path, data, **kwargs)
return response
@asyncio.coroutine
def put(self, path, data={}):
response = yield from self.http_query("PUT", path, data)
def put(self, path, data={}, **kwargs):
response = yield from self.http_query("PUT", path, data, **kwargs)
return response
@asyncio.coroutine
def delete(self, path):
return (yield from self.http_query("DELETE", path))
def delete(self, path, **kwargs):
return (yield from self.http_query("DELETE", path, **kwargs))

View File

@ -147,7 +147,7 @@ class Node:
self._console_type = value
elif key == "name":
self._name = value
elif key in ["node_id", "project_id"]:
elif key in ["node_id", "project_id", "console_host"]:
pass
else:
self._properties[key] = value
@ -166,7 +166,7 @@ class Node:
# None properties are not be send. Because it can mean the emulator doesn't support it
for key in list(data.keys()):
if data[key] is None:
if data[key] is None or key in ["console_host"]:
del data[key]
return data
@ -236,6 +236,20 @@ class Node:
else:
return (yield from self._compute.delete("/projects/{}/{}/nodes/{}{}".format(self._project.id, self._node_type, self._id, path)))
@asyncio.coroutine
def dynamips_auto_idlepc(self):
"""
Compute the idle PC for a dynamips node
"""
return (yield from self._compute.get("/projects/{}/{}/nodes/{}/auto_idlepc".format(self._project.id, self._node_type, self._id), timeout=240)).json
@asyncio.coroutine
def dynamips_idlepc_proposals(self):
"""
Compute a list of potential idle PC
"""
return (yield from self._compute.get("/projects/{}/{}/nodes/{}/idlepc_proposals".format(self._project.id, self._node_type, self._id), timeout=240)).json
def __repr__(self):
return "<gns3server.controller.Node {} {}>".format(self._node_type, self._name)
@ -248,6 +262,7 @@ class Node:
"node_directory": self._node_directory,
"name": self._name,
"console": self._console,
"console_host": self._compute.host,
"console_type": self._console_type,
"command_line": self._command_line,
"properties": self._properties,

View File

@ -262,3 +262,43 @@ class NodeHandler:
project = Controller.instance().get_project(request.match_info["project_id"])
yield from project.delete_node(request.match_info["node_id"])
response.set_status(204)
@Route.get(
r"/projects/{project_id}/nodes/{node_id}/dynamips/auto_idlepc",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance reloaded",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Compute the IDLE PC for a Dynamips node")
def auto_idlepc(request, response):
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
idle = yield from node.dynamips_auto_idlepc()
response.json(idle)
response.set_status(200)
@Route.get(
r"/projects/{project_id}/nodes/{node_id}/dynamips/idlepc_proposals",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Instance reloaded",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Compute a list of potential idle PC for a node")
def idlepc_proposals(request, response):
project = Controller.instance().get_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
idle = yield from node.dynamips_idlepc_proposals()
response.json(idle)
response.set_status(200)

View File

@ -109,6 +109,11 @@ NODE_OBJECT_SCHEMA = {
"maximum": 65535,
"type": ["integer", "null"]
},
"console_host": {
"description": "Console host",
"type": "string",
"minLength": 1,
},
"console_type": {
"description": "Console type",
"enum": ["serial", "vnc", "telnet", None]

View File

@ -55,6 +55,7 @@ def test_json(node, compute):
"name": "demo",
"console": node.console,
"console_type": node.console_type,
"console_host": compute.host,
"command_line": None,
"node_directory": None,
"properties": node.properties,
@ -172,3 +173,23 @@ def test_post(node, compute, async_run):
def test_delete(node, compute, async_run):
async_run(node.delete("/test"))
compute.delete.assert_called_with("/projects/{}/vpcs/nodes/{}/test".format(node.project.id, node.id))
def test_dynamips_idle_pc(node, async_run, compute):
node._node_type = "dynamips"
response = MagicMock()
response.json = {"idlepc": "0x60606f54"}
compute.get = AsyncioMagicMock(return_value=response)
async_run(node.dynamips_auto_idlepc())
compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/auto_idlepc".format(node.project.id, node.id))
def test_dynamips_idlepc_proposals(node, async_run, compute):
node._node_type = "dynamips"
response = MagicMock()
response.json = ["0x60606f54", "0x30ff6f37"]
compute.get = AsyncioMagicMock(return_value=response)
async_run(node.dynamips_idlepc_proposals())
compute.get.assert_called_with("/projects/{}/dynamips/nodes/{}/idlepc_proposals".format(node.project.id, node.id))

View File

@ -38,6 +38,7 @@ from gns3server.controller.node import Node
def compute(http_controller, async_run):
compute = MagicMock()
compute.id = "example.com"
compute.host = "example.org"
Controller.instance()._computes = {"example.com": compute}
return compute
@ -107,6 +108,7 @@ def test_update_node(http_controller, tmpdir, project, compute, node):
assert response.json["name"] == "test"
assert "name" not in response.json["properties"]
def test_start_all_nodes(http_controller, tmpdir, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -114,6 +116,7 @@ def test_start_all_nodes(http_controller, tmpdir, project, compute):
response = http_controller.post("/projects/{}/nodes/start".format(project.id), example=True)
assert response.status == 204
def test_stop_all_nodes(http_controller, tmpdir, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -121,6 +124,7 @@ def test_stop_all_nodes(http_controller, tmpdir, project, compute):
response = http_controller.post("/projects/{}/nodes/stop".format(project.id), example=True)
assert response.status == 204
def test_suspend_all_nodes(http_controller, tmpdir, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -128,6 +132,7 @@ def test_suspend_all_nodes(http_controller, tmpdir, project, compute):
response = http_controller.post("/projects/{}/nodes/suspend".format(project.id), example=True)
assert response.status == 204
def test_reload_all_nodes(http_controller, tmpdir, project, compute):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -135,6 +140,7 @@ def test_reload_all_nodes(http_controller, tmpdir, project, compute):
response = http_controller.post("/projects/{}/nodes/reload".format(project.id), example=True)
assert response.status == 204
def test_start_node(http_controller, tmpdir, project, compute, node):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -142,6 +148,7 @@ def test_start_node(http_controller, tmpdir, project, compute, node):
response = http_controller.post("/projects/{}/nodes/{}/start".format(project.id, node.id), example=True)
assert response.status == 204
def test_stop_node(http_controller, tmpdir, project, compute, node):
response = MagicMock()
compute.post = AsyncioMagicMock()
@ -172,3 +179,23 @@ def test_delete_node(http_controller, tmpdir, project, compute, node):
response = http_controller.delete("/projects/{}/nodes/{}".format(project.id, node.id), example=True)
assert response.status == 204
def test_dynamips_idle_pc(http_controller, tmpdir, project, compute, node):
response = MagicMock()
response.json = {"idlepc": "0x60606f54"}
compute.get = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{}/nodes/{}/dynamips/auto_idlepc".format(project.id, node.id), example=True)
assert response.status == 200
assert response.json["idlepc"] == "0x60606f54"
def test_dynamips_idlepc_proposals(http_controller, tmpdir, project, compute, node):
response = MagicMock()
response.json = ["0x60606f54", "0x33805a22"]
compute.get = AsyncioMagicMock(return_value=response)
response = http_controller.get("/projects/{}/nodes/{}/dynamips/idlepc_proposals".format(project.id, node.id), example=True)
assert response.status == 200
assert response.json == ["0x60606f54", "0x33805a22"]