Skip to content
This repository has been archived by the owner on Nov 24, 2022. It is now read-only.

[WIP] Private networks #298

Closed
wants to merge 6 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ will be coming along _soon_.
* [Vagrant 1.1+](http://www.vagrantup.com/downloads.html)
* lxc 0.7.5+
* `redir` (if you are planning to use port forwarding)
* `brctl` (if you are planning to use private networks, on Ubuntu this means `apt-get install bridge-utils`)
* A [kernel != 3.5.0-17.28](https://github.com/fgrehm/vagrant-lxc/wiki/Troubleshooting#wiki-im-unable-to-restart-containers)

The plugin is known to work better and pretty much out of the box on Ubuntu 14.04+
Expand Down
2 changes: 2 additions & 0 deletions lib/vagrant-lxc/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@
require 'vagrant-lxc/action/handle_box_metadata'
require 'vagrant-lxc/action/prepare_nfs_settings'
require 'vagrant-lxc/action/prepare_nfs_valid_ids'
require 'vagrant-lxc/action/private_networks'
require 'vagrant-lxc/action/remove_temporary_files'
require 'vagrant-lxc/action/setup_package_files'
require 'vagrant-lxc/action/warn_networks'
Expand Down Expand Up @@ -71,6 +72,7 @@ def self.action_boot
b.use ForwardPorts
b.use Boot
b.use Builtin::WaitForCommunicator
b.use PrivateNetworks
end
end

Expand Down
60 changes: 60 additions & 0 deletions lib/vagrant-lxc/action/private_networks.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,60 @@
module Vagrant
module LXC
module Action
class PrivateNetworks
def initialize(app, env)
@app = app
end

def call(env)
@app.call(env)

if private_network_configured?(env[:machine].config)
configure_private_networks(env)
end
end

def private_network_configured?(config)
config.vm.networks.find do |type, _|
type.to_sym == :private_network
end
end

def configure_private_networks(env)
env[:machine].config.vm.networks.find do |type, config|
next if type.to_sym != :private_network

container_name = env[:machine].provider.driver.container_name
ip = config[:ip]
configure_single_network('br1', container_name, ip)
end
end

def configure_single_network(bridge, container_name, ip)
cmd = [
'sudo',
Vagrant::LXC.source_root.join('scripts/private-network').to_s,
bridge,
container_name,
"#{ip}/24"
]
puts cmd.join(' ')
system cmd.join(' ')

cmd = [
'sudo',
'ip',
'addr',
'add',
# TODO: This should not be hard coded and has to run once per bridge
"192.168.1.254/24",
'dev',
bridge
]
puts cmd.join(' ')
system cmd.join(' ')
end
end
end
end
end
6 changes: 3 additions & 3 deletions lib/vagrant-lxc/action/warn_networks.rb
Original file line number Diff line number Diff line change
Expand Up @@ -7,16 +7,16 @@ def initialize(app, env)
end

def call(env)
if public_or_private_network_configured?(env[:machine].config)
if public_network_configured?(env[:machine].config)
env[:ui].warn(I18n.t("vagrant_lxc.messages.warn_networks"))
end

@app.call(env)
end

def public_or_private_network_configured?(config)
def public_network_configured?(config)
config.vm.networks.find do |type, _|
[:private_network, :public_network].include?(type.to_sym)
type.to_sym == :public_network
end
end
end
Expand Down
5 changes: 2 additions & 3 deletions locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -13,9 +13,8 @@ en:
force_shutdown: |-
Forcing shutdown of container...
warn_networks: |-
Warning! The LXC provider doesn't support any of the Vagrant public / private
network configurations (ex: `config.vm.network :private_network, ip: "some-ip"`).
They will be silently ignored.
Warning! The LXC provider doesn't support public networks, the settings
will be silently ignored.
warn_group: |-
Warning! The LXC provider doesn't support the :group parameter for synced
folders. It will be silently ignored.
Expand Down
193 changes: 193 additions & 0 deletions scripts/private-network
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
#!/bin/bash

# This is a snapshot of https://github.com/jpetazzo/pipework/blob/edbd33ab49ab0dff0bee46b019055360f325a6e5/pipework
# with docker specifics trimmed out

set -e

case "$1" in
--wait)
WAIT=1
;;
esac

IFNAME=$1
if [ "$2" == "-i" ]; then
CONTAINER_IFNAME=$3
shift 2
else
CONTAINER_IFNAME=eth1
fi
GUESTNAME=$2
IPADDR=$3
MACADDR=$4

[ "$WAIT" ] && {
while ! grep -q ^1$ /sys/class/net/$CONTAINER_IFNAME/carrier 2>/dev/null
do sleep 1
done
exit 0
}

[ "$IPADDR" ] || {
echo "Syntax:"
echo "pipework <hostinterface> [-i containerinterface] <guest> <ipaddr>/<subnet>[@default_gateway] [macaddr]"
echo "pipework <hostinterface> [-i containerinterface] <guest> dhcp [macaddr]"
echo "pipework --wait"
exit 1
}

# First step: determine type of first argument (bridge, physical interface...)
if [ -d /sys/class/net/$IFNAME ]
then
if [ -d /sys/class/net/$IFNAME/bridge ]
then
IFTYPE=bridge
BRTYPE=linux
elif $(which ovs-vsctl >/dev/null) && $(ovs-vsctl list-br|grep -q ^$IFNAME$)
then
IFTYPE=bridge
BRTYPE=openvswitch
else IFTYPE=phys
fi
else
case "$IFNAME" in
br*)
IFTYPE=bridge
BRTYPE=linux
;;
*)
echo "I do not know how to setup interface $IFNAME."
exit 1
;;
esac
fi

