Merge pull request #12983 from chrisroberts/vbox-networking
Add hostonlynets support to VirtualBox driver
This commit is contained in:
commit
da0ab49fb1
@ -21,6 +21,8 @@ module VagrantPlugins
|
||||
VBOX_NET_CONF = "/etc/vbox/networks.conf".freeze
|
||||
# Version of VirtualBox that introduced hostonly network range restrictions
|
||||
HOSTONLY_VALIDATE_VERSION = Gem::Version.new("6.1.28")
|
||||
# Version of VirtualBox on darwin platform that ignores restrictions
|
||||
DARWIN_IGNORE_HOSTONLY_VALIDATE_VERSION = Gem::Version.new("7.0.0")
|
||||
# Default valid range for hostonly networks
|
||||
HOSTONLY_DEFAULT_RANGE = [IPAddr.new("192.168.56.0/21").freeze].freeze
|
||||
|
||||
@ -479,10 +481,7 @@ module VagrantPlugins
|
||||
#-----------------------------------------------------------------
|
||||
# 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]
|
||||
)
|
||||
@env[:machine].provider.driver.create_host_only_network(config)
|
||||
end
|
||||
|
||||
# This finds a matching host only network for the given configuration.
|
||||
@ -517,7 +516,11 @@ module VagrantPlugins
|
||||
# placed on the valid ranges
|
||||
def validate_hostonly_ip!(ip, driver)
|
||||
return if Gem::Version.new(driver.version) < HOSTONLY_VALIDATE_VERSION ||
|
||||
Vagrant::Util::Platform.windows?
|
||||
(
|
||||
Vagrant::Util::Platform.darwin? &&
|
||||
Gem::Version.new(driver.version) >= DARWIN_IGNORE_HOSTONLY_VALIDATE_VERSION
|
||||
) ||
|
||||
Vagrant::Util::Platform.windows?
|
||||
|
||||
ip = IPAddr.new(ip.to_s) if !ip.is_a?(IPAddr)
|
||||
valid_ranges = load_net_conf
|
||||
|
||||
@ -104,15 +104,15 @@ module VagrantPlugins
|
||||
end
|
||||
end
|
||||
|
||||
# Creates a disk. Default format is VDI unless overridden
|
||||
#
|
||||
# @param [String] disk_file
|
||||
# @param [Integer] disk_size - size in bytes
|
||||
# @param [String] disk_format - format of disk, defaults to "VDI"
|
||||
# Creates a disk. Default format is VDI unless overridden
|
||||
#
|
||||
# @param [String] disk_file
|
||||
# @param [Integer] disk_size - size in bytes
|
||||
# @param [String] disk_format - format of disk, defaults to "VDI"
|
||||
# @param [Hash] opts - additional options
|
||||
def create_disk(disk_file, disk_size, disk_format="VDI", **opts)
|
||||
execute("createmedium", '--filename', disk_file, '--sizebyte', disk_size.to_i.to_s, '--format', disk_format)
|
||||
end
|
||||
def create_disk(disk_file, disk_size, disk_format="VDI", **opts)
|
||||
execute("createmedium", '--filename', disk_file, '--sizebyte', disk_size.to_i.to_s, '--format', disk_format)
|
||||
end
|
||||
|
||||
|
||||
def create_host_only_network(options)
|
||||
@ -322,22 +322,22 @@ module VagrantPlugins
|
||||
|
||||
if adapter[:bridge]
|
||||
args.concat(["--bridgeadapter#{adapter[:adapter]}",
|
||||
adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
adapter[:bridge], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
end
|
||||
|
||||
if adapter[:hostonly]
|
||||
args.concat(["--hostonlyadapter#{adapter[:adapter]}",
|
||||
adapter[:hostonly], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
adapter[:hostonly], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
end
|
||||
|
||||
if adapter[:intnet]
|
||||
args.concat(["--intnet#{adapter[:adapter]}",
|
||||
adapter[:intnet], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
adapter[:intnet], "--cableconnected#{adapter[:adapter]}", "on"])
|
||||
end
|
||||
|
||||
if adapter[:mac_address]
|
||||
args.concat(["--macaddress#{adapter[:adapter]}",
|
||||
adapter[:mac_address]])
|
||||
adapter[:mac_address]])
|
||||
end
|
||||
|
||||
if adapter[:nic_type]
|
||||
@ -361,7 +361,7 @@ module VagrantPlugins
|
||||
|
||||
# If the file already exists we'll throw a custom error
|
||||
raise Vagrant::Errors::VirtualBoxFileExists,
|
||||
stderr: e.extra_data[:stderr]
|
||||
stderr: e.extra_data[:stderr]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -370,14 +370,14 @@ module VagrantPlugins
|
||||
args = []
|
||||
ports.each do |options|
|
||||
pf_builder = [options[:name],
|
||||
options[:protocol] || "tcp",
|
||||
options[:hostip] || "",
|
||||
options[:hostport],
|
||||
options[:guestip] || "",
|
||||
options[:guestport]]
|
||||
options[:protocol] || "tcp",
|
||||
options[:hostip] || "",
|
||||
options[:hostport],
|
||||
options[:guestip] || "",
|
||||
options[:guestport]]
|
||||
|
||||
args.concat(["--natpf#{options[:adapter] || 1}",
|
||||
pf_builder.join(",")])
|
||||
pf_builder.join(",")])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args, retryable: true) if !args.empty?
|
||||
@ -507,11 +507,11 @@ module VagrantPlugins
|
||||
# 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
|
||||
# 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
|
||||
# Forwarding(1)="172.22.8.201tcp32977,tcp,172.22.8.201,32977,,3777"
|
||||
@ -600,7 +600,7 @@ module VagrantPlugins
|
||||
|
||||
if !valid_ip_address?(ip)
|
||||
raise Vagrant::Errors::VirtualBoxGuestPropertyNotFound,
|
||||
guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP"
|
||||
guest_property: "/VirtualBox/GuestInfo/Net/#{adapter_number}/V4/IP"
|
||||
end
|
||||
|
||||
return ip
|
||||
@ -771,7 +771,7 @@ module VagrantPlugins
|
||||
# We got VERR_ALREADY_EXISTS. This means that we're renaming to
|
||||
# a VM name that already exists. Raise a custom error.
|
||||
raise Vagrant::Errors::VirtualBoxNameExists,
|
||||
stderr: e.extra_data[:stderr]
|
||||
stderr: e.extra_data[:stderr]
|
||||
end
|
||||
end
|
||||
end
|
||||
@ -791,9 +791,9 @@ module VagrantPlugins
|
||||
hostpath = Vagrant::Util::Platform.windows_path(folder[:hostpath])
|
||||
end
|
||||
args = ["--name",
|
||||
folder[:name],
|
||||
"--hostpath",
|
||||
hostpath]
|
||||
folder[:name],
|
||||
"--hostpath",
|
||||
hostpath]
|
||||
args << "--transient" if folder.key?(:transient) && folder[:transient]
|
||||
|
||||
args << "--automount" if folder.key?(:automount) && folder[:automount]
|
||||
@ -866,8 +866,8 @@ module VagrantPlugins
|
||||
|
||||
# If we reached this point then it didn't work out.
|
||||
raise Vagrant::Errors::VBoxManageError,
|
||||
command: command.inspect,
|
||||
stderr: r.stderr
|
||||
command: command.inspect,
|
||||
stderr: r.stderr
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@ -6,12 +6,191 @@ module VagrantPlugins
|
||||
module Driver
|
||||
# Driver for VirtualBox 7.0.x
|
||||
class Version_7_0 < Version_6_1
|
||||
# VirtualBox version requirement for using host only networks
|
||||
# instead of host only interfaces
|
||||
HOSTONLY_NET_REQUIREMENT=Gem::Requirement.new(">= 7")
|
||||
# Prefix of name used for host only networks
|
||||
HOSTONLY_NAME_PREFIX="vagrantnet-vbox"
|
||||
DEFAULT_NETMASK="255.255.255.0"
|
||||
|
||||
def initialize(uuid)
|
||||
super
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::provider::virtualbox_7_0")
|
||||
end
|
||||
|
||||
def read_bridged_interfaces
|
||||
ifaces = super
|
||||
return ifaces if !use_host_only_nets?
|
||||
|
||||
# Get a list of all subnets which are in use for hostonly networks
|
||||
hostonly_ifaces = read_host_only_networks.map do |net|
|
||||
IPAddr.new(net[:lowerip]).mask(net[:networkmask])
|
||||
end
|
||||
|
||||
# Prune any hostonly interfaces in the list
|
||||
ifaces.delete_if { |i|
|
||||
addr = IPAddr.new(i[:ip]).mask(i[:netmask])
|
||||
hostonly_ifaces.include?(addr)
|
||||
}
|
||||
|
||||
ifaces
|
||||
end
|
||||
|
||||
def delete_unused_host_only_networks
|
||||
return super if !use_host_only_nets?
|
||||
|
||||
# First get the list of existing host only network names
|
||||
network_names = read_host_only_networks.map { |net| net[:name] }
|
||||
# Prune the network names to only include ones we manage
|
||||
network_names.delete_if { |name| !name.start_with?(HOSTONLY_NAME_PREFIX) }
|
||||
|
||||
@logger.debug("managed host only network names: #{network_names}")
|
||||
|
||||
return if network_names.empty?
|
||||
|
||||
# Next get the list of host only networks currently in use
|
||||
inuse_names = []
|
||||
execute("list", "vms", retryable: true).split("\n").each do |line|
|
||||
match = line.match(/^".+?"\s+\{(?<vmid>.+?)\}$/)
|
||||
next if match.nil?
|
||||
begin
|
||||
info = execute("showvminfo", match[:vmid].to_s, "--machinereadable", retryable: true)
|
||||
info.split("\n").each do |vmline|
|
||||
if vmline.start_with?("hostonly-network")
|
||||
net_name = vmline.split("=", 2).last.to_s.gsub('"', "")
|
||||
inuse_names << net_name
|
||||
end
|
||||
end
|
||||
rescue Vagrant::Errors::VBoxManageError => err
|
||||
raise if !err.extra_data[:stderr].include?("VBOX_E_OBJECT_NOT_FOUND")
|
||||
end
|
||||
end
|
||||
|
||||
@logger.debug("currently in use network names: #{inuse_names}")
|
||||
|
||||
# Now remove all the networks not in use
|
||||
(network_names - inuse_names).each do |name|
|
||||
execute("hostonlynet", "remove", "--name", name, retryable: true)
|
||||
end
|
||||
end
|
||||
|
||||
def enable_adapters(adapters)
|
||||
return super if !use_host_only_nets?
|
||||
|
||||
hostonly_adapters = adapters.find_all { |adapter| adapter[:hostonly] }
|
||||
other_adapters = adapters - hostonly_adapters
|
||||
super(other_adapters) if !other_adapters.empty?
|
||||
|
||||
if !hostonly_adapters.empty?
|
||||
args = []
|
||||
hostonly_adapters.each do |adapter|
|
||||
args.concat(["--nic#{adapter[:adapter]}", "hostonlynet"])
|
||||
args.concat(["--host-only-net#{adapter[:adapter]}", adapter[:hostonly],
|
||||
"--cableconnected#{adapter[:adapter]}", "on"])
|
||||
end
|
||||
|
||||
execute("modifyvm", @uuid, *args, retryable: true)
|
||||
end
|
||||
end
|
||||
|
||||
def create_host_only_network(options)
|
||||
# If we are not on macOS, just setup the hostonly interface
|
||||
return super if !use_host_only_nets?
|
||||
|
||||
opts = {
|
||||
netmask: options.fetch(:netmask, DEFAULT_NETMASK),
|
||||
}
|
||||
|
||||
if options[:type] == :dhcp
|
||||
opts[:lower] = options[:dhcp_lower]
|
||||
opts[:upper] = options[:dhcp_upper]
|
||||
else
|
||||
addr = IPAddr.new(options[:adapter_ip])
|
||||
opts[:upper] = opts[:lower] = addr.mask(opts[:netmask]).to_range.first.to_s
|
||||
end
|
||||
|
||||
name_idx = read_host_only_networks.map { |hn|
|
||||
next if !hn[:name].start_with?(HOSTONLY_NAME_PREFIX)
|
||||
hn[:name].sub(HOSTONLY_NAME_PREFIX, "").to_i
|
||||
}.compact.max.to_i + 1
|
||||
opts[:name] = HOSTONLY_NAME_PREFIX + name_idx.to_s
|
||||
|
||||
execute("hostonlynet", "add",
|
||||
"--name", opts[:name],
|
||||
"--netmask", opts[:netmask],
|
||||
"--lower-ip", opts[:lower],
|
||||
"--upper-ip", opts[:upper],
|
||||
retryable: true)
|
||||
|
||||
{
|
||||
name: opts[:name],
|
||||
ip: options[:adapter_ip],
|
||||
netmask: opts[:netmask],
|
||||
}
|
||||
end
|
||||
|
||||
# Disabled when host only nets are in use
|
||||
def reconfig_host_only(options)
|
||||
return super if !use_host_only_nets?
|
||||
end
|
||||
|
||||
# Disabled when host only nets are in use since
|
||||
# the host only nets will provide the dhcp server
|
||||
def remove_dhcp_server(*_, **_)
|
||||
super if !use_host_only_nets?
|
||||
end
|
||||
|
||||
# Disabled when host only nets are in use since
|
||||
# the host only nets will provide the dhcp server
|
||||
def create_dhcp_server(*_, **_)
|
||||
super if !use_host_only_nets?
|
||||
end
|
||||
|
||||
def read_host_only_interfaces
|
||||
return super if !use_host_only_nets?
|
||||
|
||||
# When host only nets are in use, read them and
|
||||
# reformat the information to line up with how
|
||||
# the interfaces is structured
|
||||
read_host_only_networks.map do |net|
|
||||
addr = IPAddr.new(net[:lowerip])
|
||||
net[:netmask] = net[:networkmask]
|
||||
if addr.ipv4?
|
||||
net[:ip] = addr.mask(net[:netmask]).to_s
|
||||
net[:ipv6] = ""
|
||||
else
|
||||
net[:ip] = ""
|
||||
net[:ipv6] = addr.mask(net[:netmwask]).to_s
|
||||
net[:ipv6_prefix] = net[:netmask]
|
||||
end
|
||||
|
||||
net[:status] = net[:state] == "Enabled" ? "Up" : "Down"
|
||||
|
||||
net
|
||||
end
|
||||
end
|
||||
|
||||
# Generate list of host only networks
|
||||
def read_host_only_networks
|
||||
networks = []
|
||||
current = nil
|
||||
execute("list", "hostonlynets", retryable: true).split("\n").each do |line|
|
||||
line.chomp!
|
||||
next if line.empty?
|
||||
key, value = line.split(":", 2).map(&:strip)
|
||||
key = key.downcase
|
||||
if key == "name"
|
||||
networks.push(current) if !current.nil?
|
||||
current = Vagrant::Util::HashWithIndifferentAccess.new
|
||||
end
|
||||
current[key] = value
|
||||
end
|
||||
networks.push(current) if !current.nil?
|
||||
|
||||
networks
|
||||
end
|
||||
|
||||
# The initial VirtualBox 7.0 release has an issue with displaying port
|
||||
# forward information. When a single port forward is defined, the forwarding
|
||||
# information can be found in the `showvminfo` output. Once more than a
|
||||
@ -20,11 +199,8 @@ module VagrantPlugins
|
||||
# file from the `showvminfo` output and extract the port forward information
|
||||
# from there instead.
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
@version ||= Meta.new.version
|
||||
|
||||
# Only use this override for the 7.0.0 release. If it is still broken
|
||||
# on the 7.0.1 release we can modify the version check.
|
||||
return super if @version != "7.0.0"
|
||||
# Only use this override for the 7.0.0 release.
|
||||
return super if get_version.to_s != "7.0.0"
|
||||
|
||||
uuid ||= @uuid
|
||||
|
||||
@ -61,6 +237,25 @@ module VagrantPlugins
|
||||
|
||||
results
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
# Returns if hostonlynets are enabled on the current
|
||||
# host platform
|
||||
#
|
||||
# @return [Boolean]
|
||||
def use_host_only_nets?
|
||||
Vagrant::Util::Platform.darwin? &&
|
||||
HOSTONLY_NET_REQUIREMENT.satisfied_by?(get_version)
|
||||
end
|
||||
|
||||
# VirtualBox version in use
|
||||
#
|
||||
# @return [Gem::Version]
|
||||
def get_version
|
||||
return @version if @version
|
||||
@version = Gem::Version.new(Meta.new.version)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@ -231,10 +231,10 @@ describe VagrantPlugins::ProviderVirtualBox::Action::Network do
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(driver).to have_received(:create_host_only_network).with({
|
||||
expect(driver).to have_received(:create_host_only_network).with(hash_including({
|
||||
adapter_ip: interface_ip,
|
||||
netmask: 64,
|
||||
})
|
||||
}))
|
||||
|
||||
expect(guest).to have_received(:capability).with(:configure_networks, [{
|
||||
type: :static6,
|
||||
@ -308,10 +308,10 @@ describe VagrantPlugins::ProviderVirtualBox::Action::Network do
|
||||
|
||||
subject.call(env)
|
||||
|
||||
expect(driver).to have_received(:create_host_only_network).with({
|
||||
expect(driver).to have_received(:create_host_only_network).with(hash_including({
|
||||
adapter_ip: '192.168.56.1',
|
||||
netmask: '255.255.255.0',
|
||||
})
|
||||
}))
|
||||
|
||||
expect(driver).to have_received(:create_dhcp_server).with('vboxnet0', {
|
||||
adapter_ip: "192.168.56.1",
|
||||
|
||||
@ -26,31 +26,7 @@ LogFldr="/VirtualBox VMs/vagrant-test_default_1665781960041_56631/Logs"
|
||||
memory=1024)
|
||||
}
|
||||
let(:config_file) {
|
||||
StringIO.new(
|
||||
%(<?xml version="1.0"?>
|
||||
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-linux">
|
||||
<Machine uuid="{623842dc-0947-4143-aa4e-7d180c5eb348}" name="vagrant-test_default_1665781960041_56631" OSType="Ubuntu_64" snapshotFolder="Snapshots">
|
||||
<Hardware>
|
||||
<Network>
|
||||
<Adapter slot="0" enabled="true" MACAddress="080027BB1475" type="82540EM">
|
||||
<NAT localhost-reachable="true">
|
||||
<DNS use-proxy="true"/>
|
||||
<Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
|
||||
<Forwarding name="tcp7700" proto="1" hostport="7700" guestport="80"/>
|
||||
</NAT>
|
||||
</Adapter>
|
||||
<Adapter slot="1" enabled="true" MACAddress="080027DD5ADF" type="82540EM">
|
||||
<DisabledModes>
|
||||
<InternalNetwork name="intnet"/>
|
||||
<NATNetwork name="NatNetwork"/>
|
||||
</DisabledModes>
|
||||
<HostOnlyInterface name="vboxnet0"/>
|
||||
</Adapter>
|
||||
</Network>
|
||||
</Hardware>
|
||||
</Machine>
|
||||
</VirtualBox>)
|
||||
)
|
||||
StringIO.new(VBOX_VMCONFIG_FILE)
|
||||
}
|
||||
|
||||
before do
|
||||
@ -106,4 +82,616 @@ memory=1024)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
describe "#use_host_only_nets?" do
|
||||
context "when platform is darwin" do
|
||||
before do
|
||||
allow(Vagrant::Util::Platform).to receive(:darwin?).and_return(true)
|
||||
end
|
||||
|
||||
context "when virtualbox version is less than 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("6.0.28")
|
||||
end
|
||||
|
||||
it "should return false" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "when virtualbox version is greater than 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("7.0.2")
|
||||
end
|
||||
|
||||
it "should return true" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(true)
|
||||
end
|
||||
end
|
||||
|
||||
context "when virtualbox version is equal to 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("7.0.0")
|
||||
end
|
||||
|
||||
it "should return true" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(true)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
context "when platform is not darwin" do
|
||||
before do
|
||||
allow(Vagrant::Util::Platform).to receive(:darwin?).and_return(false)
|
||||
end
|
||||
|
||||
context "when virtualbox version is less than 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("6.0.28")
|
||||
end
|
||||
|
||||
it "should return false" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "when virtualbox version is greater than 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("7.0.2")
|
||||
end
|
||||
|
||||
it "should return false" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(false)
|
||||
end
|
||||
end
|
||||
|
||||
context "when virtualbox version is equal to 7" do
|
||||
before do
|
||||
allow_any_instance_of(VagrantPlugins::ProviderVirtualBox::Driver::Meta).
|
||||
to receive(:version).and_return("7.0.0")
|
||||
end
|
||||
|
||||
it "should return false" do
|
||||
expect(subject.send(:use_host_only_nets?)).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#read_bridged_interfaces" do
|
||||
before do
|
||||
allow(subject).to receive(:execute).and_call_original
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("list", "bridgedifs").
|
||||
and_return(VBOX_BRIDGEDIFS)
|
||||
end
|
||||
|
||||
context "when hostonlynets are not enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should return all interfaces in list" do
|
||||
expect(subject.read_bridged_interfaces.size).to eq(5)
|
||||
end
|
||||
|
||||
it "should not read host only networks" do
|
||||
expect(subject).not_to receive(:read_host_only_networks)
|
||||
subject.read_bridged_interfaces
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
end
|
||||
|
||||
it "should return all interfaces in list" do
|
||||
expect(subject).to receive(:read_host_only_networks).and_return([])
|
||||
expect(subject.read_bridged_interfaces.size).to eq(5)
|
||||
end
|
||||
|
||||
context "when hostonly networks are defined" do
|
||||
before do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("list", "hostonlynets", any_args).
|
||||
and_return(VBOX_HOSTONLYNETS)
|
||||
end
|
||||
|
||||
it "should not return all interfaces in list" do
|
||||
expect(subject.read_bridged_interfaces.size).to_not eq(5)
|
||||
end
|
||||
|
||||
it "should not include hostonly network devices" do
|
||||
expect(
|
||||
subject.read_bridged_interfaces.any? { |int|
|
||||
int[:name].start_with?("bridge")
|
||||
}
|
||||
).to be(false)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#delete_unused_host_only_networks" do
|
||||
context "when hostonlynets are not enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should remove host only interfaces" do
|
||||
expect(subject).to receive(:execute).with("list", "hostonlyifs", any_args).and_return("")
|
||||
expect(subject).to receive(:execute).with("list", "vms", any_args).and_return("")
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
|
||||
it "should not read host only networks" do
|
||||
expect(subject).to receive(:execute).with("list", "hostonlyifs", any_args).and_return("")
|
||||
expect(subject).to receive(:execute).with("list", "vms", any_args).and_return("")
|
||||
expect(subject).not_to receive(:read_host_only_networks)
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
allow(subject).to receive(:read_host_only_networks).and_return([])
|
||||
allow(subject).to receive(:execute).with("list", "vms", any_args).and_return("")
|
||||
end
|
||||
|
||||
it "should not read host only interfaces" do
|
||||
expect(subject).not_to receive(:execute).with("list", "hostonlyifs", any_args)
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
|
||||
context "when no host only networks are defined" do
|
||||
before do
|
||||
expect(subject).to receive(:read_host_only_networks).and_return([])
|
||||
end
|
||||
|
||||
it "should not list vms" do
|
||||
expect(subject).not_to receive(:execute).with("list", "vms", any_args)
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
end
|
||||
|
||||
context "when host only networks are defined" do
|
||||
before do
|
||||
expect(subject).
|
||||
to receive(:read_host_only_networks).
|
||||
and_return([{name: "vagrantnet-vbox-1"}])
|
||||
|
||||
end
|
||||
|
||||
context "when no vms are using the network" do
|
||||
before do
|
||||
expect(subject).to receive(:execute).with("list", "vms", any_args).and_return("")
|
||||
end
|
||||
|
||||
it "should delete the network" do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlynet", "remove", "--name", "vagrantnet-vbox-1", any_args)
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
end
|
||||
|
||||
context "when vms are using the network" do
|
||||
before do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("list", "vms", any_args).
|
||||
and_return(%("VM_NAME" {VM_ID}))
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("showvminfo", "VM_ID", any_args).
|
||||
and_return(%(hostonly-network="vagrantnet-vbox-1"))
|
||||
end
|
||||
|
||||
it "should not delete the network" do
|
||||
expect(subject).not_to receive(:execute).with("hostonlynet", "remove", any_args)
|
||||
subject.delete_unused_host_only_networks
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#enable_adapters" do
|
||||
let(:adapters) {
|
||||
[{hostonly: "hostonlynetwork", adapter: 1},
|
||||
{bridge: "eth0", adapter: 2}]
|
||||
}
|
||||
|
||||
before do
|
||||
allow(subject).to receive(:execute).with("modifyvm", any_args)
|
||||
end
|
||||
|
||||
context "when hostonlynets are not enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should only call modifyvm once" do
|
||||
expect(subject).to receive(:execute).with("modifyvm", any_args).once
|
||||
subject.enable_adapters(adapters)
|
||||
end
|
||||
|
||||
it "should configure host only network using hostonlyadapter" do
|
||||
expect(subject).to receive(:execute) { |*args|
|
||||
expect(args.first).to eq("modifyvm")
|
||||
expect(args).to include("--hostonlyadapter1")
|
||||
true
|
||||
}
|
||||
subject.enable_adapters(adapters)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
end
|
||||
|
||||
it "should call modifyvm twice" do
|
||||
expect(subject).to receive(:execute).with("modifyvm", any_args).twice
|
||||
subject.enable_adapters(adapters)
|
||||
end
|
||||
|
||||
it "should configure host only network using hostonlynet" do
|
||||
expect(subject).to receive(:execute).once
|
||||
expect(subject).to receive(:execute) { |*args|
|
||||
expect(args.first).to eq("modifyvm")
|
||||
expect(args).to include("--host-only-net1")
|
||||
true
|
||||
}
|
||||
subject.enable_adapters(adapters)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_host_only_network" do
|
||||
let(:options) {
|
||||
{
|
||||
adapter_ip: "127.0.0.1",
|
||||
netmask: "255.255.255.0"
|
||||
}
|
||||
}
|
||||
|
||||
context "when hostonlynets are disabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should create using hostonlyif" do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlyif", "create", any_args).
|
||||
and_return("Interface 'host_only' was successfully created")
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlyif", "ipconfig", "host_only", any_args)
|
||||
subject.create_host_only_network(options)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
let(:prefix) { described_class.const_get(:HOSTONLY_NAME_PREFIX) }
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
allow(subject).to receive(:read_host_only_networks).and_return([])
|
||||
end
|
||||
|
||||
it "should create using hostonlynet" do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlynet", "add", "--name", prefix + "1",
|
||||
"--netmask", options[:netmask], "--lower-ip",
|
||||
"127.0.0.0", "--upper-ip", "127.0.0.0", any_args)
|
||||
subject.create_host_only_network(options)
|
||||
end
|
||||
|
||||
context "when other host only networks exist" do
|
||||
before do
|
||||
expect(subject).
|
||||
to receive(:read_host_only_networks).
|
||||
and_return(["custom", prefix + "1", prefix + "20"].map { |n| {name: n} })
|
||||
end
|
||||
|
||||
it "should create network with incremented name" do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlynet", "add", "--name", prefix + "21", any_args)
|
||||
subject.create_host_only_network(options)
|
||||
end
|
||||
end
|
||||
|
||||
context "when dhcp information is included" do
|
||||
let(:options) {
|
||||
{
|
||||
type: :dhcp,
|
||||
dhcp_lower: "127.0.0.1",
|
||||
dhcp_upper: "127.0.1.200",
|
||||
netmask: "255.255.240.0"
|
||||
}
|
||||
}
|
||||
|
||||
it "should set DHCP range" do
|
||||
expect(subject).
|
||||
to receive(:execute).
|
||||
with("hostonlynet", "add", "--name", anything, "--netmask", options[:netmask],
|
||||
"--lower-ip", options[:dhcp_lower], "--upper-ip", options[:dhcp_upper],
|
||||
any_args)
|
||||
subject.create_host_only_network(options)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#reconfig_host_only" do
|
||||
let(:interface) { {name: "iname", ipv6: "VALUE"} }
|
||||
|
||||
context "when hostonlynets are disabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should apply ipv6 update" do
|
||||
expect(subject).to receive(:execute).with("hostonlyif", "ipconfig", interface[:name],
|
||||
"--ipv6", interface[:ipv6], any_args)
|
||||
subject.reconfig_host_only(interface)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
end
|
||||
|
||||
it "should do nothing" do
|
||||
expect(subject).not_to receive(:execute)
|
||||
subject.reconfig_host_only(interface)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#remove_dhcp_server" do
|
||||
let(:dhcp_name) { double(:dhcp_name) }
|
||||
|
||||
context "when hostonlynets are disabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should remove the dhcp server" do
|
||||
expect(subject).to receive(:execute).with("dhcpserver", "remove", "--netname",
|
||||
dhcp_name, any_args)
|
||||
subject.remove_dhcp_server(dhcp_name)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets are enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
end
|
||||
|
||||
it "should do nothing" do
|
||||
expect(subject).not_to receive(:execute)
|
||||
subject.remove_dhcp_server(dhcp_name)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#create_dhcp_server" do
|
||||
let(:network) { double("network") }
|
||||
let(:options) {
|
||||
{
|
||||
dhcp_ip: "127.0.0.1",
|
||||
netmask: "255.255.255.0",
|
||||
dhcp_lower: "127.0.0.2",
|
||||
dhcp_upper: "127.0.0.200"
|
||||
}
|
||||
}
|
||||
|
||||
context "when hostonlynets is diabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
end
|
||||
|
||||
it "should create a dhcp server" do
|
||||
expect(subject).to receive(:execute).with("dhcpserver", "add", "--ifname", network,
|
||||
"--ip", options[:dhcp_ip], any_args)
|
||||
subject.create_dhcp_server(network, options)
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets is enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
end
|
||||
|
||||
it "should do nothing" do
|
||||
expect(subject).not_to receive(:execute)
|
||||
subject.create_dhcp_server(network, options)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#read_host_only_interfaces" do
|
||||
context "when hostonlynets is diabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(false)
|
||||
allow(subject).to receive(:execute).and_return("")
|
||||
end
|
||||
|
||||
it "should list hostonlyifs" do
|
||||
expect(subject).to receive(:execute).with("list", "hostonlyifs", any_args).and_return("")
|
||||
subject.read_host_only_interfaces
|
||||
end
|
||||
|
||||
it "should not call read_host_only_networks" do
|
||||
expect(subject).not_to receive(:read_host_only_networks)
|
||||
subject.read_host_only_interfaces
|
||||
end
|
||||
end
|
||||
|
||||
context "when hostonlynets is enabled" do
|
||||
before do
|
||||
allow(subject).to receive(:use_host_only_nets?).and_return(true)
|
||||
allow(subject).to receive(:execute).with("list", "hostonlynets", any_args).
|
||||
and_return(VBOX_HOSTONLYNETS)
|
||||
end
|
||||
|
||||
it "should call read_host_only_networks" do
|
||||
expect(subject).to receive(:read_host_only_networks).and_return([])
|
||||
subject.read_host_only_interfaces
|
||||
end
|
||||
|
||||
it "should return defined networks" do
|
||||
expect(subject.read_host_only_interfaces.size).to eq(2)
|
||||
end
|
||||
|
||||
it "should add compat information to network entries" do
|
||||
result = subject.read_host_only_interfaces
|
||||
expect(result.first[:netmask]).to eq(result.first[:networkmask])
|
||||
expect(result.first[:status]).to eq("Up")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
describe "#read_host_only_networks" do
|
||||
before do
|
||||
allow(subject).to receive(:execute).with("list", "hostonlynets", any_args).
|
||||
and_return(VBOX_HOSTONLYNETS)
|
||||
end
|
||||
|
||||
it "should return defined networks" do
|
||||
expect(subject.read_host_only_networks.size).to eq(2)
|
||||
end
|
||||
|
||||
it "should return expected network information" do
|
||||
result = subject.read_host_only_networks
|
||||
expect(result.first[:name]).to eq("vagrantnet-vbox1")
|
||||
expect(result.first[:lowerip]).to eq("192.168.61.0")
|
||||
expect(result.first[:networkmask]).to eq("255.255.255.0")
|
||||
expect(result.last[:name]).to eq("vagrantnet-vbox2")
|
||||
expect(result.last[:lowerip]).to eq("192.168.22.0")
|
||||
expect(result.last[:networkmask]).to eq("255.255.255.0")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
VBOX_VMCONFIG_FILE=%(<?xml version="1.0"?>
|
||||
<VirtualBox xmlns="http://www.virtualbox.org/" version="1.19-linux">
|
||||
<Machine uuid="{623842dc-0947-4143-aa4e-7d180c5eb348}" name="vagrant-test_default_1665781960041_56631" OSType="Ubuntu_64" snapshotFolder="Snapshots">
|
||||
<Hardware>
|
||||
<Network>
|
||||
<Adapter slot="0" enabled="true" MACAddress="080027BB1475" type="82540EM">
|
||||
<NAT localhost-reachable="true">
|
||||
<DNS use-proxy="true"/>
|
||||
<Forwarding name="ssh" proto="1" hostip="127.0.0.1" hostport="2222" guestport="22"/>
|
||||
<Forwarding name="tcp7700" proto="1" hostport="7700" guestport="80"/>
|
||||
</NAT>
|
||||
</Adapter>
|
||||
<Adapter slot="1" enabled="true" MACAddress="080027DD5ADF" type="82540EM">
|
||||
<DisabledModes>
|
||||
<InternalNetwork name="intnet"/>
|
||||
<NATNetwork name="NatNetwork"/>
|
||||
</DisabledModes>
|
||||
<HostOnlyInterface name="vboxnet0"/>
|
||||
</Adapter>
|
||||
</Network>
|
||||
</Hardware>
|
||||
</Machine>
|
||||
</VirtualBox>)
|
||||
|
||||
|
||||
VBOX_BRIDGEDIFS=%(Name: en1: Wi-Fi (AirPort)
|
||||
GUID: 00000000-0000-0000-0000-000000000001
|
||||
DHCP: Disabled
|
||||
IPAddress: 10.0.0.49
|
||||
NetworkMask: 255.255.255.0
|
||||
IPV6Address:
|
||||
IPV6NetworkMaskPrefixLength: 0
|
||||
HardwareAddress: xx:xx:xx:xx:xx:01
|
||||
MediumType: Ethernet
|
||||
Wireless: Yes
|
||||
Status: Up
|
||||
VBoxNetworkName: HostInterfaceNetworking-en1
|
||||
|
||||
Name: en0: Ethernet
|
||||
GUID: 00000000-0000-0000-0000-000000000002
|
||||
DHCP: Disabled
|
||||
IPAddress: 0.0.0.0
|
||||
NetworkMask: 0.0.0.0
|
||||
IPV6Address:
|
||||
IPV6NetworkMaskPrefixLength: 0
|
||||
HardwareAddress: xx:xx:xx:xx:xx:02
|
||||
MediumType: Ethernet
|
||||
Wireless: No
|
||||
Status: Up
|
||||
VBoxNetworkName: HostInterfaceNetworking-en0
|
||||
|
||||
Name: bridge100
|
||||
GUID: 00000000-0000-0000-0000-000000000003
|
||||
DHCP: Disabled
|
||||
IPAddress: 192.168.61.1
|
||||
NetworkMask: 255.255.255.0
|
||||
IPV6Address:
|
||||
IPV6NetworkMaskPrefixLength: 0
|
||||
HardwareAddress: xx:xx:xx:xx:xx:03
|
||||
MediumType: Ethernet
|
||||
Wireless: No
|
||||
Status: Up
|
||||
VBoxNetworkName: HostInterfaceNetworking-bridge100
|
||||
|
||||
Name: en2: Thunderbolt 1
|
||||
GUID: 00000000-0000-0000-0000-000000000004
|
||||
DHCP: Disabled
|
||||
IPAddress: 0.0.0.0
|
||||
NetworkMask: 0.0.0.0
|
||||
IPV6Address:
|
||||
IPV6NetworkMaskPrefixLength: 0
|
||||
HardwareAddress: xx:xx:xx:xx:xx:04
|
||||
MediumType: Ethernet
|
||||
Wireless: No
|
||||
Status: Up
|
||||
VBoxNetworkName: HostInterfaceNetworking-en2
|
||||
|
||||
Name: bridge101
|
||||
GUID: 00000000-0000-0000-0000-000000000005
|
||||
DHCP: Disabled
|
||||
IPAddress: 192.168.22.1
|
||||
NetworkMask: 255.255.255.0
|
||||
IPV6Address:
|
||||
IPV6NetworkMaskPrefixLength: 0
|
||||
HardwareAddress: xx:xx:xx:xx:xx:05
|
||||
MediumType: Ethernet
|
||||
Wireless: No
|
||||
Status: Up
|
||||
VBoxNetworkName: HostInterfaceNetworking-bridge101)
|
||||
|
||||
VBOX_HOSTONLYNETS=%(Name: vagrantnet-vbox1
|
||||
GUID: 10000000-0000-0000-0000-000000000000
|
||||
|
||||
State: Enabled
|
||||
NetworkMask: 255.255.255.0
|
||||
LowerIP: 192.168.61.0
|
||||
UpperIP: 192.168.61.0
|
||||
VBoxNetworkName: hostonly-vagrantnet-vbox1
|
||||
|
||||
Name: vagrantnet-vbox2
|
||||
GUID: 20000000-0000-0000-0000-000000000000
|
||||
|
||||
State: Enabled
|
||||
NetworkMask: 255.255.255.0
|
||||
LowerIP: 192.168.22.0
|
||||
UpperIP: 192.168.22.0
|
||||
VBoxNetworkName: hostonly-vagrantnet-vbox2)
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user