Experimental support for private networking [GH-298]

This commit is contained in:
Fabio Rehm 2015-01-11 20:59:38 -02:00
parent bf3a9a5039
commit 447d0dfc42
8 changed files with 111 additions and 53 deletions

View file

@ -56,9 +56,9 @@ module Vagrant
b.use Builtin::SetHostname
b.use WarnNetworks
b.use ForwardPorts
b.use PrivateNetworks
b.use Boot
b.use Builtin::WaitForCommunicator
b.use PrivateNetworks
end
end
@ -127,12 +127,12 @@ module Vagrant
b2.use ClearForwardedPorts
b2.use RemoveTemporaryFiles
b2.use GcPrivateNetworkBridges
b2.use Builtin::Call, Builtin::GracefulHalt, :stopped, :running do |env2, b3|
if !env2[:result]
b3.use ForcedHalt
end
end
b2.use GcPrivateNetworkBridges
end
end
end
@ -147,7 +147,6 @@ module Vagrant
next
end
# TODO: Use Vagrant's built in action once we drop support for vagrant 1.2
b2.use Builtin::Call, DestroyConfirm do |env2, b3|
if env2[:result]
b3.use Builtin::ConfigValidate

View file

@ -1,5 +1,3 @@
# sudo ifconfig br1 down && sudo brctl delbr br1
module Vagrant
module LXC
module Action
@ -9,11 +7,38 @@ module Vagrant
end
def call(env)
if env[:machine].provider.state.id != :running
puts 'Cleanup bridges!'
end
was_running = env[:machine].provider.state.id == :running
# Continue execution, we need the container to be stopped
@app.call(env)
was_running = was_running && env[:machine].provider.state.id != :running
if was_running && private_network_configured?(env[:machine].config)
private_network_configured?(env[:machine].config)
remove_bridges_that_are_not_in_use(env)
end
end
def private_network_configured?(config)
config.vm.networks.find do |type, _|
type.to_sym == :private_network
end
end
def remove_bridges_that_are_not_in_use(env)
env[:machine].config.vm.networks.find do |type, config|
next if type.to_sym != :private_network
bridge = config.fetch(:lxc__bridge_name)
driver = env[:machine].provider.driver
if ! driver.bridge_is_in_use?(bridge)
env[:ui].info I18n.t("vagrant_lxc.messages.remove_bridge", name: bridge)
# TODO: Output that bridge is being removed
driver.remove_bridge(bridge)
end
end
end
end
end

View file

@ -10,6 +10,7 @@ module Vagrant
@app.call(env)
if private_network_configured?(env[:machine].config)
env[:ui].output(I18n.t("vagrant_lxc.messages.setup_private_network"))
configure_private_networks(env)
end
end
@ -27,50 +28,15 @@ module Vagrant
container_name = env[:machine].provider.driver.container_name
ip = config[:ip]
bridge_ip = config.fetch(:lxc__bridge_ip) { build_bridge_ip(ip) }
bridge = config.fetch(:lxc__bridge_name) # { build_bridge_name(config.fetch(:lxc__bridge_prefix, 'br'), bridge_ip) }
bridge = config.fetch(:lxc__bridge_name)
# TODO: ensure_ip_is_not_in_use!
configure_single_network(bridge, bridge_ip, container_name, ip)
env[:machine].provider.driver.configure_private_network(bridge, bridge_ip, container_name, ip)
end
end
def configure_single_network(bridge, bridge_ip, container_name, ip)
cmd = [
'sudo',
Vagrant::LXC.source_root.join('scripts/private-network').to_s,
bridge,
container_name,
"#{ip}/24"
]
execute(cmd)
# TODO: Run only if bridge is not up and move it to the private network script
cmd = [
'sudo',
'ip',
'addr',
'add',
"#{bridge_ip}/24",
'dev',
bridge
]
execute(cmd)
end
def execute(cmd)
puts cmd.join(' ')
system cmd.join(' ')
end
def build_bridge_ip(ip)
ip.sub(/^(\d+\.\d+\.\d+)\.\d+/, '\1.254')
end
def bridge_name(prefix, bridge_ip)
# if a bridge with the provided ip and prefix exist, get its name and return it
# if no bridges can be found, grab the max bridge number, increment it and return the new name
'br3'
end
end
end
end

View file

