Created
July 1, 2010 07:53
-
-
Save alk/459693 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#!/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