mirror of
https://github.com/GNS3/gns3-server.git
synced 2026-11-03 14:01:15 +02:00
refactor: ruff autofixes
This commit is contained in:
455 files changed
+12322
-14762
No files matched your search
@@ -34,7 +34,12 @@ from gns3server.compute.base_manager import BaseManager
|
||||
from gns3server.compute.docker.docker_vm import DockerVM
|
||||
from gns3server.compute.docker.vendor_docker_vm import VendorDockerVM
|
||||
from gns3server.compute.docker.iol_docker_vm import IOLDockerVM
|
||||
from gns3server.compute.docker.docker_error import DockerError, DockerHttp304Error, DockerHttp404Error, DockerHttp409Error
|
||||
from gns3server.compute.docker.docker_error import (
|
||||
DockerError,
|
||||
DockerHttp304Error,
|
||||
DockerHttp404Error,
|
||||
DockerHttp409Error,
|
||||
)
|
||||
|
||||
log = logging.getLogger(__name__)
|
||||
|
||||
@@ -47,7 +52,6 @@ CHUNK_SIZE = 1024 * 8 # 8KB
|
||||
|
||||
|
||||
class Docker(BaseManager):
|
||||
|
||||
_NODE_CLASS = DockerVM
|
||||
|
||||
def __init__(self):
|
||||
@@ -97,10 +101,7 @@ class Docker(BaseManager):
|
||||
# check that busybox is statically linked
|
||||
# (dynamically linked busybox will fail to run in a container)
|
||||
proc = await asyncio.create_subprocess_exec(
|
||||
"ldd",
|
||||
busybox_path,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.DEVNULL
|
||||
"ldd", busybox_path, stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.DEVNULL
|
||||
)
|
||||
stdout, _ = await proc.communicate()
|
||||
if proc.returncode == 1 or "static" in busybox_exec:
|
||||
@@ -111,11 +112,15 @@ class Docker(BaseManager):
|
||||
shutil.copy2(busybox_path, dst_busybox, follow_symlinks=True)
|
||||
return
|
||||
else:
|
||||
log.warning(f"Busybox '{busybox_path}' is dynamically linked\n"
|
||||
f"{stdout.decode('utf-8', errors='ignore').strip()}")
|
||||
log.warning(
|
||||
f"Busybox '{busybox_path}' is dynamically linked\n"
|
||||
f"{stdout.decode('utf-8', errors='ignore').strip()}"
|
||||
)
|
||||
except OSError as e:
|
||||
raise DockerError(f"Could not install busybox: {e}")
|
||||
raise DockerError("No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH")
|
||||
raise DockerError(
|
||||
"No busybox executable could be found, please install busybox (apt install busybox-static on Debian/Ubuntu) and make sure it is in your PATH"
|
||||
)
|
||||
|
||||
@staticmethod
|
||||
def resources_path():
|
||||
@@ -142,6 +147,7 @@ class Docker(BaseManager):
|
||||
dst_path = self.resources_path()
|
||||
log.info(f"Installing Docker resources in '{dst_path}'")
|
||||
from gns3server.controller import Controller
|
||||
|
||||
await Controller.instance().install_resource_files(dst_path, "compute/docker/resources")
|
||||
await self.install_busybox(dst_path)
|
||||
except OSError as e:
|
||||
@@ -157,12 +163,12 @@ class Docker(BaseManager):
|
||||
self._connected = False
|
||||
raise DockerError("Can't connect to Docker daemon")
|
||||
|
||||
api_version = parse_version(docker_info['ApiVersion'])
|
||||
api_version = parse_version(docker_info["ApiVersion"])
|
||||
version = docker_info["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}"
|
||||
raise DockerError(
|
||||
f"Docker version is {version}. GNS3 requires a minimum version of {DOCKER_MINIMUM_VERSION}"
|
||||
)
|
||||
|
||||
preferred_api_version = parse_version(DOCKER_PREFERRED_API_VERSION)
|
||||
@@ -170,7 +176,7 @@ class Docker(BaseManager):
|
||||
self._api_version = DOCKER_PREFERRED_API_VERSION
|
||||
else:
|
||||
# use the Min API version supported by the daemon
|
||||
self._api_version = docker_info['MinAPIVersion']
|
||||
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))
|
||||
@@ -264,7 +270,7 @@ class Docker(BaseManager):
|
||||
response = await self.http_query(method, path, data=data, params=params)
|
||||
body = await response.read()
|
||||
response.close()
|
||||
if response.headers.get('CONTENT-TYPE') == 'application/json':
|
||||
if response.headers.get("CONTENT-TYPE") == "application/json":
|
||||
body = json.loads(body.decode("utf-8", errors="ignore"))
|
||||
else:
|
||||
body = body.decode("utf-8", errors="ignore")
|
||||
@@ -292,7 +298,7 @@ class Docker(BaseManager):
|
||||
if timeout is None:
|
||||
timeout = 60 * 60 * 24 * 31 # One month timeout
|
||||
|
||||
if path == 'version':
|
||||
if path == "version":
|
||||
url = "http://docker/" + path
|
||||
else:
|
||||
await self._check_connection() # version is use by check connection
|
||||
@@ -380,9 +386,7 @@ class Docker(BaseManager):
|
||||
f"Disconnected while pulling Docker image '{image}' from Docker repository"
|
||||
) from e
|
||||
except asyncio.TimeoutError as e:
|
||||
raise DockerError(
|
||||
f"Timeout while pulling Docker image '{image}' from Docker repository"
|
||||
) from e
|
||||
raise DockerError(f"Timeout while pulling Docker image '{image}' from Docker repository") from e
|
||||
if not chunk:
|
||||
break
|
||||
content += chunk.decode("utf-8")
|
||||
|
||||
@@ -236,7 +236,9 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
if not is_rfc1123_hostname_valid(new_name):
|
||||
raise DockerError(f"'{new_name}' is an invalid name to rename Docker container '{self._name}'. Allowed characters: letters (a-z, A-Z), digits (0-9), and hyphens (-). The name cannot start or end with a hyphen.")
|
||||
raise DockerError(
|
||||
f"'{new_name}' is an invalid name to rename Docker container '{self._name}'. Allowed characters: letters (a-z, A-Z), digits (0-9), and hyphens (-). The name cannot start or end with a hyphen."
|
||||
)
|
||||
super(DockerVM, DockerVM).name.__set__(self, new_name)
|
||||
|
||||
@property
|
||||
@@ -275,10 +277,10 @@ class DockerVM(BaseNode):
|
||||
else:
|
||||
self._mac_address = mac_address
|
||||
|
||||
log.debug('Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format(
|
||||
name=self._name,
|
||||
id=self._id,
|
||||
mac_addr=self._mac_address)
|
||||
log.debug(
|
||||
'Docker container "{name}" [{id}]: MAC address changed to {mac_addr}'.format(
|
||||
name=self._name, id=self._id, mac_addr=self._mac_address
|
||||
)
|
||||
)
|
||||
|
||||
@property
|
||||
@@ -453,12 +455,7 @@ class DockerVM(BaseNode):
|
||||
raise DockerError(f"Cannot access resources: {e}")
|
||||
|
||||
log.debug(f'Mount resources from "{resources_path}"')
|
||||
binds = [{
|
||||
"Type": "bind",
|
||||
"Source": resources_path,
|
||||
"Target": "/gns3",
|
||||
"ReadOnly": True
|
||||
}]
|
||||
binds = [{"Type": "bind", "Source": resources_path, "Target": "/gns3", "ReadOnly": True}]
|
||||
self._image_id = image_info.get("Id") or self._image_id
|
||||
|
||||
# We mount our own etc/network
|
||||
@@ -471,11 +468,7 @@ class DockerVM(BaseNode):
|
||||
for volume in self._volumes:
|
||||
source = os.path.join(self.working_dir, os.path.relpath(volume, "/"))
|
||||
os.makedirs(source, exist_ok=True)
|
||||
binds.append({
|
||||
"Type": "bind",
|
||||
"Source": source,
|
||||
"Target": "/gns3volumes{}".format(volume)
|
||||
})
|
||||
binds.append({"Type": "bind", "Source": source, "Target": "/gns3volumes{}".format(volume)})
|
||||
|
||||
# Inject extra config files: write each to the node working directory and
|
||||
# bind-mount it read-only at its target path. Single-file binds are applied
|
||||
@@ -485,9 +478,7 @@ class DockerVM(BaseNode):
|
||||
target = cfg["target"] if isinstance(cfg, dict) else cfg.target
|
||||
content = cfg["content"] if isinstance(cfg, dict) else cfg.content
|
||||
if not target.startswith("/") or target.endswith("/") or ".." in target.split("/"):
|
||||
raise DockerError(
|
||||
f"Extra config target '{target}' must be an absolute file path and not contain '..'."
|
||||
)
|
||||
raise DockerError(f"Extra config target '{target}' must be an absolute file path and not contain '..'.")
|
||||
for volume in self._volumes:
|
||||
# A single-file bind gets covered by the volume's bind mount at
|
||||
# start (init.sh or the vendor volume bridge), so the injected
|
||||
@@ -497,18 +488,22 @@ class DockerVM(BaseNode):
|
||||
log.warning(
|
||||
"Extra config target '%s' on container '%s' is shadowed by persisted volume '%s' "
|
||||
"and will not take effect; pick a target outside persisted volumes.",
|
||||
target, self._name, volume,
|
||||
target,
|
||||
self._name,
|
||||
volume,
|
||||
)
|
||||
host_path = os.path.join(self.working_dir, "configs", target.lstrip("/"))
|
||||
os.makedirs(os.path.dirname(host_path), exist_ok=True)
|
||||
with open(host_path, "w") as f:
|
||||
f.write(content)
|
||||
binds.append({
|
||||
"Type": "bind",
|
||||
"Source": host_path,
|
||||
"Target": target,
|
||||
"ReadOnly": True,
|
||||
})
|
||||
binds.append(
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": host_path,
|
||||
"Target": target,
|
||||
"ReadOnly": True,
|
||||
}
|
||||
)
|
||||
|
||||
return binds
|
||||
|
||||
@@ -554,7 +549,8 @@ class DockerVM(BaseNode):
|
||||
#auto eth{adapter}
|
||||
#iface eth{adapter} inet dhcp
|
||||
#\thostname {hostname}
|
||||
""".format(adapter=adapter, hostname=self._name))
|
||||
""".format(adapter=adapter, hostname=self._name)
|
||||
)
|
||||
return path
|
||||
|
||||
def _prepare_init_and_interface_env(self, params):
|
||||
@@ -573,9 +569,11 @@ class DockerVM(BaseNode):
|
||||
"""
|
||||
|
||||
if ":" in os.path.splitdrive(self.working_dir)[1]:
|
||||
raise DockerError("Cannot create a Docker container with a project directory containing a colon character (':')")
|
||||
raise DockerError(
|
||||
"Cannot create a Docker container with a project directory containing a colon character (':')"
|
||||
)
|
||||
|
||||
#await self.manager.install_resources()
|
||||
# await self.manager.install_resources()
|
||||
|
||||
try:
|
||||
image_infos = await self._get_image_information()
|
||||
@@ -601,8 +599,7 @@ class DockerVM(BaseNode):
|
||||
available_cpus = psutil.cpu_count(logical=True)
|
||||
if self._cpus > available_cpus:
|
||||
raise DockerError(
|
||||
f"You have allocated too many CPUs for the Docker container "
|
||||
f"(max available is {available_cpus} CPUs)"
|
||||
f"You have allocated too many CPUs for the Docker container (max available is {available_cpus} CPUs)"
|
||||
)
|
||||
|
||||
# Prepare persistent volume content before the container and its
|
||||
@@ -622,7 +619,7 @@ class DockerVM(BaseNode):
|
||||
"Mounts": self._mount_binds(image_infos),
|
||||
"Memory": self._memory * (1024 * 1024), # convert memory to bytes
|
||||
"NanoCpus": int(self._cpus * 1e9), # convert cpus to nano cpus
|
||||
"UsernsMode": "host"
|
||||
"UsernsMode": "host",
|
||||
},
|
||||
"Volumes": {},
|
||||
"Env": ["container=docker"], # Systemd compliant: https://github.com/GNS3/gns3-server/issues/573
|
||||
@@ -651,8 +648,11 @@ class DockerVM(BaseNode):
|
||||
devices = self._format_devices(line.split("=", 1)[1])
|
||||
if devices:
|
||||
params["HostConfig"]["Devices"] = devices
|
||||
elif line.startswith("GNS3_MASK_UDEV=") and \
|
||||
line.split("=", 1)[1].strip().lower() in ("1", "true", "yes"):
|
||||
elif line.startswith("GNS3_MASK_UDEV=") and line.split("=", 1)[1].strip().lower() in (
|
||||
"1",
|
||||
"true",
|
||||
"yes",
|
||||
):
|
||||
# A privileged systemd-based NOS container (e.g. Cisco XRd)
|
||||
# runs systemd-udevd, which coldplugs every device it can see
|
||||
# -- and in privileged mode that includes the HOST's USB/input/
|
||||
@@ -660,24 +660,28 @@ class DockerVM(BaseNode):
|
||||
# XRd doesn't need udev (interfaces are pre-created by GNS3), so
|
||||
# bind /dev/null over the udev units to keep it from running.
|
||||
for target in [f"/etc/systemd/system/{u}" for u in self._UDEV_UNITS] + list(self._UDEVADM_PATHS):
|
||||
params["HostConfig"]["Mounts"].append({
|
||||
"Type": "bind",
|
||||
"Source": "/dev/null",
|
||||
"Target": target,
|
||||
"ReadOnly": True,
|
||||
})
|
||||
params["HostConfig"]["Mounts"].append(
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/dev/null",
|
||||
"Target": target,
|
||||
"ReadOnly": True,
|
||||
}
|
||||
)
|
||||
elif line.startswith("GNS3_MASK_SYSTEMD="):
|
||||
# Generic form: comma/semicolon-separated unit names to mask
|
||||
# the same way (bind /dev/null over /etc/systemd/system/<unit>).
|
||||
for unit in line.split("=", 1)[1].replace(";", ",").split(","):
|
||||
unit = unit.strip()
|
||||
if unit and "/" not in unit and ".." not in unit:
|
||||
params["HostConfig"]["Mounts"].append({
|
||||
"Type": "bind",
|
||||
"Source": "/dev/null",
|
||||
"Target": f"/etc/systemd/system/{unit}",
|
||||
"ReadOnly": True,
|
||||
})
|
||||
params["HostConfig"]["Mounts"].append(
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": "/dev/null",
|
||||
"Target": f"/etc/systemd/system/{unit}",
|
||||
"ReadOnly": True,
|
||||
}
|
||||
)
|
||||
|
||||
# Overlapping bind targets (GNS3_MASK_UDEV together with a
|
||||
# GNS3_MASK_SYSTEMD entry for the same unit, an extra_configs target
|
||||
@@ -754,12 +758,14 @@ class DockerVM(BaseNode):
|
||||
"QT_GRAPHICSSYSTEM=native"
|
||||
) # To fix a Qt issue: https://github.com/GNS3/gns3-server/issues/556
|
||||
params["Env"].append(f"DISPLAY=:{self._display}")
|
||||
params["HostConfig"]["Mounts"].append({
|
||||
"Type": "bind",
|
||||
"Source": f"/tmp/.X11-unix/X{self._display}",
|
||||
"Target": f"/tmp/.X11-unix/X{self._display}",
|
||||
"ReadOnly": True
|
||||
})
|
||||
params["HostConfig"]["Mounts"].append(
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": f"/tmp/.X11-unix/X{self._display}",
|
||||
"Target": f"/tmp/.X11-unix/X{self._display}",
|
||||
"ReadOnly": True,
|
||||
}
|
||||
)
|
||||
|
||||
if self._extra_hosts:
|
||||
extra_hosts = self._format_extra_hosts(self._extra_hosts)
|
||||
@@ -772,7 +778,9 @@ class DockerVM(BaseNode):
|
||||
except DockerHttp409Error:
|
||||
# Container name already exists. This can happen when the server crashes
|
||||
# and leaves containers behind. Try to remove the conflicting container.
|
||||
log.warning(f"Container name '{self.docker_name}' is already in use, attempting to clean up the stale container...")
|
||||
log.warning(
|
||||
f"Container name '{self.docker_name}' is already in use, attempting to clean up the stale container..."
|
||||
)
|
||||
try:
|
||||
# Try to get and remove the conflicting container
|
||||
try:
|
||||
@@ -850,11 +858,13 @@ class DockerVM(BaseNode):
|
||||
on_host, in_container, permissions = parts
|
||||
else:
|
||||
continue
|
||||
formatted.append({
|
||||
"PathOnHost": on_host,
|
||||
"PathInContainer": in_container,
|
||||
"CgroupPermissions": permissions,
|
||||
})
|
||||
formatted.append(
|
||||
{
|
||||
"PathOnHost": on_host,
|
||||
"PathInContainer": in_container,
|
||||
"CgroupPermissions": permissions,
|
||||
}
|
||||
)
|
||||
return formatted
|
||||
|
||||
async def update(self):
|
||||
@@ -897,7 +907,6 @@ class DockerVM(BaseNode):
|
||||
await self._start_interface_monitor()
|
||||
return
|
||||
else:
|
||||
|
||||
if self._console_type == "vnc" and not self._vnc_process:
|
||||
# restart the vnc process in case it had previously crashed
|
||||
await self._start_vnc_process(restart=True)
|
||||
@@ -1042,7 +1051,9 @@ class DockerVM(BaseNode):
|
||||
stderr = (await process.stderr.read()).decode(errors="replace").strip()
|
||||
log.error(
|
||||
"Failed to fix permissions on '%s' for container '%s': %s",
|
||||
volume, self._name, stderr or f"exit code {process.returncode}"
|
||||
volume,
|
||||
self._name,
|
||||
stderr or f"exit code {process.returncode}",
|
||||
)
|
||||
else:
|
||||
self._permissions_fixed = True
|
||||
@@ -1102,7 +1113,9 @@ class DockerVM(BaseNode):
|
||||
log.warning(f"Docker container '{self._name}': cannot access resources to reclaim '{directory}': {e}")
|
||||
return False
|
||||
|
||||
log.info(f"Docker container '{self._name}': reclaiming root-owned files under '{directory}' via a one-shot container")
|
||||
log.info(
|
||||
f"Docker container '{self._name}': reclaiming root-owned files under '{directory}' via a one-shot container"
|
||||
)
|
||||
# Prefer the image's own chown over the static busybox one: busybox's
|
||||
# chown dlopens NSS modules from the image, which mismatch the static
|
||||
# glibc and abort on NOS images whose glibc differs (same reasoning
|
||||
@@ -1116,12 +1129,24 @@ class DockerVM(BaseNode):
|
||||
image_ref = self._image_id or self._image
|
||||
try:
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "run", "--rm", "--network", "none", "--pull", "never", "--user", "0:0",
|
||||
"--entrypoint", "/gns3/bin/busybox",
|
||||
"-v", f"{resources_path}:/gns3:ro",
|
||||
"-v", f"{directory}:/target",
|
||||
"docker",
|
||||
"run",
|
||||
"--rm",
|
||||
"--network",
|
||||
"none",
|
||||
"--pull",
|
||||
"never",
|
||||
"--user",
|
||||
"0:0",
|
||||
"--entrypoint",
|
||||
"/gns3/bin/busybox",
|
||||
"-v",
|
||||
f"{resources_path}:/gns3:ro",
|
||||
"-v",
|
||||
f"{directory}:/target",
|
||||
image_ref,
|
||||
"sh", "-c",
|
||||
"sh",
|
||||
"-c",
|
||||
"/gns3/bin/busybox chmod -R u+rwX /target"
|
||||
f" && ( command -v chown >/dev/null 2>&1 && chown {uid}:{gid} -R /target"
|
||||
f" || /gns3/bin/busybox chown {uid}:{gid} -R /target )",
|
||||
@@ -1159,17 +1184,27 @@ class DockerVM(BaseNode):
|
||||
|
||||
if tigervnc_path:
|
||||
with open(os.path.join(self.working_dir, "vnc.log"), "w") as fd:
|
||||
self._vnc_process = await asyncio.create_subprocess_exec(tigervnc_path,
|
||||
"-extension", "MIT-SHM",
|
||||
"-geometry", self._console_resolution,
|
||||
"-depth", "16",
|
||||
"-interface", self._manager.port_manager.console_host,
|
||||
"-rfbport", str(self.console),
|
||||
"-AlwaysShared",
|
||||
"-SecurityTypes", "None",
|
||||
"-desktop", self.name,
|
||||
":{}".format(self._display),
|
||||
stdout=fd, stderr=subprocess.STDOUT)
|
||||
self._vnc_process = await asyncio.create_subprocess_exec(
|
||||
tigervnc_path,
|
||||
"-extension",
|
||||
"MIT-SHM",
|
||||
"-geometry",
|
||||
self._console_resolution,
|
||||
"-depth",
|
||||
"16",
|
||||
"-interface",
|
||||
self._manager.port_manager.console_host,
|
||||
"-rfbport",
|
||||
str(self.console),
|
||||
"-AlwaysShared",
|
||||
"-SecurityTypes",
|
||||
"None",
|
||||
"-desktop",
|
||||
self.name,
|
||||
":{}".format(self._display),
|
||||
stdout=fd,
|
||||
stderr=subprocess.STDOUT,
|
||||
)
|
||||
|
||||
async def _start_vnc(self):
|
||||
"""
|
||||
@@ -1576,8 +1611,8 @@ class DockerVM(BaseNode):
|
||||
script = (
|
||||
"while :; do "
|
||||
"for ifname do "
|
||||
"flags=$(/gns3/bin/busybox cat \"/sys/class/net/$ifname/flags\" 2>/dev/null) || continue; "
|
||||
"/gns3/bin/busybox printf '%s=%s\\n' \"$ifname\" \"$flags\"; "
|
||||
'flags=$(/gns3/bin/busybox cat "/sys/class/net/$ifname/flags" 2>/dev/null) || continue; '
|
||||
'/gns3/bin/busybox printf \'%s=%s\\n\' "$ifname" "$flags"; '
|
||||
"done; "
|
||||
"/gns3/bin/busybox sleep 1; "
|
||||
"done"
|
||||
@@ -1621,7 +1656,7 @@ class DockerVM(BaseNode):
|
||||
asyncio.IncompleteReadError,
|
||||
asyncio.TimeoutError,
|
||||
) as e:
|
||||
if 'writer' in locals():
|
||||
if "writer" in locals():
|
||||
await self._close_interface_monitor_writer(writer)
|
||||
log.warning("Could not monitor interfaces for Docker container '%s': %s", self.name, e)
|
||||
return
|
||||
@@ -1658,8 +1693,7 @@ class DockerVM(BaseNode):
|
||||
"""Read ``ifname=flags`` records and emit changed adapter states."""
|
||||
|
||||
interfaces = {
|
||||
self._get_container_ifname(adapter_number): adapter_number
|
||||
for adapter_number in range(self.adapters)
|
||||
self._get_container_ifname(adapter_number): adapter_number for adapter_number in range(self.adapters)
|
||||
}
|
||||
try:
|
||||
while True:
|
||||
@@ -1747,9 +1781,7 @@ class DockerVM(BaseNode):
|
||||
await self._ubridge_send(f"bridge create {bridge_name}")
|
||||
self._bridges.add(bridge_name)
|
||||
await self._ubridge_send(
|
||||
"bridge add_nio_tap {bridge_name} {hostif} off".format(
|
||||
bridge_name=bridge_name, hostif=adapter.host_ifc
|
||||
)
|
||||
"bridge add_nio_tap {bridge_name} {hostif} off".format(bridge_name=bridge_name, hostif=adapter.host_ifc)
|
||||
)
|
||||
|
||||
mac_address = int_to_macaddress(macaddress_to_int(self._mac_address) + adapter_number)
|
||||
@@ -1759,17 +1791,14 @@ class DockerVM(BaseNode):
|
||||
mac_address = custom_mac_address
|
||||
|
||||
try:
|
||||
await self._ubridge_send('docker set_mac_addr {ifc} {mac}'.format(ifc=adapter.host_ifc, mac=mac_address))
|
||||
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}")
|
||||
|
||||
|
||||
ifname = self._get_container_ifname(adapter_number)
|
||||
log.debug(f"Move container {self.name} adapter {adapter.host_ifc} -> {ifname} in ns {self._namespace}")
|
||||
try:
|
||||
await self._ubridge_send(
|
||||
f"docker move_to_ns {adapter.host_ifc} {self._namespace} {ifname}"
|
||||
)
|
||||
await self._ubridge_send(f"docker move_to_ns {adapter.host_ifc} {self._namespace} {ifname}")
|
||||
except UbridgeError as e:
|
||||
raise UbridgeNamespaceError(e)
|
||||
else:
|
||||
@@ -2061,6 +2090,6 @@ class DockerVM(BaseNode):
|
||||
raise ComputeError(
|
||||
f"Could not delete the node directory '{self.working_dir}': files left owned by "
|
||||
f"another user could not be reclaimed ({e}). Reclaim them manually with: "
|
||||
f"docker run --rm --user 0:0 -v \"{self.working_dir}\":/target --entrypoint /bin/sh "
|
||||
f'docker run --rm --user 0:0 -v "{self.working_dir}":/target --entrypoint /bin/sh '
|
||||
f"{self._image} -c 'chown -R {os.getuid()}:{os.getgid()} /target'"
|
||||
)
|
||||
@@ -247,7 +247,8 @@ class IOLDockerVM(VendorDockerVM):
|
||||
|
||||
log.debug(
|
||||
"IOL container '%s': number of 4-port Ethernet adapters set to %d",
|
||||
self._name, adapters,
|
||||
self._name,
|
||||
adapters,
|
||||
)
|
||||
|
||||
def _persistent_volume_list(self, image_info, include_network_config=True):
|
||||
|
||||
@@ -113,9 +113,7 @@ class VendorDockerVM(DockerVM):
|
||||
if _line.startswith("GNS3_SKIP_INIT="):
|
||||
self._gns3_init = _line.split("=", 1)[1].strip().lower() not in ("1", "true", "yes")
|
||||
elif _line.startswith("GNS3_INTERFACE_NAMES="):
|
||||
self._interface_names = [
|
||||
n.strip() for n in _line.split("=", 1)[1].split(",") if n.strip()
|
||||
]
|
||||
self._interface_names = [n.strip() for n in _line.split("=", 1)[1].split(",") if n.strip()]
|
||||
elif _line.startswith("GNS3_CONSOLE_CMD="):
|
||||
self._console_cmd = _line.split("=", 1)[1].strip()
|
||||
elif _line.startswith("GNS3_CONSOLE_RESIZE="):
|
||||
@@ -172,19 +170,20 @@ class VendorDockerVM(DockerVM):
|
||||
if self._unix_socket_nio:
|
||||
socket_dir = self._unix_socket_dir.rstrip("/")
|
||||
if not any(
|
||||
v.rstrip("/") == socket_dir or socket_dir.startswith(v.rstrip("/") + "/")
|
||||
for v in self._volumes
|
||||
v.rstrip("/") == socket_dir or socket_dir.startswith(v.rstrip("/") + "/") for v in self._volumes
|
||||
):
|
||||
# The image's network agent drops privileges before using the
|
||||
# socket directory, so it must be writable by the server user:
|
||||
# bind a per-node directory from the runtime directory (see
|
||||
# _unix_socket_host_dir).
|
||||
binds.append({
|
||||
"Type": "bind",
|
||||
"Source": self._unix_socket_host_dir(),
|
||||
"Target": socket_dir,
|
||||
"BindOptions": {"Propagation": "rprivate"},
|
||||
})
|
||||
binds.append(
|
||||
{
|
||||
"Type": "bind",
|
||||
"Source": self._unix_socket_host_dir(),
|
||||
"Target": socket_dir,
|
||||
"BindOptions": {"Propagation": "rprivate"},
|
||||
}
|
||||
)
|
||||
if self._gns3_init:
|
||||
return binds
|
||||
binds = [b for b in binds if b.get("Target") != "/gns3volumes/etc/network"]
|
||||
@@ -198,7 +197,7 @@ class VendorDockerVM(DockerVM):
|
||||
for bind in binds:
|
||||
target = bind.get("Target", "")
|
||||
if target.startswith("/gns3volumes"):
|
||||
volume = target[len("/gns3volumes"):]
|
||||
volume = target[len("/gns3volumes") :]
|
||||
if volume in self._volumes:
|
||||
bind = {**bind, "Target": volume}
|
||||
retargeted.append(bind)
|
||||
@@ -249,8 +248,11 @@ class VendorDockerVM(DockerVM):
|
||||
|
||||
try:
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "create", self._image,
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
||||
"docker",
|
||||
"create",
|
||||
self._image,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
except OSError as e:
|
||||
raise DockerError(f"Could not seed persistent volumes for '{self._name}': {e}")
|
||||
@@ -271,8 +273,13 @@ class VendorDockerVM(DockerVM):
|
||||
|
||||
try:
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "cp", "-a", f"{seed_cid}:{volume}/.", host_dir + "/",
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
||||
"docker",
|
||||
"cp",
|
||||
"-a",
|
||||
f"{seed_cid}:{volume}/.",
|
||||
host_dir + "/",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
except OSError as e:
|
||||
raise DockerError(f"Could not seed persistent volume '{volume}' for '{self._name}': {e}")
|
||||
@@ -283,7 +290,10 @@ class VendorDockerVM(DockerVM):
|
||||
# init.sh's first copy (cp -a ... 2>/dev/null).
|
||||
log.info(
|
||||
"Persistent volume '%s' on '%s' not seedable from image '%s' (%s); starting empty",
|
||||
volume, self._name, self._image, stderr.decode(errors="replace").strip(),
|
||||
volume,
|
||||
self._name,
|
||||
self._image,
|
||||
stderr.decode(errors="replace").strip(),
|
||||
)
|
||||
return
|
||||
log.info("Seeded persistent volume '%s' for '%s' from image '%s'", volume, self._name, self._image)
|
||||
@@ -295,8 +305,12 @@ class VendorDockerVM(DockerVM):
|
||||
|
||||
try:
|
||||
process = await asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "rm", "-f", seed_cid,
|
||||
stdout=asyncio.subprocess.PIPE, stderr=asyncio.subprocess.PIPE,
|
||||
"docker",
|
||||
"rm",
|
||||
"-f",
|
||||
seed_cid,
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.PIPE,
|
||||
)
|
||||
except OSError:
|
||||
return
|
||||
@@ -348,9 +362,7 @@ class VendorDockerVM(DockerVM):
|
||||
try:
|
||||
os.makedirs(host_dir, mode=0o700, exist_ok=True)
|
||||
except OSError as e:
|
||||
raise DockerError(
|
||||
f"Could not create unix-socket directory '{host_dir}' for container '{self._name}': {e}"
|
||||
)
|
||||
raise DockerError(f"Could not create unix-socket directory '{host_dir}' for container '{self._name}': {e}")
|
||||
return host_dir
|
||||
|
||||
def _remove_unix_socket_host_dir(self):
|
||||
@@ -448,7 +460,11 @@ class VendorDockerVM(DockerVM):
|
||||
adapter.host_ifc = local_sock # bookkeeping / removal logging only
|
||||
log.debug(
|
||||
"Adapter %d port %d of container '%s' wired via unix sockets %s <-> %s",
|
||||
adapter_number, port_number, self._name, local_sock, remote_sock,
|
||||
adapter_number,
|
||||
port_number,
|
||||
self._name,
|
||||
local_sock,
|
||||
remote_sock,
|
||||
)
|
||||
|
||||
if nio:
|
||||
@@ -564,7 +580,8 @@ class VendorDockerVM(DockerVM):
|
||||
if state == "stopped" or state == "exited":
|
||||
log.info(
|
||||
"Container '%s' is %s, skipping permission fix (next start will fix)",
|
||||
self._name, state,
|
||||
self._name,
|
||||
state,
|
||||
)
|
||||
return
|
||||
|
||||
@@ -601,7 +618,9 @@ class VendorDockerVM(DockerVM):
|
||||
stderr = (await process.stderr.read()).decode(errors="replace").strip()
|
||||
log.error(
|
||||
"Failed to fix permissions on '%s' for container '%s': %s",
|
||||
volume, self._name, stderr or f"exit code {process.returncode}",
|
||||
volume,
|
||||
self._name,
|
||||
stderr or f"exit code {process.returncode}",
|
||||
)
|
||||
else:
|
||||
self._permissions_fixed = True
|
||||
@@ -640,9 +659,7 @@ class VendorDockerVM(DockerVM):
|
||||
allow_resize=self._console_resize,
|
||||
)
|
||||
try:
|
||||
self._telnet_servers.append(
|
||||
await telnet.start(self._manager.port_manager.console_host, self.console)
|
||||
)
|
||||
self._telnet_servers.append(await telnet.start(self._manager.port_manager.console_host, self.console))
|
||||
except OSError as e:
|
||||
raise DockerError(
|
||||
f"Could not start console server on socket {self._manager.port_manager.console_host}:{self.console}: {e}"
|
||||
|
||||
Reference in new issue
Block a user