diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 8d0e0f50..c88600ea 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -34,6 +34,7 @@ from gns3server.utils.asyncio import wait_for_file_creation from gns3server.utils.asyncio import monitor_process from gns3server.utils.get_resource import get_resource from gns3server.utils.hostname import is_rfc1123_hostname_valid +from gns3server.utils import macaddress_to_int, int_to_macaddress from gns3server.compute.ubridge.ubridge_error import UbridgeError, UbridgeNamespaceError from ..base_node import BaseNode @@ -105,6 +106,7 @@ class DockerVM(BaseNode): self._environment = environment self._cid = None self._ethernet_adapters = [] + self._mac_address = "" self._temporary_directory = None self._telnet_servers = [] self._vnc_process = None @@ -130,6 +132,8 @@ class DockerVM(BaseNode): else: self.adapters = adapters + self.mac_address = "" # this will generate a MAC address + log.debug( "{module}: {name} [{image}] initialized.".format( module=self.manager.module_name, name=self.name, image=self._image @@ -145,6 +149,7 @@ class DockerVM(BaseNode): "project_id": self._project.id, "image": self._image, "adapters": self.adapters, + "mac_address": self.mac_address, "console": self.console, "console_type": self.console_type, "console_resolution": self.console_resolution, @@ -190,6 +195,36 @@ class DockerVM(BaseNode): def ethernet_adapters(self): return self._ethernet_adapters + @property + def mac_address(self): + """ + Returns the MAC address for this Docker container. + + :returns: adapter type (string) + """ + + return self._mac_address + + @mac_address.setter + def mac_address(self, mac_address): + """ + Sets the MAC address for this Docker container. + + :param mac_address: MAC address + """ + + if not mac_address: + # use the node UUID to generate a random MAC address + self._mac_address = "02:42:%s:%s:%s:00" % (self.id[2:4], self.id[4:6], self.id[6:8]) + else: + self._mac_address = mac_address + + log.info('Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format( + name=self._name, + id=self._id, + mac_addr=self._mac_address) + ) + @property def start_command(self): return self._start_command @@ -1058,7 +1093,20 @@ class DockerVM(BaseNode): adapter_number=adapter_number, hostif=adapter.host_ifc ) ) - log.debug("Move container %s adapter %s to namespace %s", self.name, adapter.host_ifc, self._namespace) + + mac_address = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number) + custom_adapter = self._get_custom_adapter_settings(adapter_number) + custom_mac_address = custom_adapter.get("mac_address") + if custom_mac_address: + mac_address = custom_mac_address + + try: + await self._ubridge_send('docker set_mac_addr {ifc} {mac}'.format(ifc=adapter.host_ifc, mac=mac_address)) + except UbridgeError: + log.warning(f"Could not set MAC address {mac_address} on interface {adapter.host_ifc}") + + + log.debug(f"Move container {self.name} adapter {adapter.host_ifc} to namespace {self._namespace}") try: await self._ubridge_send( "docker move_to_ns {ifc} {ns} eth{adapter}".format( @@ -1067,6 +1115,8 @@ class DockerVM(BaseNode): ) except UbridgeError as e: raise UbridgeNamespaceError(e) + else: + log.info(f"Created adapter {adapter_number} with MAC address {mac_address} in namespace {self._namespace}") if nio: await self._connect_nio(adapter_number, nio) diff --git a/gns3server/controller/node.py b/gns3server/controller/node.py index 839f36c4..ebb7be38 100644 --- a/gns3server/controller/node.py +++ b/gns3server/controller/node.py @@ -29,8 +29,8 @@ from .controller_error import ( ) from .ports.port_factory import PortFactory, StandardPortFactory, DynamipsPortFactory from ..utils.images import images_directories +from ..utils import macaddress_to_int, int_to_macaddress from ..config import Config -from ..utils.qt import qt_font_to_style import logging @@ -758,7 +758,13 @@ class Node: break port_name = f"eth{adapter_number}" port_name = custom_adapter_settings.get("port_name", port_name) - self._ports.append(PortFactory(port_name, 0, adapter_number, 0, "ethernet", short_name=port_name)) + mac_address = custom_adapter_settings.get("mac_address") + if not mac_address and "mac_address" in self._properties: + mac_address = int_to_macaddress(macaddress_to_int(self._properties["mac_address"]) + adapter_number) + + port = PortFactory(port_name, 0, adapter_number, 0, "ethernet", short_name=port_name) + port.mac_address = mac_address + self._ports.append(port) elif self._node_type in ("ethernet_switch", "ethernet_hub"): # Basic node we don't want to have adapter number port_number = 0 diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index b957d68d..8c66ef95 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -47,6 +47,7 @@ async def vm(compute_project, manager): vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", aux_type="none") vm._cid = "e90e34656842" + vm.mac_address = '02:42:3d:b7:93:00' return vm @@ -59,6 +60,7 @@ def test_json(vm, compute_project): 'project_id': compute_project.id, 'node_id': vm.id, 'adapters': 1, + 'mac_address': '02:42:3d:b7:93:00', 'console': vm.console, 'console_type': 'telnet', 'aux_type': 'none',