Skip to content

Instantly share code, notes, and snippets.

@alk
Created July 1, 2010 07:53
Show Gist options
  • Save alk/459693 to your computer and use it in GitHub Desktop.
Save alk/459693 to your computer and use it in GitHub Desktop.
#!/usr/bin/ruby
require 'tempfile'
require 'optparse'
require 'pp'
module Kernel
def poll_for_condition(timeout = 10, fail_unless_ok = true)
start = Time.now.to_f
ok = false
begin
break if (ok = yield)
sleep 0.1
end while (Time.now.to_f - start) < timeout
unless ok
if fail_unless_ok
# puts "Timeout!!"
# STDIN.gets
raise "Timeout hit (#{timeout}) while waiting for condition"
end
end
ok
end
end
def tmp_lock
times = 3
begin
File.open("/tmp/lxc-lock", File::CREAT | File::EXCL | File::WRONLY) do |f|
begin
yield
ensure
File.unlink("/tmp/lxc-lock")
end
end
rescue Errno::EEXIST
times -= 1
raise if times < 0
sleep 0.2
retry
end
end
def parse_address_mask(arg)
raise "invalid network argument: #{arg.inspect}" unless arg =~ /^([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\.([0-9]{1,3})\/([0-9]{1,2})$/
mask_size = $5.to_i
address = [$1, $2, $3, $4].map {|s| s.to_i}.inject(0) {|s,e| s*256+e}
masked_address = (2**32 - 2**(32-mask_size)) & address
set_size = 2**mask_size
[masked_address, set_size, mask_size]
end
def allocate_address(arg)
masked_address, set_size, mask_size = *parse_address_mask(arg)
(2...set_size).each do |i|
addr = masked_address + i
ip_address = (0..3).map do
rv = addr % 256
addr /= 256
rv
end.reverse.join('.')
tmp_lock do
begin
File.open("/tmp/lxc-addr-#{ip_address}", File::CREAT|File::EXCL|File::RDWR) do |f|
f.write Process.pid
end
rv = "#{ip_address}/#{mask_size}"
puts "selected ip: #{rv}"
return rv
rescue Errno::EEXIST
used_by_pid = (IO.read("/tmp/lxc-addr-#{ip_address}") rescue "0").to_i
if used_by_pid != Process.pid && (Process.kill(0, used_by_pid) rescue false)
next
end
File.unlink("/tmp/lxc-addr-#{ip_address}")
retry
end
end
end
end
def get_eth_addresses(options)
lines = IO.popen("ip addr show dev #{options[:eth]}", "r") {|f| f.readlines}
lines.map {|l| l =~ %r|inet ([0-9.]+)/[0-9]+ brd| && $1}.compact
end
def start_container(options)
f = Tempfile.new("lxc-config")
f << <<HERE
lxc.utsname = #{options[:utsname]}
lxc.network.type = macvlan
lxc.network.macvlan.mode = bridge
lxc.network.name = eth0
lxc.network.link = #{options[:eth]}
lxc.network.ipv4 = #{options[:ip]}
lxc.mount.entry=none /tmp tmpfs defaults 0 0
lxc.mount.entry=none /var/run tmpfs defaults 0 0
HERE
f.close
ENV['LXC_CLONE_ARGS'] = [Marshal.dump(options)].pack("m").strip
system("lxc-execute", "-n", options[:utsname], "-f", f.path, __FILE__)
f.delete
end
def continue_container(options)
system "route add default dev eth0"
masked_address, set_size, mask_size = parse_address_mask(options[:ip])
addr = masked_address + (2**(32-mask_size)-1)
bcast_address = (0..3).map do
rv = addr % 256
addr /= 256
rv
end.reverse.join('.')
# puts "bcast: #{bcast_address}"
system "ifconfig eth0 broadcast #{bcast_address}"
ENV['LXC_IP'] = options[:ip].split("/")[0]
system(*options[:argv])
puts "Command exited. Cleaning up\n\n"
system "ps ax --forest"
pids = IO.popen("cd /proc && ls | grep '[0-9]'", "r") {|f| f.readlines.map {|s| s.to_i}}
pids.each do |p|
next if p == 1 || p == Process.pid
(Process.kill("KILL", p) && puts("killed #{p}")) rescue nil
end
end
if ENV['LXC_CLONE_ARGS']
continue_container(Marshal.load(ENV['LXC_CLONE_ARGS'].unpack('m')[0]))
exit
end
options = {
:eth => "vethSlave",
:ip_range => "172.25.0.0/16"
}
opts = OptionParser.new
opts.banner = "Usage: #{File.basename($0)} [options] command..."
opts.on("-r", "--ip-range=VAL", String) {|x| options[:ip_range] = x}
opts.on("-e", "--eth=VAL", String) {|x| options[:eth] = x}
opts.on("-i", "--ip=VAL", String) {|x| options[:ip] = x}
opts.on("--setup") {options[:setup] = true}
options[:argv] = opts.parse(*ARGV)
if options[:argv].empty?
options[:argv] = ["/bin/bash", "-l"]
end
options[:ip] ||= allocate_address(options[:ip_range])
options[:utsname] = "lxc-#{options[:ip].split('.').map {|s| s.to_i.to_s(16)}.join('-')}"
if options[:setup]
system "ip link add name vethMain type veth peer name vethSlave"
system "ifconfig vethMain 172.25.0.1/16 up"
system "ifconfig vethSlave up"
exit
end
puts "config is:\n#{options.pretty_inspect}"
start_container(options)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment