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

Support to reset all console connections. Ref https://github.com/GNS3/gns3-server/issues/1619

This commit is contained in:
grossmj 2020-07-26 18:27:18 +09:30
parent 4acc457674
commit bdd703a0dc
17 changed files with 283 additions and 7 deletions

View File

@ -397,6 +397,14 @@ class BaseNode:
self._wrapper_telnet_server.close() self._wrapper_telnet_server.close()
await self._wrapper_telnet_server.wait_closed() await self._wrapper_telnet_server.wait_closed()
async def reset_console(self):
"""
Reset console
"""
await self.stop_wrap_console()
await self.start_wrap_console()
async def start_websocket_console(self, request): async def start_websocket_console(self, request):
""" """
Connect to console using Websocket. Connect to console using Websocket.

View File

@ -743,6 +743,14 @@ class DockerVM(BaseNode):
break break
await self.stop() await self.stop()
async def reset_console(self):
"""
Reset the console.
"""
await self._clean_servers()
await self._start_console()
async def is_running(self): async def is_running(self):
""" """
Checks if the container is running. Checks if the container is running.

View File

@ -569,17 +569,39 @@ class IOUVM(BaseNode):
log.error("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout)) log.error("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
raise IOUError("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout)) raise IOUError("Could not start IOU {}: {}\n{}".format(self._path, e, iou_stdout))
if self.console and self.console_type == "telnet": await self.start_console()
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True, echo=True)
try:
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host, self.console)
except OSError as e:
await self.stop()
raise IOUError("Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host, self.console, e))
# configure networking support # configure networking support
await self._networking() await self._networking()
async def start_console(self):
"""
Start the Telnet server to provide console access.
"""
if self.console and self.console_type == "telnet":
server = AsyncioTelnetServer(reader=self._iou_process.stdout, writer=self._iou_process.stdin, binary=True,
echo=True)
try:
self._telnet_server = await asyncio.start_server(server.run, self._manager.port_manager.console_host,
self.console)
except OSError as e:
await self.stop()
raise IOUError(
"Could not start Telnet server on socket {}:{}: {}".format(self._manager.port_manager.console_host,
self.console, e))
async def reset_console(self):
"""
Reset the console.
"""
if self._telnet_server:
self._telnet_server.close()
await self._telnet_server.wait_closed()
self._telnet_server = None
await self.start_console()
@locking @locking
async def _networking(self): async def _networking(self):
""" """

View File

@ -979,6 +979,14 @@ class VirtualBoxVM(BaseNode):
self._remote_pipe.close() self._remote_pipe.close()
self._telnet_server = None self._telnet_server = None
async def reset_console(self):
"""
Reset the console.
"""
await self._stop_remote_console()
await self._start_console()
@BaseNode.console_type.setter @BaseNode.console_type.setter
def console_type(self, new_console_type): def console_type(self, new_console_type):
""" """

View File

@ -891,6 +891,14 @@ class VMwareVM(BaseNode):
self._remote_pipe.close() self._remote_pipe.close()
self._telnet_server = None self._telnet_server = None
async def reset_console(self):
"""
Reset the console.
"""
await self._stop_remote_console()
await self._start_console()
@BaseNode.console_type.setter @BaseNode.console_type.setter
def console_type(self, new_console_type): def console_type(self, new_console_type):
""" """

View File

@ -539,6 +539,17 @@ class Node:
except asyncio.TimeoutError: except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name)) raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reloading {}".format(self._name))
async def reset_console(self):
"""
Reset the console
"""
if self._console and self._console_type == "telnet":
try:
await self.post("/console/reset", timeout=240)
except asyncio.TimeoutError:
raise aiohttp.web.HTTPRequestTimeout(text="Timeout when reset console {}".format(self._name))
async def post(self, path, data=None, **kwargs): async def post(self, path, data=None, **kwargs):
""" """
HTTP post on the node HTTP post on the node

View File

@ -1124,6 +1124,17 @@ class Project:
pool.append(node.suspend) pool.append(node.suspend)
await pool.join() await pool.join()
@open_required
async def reset_console_all(self):
"""
Reset console for all nodes
"""
pool = Pool(concurrency=3)
for node in self.nodes.values():
pool.append(node.reset_console)
await pool.join()
@open_required @open_required
async def duplicate_node(self, node, x, y, z): async def duplicate_node(self, node, x, y, z):
""" """

View File

