diff --git a/.github/workflows/docker-build.yml b/.github/workflows/docker-build.yml new file mode 100644 index 000000000..fec449ba2 --- /dev/null +++ b/.github/workflows/docker-build.yml @@ -0,0 +1,40 @@ +name: Build and Push Docker Image + +on: + workflow_dispatch: + push: + tags: + - v2.* + - v3.* + +jobs: + build: + runs-on: ubuntu-latest + + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Login to GitHub Container Registry + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ github.repository_owner }} + password: ${{ secrets.GITHUB_TOKEN }} + + - name: Login to Docker Hub + uses: docker/login-action@v3 + with: + registry: docker.io + username: ${{ secrets.DOCKERHUB_USERNAME }} + password: ${{ secrets.DOCKERHUB_TOKEN }} + + - name: Build and push to GitHub Container Registry + run: | + docker build -t ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest . + docker push ghcr.io/$(echo "${{ github.repository }}" | tr '[:upper:]' '[:lower:]'):latest + + - name: Build and push to Docker Hub + run: | + docker build -t gns3/${{ github.event.repository.name }}:latest . + docker push gns3/${{ github.event.repository.name }}:latest diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml index 23acd4375..aac48ce34 100644 --- a/.github/workflows/testing.yml +++ b/.github/workflows/testing.yml @@ -18,7 +18,7 @@ jobs: strategy: matrix: os: ["ubuntu-latest"] - python-version: ["3.9", "3.10", "3.11", "3.12", "3.13"] + python-version: ["3.8", "3.9", "3.10", "3.11", "3.12", "3.13", "3.14"] #include: # only test with Python 3.10 on Windows # - os: windows-latest diff --git a/CHANGELOG b/CHANGELOG index b3cbbd9c0..850f8c580 100644 --- a/CHANGELOG +++ b/CHANGELOG @@ -1,5 +1,18 @@ # Change Log +## 2.2.55 19/11/2025 + +* Docker API version requirements and process to handle older daemons +* Use docker_name property when creating a container +* Set name of container in Docker +* Support for Python 3.14 +* Enable ip cef in IOU L2 and IOU L3 base configs +* Only build and push Docker image when releasing a new version +* Fix pushing Docker image to Docker hub +* Update security issues in README.md. Fixes #2535 +* Add missing 'nat' template type in schema. Fixes #2529 +* Resolve deprecation warnings of regex library + ## 3.0.5 14/05/2025 * Bundle web-ui v3.0.5 diff --git a/gns3server/appliances/asterfusion-vAsterNOS.gns3a b/gns3server/appliances/asterfusion-vAsterNOS.gns3a index e6e392c32..991c36069 100644 --- a/gns3server/appliances/asterfusion-vAsterNOS.gns3a +++ b/gns3server/appliances/asterfusion-vAsterNOS.gns3a @@ -6,7 +6,7 @@ "vendor_name": "Asterfusion", "vendor_url": "https://cloudswit.ch/", "vendor_logo_url": "https://raw.githubusercontent.com/GNS3/gns3-registry/master/vendor-logos/asterfusion.png", - "documentation_url": "https://help.cloudswit.ch/portal/en/kb/articles/vasternos", + "documentation_url": "https://asternos.com/community/space/vasternos/post/vasternos-for-data-center-network", "product_name": "vAsterNOS", "product_url": "https://cloudswit.ch/product/sonic-enterprise-distribution", "registry_version": 4, @@ -30,18 +30,18 @@ }, "images": [ { - "filename": "vAsterNOS-V3.1.img", + "filename": "AsterNOS_V3.1_R0408P01-VS.img.gz", "version": "V3.1", - "md5sum": "c323c9c3f60e1a93eca2acdc5034b85c", - "filesize": 2724659200, - "download_url": "https://drive.cloudswitch.io/external/8ae2e3932ad8bb2ec30dd25be415d288ff3e4a949c557c6bd48ac6e6265bcfc1" + "md5sum": "5c182306af67be2c79a5642551412e48", + "filesize": 1425945924, + "download_url": "http://pub.asternos.com/AsterNOS_V3.1_R0408P01-VS.img.gz" } ], "versions": [ { "name": "V3.1", "images": { - "hda_disk_image": "vAsterNOS-V3.1.img" + "hda_disk_image": "AsterNOS_V3.1_R0408P01-VS.img.gz" } } ] diff --git a/gns3server/appliances/cisco-7200.gns3a b/gns3server/appliances/cisco-7200.gns3a index 3e723e50b..78d0be865 100644 --- a/gns3server/appliances/cisco-7200.gns3a +++ b/gns3server/appliances/cisco-7200.gns3a @@ -50,6 +50,12 @@ "version": "124-25G", "md5sum": "9c7cc9b3f3b3571411a7f62faaa2c036", "filesize": 71528984 + }, + { + "filename": "c7200-itpk9-mz.124-15.SW.image", + "version": "124-15.SW", + "md5sum": "3334a0facdbd26164e83d3c201fff5b5", + "filesize": 46423952 } ], "versions": [ @@ -87,6 +93,13 @@ "images": { "image": "c7200-a3jk9s-mz.124-25g.image" } + }, + { + "name": "124-15.SW", + "idlepc": "0x60b2bc68", + "images": { + "image": "c7200-itpk9-mz.124-15.SW.image" + } } ] } diff --git a/gns3server/appliances/extreme-networks-voss.gns3a b/gns3server/appliances/extreme-networks-voss.gns3a index a5ef0a64c..08d6c4509 100644 --- a/gns3server/appliances/extreme-networks-voss.gns3a +++ b/gns3server/appliances/extreme-networks-voss.gns3a @@ -28,6 +28,13 @@ "options": "-nographic" }, "images": [ + { + "filename": "FEGNS3.9.3.0.0.qcow2", + "version": "v9.3.0.0", + "md5sum": "2abfa8d64219aa75522238f87b782726", + "filesize": 490209280, + "direct_download_url": "https://akamai-ep.extremenetworks.com/Extreme_P/github-en/Virtual_VOSS/FEGNS3.9.3.0.0.qcow2" + }, { "filename": "VOSSGNS3.8.10.1.0.qcow2", "version": "v8.10.1.0", @@ -93,6 +100,13 @@ } ], "versions": [ + { + "name": "v9.3.0.0", + "images": + { + "hda_disk_image": "FEGNS3.9.3.0.0.qcow2" + } + }, { "name": "v8.10.1.0", "images": diff --git a/gns3server/appliances/freeRouter.gns3a b/gns3server/appliances/freeRouter.gns3a index ca53df882..087ec49fb 100644 --- a/gns3server/appliances/freeRouter.gns3a +++ b/gns3server/appliances/freeRouter.gns3a @@ -25,19 +25,19 @@ }, "images": [ { - "filename": "rtr.qcow2", - "version": "20.7.1", - "md5sum": "4707415d15d20ced92423d90e52eedcd", - "filesize": 254767616, - "download_url": "http://freerouter.nop.hu/", - "direct_download_url": "http://dl.nop.hu/rtr.qcow2" + "filename": "rtr-x86_64.qcow2", + "version": "25.8.29", + "md5sum": "13d72fdfd772258808a9d4b75fa67083", + "filesize": 73269248, + "download_url": "http://www.freertr.org/", + "direct_download_url": "http://dl.nop.hu/rtr-x86_64.qcow2" } ], "versions": [ { - "name": "20.7.1", + "name": "25.8.29", "images": { - "hda_disk_image": "rtr.qcow2" + "hda_disk_image": "rtr-x86_64.qcow2" } } ] diff --git a/gns3server/appliances/openwrt.gns3a b/gns3server/appliances/openwrt.gns3a index add7eb588..ac3711d3a 100644 --- a/gns3server/appliances/openwrt.gns3a +++ b/gns3server/appliances/openwrt.gns3a @@ -23,6 +23,15 @@ "kvm": "allow" }, "images": [ + { + "filename": "openwrt-24.10.2-x86-64-generic-ext4-combined.img", + "version": "24.10.2", + "md5sum": "0769916238524161b0a9a639ae261381", + "filesize": 126353408, + "download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/", + "direct_download_url": "https://downloads.openwrt.org/releases/24.10.2/targets/x86/64/openwrt-24.10.2-x86-64-generic-ext4-combined.img.gz", + "compression": "gzip" + }, { "filename": "openwrt-23.05.0-x86-64-generic-ext4-combined.img", "version": "23.05.0", @@ -223,7 +232,13 @@ } ], "versions": [ - { + { + "name": "24.10.2", + "images": { + "hda_disk_image": "openwrt-24.10.2-x86-64-generic-ext4-combined.img" + } + }, + { "name": "23.05.0", "images": { "hda_disk_image": "openwrt-23.05.0-x86-64-generic-ext4-combined.img" diff --git a/gns3server/appliances/opnsense.gns3a b/gns3server/appliances/opnsense.gns3a index 759701763..433b4f2de 100644 --- a/gns3server/appliances/opnsense.gns3a +++ b/gns3server/appliances/opnsense.gns3a @@ -25,6 +25,20 @@ "kvm": "require" }, "images": [ + { + "filename": "OPNsense-25.7-nano-amd64.img", + "version": "25.7", + "md5sum": "07aa8b44ebc57fa9ca0145f777901a59", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.7/" + }, + { + "filename": "OPNsense-25.1-nano-amd64.img", + "version": "25.1", + "md5sum": "9c119e4fed7dd7c1c162d8be4088d983", + "filesize": 3221225472, + "download_url": "https://opnsense.c0urier.net/releases/25.1/" + }, { "filename": "OPNsense-24.7-nano-amd64.img", "version": "24.7", diff --git a/gns3server/appliances/vyos.gns3a b/gns3server/appliances/vyos.gns3a index cace44ed3..3255a80e0 100644 --- a/gns3server/appliances/vyos.gns3a +++ b/gns3server/appliances/vyos.gns3a @@ -30,6 +30,20 @@ "on_close": "shutdown_signal" }, "images": [ + { + "filename": "vyos-1.4.3-kvm-amd64.qcow2", + "version": "1.4.3", + "md5sum": "4eec0ba2ec367bbbdf2d2f212f947746", + "filesize": 515047424, + "download_url": "https://support.vyos.io/" + }, + { + "filename": "vyos-1.4.2-kvm-amd64.qcow2", + "version": "1.4.2", + "md5sum": "78c5935e05a7bbd94a639fcee18f5799", + "filesize": 514064384, + "download_url": "https://support.vyos.io/" + }, { "filename": "vyos-1.4.1-kvm-amd64.qcow2", "version": "1.4.1", @@ -116,6 +130,18 @@ } ], "versions": [ + { + "name": "1.4.3", + "images": { + "hda_disk_image": "vyos-1.4.3-kvm-amd64.qcow2" + } + }, + { + "name": "1.4.2", + "images": { + "hda_disk_image": "vyos-1.4.2-kvm-amd64.qcow2" + } + }, { "name": "1.4.1", "images": { diff --git a/gns3server/compute/docker/__init__.py b/gns3server/compute/docker/__init__.py index 1ab159de4..567d1ef32 100644 --- a/gns3server/compute/docker/__init__.py +++ b/gns3server/compute/docker/__init__.py @@ -38,9 +38,9 @@ log = logging.getLogger(__name__) # Be careful to keep it consistent -DOCKER_MINIMUM_API_VERSION = "1.25" -DOCKER_MINIMUM_VERSION = "1.13" -DOCKER_PREFERRED_API_VERSION = "1.30" +DOCKER_MINIMUM_API_VERSION = "1.40" +DOCKER_MINIMUM_VERSION = "19.03.8" +DOCKER_PREFERRED_API_VERSION = "1.44" CHUNK_SIZE = 1024 * 8 # 8KB @@ -125,22 +125,28 @@ class Docker(BaseManager): if not self._connected: try: self._connected = True - version = await self.query("GET", "version") + docker_info = await self.query("GET", "version") except (aiohttp.ClientError, FileNotFoundError): self._connected = False - raise DockerError("Can't connect to docker daemon") + raise DockerError("Can't connect to Docker daemon") - docker_version = parse_version(version["ApiVersion"]) + api_version = parse_version(docker_info['ApiVersion']) + version = docker_info["Version"] - if docker_version < parse_version(DOCKER_MINIMUM_API_VERSION): - raise DockerError( - f"Docker version is {version['Version']}. " - f"GNS3 requires a minimum version of {DOCKER_MINIMUM_VERSION}" + if api_version < parse_version(DOCKER_MINIMUM_API_VERSION): + raise DockerError(f"Docker version is {version}. " + f"GNS3 requires a minimum version of {DOCKER_MINIMUM_VERSION}" ) preferred_api_version = parse_version(DOCKER_PREFERRED_API_VERSION) - if docker_version >= preferred_api_version: + if api_version >= preferred_api_version: self._api_version = DOCKER_PREFERRED_API_VERSION + else: + # use the Min API version supported by the daemon + self._api_version = docker_info['MinAPIVersion'] + log.warning("Using Docker client with the minimum API version {}".format(self._api_version)) + + log.info("Connected to Docker daemon version {} using API version {}".format(version, self._api_version)) def connector(self): @@ -188,7 +194,7 @@ class Docker(BaseManager): :param method: HTTP method :param path: Endpoint in API - :param data: Dictionnary with the body. Will be transformed to a JSON + :param data: Dictionary with the body. Will be transformed to a JSON :param params: Parameters added as a query arg :param timeout: Timeout :returns: HTTP response @@ -199,12 +205,11 @@ class Docker(BaseManager): timeout = 60 * 60 * 24 * 31 # One month timeout if path == 'version': - url = "http://docker/v1.24/" + path + url = "http://docker/" + path else: - url = "http://docker/v" + DOCKER_MINIMUM_API_VERSION + "/" + path + await self._check_connection() # version is use by check connection + url = "http://docker/v" + self._api_version + "/" + path try: - if path != "version": # version is use by check connection - await self._check_connection() if self._session is None or self._session.closed: connector = self.connector() self._session = aiohttp.ClientSession(connector=connector) diff --git a/gns3server/compute/docker/docker_vm.py b/gns3server/compute/docker/docker_vm.py index 8c4cfb981..a72c5173c 100644 --- a/gns3server/compute/docker/docker_vm.py +++ b/gns3server/compute/docker/docker_vm.py @@ -195,6 +195,14 @@ class DockerVM(BaseNode): def ethernet_adapters(self): return self._ethernet_adapters + @property + def docker_name(self): + """ + Container name in Docker + """ + + return "GNS3.{}.{}".format(self.name, self._project.id) + @property def mac_address(self): """ @@ -539,7 +547,8 @@ class DockerVM(BaseNode): if extra_hosts: params["Env"].append(f"GNS3_EXTRA_HOSTS={extra_hosts}") - result = await self.manager.query("POST", "containers/create", data=params) + # Support name in Doker: [a-zA-Z0-9][a-zA-Z0-9_.-] + result = await self.manager.query("POST", f"containers/create?name={self.docker_name}", data=params) self._cid = result["Id"] log.info(f"Docker container '{self._name}' [{self._id}] created") if self._cpus > 0: diff --git a/gns3server/configs/iou_l2_base_startup-config.txt b/gns3server/configs/iou_l2_base_startup-config.txt index 0ce9f3653..f5d6344e8 100644 --- a/gns3server/configs/iou_l2_base_startup-config.txt +++ b/gns3server/configs/iou_l2_base_startup-config.txt @@ -13,8 +13,7 @@ logging console discriminator EXCESS ! no ip icmp rate-limit unreachable ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! diff --git a/gns3server/configs/iou_l3_base_startup-config.txt b/gns3server/configs/iou_l3_base_startup-config.txt index 2706875d7..8a25be616 100644 --- a/gns3server/configs/iou_l3_base_startup-config.txt +++ b/gns3server/configs/iou_l3_base_startup-config.txt @@ -12,8 +12,7 @@ no ip icmp rate-limit unreachable ! ! ! -! due to some bugs with IOU, try to change the following line to 'ip cef' if your routing does not work -no ip cef +ip cef no ip domain lookup ! ! diff --git a/gns3server/utils/file_watcher.py b/gns3server/utils/file_watcher.py index c4f037594..94b64626d 100644 --- a/gns3server/utils/file_watcher.py +++ b/gns3server/utils/file_watcher.py @@ -60,7 +60,14 @@ class FileWatcher: self._hashed[path] = zlib.adler32(open(path, "rb").read()) except OSError: self._hashed[path] = None - asyncio.get_event_loop().call_later(self._delay, self._check_config_file_change) + + try: + loop = asyncio.get_running_loop() + except RuntimeError: + loop = asyncio.new_event_loop() + asyncio.set_event_loop(loop) + + loop.call_later(self._delay, self._check_config_file_change) def __del__(self): self._closed = True diff --git a/requirements.txt b/requirements.txt index 37bcc8ea6..f5e25b92e 100644 --- a/requirements.txt +++ b/requirements.txt @@ -7,8 +7,8 @@ aiohttp>=3.11.16,<3.12 async-timeout==5.0.1; python_version < '3.11' aiofiles>=24.1.0,<25.0 Jinja2>=3.1.6,<3.2 -sentry-sdk>=2.26.1,<2.27 # optional dependency -psutil>=7.0.0 +sentry-sdk>=2.44.0,<2.45 # optional dependency +psutil>=7.1.3 async-timeout>=5.0.1,<5.1 distro>=1.9.0 py-cpuinfo>=9.0.0,<10.0 diff --git a/tests/compute/docker/test_docker.py b/tests/compute/docker/test_docker.py index d1f3c6e5e..d2007d974 100644 --- a/tests/compute/docker/test_docker.py +++ b/tests/compute/docker/test_docker.py @@ -49,7 +49,7 @@ async def test_query_success(vm): vm._session.request = AsyncioMagicMock(return_value=response) data = await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -72,7 +72,7 @@ async def test_query_error(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -93,7 +93,7 @@ async def test_query_error_json(vm): with pytest.raises(DockerError): await vm.query("POST", "test", data={"a": True}, params={"b": 1}) vm._session.request.assert_called_with('POST', - 'http://docker/v1.25/test', + 'http://docker/v{}/test'.format(DOCKER_MINIMUM_API_VERSION), data='{"a": true}', headers={'content-type': 'application/json'}, params={'b': 1}, @@ -188,7 +188,9 @@ async def test_docker_check_connection_docker_minimum_version(vm): async def test_docker_check_connection_docker_preferred_version_against_newer(vm): response = { - 'ApiVersion': '1.31' + 'ApiVersion': '1.52', + 'Version': '29.0.1', + } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -202,7 +204,9 @@ async def test_docker_check_connection_docker_preferred_version_against_newer(vm async def test_docker_check_connection_docker_preferred_version_against_older(vm): response = { - 'ApiVersion': '1.27', + 'ApiVersion': '1.43', + 'Version': '24.0.2', + 'MinAPIVersion': '1.40' } with patch("gns3server.compute.docker.Docker.connector"), \ @@ -212,6 +216,20 @@ async def test_docker_check_connection_docker_preferred_version_against_older(vm assert vm._api_version == DOCKER_MINIMUM_API_VERSION +async def test_docker_check_connection_docker_unsupported_version(vm): + + response = { + 'ApiVersion': '1.25', + 'Version': '1.13.1', + } + + with patch("gns3server.compute.docker.Docker.connector"), \ + asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response): + vm._connected = False + with pytest.raises(DockerError) as e: + await vm._check_connection() + + @pytest.mark.asyncio async def test_install_busybox(): diff --git a/tests/compute/docker/test_docker_vm.py b/tests/compute/docker/test_docker_vm.py index 7794371d9..66a4e4947 100644 --- a/tests/compute/docker/test_docker_vm.py +++ b/tests/compute/docker/test_docker_vm.py @@ -99,7 +99,7 @@ async def test_create(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -150,7 +150,7 @@ async def test_create_with_tag(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:16.04") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -204,7 +204,7 @@ async def test_create_vnc(compute_project, manager): vm._start_vnc = MagicMock() vm._display = 42 await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -356,7 +356,7 @@ async def test_create_start_cmd(compute_project, manager): vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") vm._start_command = "/bin/ls" await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -469,7 +469,7 @@ async def test_create_image_not_available(compute_project, manager): with asyncio_patch("gns3server.compute.docker.DockerVM.pull_image", return_value=True) as mock_pull: with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -524,7 +524,7 @@ async def test_create_with_user(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest") await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -624,7 +624,7 @@ async def test_create_with_extra_volumes_duplicate_1_image(compute_project, mana with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -680,7 +680,7 @@ async def test_create_with_extra_volumes_duplicate_2_user(compute_project, manag with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1", "/vol/1"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -736,7 +736,7 @@ async def test_create_with_extra_volumes_duplicate_3_subdir(compute_project, man with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/1/", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -792,7 +792,7 @@ async def test_create_with_extra_volumes_duplicate_4_backslash(compute_project, with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol//", "/vol"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -848,7 +848,7 @@ async def test_create_with_extra_volumes_duplicate_5_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -899,7 +899,7 @@ async def test_create_with_extra_volumes_duplicate_6_subdir_issue_1595(compute_p with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/etc/test", "/etc"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -956,7 +956,7 @@ async def test_create_with_extra_volumes(compute_project, manager): with asyncio_patch("gns3server.compute.docker.Docker.query", return_value=response) as mock: vm = DockerVM("test", str(uuid.uuid4()), compute_project, manager, "ubuntu:latest", extra_volumes=["/vol/2"]) await vm.create() - mock.assert_called_with("POST", "containers/create", data={ + mock.assert_called_with("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1243,7 +1243,7 @@ async def test_update(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False, @@ -1325,7 +1325,7 @@ async def test_update_running(vm): await vm.update() mock_query.assert_any_call("DELETE", "containers/e90e34656842", params={"force": 1, "v": 1}) - mock_query.assert_any_call("POST", "containers/create", data={ + mock_query.assert_any_call("POST", "containers/create?name={}".format(vm.docker_name), data={ "Tty": True, "OpenStdin": True, "StdinOnce": False,