Simplify container ip detection using lxc-attach + ifconfig

References: #31
This commit is contained in:
Fabio Rehm 2013-03-19 01:46:44 -03:00
parent e8388743ca
commit b4161ac8af
5 changed files with 33 additions and 119 deletions

View file

@ -103,67 +103,22 @@ module Vagrant
end
def assigned_ip
unless File.read(base_path.join('config')) =~ /^lxc\.network\.hwaddr\s*=\s*([a-z0-9:]+)\s*$/
raise 'Unknown Container MAC Address'
end
mac_addr = $1
ip = ''
retryable(:on => LXC::Errors::ExecuteError, :tries => 10, :sleep => 3) do
# See: http://programminglinuxblog.blogspot.com.br/2007/11/detecting-ip-address-from-mac-address.html
unless ip = get_container_ip_from_arp(mac_addr)
# Ping subnet and try to get ip again
ping_subnet!
unless ip = get_container_ip_from_ifconfig
# retry
raise LXC::Errors::ExecuteError, :command => ['arp', '-n'].inspect
end
end
ip
end
def get_container_ip_from_arp(mac_addr)
r = raw 'arp', '-n'
# If the command was a failure then raise an exception that is nicely
# handled by Vagrant.
if r.exit_code != 0
if @interrupted
@logger.info("Exit code != 0, but interrupted. Ignoring.")
else
raise LXC::Errors::ExecuteError, :command => ['arp', '-n'].inspect
end
end
if r.stdout.gsub("\r\n", "\n").strip =~ /^([0-9.]+).+#{Regexp.escape mac_addr}/
def get_container_ip_from_ifconfig
output = @cli.attach '/sbin/ifconfig', '-v', 'eth0', namespaces: 'network'
if output =~ /\s+inet addr:([0-9.]+)\s+/
return $1.to_s
end
end
# FIXME: Should output an error friendly message in case fping is not installed
def ping_subnet!
raise LXC::Errors::UnknownLxcConfigFile unless File.exists?(LXC_DEFAULTS_PATH)
raise LXC::Errors::UnknownLxcBridgeAddress unless
File.read(LXC_DEFAULTS_PATH) =~ /^LXC_ADDR\="?([0-9.]+)"?.*$/
cmd = ['fping', '-c', '1', '-g', '-q', "#{$1}/24"]
raw(*cmd)
raise LXC::Errors::ExecuteError, :command => cmd.inspect
end
# TODO: Review code below this line, it was pretty much a copy and paste from VirtualBox base driver
def raw(*command, &block)
int_callback = lambda do
@interrupted = true
@logger.info("Interrupted.")
end
# Append in the options for subprocess
command << { :notify => [:stdout, :stderr] }
Vagrant::Util::Busy.busy(int_callback) do
Vagrant::Util::Subprocess.execute(*command, &block)
end
end
end
end
end

View file

@ -1,4 +0,0 @@
Address HWtype HWaddress Flags Mask Iface
120.0.3.30 ether 10:16:3e:c8:f9:b7 C lxcbr0
10.0.3.30 ether 00:16:3e:64:d6:74 C lxcbr0
192.168.25.1 ether 2c:e4:12:95:90:45 C wlan0

View file

