mirror of
https://github.com/GNS3/gns3-server
synced 2024-11-13 20:08:55 +00:00
Merge pull request #461 from GNS3/ehlers-docker-init
Support /etc/network/interfaces
This commit is contained in:
commit
2a20333877
@ -159,6 +159,10 @@ class DockerVM(BaseVM):
|
||||
|
||||
binds.append("{}:/gns3:ro".format(get_resource("modules/docker/resources")))
|
||||
|
||||
# We mount our own etc/network
|
||||
network_config = self._create_network_config()
|
||||
binds.append("{}:/etc/network:rw".format(network_config))
|
||||
|
||||
volumes = image_infos.get("ContainerConfig", {}).get("Volumes")
|
||||
if volumes is None:
|
||||
return binds
|
||||
@ -169,6 +173,37 @@ class DockerVM(BaseVM):
|
||||
|
||||
return binds
|
||||
|
||||
def _create_network_config(self):
|
||||
"""
|
||||
If network config is empty we create a sample config
|
||||
"""
|
||||
path = os.path.join(self.working_dir, "etc", "network")
|
||||
os.makedirs(path, exist_ok=True)
|
||||
os.makedirs(os.path.join(path, "if-up.d"), exist_ok=True)
|
||||
os.makedirs(os.path.join(path, "if-down.d"), exist_ok=True)
|
||||
|
||||
if not os.path.exists(os.path.join(path, "interfaces")):
|
||||
with open(os.path.join(path, "interfaces"), "w+") as f:
|
||||
f.write("""#
|
||||
# This is a sample network config uncomment lines to configure the network
|
||||
#
|
||||
|
||||
""")
|
||||
for adapter in range(0, self.adapters):
|
||||
f.write("""
|
||||
# Static config for eth{adapter}
|
||||
#auto eth{adapter}
|
||||
#iface eth{adapter} inet static
|
||||
#\taddress 192.168.{adapter}.2
|
||||
#\tnetmask 255.255.255.0
|
||||
#\tgateway 192.168.{adapter}.1
|
||||
#\tup echo nameserver 192.168.{adapter}.1 > /etc/resolv.conf
|
||||
|
||||
# DHCP config for eth{adapter}
|
||||
# auto eth{adapter}
|
||||
# iface eth{adapter} inet dhcp""".format(adapter=adapter))
|
||||
return path
|
||||
|
||||
@asyncio.coroutine
|
||||
def create(self):
|
||||
"""Creates the Docker container."""
|
||||
@ -199,7 +234,6 @@ class DockerVM(BaseVM):
|
||||
"Entrypoint": image_infos.get("Config", {"Entrypoint": []})["Entrypoint"]
|
||||
}
|
||||
|
||||
|
||||
if params["Entrypoint"] is None:
|
||||
params["Entrypoint"] = []
|
||||
if self._start_command:
|
||||
@ -287,7 +321,7 @@ class DockerVM(BaseVM):
|
||||
# We can not use the API because docker doesn't expose a websocket api for exec
|
||||
# https://github.com/GNS3/gns3-gui/issues/1039
|
||||
process = yield from asyncio.subprocess.create_subprocess_exec(
|
||||
"docker", "exec", "-i", self._cid, "/bin/sh", "-i",
|
||||
"docker", "exec", "-i", self._cid, "/gns3/bin/busybox", "sh", "-i",
|
||||
stdout=asyncio.subprocess.PIPE,
|
||||
stderr=asyncio.subprocess.STDOUT,
|
||||
stdin=asyncio.subprocess.PIPE)
|
||||
|
BIN
gns3server/modules/docker/resources/bin/busybox
Executable file
BIN
gns3server/modules/docker/resources/bin/busybox
Executable file
Binary file not shown.
3
gns3server/modules/docker/resources/bin/udhcpc
Executable file
3
gns3server/modules/docker/resources/bin/udhcpc
Executable file
@ -0,0 +1,3 @@
|
||||
#!/tmp/gns3/bin/sh
|
||||
|
||||
exec busybox udhcpc -s /gns3/etc/udhcpc/default.script "$@"
|
3
gns3server/modules/docker/resources/bin/udhcpc6
Executable file
3
gns3server/modules/docker/resources/bin/udhcpc6
Executable file
@ -0,0 +1,3 @@
|
||||
#!/tmp/gns3/bin/sh
|
||||
|
||||
exec busybox udhcpc6 -s /gns3/etc/udhcpc/default.script "$@"
|
138
gns3server/modules/docker/resources/etc/udhcpc/default.script
Executable file
138
gns3server/modules/docker/resources/etc/udhcpc/default.script
Executable file
@ -0,0 +1,138 @@
|
||||
#!/tmp/gns3/bin/sh
|
||||
|
||||
# script for udhcpc
|
||||
# Copyright (c) 2008 Natanael Copa <natanael.copa@gmail.com>
|
||||
|
||||
UDHCPC="/gns3/etc/udhcpc"
|
||||
UDHCPC_CONF="$UDHCPC/udhcpc.conf"
|
||||
|
||||
RESOLV_CONF="/etc/resolv.conf"
|
||||
[ -f $UDHCPC_CONF ] && . $UDHCPC_CONF
|
||||
|
||||
export broadcast
|
||||
export dns
|
||||
export domain
|
||||
export interface
|
||||
export ip
|
||||
export mask
|
||||
export metric
|
||||
export router
|
||||
export subnet
|
||||
|
||||
#export PATH=/usr/bin:/bin:/usr/sbin:/sbin
|
||||
|
||||
run_scripts() {
|
||||
local dir=$1
|
||||
if [ -d $dir ]; then
|
||||
for i in $dir/*; do
|
||||
[ -f $i ] && $i
|
||||
done
|
||||
fi
|
||||
}
|
||||
|
||||
deconfig() {
|
||||
ip addr flush dev $interface
|
||||
}
|
||||
|
||||
is_wifi() {
|
||||
test -e /sys/class/net/$interface/phy80211
|
||||
}
|
||||
|
||||
if_index() {
|
||||
if [ -e /sys/class/net/$interface/ifindex ]; then
|
||||
cat /sys/class/net/$interface/ifindex
|
||||
else
|
||||
ip link show dev $interface | head -n1 | cut -d: -f1
|
||||
fi
|
||||
}
|
||||
|
||||
calc_metric() {
|
||||
local base=
|
||||
if is_wifi; then
|
||||
base=300
|
||||
else
|
||||
base=200
|
||||
fi
|
||||
echo $(( $base + $(if_index) ))
|
||||
}
|
||||
|
||||
routes() {
|
||||
[ -z "$router" ] && return
|
||||
local gw= num=
|
||||
while ip route del default via dev $interface 2>/dev/null; do
|
||||
:
|
||||
done
|
||||
num=0
|
||||
for gw in $router; do
|
||||
ip route add 0.0.0.0/0 via $gw dev $interface \
|
||||
metric $(( $num + ${IF_METRIC:-$(calc_metric)} ))
|
||||
num=$(( $num + 1 ))
|
||||
done
|
||||
}
|
||||
|
||||
resolvconf() {
|
||||
local i
|
||||
[ -n "$IF_PEER_DNS" ] && [ "$IF_PEER_DNS" != "yes" ] && return
|
||||
if [ "$RESOLV_CONF" = "no" ] || [ "$RESOLV_CONF" = "NO" ] \
|
||||
|| [ -z "$RESOLV_CONF" ]; then
|
||||
return
|
||||
fi
|
||||
echo -n > "$RESOLV_CONF"
|
||||
[ -n "$domain" ] && echo "search $domain" >> "$RESOLV_CONF"
|
||||
for i in $dns; do
|
||||
echo "nameserver $i" >> "$RESOLV_CONF"
|
||||
done
|
||||
}
|
||||
|
||||
bound() {
|
||||
ip addr add $ip/$mask ${broadcast:+broadcast $broadcast} dev $interface
|
||||
ip link set dev $interface up
|
||||
routes
|
||||
resolvconf
|
||||
}
|
||||
|
||||
renew() {
|
||||
if ! ip addr show dev $interface | grep $ip/$mask; then
|
||||
ip addr flush dev $interface
|
||||
ip addr add $ip/$mask ${broadcast:+broadcast $broadcast} dev $interface
|
||||
fi
|
||||
|
||||
local i
|
||||
for i in $router; do
|
||||
if ! ip route show | grep ^default | grep $i; then
|
||||
routes
|
||||
break
|
||||
fi
|
||||
done
|
||||
|
||||
if ! grep "^search $domain"; then
|
||||
resolvconf
|
||||
return
|
||||
fi
|
||||
for i in $dns; do
|
||||
if ! grep "^nameserver $i"; then
|
||||
resolvconf
|
||||
return
|
||||
fi
|
||||
done
|
||||
}
|
||||
|
||||
case "$1" in
|
||||
deconfig|renew|bound)
|
||||
run_scripts $UDHCPC/pre-$1
|
||||
$1
|
||||
run_scripts $UDHCPC/post-$1
|
||||
;;
|
||||
leasefail)
|
||||
echo "udhcpc failed to get a DHCP lease" >&2
|
||||
;;
|
||||
nak)
|
||||
echo "udhcpc received DHCP NAK" >&2
|
||||
;;
|
||||
*)
|
||||
echo "Error: this script should be called from udhcpc" >&2
|
||||
exit 1
|
||||
;;
|
||||
esac
|
||||
exit 0
|
||||
|
@ -1,4 +1,4 @@
|
||||
#!/bin/sh
|
||||
#!/gns3/bin/busybox sh
|
||||
#
|
||||
# Copyright (C) 2016 GNS3 Technologies Inc.
|
||||
#
|
||||
@ -19,6 +19,16 @@
|
||||
# This script is injected into the container and launch before
|
||||
# the start command of the container
|
||||
#
|
||||
OLD_PATH="$PATH"
|
||||
PATH=/gns3/bin:/tmp/gns3/bin
|
||||
|
||||
# bootstrap busybox commands
|
||||
if [ ! -d /tmp/gns3/bin ]; then
|
||||
busybox mkdir -p /tmp/gns3/bin
|
||||
/gns3/bin/busybox --install -s /tmp/gns3/bin
|
||||
# remove commands already available in /gns3/bin
|
||||
(cd /tmp/gns3/bin; rm -f `cd /gns3/bin; echo *`)
|
||||
fi
|
||||
|
||||
# Wait 2 seconds to settle the network interfaces
|
||||
sleep 2
|
||||
@ -42,5 +52,9 @@ sed -n 's/^ *\(eth[0-9]*\):.*/\1/p' < /proc/net/dev | while read dev; do
|
||||
ip link set dev $dev up
|
||||
done
|
||||
|
||||
|
||||
ifup -a -f
|
||||
|
||||
# continue normal docker startup
|
||||
PATH="$OLD_PATH"
|
||||
exec "$@"
|
||||
|
@ -89,7 +89,10 @@ def test_create(loop, project, manager):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
@ -126,6 +129,7 @@ def test_create_vnc(loop, project, manager):
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
'/tmp/.X11-unix/:/tmp/.X11-unix/'
|
||||
],
|
||||
"Privileged": True
|
||||
@ -161,7 +165,10 @@ def test_create_start_cmd(loop, project, manager):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
@ -194,7 +201,10 @@ def test_create_environment(loop, project, manager):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Env": ["YES=1", "NO=0"],
|
||||
@ -241,7 +251,10 @@ def test_create_image_not_available(loop, project, manager):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
@ -452,7 +465,10 @@ def test_update(loop, vm):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
@ -490,7 +506,10 @@ def test_update_running(loop, vm):
|
||||
"HostConfig":
|
||||
{
|
||||
"CapAdd": ["ALL"],
|
||||
"Binds": ["{}:/gns3:ro".format(get_resource("modules/docker/resources"))],
|
||||
"Binds": [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network"))
|
||||
],
|
||||
"Privileged": True
|
||||
},
|
||||
"Volumes": {},
|
||||
@ -763,6 +782,7 @@ def test_mount_binds(vm, tmpdir):
|
||||
dst = os.path.join(vm.working_dir, "test/experimental")
|
||||
assert vm._mount_binds(image_infos) == [
|
||||
"{}:/gns3:ro".format(get_resource("modules/docker/resources")),
|
||||
"{}:/etc/network:rw".format(os.path.join(vm.working_dir, "etc", "network")),
|
||||
"{}:{}".format(dst, "/test/experimental")
|
||||
]
|
||||
|
||||
@ -789,3 +809,17 @@ def test_start_aux(vm, loop):
|
||||
|
||||
with asyncio_patch("asyncio.subprocess.create_subprocess_exec", return_value=MagicMock()) as mock_exec:
|
||||
loop.run_until_complete(asyncio.async(vm._start_aux()))
|
||||
|
||||
|
||||
def test_create_network_interfaces(vm):
|
||||
|
||||
vm.adapters = 5
|
||||
network_config = vm._create_network_config()
|
||||
assert os.path.exists(os.path.join(network_config, "interfaces"))
|
||||
assert os.path.exists(os.path.join(network_config, "if-up.d"))
|
||||
|
||||
with open(os.path.join(network_config, "interfaces")) as f:
|
||||
content = f.read()
|
||||
assert "eth0" in content
|
||||
assert "eth4" in content
|
||||
assert "eth5" not in content
|
||||
|
Loading…
Reference in New Issue
Block a user