158 lines
5.2 KiB
Ruby
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
|