From 89e86b77782d0f80f7001d83fd74885bac147212 Mon Sep 17 00:00:00 2001 From: Julien Duponchelle Date: Tue, 9 Feb 2016 14:22:37 +0100 Subject: [PATCH] Create veth for docker even if link is not connected Fix #406 --- gns3server/modules/docker/docker_vm.py | 54 ++++++++++------------ tests/modules/docker/test_docker_vm.py | 63 +++++++++++++++----------- 2 files changed, 62 insertions(+), 55 deletions(-) diff --git a/gns3server/modules/docker/docker_vm.py b/gns3server/modules/docker/docker_vm.py index fce53c35..eb15f90c 100644 --- a/gns3server/modules/docker/docker_vm.py +++ b/gns3server/modules/docker/docker_vm.py @@ -168,9 +168,8 @@ class DockerVM(BaseVM): yield from self._start_ubridge() for adapter_number in range(0, self.adapters): nio = self._ethernet_adapters[adapter_number].get_nio(0) - if nio: - with (yield from self.manager.ubridge_lock): - yield from self._add_ubridge_connection(nio, adapter_number) + with (yield from self.manager.ubridge_lock): + yield from self._add_ubridge_connection(nio, adapter_number) yield from self._start_console() @@ -339,7 +338,7 @@ class DockerVM(BaseVM): """ Creates a connection in uBridge. - :param nio: NIO instance + :param nio: NIO instance or None if it's a dummu interface (if an interface is missing in ubridge you can't see it via ifconfig in the container) :param adapter_number: adapter number """ try: @@ -348,19 +347,16 @@ class DockerVM(BaseVM): raise DockerError( "Adapter {adapter_number} doesn't exist on Docker container '{name}'".format(name=self.name, adapter_number=adapter_number)) - if nio and isinstance(nio, NIOUDP): - for index in range(128): - if "gns3-veth{}ext".format(index) not in psutil.net_if_addrs(): - adapter.ifc = "eth{}".format(str(index)) - adapter.host_ifc = "gns3-veth{}ext".format(str(index)) - adapter.guest_ifc = "gns3-veth{}int".format(str(index)) - break - if not hasattr(adapter, "ifc"): - raise DockerError( - "Adapter {adapter_number} couldn't allocate interface on Docker container '{name}'. Too many Docker interfaces already exists".format( - name=self.name, adapter_number=adapter_number)) - else: - raise ValueError("Invalid NIO") + for index in range(128): + if "gns3-veth{}ext".format(index) not in psutil.net_if_addrs(): + adapter.ifc = "eth{}".format(str(index)) + adapter.host_ifc = "gns3-veth{}ext".format(str(index)) + adapter.guest_ifc = "gns3-veth{}int".format(str(index)) + break + if not hasattr(adapter, "ifc"): + raise DockerError( + "Adapter {adapter_number} couldn't allocate interface on Docker container '{name}'. Too many Docker interfaces already exists".format( + name=self.name, adapter_number=adapter_number)) yield from self._ubridge_hypervisor.send( 'docker create_veth {hostif} {guestif}'.format( @@ -372,25 +368,25 @@ class DockerVM(BaseVM): 'docker move_to_ns {ifc} {ns} eth{adapter}'.format( ifc=adapter.guest_ifc, ns=namespace, adapter=adapter_number)) - yield from self._ubridge_hypervisor.send( - 'bridge create bridge{}'.format(adapter_number)) - yield from self._ubridge_hypervisor.send( - 'bridge add_nio_linux_raw bridge{adapter} {ifc}'.format( - ifc=adapter.host_ifc, adapter=adapter_number)) - if isinstance(nio, NIOUDP): + yield from self._ubridge_hypervisor.send( + 'bridge create bridge{}'.format(adapter_number)) + yield from self._ubridge_hypervisor.send( + 'bridge add_nio_linux_raw bridge{adapter} {ifc}'.format( + ifc=adapter.host_ifc, adapter=adapter_number)) + yield from self._ubridge_hypervisor.send( 'bridge add_nio_udp bridge{adapter} {lport} {rhost} {rport}'.format( adapter=adapter_number, lport=nio.lport, rhost=nio.rhost, rport=nio.rport)) - if nio.capturing: - yield from self._ubridge_hypervisor.send( - 'bridge start_capture bridge{adapter} "{pcap_file}"'.format( - adapter=adapter_number, pcap_file=nio.pcap_output_file)) + if nio.capturing: + yield from self._ubridge_hypervisor.send( + 'bridge start_capture bridge{adapter} "{pcap_file}"'.format( + adapter=adapter_number, pcap_file=nio.pcap_output_file)) - yield from self._ubridge_hypervisor.send( - 'bridge start bridge{adapter}'.format(adapter=adapter_number)) + yield from self._ubridge_hypervisor.send( + 'bridge start bridge{adapter}'.format(adapter=adapter_number)) def _delete_ubridge_connection(self, adapter_number): """Deletes a connection in uBridge. diff --git a/tests/modules/docker/test_docker_vm.py b/tests/modules/docker/test_docker_vm.py index 643dd005..ced26168 100644 --- a/tests/modules/docker/test_docker_vm.py +++ b/tests/modules/docker/test_docker_vm.py @@ -251,6 +251,31 @@ def test_start(loop, vm, manager, free_console_port): assert vm.status == "started" +def test_start_without_nio(loop, vm, manager, free_console_port): + """ + If no nio exists we will create one. + """ + + assert vm.status != "started" + vm.adapters = 1 + + # nio = manager.create_nio(0, {"type": "nio_udp", "lport": free_console_port, "rport": free_console_port, "rhost": "127.0.0.1"}) + # loop.run_until_complete(asyncio.async(vm.adapter_add_nio_binding(0, nio))) + + with asyncio_patch("gns3server.modules.docker.DockerVM._get_container_state", return_value="stopped"): + with asyncio_patch("gns3server.modules.docker.Docker.query") as mock_query: + with asyncio_patch("gns3server.modules.docker.DockerVM._start_ubridge") as mock_start_ubridge: + with asyncio_patch("gns3server.modules.docker.DockerVM._add_ubridge_connection") as mock_add_ubridge_connection: + with asyncio_patch("gns3server.modules.docker.DockerVM._start_console") as mock_start_console: + loop.run_until_complete(asyncio.async(vm.start())) + + mock_query.assert_called_with("POST", "containers/e90e34656842/start") + assert mock_add_ubridge_connection.called + assert mock_start_ubridge.called + assert mock_start_console.called + assert vm.status == "started" + + def test_start_unpause(loop, vm, manager, free_console_port): with asyncio_patch("gns3server.modules.docker.DockerVM._get_container_state", return_value="paused"): @@ -370,29 +395,6 @@ def test_get_namespace(loop, vm): mock_query.assert_called_with("GET", "containers/e90e34656842/json") -def test_add_ubridge_connection(loop, vm): - - nio = {"type": "nio_udp", - "lport": 4242, - "rport": 4343, - "rhost": "127.0.0.1"} - nio = vm.manager.create_nio(0, nio) - vm._ubridge_hypervisor = MagicMock() - with asyncio_patch("gns3server.modules.docker.DockerVM._get_namespace", return_value=42): - loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0))) - - calls = [ - call.send("docker create_veth gns3-veth0ext gns3-veth0int"), - call.send('docker move_to_ns gns3-veth0int 42'), - call.send('bridge create bridge0'), - call.send('bridge add_nio_linux_raw bridge0 gns3-veth0ext'), - call.send('bridge add_nio_udp bridge0 4242 127.0.0.1 4343'), - call.send('bridge start bridge0') - ] - # We need to check any_order ortherwise mock is confused by asyncio - vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True) - - def test_add_ubridge_connection(loop, vm): nio = {"type": "nio_udp", @@ -418,10 +420,19 @@ def test_add_ubridge_connection(loop, vm): vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True) -def test_add_ubridge_connection_invalid_nio(loop, vm): +def test_add_ubridge_connection_none_nio(loop, vm): - with pytest.raises(ValueError): - loop.run_until_complete(asyncio.async(vm._add_ubridge_connection({}, 0))) + nio = None + vm._ubridge_hypervisor = MagicMock() + with asyncio_patch("gns3server.modules.docker.DockerVM._get_namespace", return_value=42): + loop.run_until_complete(asyncio.async(vm._add_ubridge_connection(nio, 0))) + + calls = [ + call.send("docker create_veth gns3-veth0ext gns3-veth0int"), + call.send('docker move_to_ns gns3-veth0int 42 eth0'), + ] + # We need to check any_order ortherwise mock is confused by asyncio + vm._ubridge_hypervisor.assert_has_calls(calls, any_order=True) def test_add_ubridge_connection_invalid_adapter_number(loop, vm):