# Second step: find the guest
while read dev mnt fstype options dump fsck
do
[ "$fstype" != "cgroup" ] && continue
echo $options | grep -qw devices || continue
CGROUPMNT=$mnt
done < /proc/mounts

[ "$CGROUPMNT" ] || {
echo "Could not locate cgroup mount point."
exit 1
}

# Try to find a cgroup matching exactly the provided name.
N=$(find "$CGROUPMNT" -name "$GUESTNAME" | wc -l)
case "$N" in
0)
echo "Container $GUESTNAME not found."
exit 1
;;
1)
true
;;
*)
echo "Found more than one container matching $GUESTNAME."
exit 1
;;
esac

if [ "$IPADDR" = "dhcp" ]
then
# Check for first available dhcp client
DHCP_CLIENT_LIST="udhcpc dhcpcd dhclient"
for CLIENT in $DHCP_CLIENT_LIST; do
which $CLIENT >/dev/null && {
DHCP_CLIENT=$CLIENT
break
}
done
[ -z $DHCP_CLIENT ] && {
echo "You asked for DHCP; but no DHCP client could be found."
exit 1
}
else
# Check if a subnet mask was provided.
echo $IPADDR | grep -q / || {
echo "The IP address should include a netmask."
echo "Maybe you meant $IPADDR/24 ?"
exit 1
}
# Check if a gateway address was provided.
if echo $IPADDR | grep -q @
then
GATEWAY=$(echo $IPADDR | cut -d@ -f2)
IPADDR=$(echo $IPADDR | cut -d@ -f1)
else
GATEWAY=
fi
fi

NSPID=$(head -n 1 $(find "$CGROUPMNT" -name "$GUESTNAME" | head -n 1)/tasks)
[ "$NSPID" ] || {
echo "Could not find a process inside container $GUESTNAME."
exit 1
}

[ ! -d /var/run/netns ] && mkdir -p /var/run/netns
[ -f /var/run/netns/$NSPID ] && rm -f /var/run/netns/$NSPID
ln -s /proc/$NSPID/ns/net /var/run/netns/$NSPID


# Check if we need to create a bridge.
[ $IFTYPE = bridge ] && [ ! -d /sys/class/net/$IFNAME ] && {
(ip link set $IFNAME type bridge > /dev/null 2>&1) || (brctl addbr $IFNAME)
ip link set $IFNAME up
}

# If it's a bridge, we need to create a veth pair
[ $IFTYPE = bridge ] && {
LOCAL_IFNAME=pl$NSPID$CONTAINER_IFNAME
GUEST_IFNAME=pg$NSPID$CONTAINER_IFNAME
ip link add name $LOCAL_IFNAME type veth peer name $GUEST_IFNAME
case "$BRTYPE" in
linux)
(ip link set $LOCAL_IFNAME master $IFNAME > /dev/null 2>&1) || (brctl addif $IFNAME $LOCAL_IFNAME)
;;
openvswitch)
ovs-vsctl add-port $IFNAME $LOCAL_IFNAME
;;
esac
ip link set $LOCAL_IFNAME up
}

# If it's a physical interface, create a macvlan subinterface
[ $IFTYPE = phys ] && {
GUEST_IFNAME=ph$NSPID$CONTAINER_IFNAME
ip link add link $IFNAME dev $GUEST_IFNAME type macvlan mode bridge
ip link set $IFNAME up
}

ip link set $GUEST_IFNAME netns $NSPID
ip netns exec $NSPID ip link set $GUEST_IFNAME name $CONTAINER_IFNAME
[ "$MACADDR" ] && ip netns exec $NSPID ip link set $CONTAINER_IFNAME address $MACADDR
if [ "$IPADDR" = "dhcp" ]
then
[ $DHCP_CLIENT = "udhcpc" ] && ip netns exec $NSPID $DHCP_CLIENT -qi $CONTAINER_IFNAME
[ $DHCP_CLIENT = "dhclient" ] && ip netns exec $NSPID $DHCP_CLIENT $CONTAINER_IFNAME
[ $DHCP_CLIENT = "dhcpcd" ] && ip netns exec $NSPID $DHCP_CLIENT -q $CONTAINER_IFNAME
else
ip netns exec $NSPID ip addr add $IPADDR dev $CONTAINER_IFNAME
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route delete default >/dev/null 2>&1 && true
}
ip netns exec $NSPID ip link set $CONTAINER_IFNAME up
[ "$GATEWAY" ] && {
ip netns exec $NSPID ip route replace default via $GATEWAY
}
fi

# Give our ARP neighbors a nudge about the new interface
if which arping > /dev/null 2>&1
then
IPADDR=$(echo $IPADDR | cut -d/ -f1)
ip netns exec $NSPID arping -c 1 -A -I $CONTAINER_IFNAME $IPADDR > /dev/null 2>&1
else
echo "Warning: arping not found; interface may not be immediately reachable"
fi
exit 0