2011-12-21 22:27:06 -08:00

158 lines
5.2 KiB
Ruby

module Vagrant
module Action
module VM
# Networking middleware for Vagrant. This enables host only
# networking on VMs if configured as such.
class Network
def initialize(app, env)
@app = app
@env = env
env[:vm].config.vm.network_options.compact.each do |network_options|
raise Errors::NetworkCollision if !verify_no_bridge_collision(network_options)
end
end
def call(env)
@env = env
assign_network if enable_network?
@app.call(env)
if enable_network?
@env[:ui].info I18n.t("vagrant.actions.vm.network.enabling")
# Prepare for new networks...
options = @env[:vm].config.vm.network_options.compact
options.each do |network_options|
@env["vm"].guest.prepare_host_only_network(network_options)
end
# Then enable the networks...
options.each do |network_options|
@env["vm"].guest.enable_host_only_network(network_options)
end
end
end
# Verifies that there is no collision with a bridged network interface
# for the given network options.
def verify_no_bridge_collision(net_options)
@env[:vm].driver.read_bridged_interfaces.each do |interface|
return false if matching_network?(interface, net_options)
end
true
end
def enable_network?
!@env[:vm].config.vm.network_options.compact.empty?
end
# Enables and assigns the host only network to the proper
# adapter on the VM, and saves the adapter.
def assign_network
@env[:ui].info I18n.t("vagrant.actions.vm.network.preparing")
networks = @env[:vm].driver.read_host_only_interfaces
adapters = []
# Build the networks and the list of adapters we need to enable
@env[:vm].config.vm.network_options.compact.each do |network_options|
network = find_matching_network(networks, network_options)
if !network
# It is an error case if a specific name was given but the network
# doesn't exist.
if network_options[:name]
raise Errors::NetworkNotFound, :name => network_options[:name]
end
# Otherwise, we create a new network and put the net network
# in the list of available networks so other network definitions
# can use it!
network = create_network(network_options)
networks << network
end
adapters << {
:adapter => network_options[:adapter] + 1,
:type => :hostonly,
:hostonly => network[:name],
:mac_address => network_options[:mac]
}
end
# Enable the host only adapters!
@env[:vm].driver.enable_adapters(adapters)
end
# This looks through a list of available host only networks and
# finds a matching network.
#
# If one is not available, `nil` is returned.
def find_matching_network(networks, needle_options)
networks.each do |network|
if needle_options[:name] && needle_options[:name] == network[:name]
return network
elsif matching_network?(network, needle_options)
return network
end
end
nil
end
# Creates a host only network with the given options and returns
# the hash of the options it was created with.
#
# @return [Hash]
def create_network(network_options)
# Create the options for the host only network, specifically
# figuring out the host only network IP based on the netmask.
options = network_options.merge({
:ip => network_ip(network_options[:ip], network_options[:netmask])
})
@env[:vm].driver.create_host_only_network(options)
end
# Tests if a network matches the given options by applying the
# netmask to the IP of the network and also to the IP of the
# virtual machine and see if they match.
def matching_network?(interface, net_options)
interface[:netmask] == net_options[:netmask] &&
apply_netmask(interface[:ip], interface[:netmask]) ==
apply_netmask(net_options[:ip], net_options[:netmask])
end
# Applies a netmask to an IP and returns the corresponding
# parts.
def apply_netmask(ip, netmask)
ip = split_ip(ip)
netmask = split_ip(netmask)
ip.map do |part|
part & netmask.shift
end
end
# Splits an IP and converts each portion into an int.
def split_ip(ip)
ip.split(".").map do |i|
i.to_i
end
end
# Returns a "network IP" which is a "good choice" for the IP
# for the actual network based on the netmask.
def network_ip(ip, netmask)
parts = apply_netmask(ip, netmask)
parts[3] += 1;
parts.join(".")
end
end
end
end
end