@ -46,8 +46,9 @@ module Vagrant
wrapper = Tempfile.new('lxc-wrapper').tap do |file|
template = Vagrant::Util::TemplateRenderer.new(
'sudoers.rb',
:template_root => Vagrant::LXC.source_root.join('templates').to_s,
:cmd_paths => build_cmd_paths_hash
:template_root => Vagrant::LXC.source_root.join('templates').to_s,
:cmd_paths => build_cmd_paths_hash,
:pipework_regex => "#{ENV['HOME']}/\.vagrant\.d/gems/gems/vagrant-lxc.+/scripts/pipework"
)
file.puts template.render
end
@ -78,7 +79,7 @@ module Vagrant
def build_cmd_paths_hash
{}.tap do |hash|
%w( which cat mkdir cp chown chmod rm tar chown ).each do |cmd|
%w( which cat mkdir cp chown chmod rm tar chown ip ifconfig brctl ).each do |cmd|
hash[cmd] = `which #{cmd}`.strip
end
hash['lxc_bin'] = Pathname(`which lxc-create`.strip).parent.to_s

View file

@ -126,6 +126,52 @@ module Vagrant
@cli.attach(*command)
end
def configure_private_network(bridge_name, bridge_ip, container_name, ip)
@logger.info "Configuring network interface for #{container_name} using #{ip} and bridge #{bridge_name}"
cmd = [
Vagrant::LXC.source_root.join('scripts/pipework').to_s,
bridge_name,
container_name,
"#{ip}/24"
]
@sudo_wrapper.run(*cmd)
if ! bridge_has_an_ip?(bridge_name)
@logger.info "Adding #{bridge_ip} to the bridge #{bridge_name}"
cmd = [
'ip',
'addr',
'add',
"#{bridge_ip}/24",
'dev',
bridge_name
]
@sudo_wrapper.run(*cmd)
end
end
def bridge_has_an_ip?(bridge_name)
@logger.info "Checking whether the bridge #{bridge_name} has an IP"
`ip -4 addr show scope global #{bridge_name}` =~ /^\s+inet ([0-9.]+)\/[0-9]+\s+/
end
def bridge_is_in_use?(bridge_name)
# REFACTOR: This method is **VERY** hacky
@logger.info "Checking if bridge #{bridge_name} is in use"
brctl_output = `brctl show #{bridge_name} 2>/dev/null | tail -n +2 | grep -q veth`
$?.to_i == 0
end
def remove_bridge(bridge_name)
@logger.info "Checking whether bridge #{bridge_name} exists"
brctl_output = `ifconfig -a | grep -q #{bridge_name}`
return if $?.to_i != 0
@logger.info "Removing bridge #{bridge_name}"
@sudo_wrapper.run('ifconfig', bridge_name, 'down')
@sudo_wrapper.run('brctl', 'delbr', bridge_name)
end
def version
@version ||= @cli.version
end

View file

@ -21,6 +21,10 @@ en:
warn_owner: |-
Warning! The LXC provider doesn't support the :owner parameter for synced
folders. It will be silently ignored.
setup_private_network: |-
Setting up private networks...
remove_bridge: |-
Removing bridge '%{name}'...
vagrant:
commands:

View file

@ -4,24 +4,36 @@
class Whitelist
class << self
def add(command, *args)
list[command] ||= []
list[command] << args
end
def add_regex(regex, *args)
regex_list << [regex, [args]]
end
def list
@list ||= Hash.new do |key, hsh|
key[hsh] = []
end
@list ||= {}
end
def regex_list
@regex_list ||= []
end
def allowed(command)
list[command] || []
list[command] || allowed_regex(command) || []
end
def allowed_regex(command)
found = regex_list.find { |r| r[0] =~ command }
return found[1] if found
end
def run!(argv)
begin
command, args = `which #{argv.shift}`.chomp, argv || []
check!(command, args)
puts `#{command} #{args.join(" ")}`
system "#{command} #{args.join(" ")}"
exit_code = $?.to_i
exit_code = 1 if exit_code == 256
@ -92,6 +104,11 @@ Whitelist.add '<%= cmd_paths['rm'] %>', templates_path
# - Packaging
Whitelist.add '<%= cmd_paths['tar'] %>', '--numeric-owner', '-cvzf', %r{/tmp/.*/rootfs.tar.gz}, '-C', base_path, './rootfs'
Whitelist.add '<%= cmd_paths['chown'] %>', /\A\d+:\d+\z/, %r{\A/tmp/.*/rootfs\.tar\.gz\z}
# - Private network script and commands
Whitelist.add '<%= cmd_paths['ip'] %>', 'addr', 'add', /(\d+|\.)+\/24/, 'dev', /.+/
Whitelist.add '<%= cmd_paths['ifconfig'] %>', /.+/, 'down'
Whitelist.add '<%= cmd_paths['brctl'] %>', 'delbr', /.+/
Whitelist.add_regex %r{<%= pipework_regex %>}, '**'
##
# Commands from driver/cli.rb