mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-24 09:18:08 +00:00
Capture is OK on server side
This commit is contained in:
parent
ff7f014423
commit
6c3a926ce3
@ -15,12 +15,16 @@
|
|||||||
# 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
|
||||||
|
|
||||||
|
|
||||||
from ..web.route import Route
|
from ..web.route import Route
|
||||||
from ..modules.port_manager import PortManager
|
from ..modules.port_manager import PortManager
|
||||||
from ..schemas.iou import IOU_CREATE_SCHEMA
|
from ..schemas.iou import IOU_CREATE_SCHEMA
|
||||||
from ..schemas.iou import IOU_UPDATE_SCHEMA
|
from ..schemas.iou import IOU_UPDATE_SCHEMA
|
||||||
from ..schemas.iou import IOU_OBJECT_SCHEMA
|
from ..schemas.iou import IOU_OBJECT_SCHEMA
|
||||||
from ..schemas.iou import IOU_NIO_SCHEMA
|
from ..schemas.iou import IOU_NIO_SCHEMA
|
||||||
|
from ..schemas.iou import IOU_CAPTURE_SCHEMA
|
||||||
from ..modules.iou import IOU
|
from ..modules.iou import IOU
|
||||||
|
|
||||||
|
|
||||||
@ -216,7 +220,7 @@ class IOUHandler:
|
|||||||
iou_manager = IOU.instance()
|
iou_manager = IOU.instance()
|
||||||
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
nio = iou_manager.create_nio(vm.iouyap_path, request.json)
|
nio = iou_manager.create_nio(vm.iouyap_path, request.json)
|
||||||
vm.slot_add_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]), nio)
|
vm.adapter_add_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]), nio)
|
||||||
response.set_status(201)
|
response.set_status(201)
|
||||||
response.json(nio)
|
response.json(nio)
|
||||||
|
|
||||||
@ -239,5 +243,53 @@ class IOUHandler:
|
|||||||
|
|
||||||
iou_manager = IOU.instance()
|
iou_manager = IOU.instance()
|
||||||
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
vm.slot_remove_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]))
|
vm.adapter_remove_nio_binding(int(request.match_info["adapter_number"]), int(request.match_info["port_number"]))
|
||||||
|
response.set_status(204)
|
||||||
|
|
||||||
|
@Route.post(
|
||||||
|
r"/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/start_capture",
|
||||||
|
parameters={
|
||||||
|
"project_id": "UUID for the project",
|
||||||
|
"vm_id": "UUID for the instance",
|
||||||
|
"adapter_number": "Adapter to start a packet capture",
|
||||||
|
"port_number": "Port on the adapter"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
200: "Capture started",
|
||||||
|
400: "Invalid request",
|
||||||
|
404: "Instance doesn't exist"
|
||||||
|
},
|
||||||
|
description="Start a packet capture on a IOU VM instance",
|
||||||
|
input=IOU_CAPTURE_SCHEMA)
|
||||||
|
def start_capture(request, response):
|
||||||
|
|
||||||
|
iou_manager = IOU.instance()
|
||||||
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
|
adapter_number = int(request.match_info["adapter_number"])
|
||||||
|
port_number = int(request.match_info["port_number"])
|
||||||
|
pcap_file_path = os.path.join(vm.project.capture_working_directory(), request.json["capture_file_name"])
|
||||||
|
yield from vm.start_capture(adapter_number, port_number, pcap_file_path, request.json["data_link_type"])
|
||||||
|
response.json({"pcap_file_path": pcap_file_path})
|
||||||
|
|
||||||
|
@Route.post(
|
||||||
|
r"/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/stop_capture",
|
||||||
|
parameters={
|
||||||
|
"project_id": "UUID for the project",
|
||||||
|
"vm_id": "UUID for the instance",
|
||||||
|
"adapter_number": "Adapter to stop a packet capture",
|
||||||
|
"port_number": "Port on the adapter (always 0)"
|
||||||
|
},
|
||||||
|
status_codes={
|
||||||
|
204: "Capture stopped",
|
||||||
|
400: "Invalid request",
|
||||||
|
404: "Instance doesn't exist"
|
||||||
|
},
|
||||||
|
description="Stop a packet capture on a IOU VM instance")
|
||||||
|
def stop_capture(request, response):
|
||||||
|
|
||||||
|
iou_manager = IOU.instance()
|
||||||
|
vm = iou_manager.get_vm(request.match_info["vm_id"], project_id=request.match_info["project_id"])
|
||||||
|
adapter_number = int(request.match_info["adapter_number"])
|
||||||
|
port_number = int(request.match_info["port_number"])
|
||||||
|
yield from vm.stop_capture(adapter_number, port_number)
|
||||||
response.set_status(204)
|
response.set_status(204)
|
||||||
|
@ -445,7 +445,7 @@ class IOUVM(BaseVM):
|
|||||||
"base_port": "49000"}
|
"base_port": "49000"}
|
||||||
|
|
||||||
bay_id = 0
|
bay_id = 0
|
||||||
for adapter in self._slots:
|
for adapter in self._adapters:
|
||||||
unit_id = 0
|
unit_id = 0
|
||||||
for unit in adapter.ports.keys():
|
for unit in adapter.ports.keys():
|
||||||
nio = adapter.get_nio(unit)
|
nio = adapter.get_nio(unit)
|
||||||
@ -716,7 +716,7 @@ class IOUVM(BaseVM):
|
|||||||
id=self._id,
|
id=self._id,
|
||||||
adapters=len(self._ethernet_adapters)))
|
adapters=len(self._ethernet_adapters)))
|
||||||
|
|
||||||
self._slots = self._ethernet_adapters + self._serial_adapters
|
self._adapters = self._ethernet_adapters + self._serial_adapters
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def serial_adapters(self):
|
def serial_adapters(self):
|
||||||
@ -742,21 +742,21 @@ class IOUVM(BaseVM):
|
|||||||
id=self._id,
|
id=self._id,
|
||||||
adapters=len(self._serial_adapters)))
|
adapters=len(self._serial_adapters)))
|
||||||
|
|
||||||
self._slots = self._ethernet_adapters + self._serial_adapters
|
self._adapters = self._ethernet_adapters + self._serial_adapters
|
||||||
|
|
||||||
def slot_add_nio_binding(self, adapter_number, port_number, nio):
|
def adapter_add_nio_binding(self, adapter_number, port_number, nio):
|
||||||
"""
|
"""
|
||||||
Adds a slot NIO binding.
|
Adds a adapter NIO binding.
|
||||||
:param adapter_number: slot ID
|
:param adapter_number: adapter ID
|
||||||
:param port_number: port ID
|
:param port_number: port ID
|
||||||
:param nio: NIO instance to add to the slot/port
|
:param nio: NIO instance to add to the adapter/port
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
adapter = self._slots[adapter_number]
|
adapter = self._adapters[adapter_number]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise IOUError("Slot {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
adapter_number=adapter_number))
|
adapter_number=adapter_number))
|
||||||
|
|
||||||
if not adapter.port_exists(port_number):
|
if not adapter.port_exists(port_number):
|
||||||
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
@ -772,19 +772,19 @@ class IOUVM(BaseVM):
|
|||||||
self._update_iouyap_config()
|
self._update_iouyap_config()
|
||||||
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
||||||
|
|
||||||
def slot_remove_nio_binding(self, adapter_number, port_number):
|
def adapter_remove_nio_binding(self, adapter_number, port_number):
|
||||||
"""
|
"""
|
||||||
Removes a slot NIO binding.
|
Removes a adapter NIO binding.
|
||||||
:param adapter_number: slot ID
|
:param adapter_number: adapter ID
|
||||||
:param port_number: port ID
|
:param port_number: port ID
|
||||||
:returns: NIO instance
|
:returns: NIO instance
|
||||||
"""
|
"""
|
||||||
|
|
||||||
try:
|
try:
|
||||||
adapter = self._slots[adapter_number]
|
adapter = self._adapters[adapter_number]
|
||||||
except IndexError:
|
except IndexError:
|
||||||
raise IOUError("Slot {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
adapter_number=adapter_number))
|
adapter_number=adapter_number))
|
||||||
|
|
||||||
if not adapter.port_exists(port_number):
|
if not adapter.port_exists(port_number):
|
||||||
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
@ -889,3 +889,73 @@ class IOUVM(BaseVM):
|
|||||||
return path
|
return path
|
||||||
else:
|
else:
|
||||||
return None
|
return None
|
||||||
|
|
||||||
|
def start_capture(self, adapter_number, port_number, output_file, data_link_type="DLT_EN10MB"):
|
||||||
|
"""
|
||||||
|
Starts a packet capture.
|
||||||
|
:param adapter_number: adapter ID
|
||||||
|
:param port_number: port ID
|
||||||
|
:param port: allocated port
|
||||||
|
:param output_file: PCAP destination file for the capture
|
||||||
|
:param data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
adapter = self._adapters[adapter_number]
|
||||||
|
except IndexError:
|
||||||
|
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
|
adapter_number=adapter_number))
|
||||||
|
|
||||||
|
if not adapter.port_exists(port_number):
|
||||||
|
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
|
port_number=port_number))
|
||||||
|
|
||||||
|
nio = adapter.get_nio(port_number)
|
||||||
|
if nio.capturing:
|
||||||
|
raise IOUError("Packet capture is already activated on {adapter_number}/{port_number}".format(adapter_number=adapter_number,
|
||||||
|
port_number=port_number))
|
||||||
|
|
||||||
|
try:
|
||||||
|
os.makedirs(os.path.dirname(output_file))
|
||||||
|
except FileExistsError:
|
||||||
|
pass
|
||||||
|
except OSError as e:
|
||||||
|
raise IOUError("Could not create captures directory {}".format(e))
|
||||||
|
|
||||||
|
nio.startPacketCapture(output_file, data_link_type)
|
||||||
|
|
||||||
|
log.info("IOU {name} [id={id}]: starting packet capture on {adapter_number}/{port_number}".format(name=self._name,
|
||||||
|
id=self._id,
|
||||||
|
adapter_number=adapter_number,
|
||||||
|
port_number=port_number))
|
||||||
|
|
||||||
|
if self.is_iouyap_running():
|
||||||
|
self._update_iouyap_config()
|
||||||
|
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
||||||
|
|
||||||
|
def stop_capture(self, adapter_number, port_number):
|
||||||
|
"""
|
||||||
|
Stops a packet capture.
|
||||||
|
:param adapter_number: adapter ID
|
||||||
|
:param port_number: port ID
|
||||||
|
"""
|
||||||
|
|
||||||
|
try:
|
||||||
|
adapter = self._adapters[adapter_number]
|
||||||
|
except IndexError:
|
||||||
|
raise IOUError("Adapter {adapter_number} doesn't exist on IOU {name}".format(name=self._name,
|
||||||
|
adapter_number=adapter_number))
|
||||||
|
|
||||||
|
if not adapter.port_exists(port_number):
|
||||||
|
raise IOUError("Port {port_number} doesn't exist in adapter {adapter}".format(adapter=adapter,
|
||||||
|
port_number=port_number))
|
||||||
|
|
||||||
|
nio = adapter.get_nio(port_number)
|
||||||
|
nio.stopPacketCapture()
|
||||||
|
log.info("IOU {name} [id={id}]: stopping packet capture on {adapter_number}/{port_number}".format(name=self._name,
|
||||||
|
id=self._id,
|
||||||
|
adapter_number=adapter_number,
|
||||||
|
port_number=port_number))
|
||||||
|
if self.is_iouyap_running():
|
||||||
|
self._update_iouyap_config()
|
||||||
|
os.kill(self._iouyap_process.pid, signal.SIGHUP)
|
||||||
|
@ -23,33 +23,35 @@ Base interface for NIOs.
|
|||||||
class NIO(object):
|
class NIO(object):
|
||||||
|
|
||||||
"""
|
"""
|
||||||
Network Input/Output.
|
IOU NIO.
|
||||||
"""
|
"""
|
||||||
|
|
||||||
def __init__(self):
|
def __init__(self):
|
||||||
|
|
||||||
self._capturing = False
|
self._capturing = False
|
||||||
self._pcap_output_file = ""
|
self._pcap_output_file = ""
|
||||||
|
self._pcap_data_link_type = ""
|
||||||
|
|
||||||
def startPacketCapture(self, pcap_output_file):
|
def startPacketCapture(self, pcap_output_file, pcap_data_link_type="DLT_EN10MB"):
|
||||||
"""
|
"""
|
||||||
|
|
||||||
:param pcap_output_file: PCAP destination file for the capture
|
:param pcap_output_file: PCAP destination file for the capture
|
||||||
|
:param pcap_data_link_type: PCAP data link type (DLT_*), default is DLT_EN10MB
|
||||||
"""
|
"""
|
||||||
|
|
||||||
self._capturing = True
|
self._capturing = True
|
||||||
self._pcap_output_file = pcap_output_file
|
self._pcap_output_file = pcap_output_file
|
||||||
|
self._pcap_data_link_type = pcap_data_link_type
|
||||||
|
|
||||||
def stopPacketCapture(self):
|
def stopPacketCapture(self):
|
||||||
|
|
||||||
self._capturing = False
|
self._capturing = False
|
||||||
self._pcap_output_file = ""
|
self._pcap_output_file = ""
|
||||||
|
self._pcap_data_link_type = ""
|
||||||
|
|
||||||
@property
|
@property
|
||||||
def capturing(self):
|
def capturing(self):
|
||||||
"""
|
"""
|
||||||
Returns either a capture is configured on this NIO.
|
Returns either a capture is configured on this NIO.
|
||||||
|
|
||||||
:returns: boolean
|
:returns: boolean
|
||||||
"""
|
"""
|
||||||
|
|
||||||
@ -59,8 +61,16 @@ class NIO(object):
|
|||||||
def pcap_output_file(self):
|
def pcap_output_file(self):
|
||||||
"""
|
"""
|
||||||
Returns the path to the PCAP output file.
|
Returns the path to the PCAP output file.
|
||||||
|
|
||||||
:returns: path to the PCAP output file
|
:returns: path to the PCAP output file
|
||||||
"""
|
"""
|
||||||
|
|
||||||
return self._pcap_output_file
|
return self._pcap_output_file
|
||||||
|
|
||||||
|
@property
|
||||||
|
def pcap_data_link_type(self):
|
||||||
|
"""
|
||||||
|
Returns the PCAP data link type
|
||||||
|
:returns: PCAP data link type (DLT_* value)
|
||||||
|
"""
|
||||||
|
|
||||||
|
return self._pcap_data_link_type
|
||||||
|
@ -103,10 +103,6 @@ IOU_UPDATE_SCHEMA = {
|
|||||||
"description": "Path of iourc",
|
"description": "Path of iourc",
|
||||||
"type": ["string", "null"]
|
"type": ["string", "null"]
|
||||||
},
|
},
|
||||||
"initial_config": {
|
|
||||||
"description": "Initial configuration path",
|
|
||||||
"type": ["string", "null"]
|
|
||||||
},
|
|
||||||
"serial_adapters": {
|
"serial_adapters": {
|
||||||
"description": "How many serial adapters are connected to the IOU",
|
"description": "How many serial adapters are connected to the IOU",
|
||||||
"type": ["integer", "null"]
|
"type": ["integer", "null"]
|
||||||
@ -265,3 +261,23 @@ IOU_NIO_SCHEMA = {
|
|||||||
"additionalProperties": True,
|
"additionalProperties": True,
|
||||||
"required": ["type"]
|
"required": ["type"]
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IOU_CAPTURE_SCHEMA = {
|
||||||
|
"$schema": "http://json-schema.org/draft-04/schema#",
|
||||||
|
"description": "Request validation to start a packet capture on a IOU instance",
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"capture_file_name": {
|
||||||
|
"description": "Capture file name",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
"data_link_type": {
|
||||||
|
"description": "PCAP data link type",
|
||||||
|
"type": "string",
|
||||||
|
"minLength": 1,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
"additionalProperties": False,
|
||||||
|
"required": ["capture_file_name", "data_link_type"]
|
||||||
|
}
|
||||||
|
@ -201,3 +201,25 @@ def test_iou_delete_nio(server, vm):
|
|||||||
response = server.delete("/projects/{project_id}/iou/vms/{vm_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True)
|
response = server.delete("/projects/{project_id}/iou/vms/{vm_id}/adapters/1/ports/0/nio".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), example=True)
|
||||||
assert response.status == 204
|
assert response.status == 204
|
||||||
assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
|
assert response.route == "/projects/{project_id}/iou/vms/{vm_id}/adapters/{adapter_number:\d+}/ports/{port_number:\d+}/nio"
|
||||||
|
|
||||||
|
|
||||||
|
def test_iou_start_capture(server, vm, tmpdir):
|
||||||
|
|
||||||
|
with asyncio_patch("gns3server.modules.iou.iou_vm.IOUVM.start_capture", return_value=True) as mock:
|
||||||
|
|
||||||
|
params = {"capture_file_name": "test.pcap", "data_link_type": "DLT_EN10MB"}
|
||||||
|
response = server.post("/projects/{project_id}/iou/vms/{vm_id}/adapters/0/ports/0/start_capture".format(project_id=vm["project_id"], vm_id=vm["vm_id"]), body=params)
|
||||||
|
|
||||||
|
assert mock.called
|
||||||
|
assert response.status == 200
|
||||||
|
assert "test.pcap" in response.json["pcap_file_path"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_iou_stop_capture(server, vm, tmpdir):
|
||||||
|
|
||||||
|
with asyncio_patch("gns3server.modules.iou.iou_vm.IOUVM.stop_capture", return_value=True) as mock:
|
||||||
|
|
||||||
|
response = server.post("/projects/{project_id}/iou/vms/{vm_id}/adapters/0/ports/0/stop_capture".format(project_id=vm["project_id"], vm_id=vm["vm_id"]))
|
||||||
|
|
||||||
|
assert mock.called
|
||||||
|
assert response.status == 204
|
||||||
|
@ -258,3 +258,23 @@ def test_enable_l1_keepalives(loop, vm):
|
|||||||
with pytest.raises(IOUError):
|
with pytest.raises(IOUError):
|
||||||
loop.run_until_complete(asyncio.async(vm._enable_l1_keepalives(command)))
|
loop.run_until_complete(asyncio.async(vm._enable_l1_keepalives(command)))
|
||||||
assert command == ["test"]
|
assert command == ["test"]
|
||||||
|
|
||||||
|
|
||||||
|
def test_start_capture(vm, tmpdir, manager, free_console_port):
|
||||||
|
|
||||||
|
output_file = str(tmpdir / "test.pcap")
|
||||||
|
nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"})
|
||||||
|
vm.adapter_add_nio_binding(0, 0, nio)
|
||||||
|
vm.start_capture(0, 0, output_file)
|
||||||
|
assert vm._adapters[0].get_nio(0).capturing
|
||||||
|
|
||||||
|
|
||||||
|
def test_stop_capture(vm, tmpdir, manager, free_console_port):
|
||||||
|
|
||||||
|
output_file = str(tmpdir / "test.pcap")
|
||||||
|
nio = manager.create_nio(vm.iouyap_path, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "192.168.1.2"})
|
||||||
|
vm.adapter_add_nio_binding(0, 0, nio)
|
||||||
|
vm.start_capture(0, 0, output_file)
|
||||||
|
assert vm._adapters[0].get_nio(0).capturing
|
||||||
|
vm.stop_capture(0, 0)
|
||||||
|
assert vm._adapters[0].get_nio(0).capturing == False
|
||||||
|
Loading…
Reference in New Issue
Block a user