@ -425,3 +425,23 @@ class DockerHandler:
docker_manager = Docker.instance() docker_manager = Docker.instance()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await container.start_websocket_console(request) return await container.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/docker/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
docker_manager = Docker.instance()
container = docker_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await container.reset_console()
response.set_status(204)

View File

@ -526,3 +526,23 @@ class DynamipsVMHandler:
dynamips_manager = Dynamips.instance() dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/dynamips/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
dynamips_manager = Dynamips.instance()
vm = dynamips_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -467,3 +467,23 @@ class IOUHandler:
iou_manager = IOU.instance() iou_manager = IOU.instance()
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/iou/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
iou_manager = IOU.instance()
vm = iou_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -590,3 +590,23 @@ class QEMUHandler:
qemu_manager = Qemu.instance() qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/qemu/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
qemu_manager = Qemu.instance()
vm = qemu_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -437,3 +437,23 @@ class VirtualBoxHandler:
virtualbox_manager = VirtualBox.instance() virtualbox_manager = VirtualBox.instance()
vm = virtualbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = virtualbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/virtualbox/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
virtualbox_manager = VirtualBox.instance()
vm = virtualbox_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -422,3 +422,23 @@ class VMwareHandler:
vmware_manager = VMware.instance() vmware_manager = VMware.instance()
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/vmware/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
vmware_manager = VMware.instance()
vm = vmware_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -375,3 +375,23 @@ class VPCSHandler:
vpcs_manager = VPCS.instance() vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"]) vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
return await vm.start_websocket_console(request) return await vm.start_websocket_console(request)
@Route.post(
r"/projects/{project_id}/vpcs/nodes/{node_id}/console/reset",
description="Reset console",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID",
},
status_codes={
204: "Console has been reset",
400: "Invalid request",
404: "Instance doesn't exist",
409: "Container not started"
})
async def reset_console(request, response):
vpcs_manager = VPCS.instance()
vm = vpcs_manager.get_node(request.match_info["node_id"], project_id=request.match_info["project_id"])
await vm.reset_console()
response.set_status(204)

View File

@ -506,3 +506,40 @@ class NodeHandler:
request.app['websockets'].discard(ws) request.app['websockets'].discard(ws)
return ws return ws
@Route.post(
r"/projects/{project_id}/nodes/console/reset",
parameters={
"project_id": "Project UUID"
},
status_codes={
204: "All nodes successfully reset consoles",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reset console for all nodes belonging to the project",
output=NODE_OBJECT_SCHEMA)
async def reset_console_all(request, response):
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
await project.reset_console_all()
response.set_status(204)
@Route.post(
r"/projects/{project_id}/nodes/{node_id}/console/reset",
parameters={
"project_id": "Project UUID",
"node_id": "Node UUID"
},
status_codes={
204: "Console reset",
400: "Invalid request",
404: "Instance doesn't exist"
},
description="Reload a node instance")
async def console_reset(request, response):
project = await Controller.instance().get_loaded_project(request.match_info["project_id"])
node = project.get_node(request.match_info["node_id"])
await node.post("/console/reset", request.json)
response.set_status(204)

View File

@ -829,6 +829,22 @@ async def test_suspend_all(project):
assert len(compute.post.call_args_list) == 10 assert len(compute.post.call_args_list) == 10
async def test_console_reset_all(project):
compute = MagicMock()
compute.id = "local"
response = MagicMock()
response.json = {"console": 2048, "console_type": "telnet"}
compute.post = AsyncioMagicMock(return_value=response)
for node_i in range(0, 10):
await project.add_node(compute, "test", None, node_type="vpcs", properties={"startup_config": "test.cfg"})
compute.post = AsyncioMagicMock()
await project.reset_console_all()
assert len(compute.post.call_args_list) == 10
async def test_node_name(project): async def test_node_name(project):
compute = MagicMock() compute = MagicMock()

View File

@ -140,6 +140,13 @@ async def test_reload_all_nodes(controller_api, project, compute):
assert response.status == 204 assert response.status == 204
async def test_reset_console_all_nodes(controller_api, project, compute):
compute.post = AsyncioMagicMock()
response = await controller_api.post("/projects/{}/nodes/console/reset".format(project.id))
assert response.status == 204
async def test_start_node(controller_api, project, node, compute): async def test_start_node(controller_api, project, node, compute):
compute.post = AsyncioMagicMock() compute.post = AsyncioMagicMock()