module Vagrant module Actions module VM class Network < Base def prepare # Verify that the given network options are valid runner.env.config.vm.network_options.compact.each do |network_options| verify_no_bridge_collision(network_options) end end def before_destroy # We need to check if the host only network specified by any # of the adapters would not have any more clients if it was # destroyed. And if so, then destroy the host only network # itself. interfaces = runner.vm.network_adapters.collect do |adapter| adapter.host_interface_object end interfaces.compact.uniq.each do |interface| # Destroy the network interface if there is only one # attached VM (which must be this VM) if interface.attached_vms.length == 1 logger.info "Destroying unused network interface..." interface.destroy end end end def before_boot assign_network if enable_network? end def after_boot if enable_network? logger.info "Enabling host only network..." runner.system.prepare_host_only_network runner.env.config.vm.network_options.compact.each do |network_options| runner.system.enable_host_only_network(network_options) end end end def enable_network? !runner.env.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 logger.info "Preparing host only network..." runner.env.config.vm.network_options.compact.each do |network_options| adapter = runner.vm.network_adapters[network_options[:adapter]] adapter.enabled = true adapter.attachment_type = :host_only adapter.host_interface = network_name(network_options) adapter.save 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) # First try to find a matching network interfaces = VirtualBox::Global.global.host.network_interfaces interfaces.each do |ni| next if ni.interface_type == :host_only result = if net_options[:name] true if net_options[:name] == ni.name else true if matching_network?(ni, net_options) end raise ActionException.new(:network_collides) if result end end # Returns the name of the proper host only network, or creates # it if it does not exist. Vagrant determines if the host only # network exists by comparing the netmask and the IP. def network_name(net_options) # First try to find a matching network interfaces = VirtualBox::Global.global.host.network_interfaces interfaces.each do |ni| # Ignore non-host only interfaces which may also match, # since they're not valid options. next if ni.interface_type != :host_only if net_options[:name] return ni.name if net_options[:name] == ni.name else return ni.name if matching_network?(ni, net_options) end end raise ActionException.new(:network_not_found, :name => net_options[:name]) if net_options[:name] # One doesn't exist, create it. logger.info "Creating new host only network for environment..." ni = interfaces.create ni.enable_static(network_ip(net_options[:ip], net_options[:netmask]), net_options[:netmask]) ni.name 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.network_mask == net_options[:netmask] && apply_netmask(interface.ip_address, interface.network_mask) == 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