From a39df9f150d3d82bd0174f9f1dd056876849951c Mon Sep 17 00:00:00 2001 From: Mitchell Hashimoto Date: Tue, 20 Dec 2011 22:28:47 -0800 Subject: [PATCH] Forwarded ports --- .../action/vm/clear_forwarded_ports.rb | 12 +-- lib/vagrant/action/vm/forward_ports.rb | 44 ++++---- .../action/vm/forward_ports_helpers.rb | 28 ----- lib/vagrant/driver/virtualbox.rb | 100 +++++++++++++++++- 4 files changed, 120 insertions(+), 64 deletions(-) delete mode 100644 lib/vagrant/action/vm/forward_ports_helpers.rb diff --git a/lib/vagrant/action/vm/clear_forwarded_ports.rb b/lib/vagrant/action/vm/clear_forwarded_ports.rb index bc90ded2b..cf25df861 100644 --- a/lib/vagrant/action/vm/clear_forwarded_ports.rb +++ b/lib/vagrant/action/vm/clear_forwarded_ports.rb @@ -7,17 +7,9 @@ module Vagrant end def call(env) - proc = lambda do |vm| - env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting") + env[:ui].info I18n.t("vagrant.actions.vm.clear_forward_ports.deleting") + env[:vm].driver.clear_forwarded_ports - vm.network_adapters.each do |na| - na.nat_driver.forwarded_ports.dup.each do |fp| - fp.destroy - end - end - end - - env["vm.modify"].call(proc) @app.call(env) end end diff --git a/lib/vagrant/action/vm/forward_ports.rb b/lib/vagrant/action/vm/forward_ports.rb index 9cc8f8354..3458c3ec1 100644 --- a/lib/vagrant/action/vm/forward_ports.rb +++ b/lib/vagrant/action/vm/forward_ports.rb @@ -1,11 +1,7 @@ -require File.join(File.dirname(__FILE__), 'forward_ports_helpers') - module Vagrant module Action module VM class ForwardPorts - include ForwardPortsHelpers - def initialize(app,env) @app = app @env = env @@ -35,7 +31,7 @@ module Vagrant # report the collisions detected or will attempt to fix them # automatically if the port is configured to do so. def external_collision_check - existing = used_ports + existing = @env[:vm].driver.read_used_ports @env[:vm].config.vm.forwarded_ports.each do |name, options| if existing.include?(options[:hostport].to_i) handle_collision(name, options, existing) @@ -86,35 +82,41 @@ module Vagrant def call(env) @env = env - proc = lambda do |vm| - env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding") - forward_ports(vm) - end + env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding") + forward_ports(env[:vm]) - env["vm.modify"].call(proc) @app.call(env) end def forward_ports(vm) + ports = [] + @env[:vm].config.vm.forwarded_ports.each do |name, options| - adapter = options[:adapter] + adapter = options[:adapter] + 1 message_attributes = { :name => name, :guest_port => options[:guestport], :host_port => options[:hostport], - :adapter => adapter + 1 + :adapter => adapter } - # Assuming the only reason to establish port forwarding is because the VM is using Virtualbox NAT networking. - # Host-only or Bridged networking don't require port-forwarding and establishing forwarded ports on these - # attachment types has uncertain behaviour. - if vm.network_adapters[adapter].attachment_type == :nat - @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry", message_attributes)) - forward_port(vm, name, options) - else - @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.non_nat", message_attributes)) - end + # Assuming the only reason to establish port forwarding is + # because the VM is using Virtualbox NAT networking. Host-only + # bridged networking don't require port-forwarding and establishing + # forwarded ports on these attachment types has uncertain behaviour. + @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.forwarding_entry", + message_attributes)) + + # Add the options to the ports array to send to the driver later + ports << options.merge(:name => name, :adapter => adapter) + + # TODO: Check for non-nat again... This was removed during the VBoxManage + # transition but should be brought back. + # @env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.non_nat", + # message_attributes)) end + + @env[:vm].driver.forward_ports(ports) end #-------------------------------------------------------------- diff --git a/lib/vagrant/action/vm/forward_ports_helpers.rb b/lib/vagrant/action/vm/forward_ports_helpers.rb deleted file mode 100644 index 4b81f50cf..000000000 --- a/lib/vagrant/action/vm/forward_ports_helpers.rb +++ /dev/null @@ -1,28 +0,0 @@ -module Vagrant - module Action - module VM - # Helper methods for forwarding ports. Requires that the environment - # is set to the `@env` instance variable. - module ForwardPortsHelpers - # Returns an array of used ports. This method is implemented - # differently depending on the VirtualBox version, but the - # behavior is the same. - # - # @return [Array] - def used_ports - result = VirtualBox::VM.all.collect do |vm| - if vm.accessible? && vm.running? && vm.uuid != @env["vm"].uuid - vm.network_adapters.collect do |na| - na.nat_driver.forwarded_ports.collect do |fp| - fp.hostport.to_i - end - end - end - end - - result.flatten.uniq - end - end - end - end -end diff --git a/lib/vagrant/driver/virtualbox.rb b/lib/vagrant/driver/virtualbox.rb index 7313197e2..283bc2757 100644 --- a/lib/vagrant/driver/virtualbox.rb +++ b/lib/vagrant/driver/virtualbox.rb @@ -24,6 +24,59 @@ module Vagrant end end + # This clears the forwarded ports that have been set on the + # virtual machine. + def clear_forwarded_ports + args = [] + read_forwarded_ports(@uuid).each do |nic, name, _, _| + args.concat(["--natpf#{nic}", "delete", name]) + end + + execute("modifyvm", @uuid, *args) if !args.empty? + end + + # This deletes the VM with the given name. + def delete + execute("unregistervm", @uuid, "--delete") + end + + # Forwards a set of ports for a VM. + # + # This will not affect any previously set forwarded ports, + # so be sure to delete those if you need to. + # + # The format of each port hash should be the following: + # + # { + # :name => "foo", + # :host_port => 8500, + # :guest_port => 80, + # :adapter => 1, + # :protocol => "tcp" + # } + # + # Note that "adapter" and "protocol" are optional and will default + # to 1 and "tcp" respectively. + # + # @param [Array] ports An array of ports to set. See documentation + # for more information on the format. + def forward_ports(ports) + args = [] + ports.each do |options| + pf_builder = [options[:name], + options[:protocol] || "tcp", + "", + options[:host_port], + "", + options[:guest_port]] + + args.concat(["--natpf#{options[:adapter] || 1}", + pf_builder.join(",")]) + end + + execute("modifyvm", @uuid, *args) + end + # Imports the VM with the given path to the OVF file. It returns # the UUID as a string. def import(ovf, name) @@ -36,11 +89,6 @@ module Vagrant nil end - # This deletes the VM with the given name. - def delete - execute("unregistervm", @uuid, "--delete") - end - # This reads the guest additions version for a VM. def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version") @@ -61,6 +109,21 @@ module Vagrant nil end + # This will read all the used ports for port forwarding by + # all virtual machines. + def read_used_ports + ports = [] + execute("list", "vms").split("\n").each do |line| + if line =~ /^".+?" \{(.+?)\}$/ + read_forwarded_ports($1.to_s, true).each do |_, _, hostport, _| + ports << hostport + end + end + end + + ports + end + # This sets the MAC address for a network adapter. def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) @@ -68,6 +131,33 @@ module Vagrant protected + # This returns a list of the forwarded ports in the form + # of `[nic, name, hostport, guestport]`. + # + # @return [Array] + def read_forwarded_ports(uuid, active_only=false) + results = [] + current_nic = nil + execute("showvminfo", uuid, "--machinereadable").split("\n").each do |line| + # This is how we find the nic that a FP is attached to, + # since this comes first. + current_nic = $1.to_i if line =~ /^nic(\d+)=".+?"$/ + + # If we care about active VMs only, then we check the state + # to verify the VM is running. + if active_only && line =~ /^VMState="(.+?)"$/ && $1.to_s != "running" + return [] + end + + # Parse out the forwarded port information + if line =~ /^Forwarding.+?="(.+?),.+?,.*?,(.+?),.*?,(.+?)"$/ + results << [current_nic, $1.to_s, $2.to_i, $3.to_i] + end + end + + results + end + # This returns the version of VirtualBox that is running. # # @return [String]