@ -1,47 +0,0 @@
lxc.network.type=veth
lxc.network.link=lxcbr0
lxc.network.flags=up
lxc.network.hwaddr = 00:16:3e:64:d6:74
lxc.rootfs = /var/lib/lxc/a91df47bfc6e/rootfs
lxc.utsname = a91df47bfc6e
lxc.devttydir = lxc
lxc.tty = 4
lxc.pts = 1024
lxc.mount = /var/lib/lxc/a91df47bfc6e/fstab
lxc.arch = amd64
lxc.cap.drop = sys_module mac_admin mac_override
lxc.pivotdir = lxc_putold
# uncomment the next line to run the container unconfined:
#lxc.aa_profile = unconfined
lxc.cgroup.devices.deny = a
# Allow any mknod (but not using the node)
lxc.cgroup.devices.allow = c *:* m
lxc.cgroup.devices.allow = b *:* m
# /dev/null and zero
lxc.cgroup.devices.allow = c 1:3 rwm
lxc.cgroup.devices.allow = c 1:5 rwm
# consoles
lxc.cgroup.devices.allow = c 5:1 rwm
lxc.cgroup.devices.allow = c 5:0 rwm
#lxc.cgroup.devices.allow = c 4:0 rwm
#lxc.cgroup.devices.allow = c 4:1 rwm
# /dev/{,u}random
lxc.cgroup.devices.allow = c 1:9 rwm
lxc.cgroup.devices.allow = c 1:8 rwm
lxc.cgroup.devices.allow = c 136:* rwm
lxc.cgroup.devices.allow = c 5:2 rwm
# rtc
lxc.cgroup.devices.allow = c 254:0 rwm
#fuse
lxc.cgroup.devices.allow = c 10:229 rwm
#tun
lxc.cgroup.devices.allow = c 10:200 rwm
#full
lxc.cgroup.devices.allow = c 1:7 rwm
#hpet
lxc.cgroup.devices.allow = c 10:228 rwm
#kvm
lxc.cgroup.devices.allow = c 10:232 rwm

18
spec/fixtures/sample-ifconfig-output vendored Normal file
View file

@ -0,0 +1,18 @@
eth0 Link encap:Ethernet HWaddr 00:16:3e:7c:dd:44
inet addr:10.0.3.109 Bcast:10.0.3.255 Mask:255.255.255.0
inet6 addr: fe80::216:3eff:fe7c:dd44/64 Scope:Link
UP BROADCAST RUNNING MULTICAST MTU:1500 Metric:1
RX packets:204 errors:0 dropped:0 overruns:0 frame:0
TX packets:203 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:1000
RX bytes:29663 (29.6 KB) TX bytes:30168 (30.1 KB)
lo Link encap:Local Loopback
inet addr:127.0.0.1 Mask:255.0.0.0
inet6 addr: ::1/128 Scope:Host
UP LOOPBACK RUNNING MTU:16436 Metric:1
RX packets:73 errors:0 dropped:0 overruns:0 frame:0
TX packets:73 errors:0 dropped:0 overruns:0 carrier:0
collisions:0 txqueuelen:0
RX bytes:34173 (34.1 KB) TX bytes:34173 (34.1 KB)

View file

@ -125,27 +125,19 @@ describe Vagrant::LXC::Container do
end
describe 'assigned ip' do
# This ip is set on the sample-arp-output fixture based on mac address from
# sample-config fixture
let(:ip) { "10.0.3.30" }
let(:conf_file_contents) { File.read('spec/fixtures/sample-config') }
let(:name) { 'random-container-name' }
# This ip is set on the sample-ifconfig-output fixture
let(:ip) { "10.0.3.109" }
let(:ifconfig_output) { File.read('spec/fixtures/sample-ifconfig-output') }
let(:name) { 'random-container-name' }
let(:cli) { fire_double('Vagrant::LXC::Container::CLI', :attach => ifconfig_output) }
context 'when container mac address gets returned from the first `arp` call' do
before do
@arp_output = File.read('spec/fixtures/sample-arp-output')
subject.stub(:raw) {
mock(stdout: "#{@arp_output}\n", exit_code: 0)
}
File.stub(read: conf_file_contents)
end
subject { described_class.new(name, cli) }
it 'gets parsed from `arp` based on lxc mac address' do
context 'when ip for eth0 gets returned from lxc-attach call' do
it 'gets parsed from ifconfig output' do
subject.assigned_ip.should == ip
subject.should have_received(:raw).with('arp', '-n')
cli.should have_received(:attach).with('/sbin/ifconfig', '-v', 'eth0', namespaces: 'network')
end
end
pending 'when mac address is not returned from an `arp` call'
end
end