Merge branch 'abstract-networks'
This introduces the new network configuration syntax for Vagrant 1.1 and forward. == The Problem With multiple providers, the concept of networking as it stands in Vagrant 1.0.x becomes really muddy. We have `config.vm.forward_port` and `config.vm.network :hostonly` and `config.vm.network :bridged`. But what if someone writes an AWS provider? What is a bridged network in AWS? It just doesn't make sense. Networking working out of the box with Vagrant is a core part of what makes Vagrant "magic" to new users. It is a core part of what makes Vagrant simple to use. One option to punt networking to provider-specific configuration was considered, but I found the whole idea of networking too core to Vagrant to simply punt. Because of this, a whole new method of networking is introduced. == The Solution The solution is to have a high-level notion of networking for Vagrant configuration. This should cover the most _common_ cases of networking, and every provider should do their best to implement these high-level abstractions, to ensure the "just works" nature of Vagrant. In addition to this high-level networking, low-level networking options should be exposed on the provider configuration. This allows users to do advanced provider-specific networking configuration if they want, but aren't required to. == High-Level Abstractions === Available Types The high-level abstractions built into Vagrant will be the following: * Forwarded ports - A mapping of host port to guest port that one can hit using `localhost`. * Private network - A private network, the machine should ideally be protected from public access. * Public network - A public network, one that is easily accessible by others. I'm not sure if these are the proper abstractions. They can change up until 2.0, but these are what we have so far. Theoretically, here is how mappings would work. Note that this is just an example, and the mappings in practice of such providers may or may not map to this as follows. **VirtualBox** * Forwarded ports - NAT network, forwared ports. * Private network - Hostonly network, static IP assigned. * Public network - Bridged network, IP assigned via DHCP from router. **VMWare** * Forwarded ports - NAT network, forwarded ports. * Private network - Hostonly network, static IP assigned. * Public network - Bridged network, IP assigned via DHCP from router. **AWS** * Forwarded ports - Unimplemented. * Private network - Public DNS in EC2, private IP in VPC. * Public network - Elastic IP in EC2 and VPC. === Syntax Networks are configured at the top-level of a Vagrantfile: ```ruby Vagrant.configure("2") do |config| # ... config.vm.network :forwarded_port, 80, 8080 config.vm.network :private_network, "192.168.1.12" config.vm.network :public_network end ``` Providers should do their best to honor these configurations. === Advanced Options While providers should do their best to satisfy the requirements for the high-level abstractions, it is expected that provider-specific configuration may be possible per network, even for the high-level configurations. For this, provider-prefixed configuration options should be done: ```ruby config.vm.network :forwarded_port, 80, 8000, :vmware__device => "vmnet8" config.vm.network :public_network, :aws__elastic_ip => "1.2.3.4", :vmware__device => "en0" ``` If at all possible, providers should **not** require advanced options for these to function. == Low-level Configuration While the high-level configuration should satisfy the common case and make Vagrant work out of the box for most providers, one of the large benefits of many providers is the ability to do certain networking tricks. For example, KVM, Hyper-V, vSphere, etc. can create and be a part of true VLANs, which may be required for certain upstream networking rules/ACLs. For things like this, the network configuration should go directly into the provider configuration in some way. Examples: ```ruby config.vm.provider :virtualbox do |vb| vb.network_adapter 2, :hostonly vb.network_adapter 3, :nat end config.vm.provider :aws do |aws| aws.routing_table = "route-123456" end ``` It is up to the provider implementation to define the configuration syntax as well as the implementation details of such an option. Other providers are unable to see provider configurations other than their own so it is truly private to the provider.
This commit is contained in:
commit
2d8a048946
|
@ -11,12 +11,16 @@ Vagrant.configure("2") do |config|
|
|||
config.ssh.forward_x11 = false
|
||||
config.ssh.shell = "bash -l"
|
||||
|
||||
config.vm.auto_port_range = (2200..2250)
|
||||
config.vm.usable_port_range = (2200..2250)
|
||||
config.vm.box_url = nil
|
||||
config.vm.base_mac = nil
|
||||
config.vm.forward_port 22, 2222, :name => "ssh", :auto => true
|
||||
config.vm.guest = :linux
|
||||
|
||||
# Share SSH locally by default
|
||||
config.vm.network :forwarded_port, 22, 2222,
|
||||
:id => "ssh",
|
||||
:auto_correct => true
|
||||
|
||||
# Share the root folder. This can then be overridden by
|
||||
# other Vagrantfiles, if they wish.
|
||||
config.vm.share_folder("v-root", "/vagrant", ".")
|
||||
|
|
|
@ -437,6 +437,10 @@ module Vagrant
|
|||
error_key(:virtualbox_invalid_version)
|
||||
end
|
||||
|
||||
class VirtualBoxNoRoomForHighLevelNetwork < VagrantError
|
||||
error_key(:virtualbox_no_room_for_high_level_network)
|
||||
end
|
||||
|
||||
class VirtualBoxNotDetected < VagrantError
|
||||
status_code(8)
|
||||
error_key(:virtualbox_not_detected)
|
||||
|
|
|
@ -0,0 +1,45 @@
|
|||
module Vagrant
|
||||
module Util
|
||||
# This allows for hash options to be overridden by a scope key
|
||||
# prefix. An example speaks best here. Imagine the following hash:
|
||||
#
|
||||
# original = {
|
||||
# :id => "foo",
|
||||
# :mitchellh__id => "bar",
|
||||
# :mitchellh__other => "foo"
|
||||
# }
|
||||
#
|
||||
# scoped = scoped_hash_override(original, "mitchellh")
|
||||
#
|
||||
# scoped == {
|
||||
# :id => "bar",
|
||||
# :other => "foo"
|
||||
# }
|
||||
#
|
||||
module ScopedHashOverride
|
||||
def scoped_hash_override(original, scope)
|
||||
# Convert the scope to a string in case a symbol was given since
|
||||
# we use string comparisons for everything.
|
||||
scope = scope.to_s
|
||||
|
||||
# Shallow copy the hash for the result
|
||||
result = original.dup
|
||||
|
||||
original.each do |key, value|
|
||||
parts = key.to_s.split("__", 2)
|
||||
|
||||
# If we don't have the proper parts, then bail
|
||||
next if parts.length != 2
|
||||
|
||||
# If this is our scope, then override
|
||||
if parts[0] == scope
|
||||
result[parts[1].to_sym] = value
|
||||
result.delete(key)
|
||||
end
|
||||
end
|
||||
|
||||
result
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -1,5 +1,3 @@
|
|||
require "pathname"
|
||||
|
||||
module VagrantPlugins
|
||||
module Kernel_V1
|
||||
# This is the Version 1.0.x Vagrant VM configuration. This is
|
||||
|
@ -17,13 +15,11 @@ module VagrantPlugins
|
|||
attr_accessor :guest
|
||||
attr_accessor :host_name
|
||||
attr_reader :customizations
|
||||
attr_reader :forwarded_ports
|
||||
attr_reader :networks
|
||||
attr_reader :provisioners
|
||||
attr_reader :shared_folders
|
||||
|
||||
def initialize
|
||||
@forwarded_ports = []
|
||||
@shared_folders = {}
|
||||
@networks = []
|
||||
@provisioners = []
|
||||
|
@ -32,14 +28,13 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
def forward_port(guestport, hostport, options=nil)
|
||||
@forwarded_ports << {
|
||||
:name => "#{guestport.to_s(32)}-#{hostport.to_s(32)}",
|
||||
:guestport => guestport,
|
||||
:hostport => hostport,
|
||||
:protocol => :tcp,
|
||||
:adapter => 1,
|
||||
:auto => false
|
||||
}.merge(options || {})
|
||||
# Build up the network options for V2
|
||||
network_options = {}
|
||||
network_options[:virtualbox__adapter] = options[:adapter]
|
||||
network_options[:virtualbox__protocol] = options[:protocol]
|
||||
|
||||
# Just append the forwarded port to the networks
|
||||
@networks << [:forwarded_port, guestport, hostport, network_options]
|
||||
end
|
||||
|
||||
def share_folder(name, guestpath, hostpath, opts=nil)
|
||||
|
@ -83,12 +78,12 @@ module VagrantPlugins
|
|||
|
||||
# Upgrade to a V2 configuration
|
||||
def upgrade(new)
|
||||
new.vm.auto_port_range = self.auto_port_range if self.auto_port_range
|
||||
new.vm.base_mac = self.base_mac if self.base_mac
|
||||
new.vm.box = self.box if self.box
|
||||
new.vm.box_url = self.box_url if self.box_url
|
||||
new.vm.guest = self.guest if self.guest
|
||||
new.vm.host_name = self.host_name if self.host_name
|
||||
new.vm.base_mac = self.base_mac if self.base_mac
|
||||
new.vm.box = self.box if self.box
|
||||
new.vm.box_url = self.box_url if self.box_url
|
||||
new.vm.guest = self.guest if self.guest
|
||||
new.vm.host_name = self.host_name if self.host_name
|
||||
new.vm.usable_port_range = self.auto_port_range if self.auto_port_range
|
||||
|
||||
if self.boot_mode
|
||||
# Enable the GUI if the boot mode is GUI.
|
||||
|
@ -101,15 +96,6 @@ module VagrantPlugins
|
|||
new.vm.providers[:virtualbox].config.customize(customization)
|
||||
end
|
||||
|
||||
# Take all the defined forwarded ports and re-define them
|
||||
self.forwarded_ports.each do |fp|
|
||||
options = fp.dup
|
||||
guestport = options.delete(:guestport)
|
||||
hostport = options.delete(:hostport)
|
||||
|
||||
new.vm.forward_port(guestport, hostport, options)
|
||||
end
|
||||
|
||||
# Re-define all networks.
|
||||
self.networks.each do |type, args|
|
||||
new.vm.network(type, *args)
|
||||
|
|
|
@ -11,12 +11,12 @@ module VagrantPlugins
|
|||
class VMConfig < Vagrant.plugin("2", :config)
|
||||
DEFAULT_VM_NAME = :default
|
||||
|
||||
attr_accessor :auto_port_range
|
||||
attr_accessor :base_mac
|
||||
attr_accessor :box
|
||||
attr_accessor :box_url
|
||||
attr_accessor :guest
|
||||
attr_accessor :host_name
|
||||
attr_accessor :usable_port_range
|
||||
attr_reader :forwarded_ports
|
||||
attr_reader :shared_folders
|
||||
attr_reader :networks
|
||||
|
@ -45,17 +45,6 @@ module VagrantPlugins
|
|||
result
|
||||
end
|
||||
|
||||
def forward_port(guestport, hostport, options=nil)
|
||||
@forwarded_ports << {
|
||||
:name => "#{guestport.to_s(32)}-#{hostport.to_s(32)}",
|
||||
:guestport => guestport,
|
||||
:hostport => hostport,
|
||||
:protocol => :tcp,
|
||||
:adapter => 1,
|
||||
:auto => false
|
||||
}.merge(options || {})
|
||||
end
|
||||
|
||||
def share_folder(name, guestpath, hostpath, opts=nil)
|
||||
@shared_folders[name] = {
|
||||
:guestpath => guestpath.to_s,
|
||||
|
@ -69,6 +58,21 @@ module VagrantPlugins
|
|||
}.merge(opts || {})
|
||||
end
|
||||
|
||||
# Define a way to access the machine via a network. This exposes a
|
||||
# high-level abstraction for networking that may not directly map
|
||||
# 1-to-1 for every provider. For example, AWS has no equivalent to
|
||||
# "port forwarding." But most providers will attempt to implement this
|
||||
# in a way that behaves similarly.
|
||||
#
|
||||
# `type` can be one of:
|
||||
#
|
||||
# * `:forwarded_port` - A port that is accessible via localhost
|
||||
# that forwards into the machine.
|
||||
# * `:private_network` - The machine gets an IP that is not directly
|
||||
# publicly accessible, but ideally accessible from this machine.
|
||||
# * `:public_network` - The machine gets an IP on a shared network.
|
||||
#
|
||||
# @param [Symbol] type Type of network
|
||||
def network(type, *args)
|
||||
@networks << [type, args]
|
||||
end
|
||||
|
@ -139,39 +143,6 @@ module VagrantPlugins
|
|||
end
|
||||
end
|
||||
|
||||
# Validate some basic networking
|
||||
#
|
||||
# TODO: One day we need to abstract this out, since in the future
|
||||
# providers other than VirtualBox will not be able to satisfy
|
||||
# all types of networks.
|
||||
networks.each do |type, args|
|
||||
if type == :hostonly && args[0] == :dhcp
|
||||
# Valid. There is no real way this can be invalid at the moment.
|
||||
elsif type == :hostonly
|
||||
# Validate the host-only network
|
||||
ip = args[0]
|
||||
|
||||
if !ip
|
||||
errors.add(I18n.t("vagrant.config.vm.network_ip_required"))
|
||||
else
|
||||
ip_parts = ip.split(".")
|
||||
|
||||
if ip_parts.length != 4
|
||||
errors.add(I18n.t("vagrant.config.vm.network_ip_invalid",
|
||||
:ip => ip))
|
||||
elsif ip_parts.last == "1"
|
||||
errors.add(I18n.t("vagrant.config.vm.network_ip_ends_one",
|
||||
:ip => ip))
|
||||
end
|
||||
end
|
||||
elsif type == :bridged
|
||||
else
|
||||
# Invalid network type
|
||||
errors.add(I18n.t("vagrant.config.vm.network_invalid",
|
||||
:type => type.to_s))
|
||||
end
|
||||
end
|
||||
|
||||
# Each provisioner can validate itself
|
||||
provisioners.each do |prov|
|
||||
prov.validate(env, errors)
|
||||
|
|
|
@ -58,7 +58,7 @@ module VagrantPlugins
|
|||
b.use CleanMachineFolder
|
||||
b.use ClearForwardedPorts
|
||||
b.use EnvSet, :port_collision_handler => :correct
|
||||
b.use ForwardPorts
|
||||
b.use CheckPortCollisions
|
||||
b.use Provision
|
||||
b.use PruneNFSExports
|
||||
b.use NFS
|
||||
|
@ -66,6 +66,7 @@ module VagrantPlugins
|
|||
b.use ShareFolders
|
||||
b.use ClearNetworkInterfaces
|
||||
b.use Network
|
||||
b.use ForwardPorts
|
||||
b.use HostName
|
||||
b.use SaneDefaults
|
||||
b.use Customize
|
||||
|
@ -193,6 +194,7 @@ module VagrantPlugins
|
|||
b.use Call, Created do |env, b2|
|
||||
if env[:result]
|
||||
b2.use CheckAccessible
|
||||
b2.use EnvSet, :port_collision_handler => :error
|
||||
b2.use CheckPortCollisions
|
||||
b2.use Resume
|
||||
else
|
||||
|
|
|
@ -1,9 +1,12 @@
|
|||
require "set"
|
||||
|
||||
require "vagrant/util/is_port_open"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
class CheckPortCollisions
|
||||
include Util::CompileForwardedPorts
|
||||
include Vagrant::Util::IsPortOpen
|
||||
|
||||
def initialize(app, env)
|
||||
|
@ -14,6 +17,17 @@ module VagrantPlugins
|
|||
# For the handlers...
|
||||
@env = env
|
||||
|
||||
# If we don't have forwarded ports set on the environment, then
|
||||
# we compile them.
|
||||
env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config)
|
||||
|
||||
existing = env[:machine].provider.driver.read_used_ports
|
||||
|
||||
# Calculate the auto-correct port range
|
||||
@usable_ports = Set.new(env[:machine].config.vm.usable_port_range)
|
||||
@usable_ports.subtract(env[:forwarded_ports].collect { |fp| fp.host_port })
|
||||
@usable_ports.subtract(existing)
|
||||
|
||||
# Figure out how we handle port collisions. By default we error.
|
||||
handler = env[:port_collision_handler] || :error
|
||||
|
||||
|
@ -24,16 +38,15 @@ module VagrantPlugins
|
|||
current[name] = hostport.to_i
|
||||
end
|
||||
|
||||
existing = env[:machine].provider.driver.read_used_ports
|
||||
env[:machine].config.vm.forwarded_ports.each do |options|
|
||||
env[:forwarded_ports].each do |fp|
|
||||
# Use the proper port, whether that be the configured port or the
|
||||
# port that is currently on use of the VM.
|
||||
hostport = options[:hostport].to_i
|
||||
hostport = current[options[:name]] if current.has_key?(options[:name])
|
||||
host_port = fp.host_port
|
||||
host_port = current[fp.id] if current.has_key?(fp.id)
|
||||
|
||||
if existing.include?(hostport) || is_port_open?("127.0.0.1", hostport)
|
||||
if existing.include?(host_port) || is_port_open?("127.0.0.1", host_port)
|
||||
# We have a collision! Handle it
|
||||
send("handle_#{handler}".to_sym, options, existing)
|
||||
send("handle_#{handler}".to_sym, fp, existing)
|
||||
end
|
||||
end
|
||||
|
||||
|
@ -41,47 +54,41 @@ module VagrantPlugins
|
|||
end
|
||||
|
||||
# Handles a port collision by raising an exception.
|
||||
def handle_error(options, existing_ports)
|
||||
def handle_error(fp, existing_ports)
|
||||
raise Vagrant::Errors::ForwardPortCollisionResume
|
||||
end
|
||||
|
||||
# Handles a port collision by attempting to fix it.
|
||||
def handle_correct(options, existing_ports)
|
||||
def handle_correct(fp, existing_ports)
|
||||
# We need to keep this for messaging purposes
|
||||
original_hostport = options[:hostport]
|
||||
original_hostport = fp.host_port
|
||||
|
||||
if !options[:auto]
|
||||
if !fp.auto_correct
|
||||
# Auto fixing is disabled for this port forward, so we
|
||||
# must throw an error so the user can fix it.
|
||||
raise Vagrant::Errors::ForwardPortCollision,
|
||||
:host_port => options[:hostport].to_s,
|
||||
:guest_port => options[:guestport].to_s
|
||||
:host_port => fp.host_port.to_s,
|
||||
:guest_port => fp.guest_port.to_s
|
||||
end
|
||||
|
||||
# Get the auto port range and get rid of the used ports and
|
||||
# ports which are being used in other forwards so we're just
|
||||
# left with available ports.
|
||||
range = @env[:machine].config.vm.auto_port_range.to_a
|
||||
range -= @env[:machine].config.vm.forwarded_ports.collect { |opts| opts[:hostport].to_i }
|
||||
range -= existing_ports
|
||||
|
||||
if range.empty?
|
||||
if @usable_ports.empty?
|
||||
raise Vagrant::Errors::ForwardPortAutolistEmpty,
|
||||
:vm_name => @env[:machine].name,
|
||||
:host_port => options[:hostport].to_s,
|
||||
:guest_port => options[:guestport].to_s
|
||||
:host_port => fp.host_port.to_s,
|
||||
:guest_port => fp.guest_port.to_s
|
||||
end
|
||||
|
||||
# Set the port up to be the first one and add that port to
|
||||
# the used list.
|
||||
options[:hostport] = range.shift
|
||||
existing_ports << options[:hostport]
|
||||
# Get the first usable port and set it up
|
||||
new_port = @usable_ports.to_a.sort[0]
|
||||
@usable_ports.delete(new_port)
|
||||
fp.correct_host_port(new_port)
|
||||
existing_ports << new_port
|
||||
|
||||
# Notify the user
|
||||
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.fixed_collision",
|
||||
:host_port => original_hostport.to_s,
|
||||
:guest_port => options[:guestport].to_s,
|
||||
:new_port => options[:hostport]))
|
||||
:guest_port => fp.guest_port.to_s,
|
||||
:new_port => fp.host_port.to_s))
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -2,6 +2,8 @@ module VagrantPlugins
|
|||
module ProviderVirtualBox
|
||||
module Action
|
||||
class ForwardPorts
|
||||
include Util::CompileForwardedPorts
|
||||
|
||||
def initialize(app, env)
|
||||
@app = app
|
||||
end
|
||||
|
@ -13,54 +15,32 @@ module VagrantPlugins
|
|||
@env = env
|
||||
|
||||
# Get the ports we're forwarding
|
||||
ports = forward_port_definitions
|
||||
env[:forwarded_ports] ||= compile_forwarded_ports(env[:machine].config)
|
||||
|
||||
# Warn if we're port forwarding to any privileged ports...
|
||||
threshold_check(ports)
|
||||
env[:forwarded_ports].each do |fp|
|
||||
if fp.host_port <= 1024
|
||||
env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.forward_ports.forwarding")
|
||||
forward_ports(ports)
|
||||
forward_ports
|
||||
|
||||
@app.call(env)
|
||||
end
|
||||
|
||||
# This returns an array of forwarded ports with overrides properly
|
||||
# squashed.
|
||||
def forward_port_definitions
|
||||
# Get all the port mappings in the order they're defined and
|
||||
# organize them by their guestport, taking the "last one wins"
|
||||
# approach.
|
||||
guest_port_mapping = {}
|
||||
@env[:machine].config.vm.forwarded_ports.each do |options|
|
||||
key = options[:protocol].to_s + options[:guestport].to_s
|
||||
guest_port_mapping[key] = options
|
||||
end
|
||||
|
||||
# Return the values, since the order doesn't really matter
|
||||
guest_port_mapping.values
|
||||
end
|
||||
|
||||
# This method checks for any forwarded ports on the host below
|
||||
# 1024, which causes the forwarded ports to fail.
|
||||
def threshold_check(ports)
|
||||
ports.each do |options|
|
||||
if options[:hostport] <= 1024
|
||||
@env[:ui].warn I18n.t("vagrant.actions.vm.forward_ports.privileged_ports")
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def forward_ports(mappings)
|
||||
def forward_ports
|
||||
ports = []
|
||||
|
||||
interfaces = @env[:machine].provider.driver.read_network_interfaces
|
||||
|
||||
mappings.each do |options|
|
||||
@env[:forwarded_ports].each do |fp|
|
||||
message_attributes = {
|
||||
:guest_port => options[:guestport],
|
||||
:host_port => options[:hostport],
|
||||
:adapter => options[:adapter]
|
||||
:adapter => fp.adapter,
|
||||
:guest_port => fp.guest_port,
|
||||
:host_port => fp.host_port
|
||||
}
|
||||
|
||||
# Assuming the only reason to establish port forwarding is
|
||||
|
@ -72,14 +52,20 @@ module VagrantPlugins
|
|||
|
||||
# Port forwarding requires the network interface to be a NAT interface,
|
||||
# so verify that that is the case.
|
||||
if interfaces[options[:adapter]][:type] != :nat
|
||||
if interfaces[fp.adapter][:type] != :nat
|
||||
@env[:ui].info(I18n.t("vagrant.actions.vm.forward_ports.non_nat",
|
||||
message_attributes))
|
||||
next
|
||||
end
|
||||
|
||||
# Add the options to the ports array to send to the driver later
|
||||
ports << options.merge(:name => options[:name], :adapter => options[:adapter])
|
||||
ports << {
|
||||
:adapter => fp.adapter,
|
||||
:guestport => fp.guest_port,
|
||||
:hostport => fp.host_port,
|
||||
:name => fp.id,
|
||||
:protocol => fp.protocol
|
||||
}
|
||||
end
|
||||
|
||||
if !ports.empty?
|
||||
|
|
|
@ -3,320 +3,134 @@ require "set"
|
|||
require "log4r"
|
||||
|
||||
require "vagrant/util/network_ip"
|
||||
require "vagrant/util/scoped_hash_override"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Action
|
||||
# This middleware class sets up all networking for the VirtualBox
|
||||
# instance. This includes host only networks, bridged networking,
|
||||
# forwarded ports, etc.
|
||||
#
|
||||
# This handles all the `config.vm.network` configurations.
|
||||
class Network
|
||||
# Utilities to deal with network addresses
|
||||
include Vagrant::Util::NetworkIP
|
||||
include Vagrant::Util::ScopedHashOverride
|
||||
|
||||
def initialize(app, env)
|
||||
@logger = Log4r::Logger.new("vagrant::action::vm::network")
|
||||
|
||||
@app = app
|
||||
@logger = Log4r::Logger.new("vagrant::plugins::virtualbox::network")
|
||||
@app = app
|
||||
end
|
||||
|
||||
def call(env)
|
||||
# TODO: Validate network configuration prior to anything below
|
||||
@env = env
|
||||
|
||||
# First we have to get the array of adapters that we need
|
||||
# to create on the virtual machine itself, as well as the
|
||||
# driver-agnostic network configurations for each.
|
||||
@logger.debug("Determining adapters and networks...")
|
||||
# Get the list of network adapters from the configuration
|
||||
network_adapters_config = env[:machine].provider_config.network_adapters.dup
|
||||
|
||||
# Assign the adapter slot for each high-level network
|
||||
available_slots = Set.new(1..8)
|
||||
network_adapters_config.each do |slot, _data|
|
||||
available_slots.delete(slot)
|
||||
end
|
||||
|
||||
@logger.debug("Available slots for high-level adapters: #{available_slots.inspect}")
|
||||
@logger.info("Determinging network adapters required for high-level configuration...")
|
||||
available_slots = available_slots.to_a.sort
|
||||
env[:machine].config.vm.networks.each do |type, args|
|
||||
# We only handle private and public networks
|
||||
next if type != :private_network && type != :public_network
|
||||
|
||||
options = nil
|
||||
options = args.last if args.last.is_a?(Hash)
|
||||
options ||= {}
|
||||
options = scoped_hash_override(options, :virtualbox)
|
||||
|
||||
# Figure out the slot that this adapter will go into
|
||||
slot = options[:adapter]
|
||||
if !slot
|
||||
if available_slots.empty?
|
||||
raise Vagrant::Errors::VirtualBoxNoRoomForHighLevelNetwork
|
||||
end
|
||||
|
||||
slot = available_slots.shift
|
||||
end
|
||||
|
||||
# Configure it
|
||||
data = nil
|
||||
if type == :private_network
|
||||
# private_network = hostonly
|
||||
|
||||
config_args = [args[0], options]
|
||||
data = [:hostonly, config_args]
|
||||
elsif type == :public_network
|
||||
# public_network = bridged
|
||||
|
||||
config_args = [options]
|
||||
data = [:bridged, config_args]
|
||||
end
|
||||
|
||||
# Store it!
|
||||
@logger.info(" -- Slot #{slot}: #{data[0]}")
|
||||
network_adapters_config[slot] = data
|
||||
end
|
||||
|
||||
@logger.info("Determining adapters and compiling network configuration...")
|
||||
adapters = []
|
||||
networks = []
|
||||
env[:machine].config.vm.networks.each do |type, args|
|
||||
# Get the normalized configuration we'll use around
|
||||
config = send("#{type}_config", args)
|
||||
network_adapters_config.each do |slot, data|
|
||||
type = data[0]
|
||||
args = data[1]
|
||||
|
||||
# Get the virtualbox adapter configuration
|
||||
@logger.info("Network slot #{slot}. Type: #{type}.")
|
||||
|
||||
# Get the normalized configuration for this type
|
||||
config = send("#{type}_config", args)
|
||||
config[:adapter] = slot
|
||||
@logger.debug("Normalized configuration: #{config.inspect}")
|
||||
|
||||
# Get the VirtualBox adapter configuration
|
||||
adapter = send("#{type}_adapter", config)
|
||||
adapters << adapter
|
||||
@logger.debug("Adapter configuration: #{adapter.inspect}")
|
||||
|
||||
# Get the network configuration
|
||||
network = send("#{type}_network_config", config)
|
||||
network[:_auto_config] = true if config[:auto_config]
|
||||
network[:auto_config] = config[:auto_config]
|
||||
networks << network
|
||||
end
|
||||
|
||||
if !adapters.empty?
|
||||
# Automatically assign an adapter number to any adapters
|
||||
# that aren't explicitly set.
|
||||
@logger.debug("Assigning adapter locations...")
|
||||
assign_adapter_locations(adapters)
|
||||
|
||||
# Verify that our adapters are good just prior to enabling them.
|
||||
verify_adapters(adapters)
|
||||
|
||||
# Create all the network interfaces
|
||||
# Enable the adapters
|
||||
@logger.info("Enabling adapters...")
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.network.preparing")
|
||||
env[:machine].provider.driver.enable_adapters(adapters)
|
||||
end
|
||||
|
||||
# Continue the middleware chain. We're done with our VM
|
||||
# setup until after it is booted.
|
||||
# Continue the middleware chain.
|
||||
@app.call(env)
|
||||
|
||||
# If we have networks to configure, then we configure it now, since
|
||||
# that requires the machine to be up and running.
|
||||
if !adapters.empty? && !networks.empty?
|
||||
# Determine the interface numbers for the guest.
|
||||
assign_interface_numbers(networks, adapters)
|
||||
|
||||
# Configure all the network interfaces on the guest. We only
|
||||
# want to configure the networks that have `auto_config` setup.
|
||||
networks_to_configure = networks.select { |n| n[:_auto_config] }
|
||||
# Only configure the networks the user requested us to configure
|
||||
networks_to_configure = networks.select { |n| n[:auto_config] }
|
||||
env[:ui].info I18n.t("vagrant.actions.vm.network.configuring")
|
||||
env[:machine].guest.configure_networks(networks_to_configure)
|
||||
end
|
||||
end
|
||||
|
||||
# This method assigns the adapter to use for the adapter.
|
||||
# e.g. it says that the first adapter is actually on the
|
||||
# virtual machine's 2nd adapter location.
|
||||
#
|
||||
# It determines the adapter numbers by simply finding the
|
||||
# "next available" in each case.
|
||||
#
|
||||
# The adapters are modified in place by adding an ":adapter"
|
||||
# field to each.
|
||||
def assign_adapter_locations(adapters)
|
||||
available = Set.new(1..8)
|
||||
|
||||
# Determine which NICs are actually available.
|
||||
interfaces = @env[:machine].provider.driver.read_network_interfaces
|
||||
interfaces.each do |number, nic|
|
||||
# Remove the number from the available NICs if the
|
||||
# NIC is in use.
|
||||
available.delete(number) if nic[:type] != :none
|
||||
end
|
||||
|
||||
# Based on the available set, assign in order to
|
||||
# the adapters.
|
||||
available = available.to_a.sort
|
||||
@logger.debug("Available NICs: #{available.inspect}")
|
||||
adapters.each do |adapter|
|
||||
# Ignore the adapters that already have been assigned
|
||||
if !adapter[:adapter]
|
||||
# If we have no available adapters, then that is an exceptional
|
||||
# event.
|
||||
raise Vagrant::Errors::NetworkNoAdapters if available.empty?
|
||||
|
||||
# Otherwise, assign as the adapter the next available item
|
||||
adapter[:adapter] = available.shift
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Verifies that the adapter configurations look good. This will
|
||||
# raise an exception in the case that any errors occur.
|
||||
def verify_adapters(adapters)
|
||||
# Verify that there are no collisions in the adapters being used.
|
||||
used = Set.new
|
||||
adapters.each do |adapter|
|
||||
raise Vagrant::Errors::NetworkAdapterCollision if used.include?(adapter[:adapter])
|
||||
used.add(adapter[:adapter])
|
||||
end
|
||||
end
|
||||
|
||||
# Assigns the actual interface number of a network based on the
|
||||
# enabled NICs on the virtual machine.
|
||||
#
|
||||
# This interface number is used by the guest to configure the
|
||||
# NIC on the guest VM.
|
||||
#
|
||||
# The networks are modified in place by adding an ":interface"
|
||||
# field to each.
|
||||
def assign_interface_numbers(networks, adapters)
|
||||
current = 0
|
||||
adapter_to_interface = {}
|
||||
|
||||
# Make a first pass to assign interface numbers by adapter location
|
||||
vm_adapters = @env[:machine].provider.driver.read_network_interfaces
|
||||
vm_adapters.sort.each do |number, adapter|
|
||||
if adapter[:type] != :none
|
||||
# Not used, so assign the interface number and increment
|
||||
adapter_to_interface[number] = current
|
||||
current += 1
|
||||
end
|
||||
end
|
||||
|
||||
# Make a pass through the adapters to assign the :interface
|
||||
# key to each network configuration.
|
||||
adapters.each_index do |i|
|
||||
adapter = adapters[i]
|
||||
network = networks[i]
|
||||
|
||||
# Figure out the interface number by simple lookup
|
||||
network[:interface] = adapter_to_interface[adapter[:adapter]]
|
||||
end
|
||||
end
|
||||
|
||||
def hostonly_config(args)
|
||||
ip = args[0]
|
||||
options = args[1] || {}
|
||||
|
||||
# Determine if we're dealing with a static IP or a DHCP-served IP.
|
||||
type = ip == :dhcp ? :dhcp : :static
|
||||
|
||||
# Default IP is in the 20-bit private network block for DHCP based networks
|
||||
ip = "172.28.128.1" if type == :dhcp
|
||||
|
||||
options = {
|
||||
:type => type,
|
||||
:ip => ip,
|
||||
:netmask => "255.255.255.0",
|
||||
:adapter => nil,
|
||||
:mac => nil,
|
||||
:name => nil,
|
||||
:auto_config => true
|
||||
}.merge(options)
|
||||
|
||||
# Verify that this hostonly network wouldn't conflict with any
|
||||
# bridged interfaces
|
||||
verify_no_bridge_collision(options)
|
||||
|
||||
# Get the network address and IP parts which are used for many
|
||||
# default calculations
|
||||
netaddr = network_address(options[:ip], options[:netmask])
|
||||
ip_parts = netaddr.split(".").map { |i| i.to_i }
|
||||
|
||||
# Calculate the adapter IP, which we assume is the IP ".1" at the
|
||||
# end usually.
|
||||
adapter_ip = ip_parts.dup
|
||||
adapter_ip[3] += 1
|
||||
options[:adapter_ip] ||= adapter_ip.join(".")
|
||||
|
||||
if type == :dhcp
|
||||
# Calculate the DHCP server IP, which is the network address
|
||||
# with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2"
|
||||
dhcp_ip = ip_parts.dup
|
||||
dhcp_ip[3] += 2
|
||||
options[:dhcp_ip] ||= dhcp_ip.join(".")
|
||||
|
||||
# Calculate the lower and upper bound for the DHCP server
|
||||
dhcp_lower = ip_parts.dup
|
||||
dhcp_lower[3] += 3
|
||||
options[:dhcp_lower] ||= dhcp_lower.join(".")
|
||||
|
||||
dhcp_upper = ip_parts.dup
|
||||
dhcp_upper[3] = 254
|
||||
options[:dhcp_upper] ||= dhcp_upper.join(".")
|
||||
end
|
||||
|
||||
# Return the hostonly network configuration
|
||||
return options
|
||||
end
|
||||
|
||||
def hostonly_adapter(config)
|
||||
@logger.debug("Searching for matching network: #{config[:ip]}")
|
||||
interface = find_matching_hostonly_network(config)
|
||||
|
||||
if !interface
|
||||
@logger.debug("Network not found. Creating if we can.")
|
||||
|
||||
# It is an error case if a specific name was given but the network
|
||||
# doesn't exist.
|
||||
if config[:name]
|
||||
raise Vagrant::Errors::NetworkNotFound, :name => config[: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!
|
||||
interface = create_hostonly_network(config)
|
||||
@logger.debug("Created network: #{interface[:name]}")
|
||||
end
|
||||
|
||||
if config[:type] == :dhcp
|
||||
# Check that if there is a DHCP server attached on our interface,
|
||||
# then it is identical. Otherwise, we can't set it.
|
||||
if interface[:dhcp]
|
||||
valid = interface[:dhcp][:ip] == config[:dhcp_ip] &&
|
||||
interface[:dhcp][:lower] == config[:dhcp_lower] &&
|
||||
interface[:dhcp][:upper] == config[:dhcp_upper]
|
||||
|
||||
raise Vagrant::Errors::NetworkDHCPAlreadyAttached if !valid
|
||||
|
||||
@logger.debug("DHCP server already properly configured")
|
||||
else
|
||||
# Configure the DHCP server for the network.
|
||||
@logger.debug("Creating a DHCP server...")
|
||||
@env[:machine].provider.driver.create_dhcp_server(interface[:name], config)
|
||||
end
|
||||
end
|
||||
|
||||
return {
|
||||
:adapter => config[:adapter],
|
||||
:type => :hostonly,
|
||||
:hostonly => interface[:name],
|
||||
:mac_address => config[:mac],
|
||||
:nic_type => config[:nic_type]
|
||||
}
|
||||
end
|
||||
|
||||
def hostonly_network_config(config)
|
||||
return {
|
||||
:type => config[:type],
|
||||
:adapter_ip => config[:adapter_ip],
|
||||
:ip => config[:ip],
|
||||
:netmask => config[:netmask]
|
||||
}
|
||||
end
|
||||
|
||||
# Creates a new hostonly network that matches the network requested
|
||||
# by the given host-only network configuration.
|
||||
def create_hostonly_network(config)
|
||||
# Create the options that are going to be used to create our
|
||||
# new network.
|
||||
options = config.dup
|
||||
options[:ip] = options[:adapter_ip]
|
||||
|
||||
@env[:machine].provider.driver.create_host_only_network(options)
|
||||
end
|
||||
|
||||
# Finds a host only network that matches our configuration on VirtualBox.
|
||||
# This will return nil if a matching network does not exist.
|
||||
def find_matching_hostonly_network(config)
|
||||
this_netaddr = network_address(config[:ip], config[:netmask])
|
||||
|
||||
@env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
|
||||
if config[:name] && config[:name] == interface[:name]
|
||||
return interface
|
||||
elsif this_netaddr == network_address(interface[:ip], interface[:netmask])
|
||||
return interface
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
# Verifies that a host-only network subnet would not collide with
|
||||
# a bridged networking interface.
|
||||
#
|
||||
# If the subnets overlap in any way then the host only network
|
||||
# will not work because the routing tables will force the traffic
|
||||
# onto the real interface rather than the virtualbox interface.
|
||||
def verify_no_bridge_collision(options)
|
||||
this_netaddr = network_address(options[:ip], options[:netmask])
|
||||
|
||||
@env[:machine].provider.driver.read_bridged_interfaces.each do |interface|
|
||||
that_netaddr = network_address(interface[:ip], interface[:netmask])
|
||||
raise Vagrant::Errors::NetworkCollision if this_netaddr == that_netaddr && interface[:status] != "Down"
|
||||
end
|
||||
end
|
||||
|
||||
def bridged_config(args)
|
||||
options = args[0] || {}
|
||||
options = {} if !options.is_a?(Hash)
|
||||
|
||||
return {
|
||||
:adapter => nil,
|
||||
:mac => nil,
|
||||
:bridge => nil,
|
||||
:auto_config => true,
|
||||
:auto_config => true,
|
||||
:bridge => nil,
|
||||
:mac => nil,
|
||||
:nic_type => nil,
|
||||
:use_dhcp_assigned_default_route => false
|
||||
}.merge(options)
|
||||
}.merge(args[0] || {})
|
||||
end
|
||||
|
||||
def bridged_adapter(config)
|
||||
|
@ -398,6 +212,162 @@ module VagrantPlugins
|
|||
:use_dhcp_assigned_default_route => config[:use_dhcp_assigned_default_route]
|
||||
}
|
||||
end
|
||||
|
||||
def hostonly_config(args)
|
||||
ip = args[0]
|
||||
options = {
|
||||
:auto_config => true,
|
||||
:netmask => "255.255.255.0"
|
||||
}.merge(args[1] || {})
|
||||
|
||||
# Calculate our network address for the given IP/netmask
|
||||
netaddr = network_address(ip, options[:netmask])
|
||||
|
||||
# Verify that a host-only network subnet would not collide
|
||||
# with a bridged networking interface.
|
||||
#
|
||||
# If the subnets overlap in any way then the host only network
|
||||
# will not work because the routing tables will force the
|
||||
# traffic onto the real interface rather than the VirtualBox
|
||||
# interface.
|
||||
@env[:machine].provider.driver.read_bridged_interfaces.each do |interface|
|
||||
that_netaddr = network_address(interface[:ip], interface[:netmask])
|
||||
raise Vagrant::Errors::NetworkCollision if \
|
||||
netaddr == that_netaddr && interface[:status] != "Down"
|
||||
end
|
||||
|
||||
# Split the IP address into its components
|
||||
ip_parts = netaddr.split(".").map { |i| i.to_i }
|
||||
|
||||
# Calculate the adapter IP, which we assume is the IP ".1" at
|
||||
# the end usually.
|
||||
adapter_ip = ip_parts.dup
|
||||
adapter_ip[3] += 1
|
||||
options[:adapter_ip] ||= adapter_ip.join(".")
|
||||
|
||||
return {
|
||||
:adapter_ip => adapter_ip,
|
||||
:auto_config => options[:auto_config],
|
||||
:ip => ip,
|
||||
:mac => nil,
|
||||
:netmask => options[:netmask],
|
||||
:nic_type => nil,
|
||||
:type => :static
|
||||
}
|
||||
end
|
||||
|
||||
def hostonly_adapter(config)
|
||||
@logger.info("Searching for matching hostonly network: #{config[:ip]}")
|
||||
interface = hostonly_find_matching_network(config)
|
||||
|
||||
if !interface
|
||||
@logger.info("Network not found. Creating if we can.")
|
||||
|
||||
# It is an error if a specific host only network name was specified
|
||||
# but the network wasn't found.
|
||||
if config[:name]
|
||||
raise Vagrant::Errors::NetworkNotFound, :name => config[:name]
|
||||
end
|
||||
|
||||
# Create a new network
|
||||
interface = hostonly_create_network(config)
|
||||
@logger.info("Created network: #{interface[:name]}")
|
||||
end
|
||||
|
||||
return {
|
||||
:adapter => config[:adapter],
|
||||
:hostonly => interface[:name],
|
||||
:mac => config[:mac],
|
||||
:nic_type => config[:nic_type],
|
||||
:type => :hostonly
|
||||
}
|
||||
end
|
||||
|
||||
def hostonly_network_config(config)
|
||||
return {
|
||||
:type => config[:type],
|
||||
:adapter_ip => config[:adapter_ip],
|
||||
:ip => config[:ip],
|
||||
:netmask => config[:netmask]
|
||||
}
|
||||
end
|
||||
|
||||
def nat_config(options)
|
||||
return {
|
||||
:auto_config => false
|
||||
}
|
||||
end
|
||||
|
||||
def nat_adapter(config)
|
||||
return {
|
||||
:adapter => config[:adapter],
|
||||
:type => :nat,
|
||||
}
|
||||
end
|
||||
|
||||
def nat_network_config(config)
|
||||
return {}
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
# Misc. helpers
|
||||
#-----------------------------------------------------------------
|
||||
# Assigns the actual interface number of a network based on the
|
||||
# enabled NICs on the virtual machine.
|
||||
#
|
||||
# This interface number is used by the guest to configure the
|
||||
# NIC on the guest VM.
|
||||
#
|
||||
# The networks are modified in place by adding an ":interface"
|
||||
# field to each.
|
||||
def assign_interface_numbers(networks, adapters)
|
||||
current = 0
|
||||
adapter_to_interface = {}
|
||||
|
||||
# Make a first pass to assign interface numbers by adapter location
|
||||
vm_adapters = @env[:machine].provider.driver.read_network_interfaces
|
||||
vm_adapters.sort.each do |number, adapter|
|
||||
if adapter[:type] != :none
|
||||
# Not used, so assign the interface number and increment
|
||||
adapter_to_interface[number] = current
|
||||
current += 1
|
||||
end
|
||||
end
|
||||
|
||||
# Make a pass through the adapters to assign the :interface
|
||||
# key to each network configuration.
|
||||
adapters.each_index do |i|
|
||||
adapter = adapters[i]
|
||||
network = networks[i]
|
||||
|
||||
# Figure out the interface number by simple lookup
|
||||
network[:interface] = adapter_to_interface[adapter[:adapter]]
|
||||
end
|
||||
end
|
||||
|
||||
#-----------------------------------------------------------------
|
||||
# Hostonly Helper Functions
|
||||
#-----------------------------------------------------------------
|
||||
# This creates a host only network for the given configuration.
|
||||
def hostonly_create_network(config)
|
||||
@env[:machine].provider.driver.create_host_only_network(
|
||||
:adapter_ip => config[:adapter_ip],
|
||||
:netmask => config[:netmask]
|
||||
)
|
||||
end
|
||||
|
||||
# This finds a matching host only network for the given configuration.
|
||||
def hostonly_find_matching_network(config)
|
||||
this_netaddr = network_address(config[:ip], config[:netmask])
|
||||
|
||||
@env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
|
||||
return interface if config[:name] && config[:name] == interface[:name]
|
||||
return interface if this_netaddr == \
|
||||
network_address(interface[:ip], interface[:netmask])
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -1,14 +1,28 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
class Config < Vagrant.plugin("2", :config)
|
||||
# An array of customizations to make on the VM prior to booting it.
|
||||
#
|
||||
# @return [Array]
|
||||
attr_reader :customizations
|
||||
|
||||
# If set to `true`, then VirtualBox will be launched with a GUI.
|
||||
#
|
||||
# @return [Boolean]
|
||||
attr_accessor :gui
|
||||
|
||||
# The defined network adapters.
|
||||
#
|
||||
# @return [Hash]
|
||||
attr_reader :network_adapters
|
||||
|
||||
def initialize
|
||||
@customizations = []
|
||||
@gui = UNSET_VALUE
|
||||
@customizations = []
|
||||
@network_adapters = {}
|
||||
@gui = UNSET_VALUE
|
||||
|
||||
# We require that network adapter 1 is a NAT device.
|
||||
network_adapter(1, :nat)
|
||||
end
|
||||
|
||||
# Customize the VM by calling `VBoxManage` with the given
|
||||
|
@ -27,6 +41,15 @@ module VagrantPlugins
|
|||
@customizations << command
|
||||
end
|
||||
|
||||
# This defines a network adapter that will be added to the VirtualBox
|
||||
# virtual machine in the given slot.
|
||||
#
|
||||
# @param [Integer] slot The slot for this network adapter.
|
||||
# @param [Symbol] type The type of adapter.
|
||||
def network_adapter(slot, type, *args)
|
||||
@network_adapters[slot] = [type, args]
|
||||
end
|
||||
|
||||
# This is the hook that is called to finalize the object before it
|
||||
# is put into use.
|
||||
def finalize!
|
||||
|
|
|
@ -73,8 +73,8 @@ module VagrantPlugins
|
|||
execute("list", "vms").split("\n").each do |line|
|
||||
if line =~ /^".+?"\s+\{(.+?)\}$/
|
||||
info = execute("showvminfo", $1.to_s, "--machinereadable", :retryable => true)
|
||||
info.split("\n").each do |line|
|
||||
if line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
info.split("\n").each do |inner_line|
|
||||
if inner_line =~ /^hostonlyadapter\d+="(.+?)"$/
|
||||
networks.delete($1.to_s)
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Model
|
||||
# Represents a single forwarded port for VirtualBox. This has various
|
||||
# helpers and defaults for a forwarded port.
|
||||
class ForwardedPort
|
||||
# The NAT adapter on which to attach the forwarded port.
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_reader :adapter
|
||||
|
||||
# If true, this port should be auto-corrected.
|
||||
#
|
||||
# @return [Boolean]
|
||||
attr_reader :auto_correct
|
||||
|
||||
# The unique ID for the forwarded port.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :id
|
||||
|
||||
# The protocol to forward.
|
||||
#
|
||||
# @return [String]
|
||||
attr_reader :protocol
|
||||
|
||||
# The port on the guest to be exposed on the host.
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_reader :guest_port
|
||||
|
||||
# The port on the host used to access the port on the guest.
|
||||
#
|
||||
# @return [Integer]
|
||||
attr_reader :host_port
|
||||
|
||||
def initialize(id, host_port, guest_port, options)
|
||||
@id = id
|
||||
@guest_port = guest_port
|
||||
@host_port = host_port
|
||||
|
||||
options ||= {}
|
||||
@auto_correct = true
|
||||
@auto_correct = options[:auto_correct] if options.has_key?(:auto_correct)
|
||||
@adapter = options[:adapter] || 1
|
||||
@protocol = options[:protocol] || "tcp"
|
||||
end
|
||||
|
||||
# This corrects the host port and changes it to the given new port.
|
||||
#
|
||||
# @param [Integer] new_port The new port
|
||||
def correct_host_port(new_port)
|
||||
@host_port = new_port
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -30,5 +30,13 @@ module VagrantPlugins
|
|||
autoload :Version_4_1, File.expand_path("../driver/version_4_1", __FILE__)
|
||||
autoload :Version_4_2, File.expand_path("../driver/version_4_2", __FILE__)
|
||||
end
|
||||
|
||||
module Model
|
||||
autoload :ForwardedPort, File.expand_path("../model/forwarded_port", __FILE__)
|
||||
end
|
||||
|
||||
module Util
|
||||
autoload :CompileForwardedPorts, File.expand_path("../util/compile_forwarded_ports", __FILE__)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,33 @@
|
|||
require "vagrant/util/scoped_hash_override"
|
||||
|
||||
module VagrantPlugins
|
||||
module ProviderVirtualBox
|
||||
module Util
|
||||
module CompileForwardedPorts
|
||||
include Vagrant::Util::ScopedHashOverride
|
||||
|
||||
# This method compiles the forwarded ports into {ForwardedPort}
|
||||
# models.
|
||||
def compile_forwarded_ports(config)
|
||||
mappings = {}
|
||||
|
||||
config.vm.networks.each do |type, args|
|
||||
if type == :forwarded_port
|
||||
guest_port = args[0]
|
||||
host_port = args[1]
|
||||
options = args[2] || {}
|
||||
options = scoped_hash_override(options, :virtualbox)
|
||||
id = options[:id] ||
|
||||
"#{guest_port.to_s(32)}-#{host_port.to_s(32)}"
|
||||
|
||||
mappings[host_port] =
|
||||
Model::ForwardedPort.new(id, host_port, guest_port, options)
|
||||
end
|
||||
end
|
||||
|
||||
mappings.values
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -280,6 +280,13 @@ en:
|
|||
VirtualBox is complaining that the installation is incomplete. Please
|
||||
run `VBoxManage --version` to see the error message which should contain
|
||||
instructions on how to fix this error.
|
||||
virtualbox_no_room_for_high_level_network: |-
|
||||
There is no available slots on the VirtualBox VM for the configured
|
||||
high-level network interfaces. "private_network" and "public_network"
|
||||
network configurations consume a single network adapter slot on the
|
||||
VirtualBox VM. VirtualBox limits the number of slots to 8, and it
|
||||
appears that every slot is in use. Please lower the number of used
|
||||
network adapters.
|
||||
virtualbox_not_detected: |-
|
||||
Vagrant could not detect VirtualBox! Make sure VirtualBox is properly installed.
|
||||
Vagrant uses the `VBoxManage` binary that ships with VirtualBox, and requires
|
||||
|
|
|
@ -0,0 +1,48 @@
|
|||
require File.expand_path("../../../base", __FILE__)
|
||||
|
||||
require "vagrant/util/scoped_hash_override"
|
||||
|
||||
describe Vagrant::Util::ScopedHashOverride do
|
||||
let(:klass) do
|
||||
Class.new do
|
||||
extend Vagrant::Util::ScopedHashOverride
|
||||
end
|
||||
end
|
||||
|
||||
it "should not mess with non-overrides" do
|
||||
original = {
|
||||
:key => "value",
|
||||
:another_value => "foo"
|
||||
}
|
||||
|
||||
klass.scoped_hash_override(original, "foo").should == original
|
||||
end
|
||||
|
||||
it "should override if the scope matches" do
|
||||
original = {
|
||||
:key => "value",
|
||||
:scope__key => "replaced"
|
||||
}
|
||||
|
||||
expected = {
|
||||
:key => "replaced"
|
||||
}
|
||||
|
||||
klass.scoped_hash_override(original, "scope").should == expected
|
||||
end
|
||||
|
||||
it "should ignore non-matching scopes" do
|
||||
original = {
|
||||
:key => "value",
|
||||
:scope__key => "replaced",
|
||||
:another__key => "value"
|
||||
}
|
||||
|
||||
expected = {
|
||||
:key => "replaced",
|
||||
:another__key => "value"
|
||||
}
|
||||
|
||||
klass.scoped_hash_override(original, "scope").should == expected
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue