Host only networks now work with VBoxManage

This commit is contained in:
Mitchell Hashimoto 2011-12-21 22:27:06 -08:00
parent 79460f6122
commit ecbf7df859
4 changed files with 136 additions and 57 deletions

View File

@ -8,10 +8,6 @@ module Vagrant
@app = app
@env = env
if enable_network? && Util::Platform.windows? && Util::Platform.bit64?
raise Errors::NetworkNotImplemented
end
env[:vm].config.vm.network_options.compact.each do |network_options|
raise Errors::NetworkCollision if !verify_no_bridge_collision(network_options)
end
@ -42,17 +38,8 @@ module Vagrant
# Verifies that there is no collision with a bridged network interface
# for the given network options.
def verify_no_bridge_collision(net_options)
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
return false if result
@env[:vm].driver.read_bridged_interfaces.each do |interface|
return false if matching_network?(interface, net_options)
end
true
@ -67,51 +54,75 @@ module Vagrant
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|
adapter = @env["vm"].vm.network_adapters[network_options[:adapter]]
adapter.enabled = true
adapter.attachment_type = :host_only
adapter.host_only_interface = network_name(network_options)
adapter.mac_address = network_options[:mac].gsub(':', '') if network_options[:mac]
adapter.save
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
# 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)
# 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
raise Errors::NetworkNotFound, :name => net_options[:name] if net_options[:name]
nil
end
# One doesn't exist, create it.
@env[:ui].info I18n.t("vagrant.actions.vm.network.creating")
# 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])
})
ni = interfaces.create
ni.enable_static(network_ip(net_options[:ip], net_options[:netmask]),
net_options[:netmask])
ni.name
@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.network_mask == net_options[:netmask] &&
apply_netmask(interface.ip_address, interface.network_mask) ==
interface[:netmask] == net_options[:netmask] &&
apply_netmask(interface[:ip], interface[:netmask]) ==
apply_netmask(net_options[:ip], net_options[:netmask])
end

View File

@ -47,6 +47,25 @@ module Vagrant
end
end
# Creates a host only network with the given options.
def create_host_only_network(options)
# Create the interface
execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/
name = $1.to_s
# Configure it
execute("hostonlyif", "ipconfig", name,
"--ip", options[:ip],
"--netmask", options[:netmask])
# Return the details
return {
:name => name,
:ip => options[:ip],
:netmask => options[:netmask]
}
end
# This deletes the VM with the given name.
def delete
execute("unregistervm", @uuid, "--delete")
@ -79,6 +98,26 @@ module Vagrant
raw(*command)
end
# Enables network adapters on this virtual machine.
def enable_adapters(adapters)
args = []
adapters.each do |adapter|
args.concat(["--nic#{adapter[:adapter]}", adapter[:type].to_s])
if adapter[:hostonly]
args.concat(["--hostonlyadapter#{adapter[:adapter]}",
adapter[:hostonly]])
end
if adapter[:mac_address]
args.concat(["--macaddress#{adapter[:adapter]}",
adapter[:mac_address]])
end
end
execute("modifyvm", @uuid, *args)
end
# Forwards a set of ports for a VM.
#
# This will not affect any previously set forwarded ports,
@ -173,6 +212,47 @@ module Vagrant
return nil
end
# This reads the list of host only networks.
def read_bridged_interfaces
execute("list", "bridgedifs").split("\n\n").collect do |block|
info = {}
block.split("\n").each do |line|
if line =~ /^Name:\s+(.+?)$/
info[:name] = $1.to_s
elsif line =~ /^IPAddress:\s+(.+?)$/
info[:ip] = $1.to_s
elsif line =~ /^NetworkMask:\s+(.+?)$/
info[:netmask] = $1.to_s
elsif line =~ /^Status:\s+(.+?)$/
info[:status] = $1.to_s
end
end
# Return the info to build up the results
info
end
end
# Reads and returns the available host only interfaces.
def read_host_only_interfaces
execute("list", "hostonlyifs").split("\n\n").collect do |block|
info = {}
block.split("\n").each do |line|
if line =~ /^Name:\s+(.+?)$/
info[:name] = $1.to_s
elsif line =~ /^IPAddress:\s+(.+?)$/
info[:ip] = $1.to_s
elsif line =~ /^NetworkMask:\s+(.+?)$/
info[:netmask] = $1.to_s
end
end
info
end
end
# This reads the folder where VirtualBox places it's VMs.
def read_machine_folder
execute("list", "systemproperties").split("\n").each do |line|

View File

@ -208,13 +208,6 @@ module Vagrant
error_key(:not_found, "vagrant.actions.vm.network")
end
# Note: This is a temporary error for Windows users while host-only
# networking doesn't quite work.
class NetworkNotImplemented < VagrantError
status_code(49)
error_key(:windows_not_implemented, "vagrant.actions.vm.network")
end
class NFSHostRequired < VagrantError
status_code(31)
error_key(:host_required, "vagrant.actions.vm.nfs")

View File

@ -397,11 +397,6 @@ en:
host only network for you. Alternatively, please create the
specified network manually.
preparing: "Preparing host only network..."
windows_not_implemented: |-
Host only networking is currently broken on Windows due to a bug
in jruby-win32ole. When the bug is fixed, a patch release for Vagrant
will be released to remove this error. Until then, please just use
forwarded ports.
nfs:
host_required: |-
A host class is required for NFS shared folders. By default, these