Skip to content

Commit

Permalink
Host only networks now work with VBoxManage
Browse files Browse the repository at this point in the history
  • Loading branch information
mitchellh committed Dec 22, 2011
1 parent 79460f6 commit ecbf7df
Show file tree
Hide file tree
Showing 4 changed files with 137 additions and 58 deletions.
103 changes: 57 additions & 46 deletions lib/vagrant/action/vm/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,6 @@ def initialize(app, env)
@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
Expand Down Expand Up @@ -42,17 +38,8 @@ def call(env)
# 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
Expand All @@ -67,51 +54,75 @@ def enable_network?
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]

# One doesn't exist, create it.
@env[:ui].info I18n.t("vagrant.actions.vm.network.creating")
nil
end

ni = interfaces.create
ni.enable_static(network_ip(net_options[:ip], net_options[:netmask]),
net_options[:netmask])
ni.name
# 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.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

Expand Down
80 changes: 80 additions & 0 deletions lib/vagrant/driver/virtualbox.rb
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,25 @@ def clear_shared_folders
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")
Expand Down Expand Up @@ -79,6 +98,26 @@ def execute_command(command)
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,
Expand Down Expand Up @@ -173,6 +212,47 @@ def read_guest_additions_version
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|
Expand Down
7 changes: 0 additions & 7 deletions lib/vagrant/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -208,13 +208,6 @@ class NetworkNotFound < VagrantError
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")
Expand Down
5 changes: 0 additions & 5 deletions templates/locales/en.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down

0 comments on commit ecbf7df

Please sign in to comment.