diff --git a/lib/vagrant/bundler.rb b/lib/vagrant/bundler.rb index dfe8a17c1..05867da15 100644 --- a/lib/vagrant/bundler.rb +++ b/lib/vagrant/bundler.rb @@ -1,4 +1,3 @@ -require "fileutils" require "monitor" require "pathname" require "set" @@ -80,9 +79,9 @@ module Vagrant # Removes any temporary files created by init def deinit - %w{ BUNDLE_APP_CONFIG BUNDLE_CONFIG BUNDLE_GEMFILE }.each do |entry| - FileUtils.remove_entry_secure(ENV[entry], true) - end + File.unlink(ENV["BUNDLE_APP_CONFIG"]) rescue nil + File.unlink(ENV["BUNDLE_CONFIG"]) rescue nil + File.unlink(ENV["GEMFILE"]) rescue nil end # Installs the list of plugins. diff --git a/plugins/providers/virtualbox/action/network.rb b/plugins/providers/virtualbox/action/network.rb index 3fcffeca6..22eb08cb1 100644 --- a/plugins/providers/virtualbox/action/network.rb +++ b/plugins/providers/virtualbox/action/network.rb @@ -285,16 +285,16 @@ module VagrantPlugins # 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 - dhcp_options[:dhcp_ip] ||= dhcp_ip.join(".") + dhcp_options[:dhcp_ip] = 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 - dhcp_options[:dhcp_lower] ||= dhcp_lower.join(".") + dhcp_options[:dhcp_lower] = options[:dhcp_lower] || dhcp_lower.join(".") dhcp_upper = ip_parts.dup dhcp_upper[3] = 254 - dhcp_options[:dhcp_upper] ||= dhcp_upper.join(".") + dhcp_options[:dhcp_upper] = options[:dhcp_upper] || dhcp_upper.join(".") end return { @@ -327,21 +327,7 @@ module VagrantPlugins 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 + create_dhcp_server_if_necessary(interface, config) end return { @@ -471,6 +457,70 @@ module VagrantPlugins nil end + + #----------------------------------------------------------------- + # DHCP Server Helper Functions + #----------------------------------------------------------------- + + DEFAULT_DHCP_SERVER_FROM_VBOX_INSTALL = { + network_name: 'HostInterfaceNetworking-vboxnet0', + network: 'vboxnet0', + ip: '192.168.56.100', + netmask: '255.255.255.0', + lower: '192.168.56.101', + upper: '192.168.56.254' + }.freeze + + # + # When a host-only network of type: :dhcp is configured, + # this handles the potential creation of a vbox dhcpserver to manage + # it. + # + # @param [Hash] interface hash as returned from read_host_only_interfaces + # @param [Hash] config hash as returned from hostonly_config + def create_dhcp_server_if_necessary(interface, config) + existing_dhcp_server = find_matching_dhcp_server(interface) + + if existing_dhcp_server + if dhcp_server_matches_config?(existing_dhcp_server, config) + @logger.debug("DHCP server already properly configured") + return + elsif existing_dhcp_server == DEFAULT_DHCP_SERVER_FROM_VBOX_INSTALL + @env[:ui].info I18n.t("vagrant.actions.vm.network.cleanup_vbox_default_dhcp") + @env[:machine].provider.driver.remove_dhcp_server(existing_dhcp_server[:network_name]) + else + # We have an invalid DHCP server that we're not able to + # automatically clean up, so we need to give up and tell the user + # to sort out their own vbox dhcpservers and hostonlyifs + raise Vagrant::Errors::NetworkDHCPAlreadyAttached + end + end + + @logger.debug("Creating a DHCP server...") + @env[:machine].provider.driver.create_dhcp_server(interface[:name], config) + end + + # Detect when an existing DHCP server matches precisely the + # requested config for a hostonly interface. + # + # @param [Hash] dhcp_server as found by read_dhcp_servers + # @param [Hash] config as returned from hostonly_config + # @return [Boolean] + def dhcp_server_matches_config?(dhcp_server, config) + dhcp_server[:ip] == config[:dhcp_ip] && + dhcp_server[:lower] == config[:dhcp_lower] && + dhcp_server[:upper] == config[:dhcp_upper] + end + + # Returns the existing dhcp server, if any, that is attached to the + # specified interface. + # + # @return [Hash] dhcp_server or nil if not found + def find_matching_dhcp_server(interface) + @env[:machine].provider.driver.read_dhcp_servers.detect do |dhcp_server| + interface[:name] && interface[:name] == dhcp_server[:network] + end + end end end end diff --git a/plugins/providers/virtualbox/driver/base.rb b/plugins/providers/virtualbox/driver/base.rb index 2cb1a7093..3002418f7 100644 --- a/plugins/providers/virtualbox/driver/base.rb +++ b/plugins/providers/virtualbox/driver/base.rb @@ -180,6 +180,22 @@ module VagrantPlugins def read_bridged_interfaces end + # Returns a list of configured DHCP servers + # + # Each DHCP server is represented as a Hash with the following details: + # + # { + # :network => String, # name of the associated network interface as + # # parsed from the NetworkName, e.g. "vboxnet0" + # :ip => String, # IP address of the DHCP server, e.g. "172.28.128.2" + # :lower => String, # lower IP address of the DHCP lease range, e.g. "172.28.128.3" + # :upper => String, # upper IP address of the DHCP lease range, e.g. "172.28.128.254" + # } + # + # @return [Array] See comment above for details + def read_dhcp_servers + end + # Returns the guest additions version that is installed on this VM. # # @return [String] @@ -196,7 +212,16 @@ module VagrantPlugins # Returns a list of available host only interfaces. # - # @return [Hash] + # Each interface is represented as a Hash with the following details: + # + # { + # :name => String, # interface name, e.g. "vboxnet0" + # :ip => String, # IP address of the interface, e.g. "172.28.128.1" + # :netmask => String, # netmask associated with the interface, e.g. "255.255.255.0" + # :status => String, # status of the interface, e.g. "Up", "Down" + # } + # + # @return [Array] See comment above for details def read_host_only_interfaces end @@ -238,6 +263,13 @@ module VagrantPlugins def read_vms end + # Removes the DHCP server identified by the provided network name. + # + # @param [String] network_name The the full network name associated + # with the DHCP server to be removed, e.g. "HostInterfaceNetworking-vboxnet0" + def remove_dhcp_server(network_name) + end + # Sets the MAC address of the first network adapter. # # @param [String] mac MAC address without any spaces/hyphens. diff --git a/plugins/providers/virtualbox/driver/meta.rb b/plugins/providers/virtualbox/driver/meta.rb index 163631cca..3a3cfd396 100644 --- a/plugins/providers/virtualbox/driver/meta.rb +++ b/plugins/providers/virtualbox/driver/meta.rb @@ -96,6 +96,7 @@ module VagrantPlugins :import, :read_forwarded_ports, :read_bridged_interfaces, + :read_dhcp_servers, :read_guest_additions_version, :read_guest_ip, :read_guest_property, @@ -107,6 +108,7 @@ module VagrantPlugins :read_state, :read_used_ports, :read_vms, + :remove_dhcp_server, :resume, :set_mac_address, :set_name, diff --git a/plugins/providers/virtualbox/driver/version_4_0.rb b/plugins/providers/virtualbox/driver/version_4_0.rb index 1f032f3ed..04070b559 100644 --- a/plugins/providers/virtualbox/driver/version_4_0.rb +++ b/plugins/providers/virtualbox/driver/version_4_0.rb @@ -255,6 +255,27 @@ module VagrantPlugins end end + def read_dhcp_servers + execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| + info = {} + + block.split("\n").each do |line| + if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] + info[:network] = network + info[:network_name] = "HostInterfaceNetworking-#{network}" + elsif ip = line[/^IP:\s+(.+?)$/, 1] + info[:ip] = ip + elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] + info[:lower] = lower + elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] + info[:upper] = upper + end + end + + info + end + end + def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) @@ -287,26 +308,6 @@ module VagrantPlugins end def read_host_only_interfaces - dhcp = {} - execute("list", "dhcpservers", retryable: true).split("\n\n").each do |block| - info = {} - - block.split("\n").each do |line| - if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] - info[:network] = network - elsif ip = line[/^IP:\s+(.+?)$/, 1] - info[:ip] = ip - elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] - info[:lower] = lower - elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] - info[:upper] = upper - end - end - - # Set the DHCP info - dhcp[info[:network]] = info - end - execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} @@ -322,9 +323,6 @@ module VagrantPlugins end end - # Set the DHCP info if it exists - info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] - info end end @@ -429,6 +427,10 @@ module VagrantPlugins results end + def remove_dhcp_server(network_name) + execute("dhcpserver", "remove", "--netname", network_name) + end + def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end diff --git a/plugins/providers/virtualbox/driver/version_4_1.rb b/plugins/providers/virtualbox/driver/version_4_1.rb index 28fe76f4d..b3fd44978 100644 --- a/plugins/providers/virtualbox/driver/version_4_1.rb +++ b/plugins/providers/virtualbox/driver/version_4_1.rb @@ -260,6 +260,27 @@ module VagrantPlugins end end + def read_dhcp_servers + execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| + info = {} + + block.split("\n").each do |line| + if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] + info[:network] = network + info[:network_name] = "HostInterfaceNetworking-#{network}" + elsif ip = line[/^IP:\s+(.+?)$/, 1] + info[:ip] = ip + elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] + info[:lower] = lower + elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] + info[:upper] = upper + end + end + + info + end + end + def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) @@ -292,26 +313,6 @@ module VagrantPlugins end def read_host_only_interfaces - dhcp = {} - execute("list", "dhcpservers", retryable: true).split("\n\n").each do |block| - info = {} - - block.split("\n").each do |line| - if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] - info[:network] = network - elsif ip = line[/^IP:\s+(.+?)$/, 1] - info[:ip] = ip - elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] - info[:lower] = lower - elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] - info[:upper] = upper - end - end - - # Set the DHCP info - dhcp[info[:network]] = info - end - execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} @@ -327,9 +328,6 @@ module VagrantPlugins end end - # Set the DHCP info if it exists - info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] - info end end @@ -434,6 +432,10 @@ module VagrantPlugins results end + def remove_dhcp_server(network_name) + execute("dhcpserver", "remove", "--netname", network_name) + end + def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end diff --git a/plugins/providers/virtualbox/driver/version_4_2.rb b/plugins/providers/virtualbox/driver/version_4_2.rb index e867aa3d3..765855e77 100644 --- a/plugins/providers/virtualbox/driver/version_4_2.rb +++ b/plugins/providers/virtualbox/driver/version_4_2.rb @@ -283,6 +283,27 @@ module VagrantPlugins end end + def read_dhcp_servers + execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| + info = {} + + block.split("\n").each do |line| + if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] + info[:network] = network + info[:network_name] = "HostInterfaceNetworking-#{network}" + elsif ip = line[/^IP:\s+(.+?)$/, 1] + info[:ip] = ip + elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] + info[:lower] = lower + elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] + info[:upper] = upper + end + end + + info + end + end + def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) @@ -323,26 +344,6 @@ module VagrantPlugins end def read_host_only_interfaces - dhcp = {} - execute("list", "dhcpservers", retryable: true).split("\n\n").each do |block| - info = {} - - block.split("\n").each do |line| - if line =~ /^NetworkName:\s+HostInterfaceNetworking-(.+?)$/ - info[:network] = $1.to_s - elsif line =~ /^IP:\s+(.+?)$/ - info[:ip] = $1.to_s - elsif line =~ /^lowerIPAddress:\s+(.+?)$/ - info[:lower] = $1.to_s - elsif line =~ /^upperIPAddress:\s+(.+?)$/ - info[:upper] = $1.to_s - end - end - - # Set the DHCP info - dhcp[info[:network]] = info - end - execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} @@ -358,9 +359,6 @@ module VagrantPlugins end end - # Set the DHCP info if it exists - info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] - info end end @@ -465,6 +463,10 @@ module VagrantPlugins results end + def remove_dhcp_server(network_name) + execute("dhcpserver", "remove", "--netname", network_name) + end + def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end diff --git a/plugins/providers/virtualbox/driver/version_4_3.rb b/plugins/providers/virtualbox/driver/version_4_3.rb index 70bded802..ad3cc4cd9 100644 --- a/plugins/providers/virtualbox/driver/version_4_3.rb +++ b/plugins/providers/virtualbox/driver/version_4_3.rb @@ -292,6 +292,27 @@ module VagrantPlugins end end + def read_dhcp_servers + execute("list", "dhcpservers", retryable: true).split("\n\n").collect do |block| + info = {} + + block.split("\n").each do |line| + if network = line[/^NetworkName:\s+HostInterfaceNetworking-(.+?)$/, 1] + info[:network] = network + info[:network_name] = "HostInterfaceNetworking-#{network}" + elsif ip = line[/^IP:\s+(.+?)$/, 1] + info[:ip] = ip + elsif lower = line[/^lowerIPAddress:\s+(.+?)$/, 1] + info[:lower] = lower + elsif upper = line[/^upperIPAddress:\s+(.+?)$/, 1] + info[:upper] = upper + end + end + + info + end + end + def read_guest_additions_version output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version", retryable: true) @@ -333,26 +354,6 @@ module VagrantPlugins end def read_host_only_interfaces - dhcp = {} - execute("list", "dhcpservers", retryable: true).split("\n\n").each do |block| - info = {} - - block.split("\n").each do |line| - if line =~ /^NetworkName:\s+HostInterfaceNetworking-(.+?)$/ - info[:network] = $1.to_s - elsif line =~ /^IP:\s+(.+?)$/ - info[:ip] = $1.to_s - elsif line =~ /^lowerIPAddress:\s+(.+?)$/ - info[:lower] = $1.to_s - elsif line =~ /^upperIPAddress:\s+(.+?)$/ - info[:upper] = $1.to_s - end - end - - # Set the DHCP info - dhcp[info[:network]] = info - end - execute("list", "hostonlyifs", retryable: true).split("\n\n").collect do |block| info = {} @@ -368,9 +369,6 @@ module VagrantPlugins end end - # Set the DHCP info if it exists - info[:dhcp] = dhcp[info[:name]] if dhcp[info[:name]] - info end end @@ -475,6 +473,10 @@ module VagrantPlugins results end + def remove_dhcp_server(network_name) + execute("dhcpserver", "remove", "--netname", network_name) + end + def set_mac_address(mac) execute("modifyvm", @uuid, "--macaddress1", mac) end diff --git a/templates/locales/en.yml b/templates/locales/en.yml index 908e3bfea..e761ac961 100644 --- a/templates/locales/en.yml +++ b/templates/locales/en.yml @@ -1653,6 +1653,8 @@ en: using the other host only network. preparing: |- Preparing network interfaces based on configuration... + cleanup_vbox_default_dhcp: |- + Found default DHCP server from initial VirtualBox install. Cleaning it up... host_only_network: collides: |- The specified host network collides with a non-hostonly network! diff --git a/test/unit/plugins/providers/virtualbox/action/network_test.rb b/test/unit/plugins/providers/virtualbox/action/network_test.rb new file mode 100644 index 000000000..82dc14865 --- /dev/null +++ b/test/unit/plugins/providers/virtualbox/action/network_test.rb @@ -0,0 +1,141 @@ +require_relative "../base" + +require "vagrant/util/platform" + +describe VagrantPlugins::ProviderVirtualBox::Action::Network do + include_context "unit" + include_context "virtualbox" + + let(:iso_env) do + # We have to create a Vagrantfile so there is a root path + env = isolated_environment + env.vagrantfile("") + env.create_vagrant_env + end + + let(:machine) do + iso_env.machine(iso_env.machine_names[0], :virtualbox).tap do |m| + m.provider.stub(driver: driver) + end + end + + let(:env) {{ machine: machine, ui: machine.ui }} + let(:app) { lambda { |*args| }} + let(:driver) { double("driver") } + + let(:nics) { {} } + + subject { described_class.new(app, env) } + + before do + allow(driver).to receive(:enable_adapters) + allow(driver).to receive(:read_network_interfaces) { nics } + end + + it "calls the next action in the chain" do + called = false + app = lambda { |*args| called = true } + + action = described_class.new(app, env) + action.call(env) + + expect(called).to eq(true) + end + + context "with a dhcp private network" do + let(:bridgedifs) { [] } + let(:hostonlyifs) { [] } + let(:dhcpservers) { [] } + let(:guest) { double("guest") } + let(:network_args) {{ type: :dhcp }} + + before do + machine.config.vm.network 'private_network', network_args + allow(driver).to receive(:read_bridged_interfaces) { bridgedifs } + allow(driver).to receive(:read_host_only_interfaces) { hostonlyifs } + allow(driver).to receive(:read_dhcp_servers) { dhcpservers } + allow(machine).to receive(:guest) { guest } + end + + it "creates a host only interface and a dhcp server using default ips, then tells the guest to configure the network after boot" do + allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} + allow(driver).to receive(:create_dhcp_server) + allow(guest).to receive(:capability) + + subject.call(env) + + expect(driver).to have_received(:create_host_only_network).with({ + adapter_ip: '172.28.128.1', + netmask: '255.255.255.0', + }) + + expect(driver).to have_received(:create_dhcp_server).with('vboxnet0', { + adapter_ip: "172.28.128.1", + auto_config: true, + ip: "172.28.128.1", + mac: nil, + netmask: "255.255.255.0", + nic_type: nil, + type: :dhcp, + dhcp_ip: "172.28.128.2", + dhcp_lower: "172.28.128.3", + dhcp_upper: "172.28.128.254", + adapter: 2 + }) + + expect(guest).to have_received(:capability).with(:configure_networks, [{ + type: :dhcp, + adapter_ip: "172.28.128.1", + ip: "172.28.128.1", + netmask: "255.255.255.0", + auto_config: true, + interface: nil + }]) + end + + context "when the default vbox dhcpserver is present from a fresh vbox install (see issue #3803)" do + let(:dhcpservers) {[ + { + network_name: 'HostInterfaceNetworking-vboxnet0', + network: 'vboxnet0', + ip: '192.168.56.100', + netmask: '255.255.255.0', + lower: '192.168.56.101', + upper: '192.168.56.254' + } + ]} + + it "removes the invalid dhcpserver so it won't collide with any host only interface" do + allow(driver).to receive(:remove_dhcp_server) + allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} + allow(driver).to receive(:create_dhcp_server) + allow(guest).to receive(:capability) + + subject.call(env) + + expect(driver).to have_received(:remove_dhcp_server).with('HostInterfaceNetworking-vboxnet0') + end + + context "but the user has intentionally configured their network just that way" do + let (:network_args) {{ + type: :dhcp, + adapter_ip: '192.168.56.1', + dhcp_ip: '192.168.56.100', + dhcp_lower: '192.168.56.101', + dhcp_upper: '192.168.56.254' + }} + + it "does not attempt to remove the dhcpserver" do + allow(driver).to receive(:remove_dhcp_server) + allow(driver).to receive(:create_host_only_network) {{ name: 'vboxnet0' }} + allow(driver).to receive(:create_dhcp_server) + allow(guest).to receive(:capability) + + subject.call(env) + + expect(driver).not_to have_received(:remove_dhcp_server).with('HostInterfaceNetworking-vboxnet0') + end + end + end + end +end diff --git a/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb b/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb index dfef293ec..3cead74f4 100644 --- a/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb +++ b/test/unit/plugins/providers/virtualbox/action/prepare_nfs_settings_test.rb @@ -22,14 +22,21 @@ describe VagrantPlugins::ProviderVirtualBox::Action::PrepareNFSSettings do let(:env) {{ machine: machine }} let(:app) { lambda { |*args| }} let(:driver) { double("driver") } + let(:host) { double("host") } subject { described_class.new(app, env) } before do env[:test] = true + allow(machine.env).to receive(:host) { host } + allow(host).to receive(:capability).with(:nfs_installed) { true } end it "calls the next action in the chain" do + driver.stub(read_network_interfaces: {2 => {type: :hostonly, hostonly: "vmnet2"}}) + driver.stub(read_host_only_interfaces: [{name: "vmnet2", ip: "1.2.3.4"}]) + allow(driver).to receive(:read_guest_ip).with(1).and_return("2.3.4.5") + called = false app = lambda { |*args| called = true } diff --git a/test/unit/plugins/providers/virtualbox/support/shared/virtualbox_driver_version_4_x_examples.rb b/test/unit/plugins/providers/virtualbox/support/shared/virtualbox_driver_version_4_x_examples.rb index ad99827c6..513f065a7 100644 --- a/test/unit/plugins/providers/virtualbox/support/shared/virtualbox_driver_version_4_x_examples.rb +++ b/test/unit/plugins/providers/virtualbox/support/shared/virtualbox_driver_version_4_x_examples.rb @@ -3,6 +3,75 @@ shared_examples "a version 4.x virtualbox driver" do |options| raise ArgumentError, "Need virtualbox context to use these shared examples." if !(defined? vbox_context) end + describe "read_dhcp_servers" do + before { + expect(subprocess).to receive(:execute). + with("VBoxManage", "list", "dhcpservers", an_instance_of(Hash)). + and_return(subprocess_result(stdout: output)) + } + + context "with empty output" do + let(:output) { "" } + + it "returns an empty list" do + expect(subject.read_dhcp_servers).to eq([]) + end + end + + context "with a single dhcp server" do + let(:output) { + <<-OUTPUT.gsub(/^ */, '') + NetworkName: HostInterfaceNetworking-vboxnet0 + IP: 172.28.128.2 + NetworkMask: 255.255.255.0 + lowerIPAddress: 172.28.128.3 + upperIPAddress: 172.28.128.254 + Enabled: Yes + + OUTPUT + } + + + it "returns a list with one entry describing that server" do + expect(subject.read_dhcp_servers).to eq([{ + network_name: 'HostInterfaceNetworking-vboxnet0', + network: 'vboxnet0', + ip: '172.28.128.2', + lower: '172.28.128.3', + upper: '172.28.128.254', + }]) + end + end + + context "with a multiple dhcp servers" do + let(:output) { + <<-OUTPUT.gsub(/^ */, '') + NetworkName: HostInterfaceNetworking-vboxnet0 + IP: 172.28.128.2 + NetworkMask: 255.255.255.0 + lowerIPAddress: 172.28.128.3 + upperIPAddress: 172.28.128.254 + Enabled: Yes + + NetworkName: HostInterfaceNetworking-vboxnet1 + IP: 10.0.0.2 + NetworkMask: 255.255.255.0 + lowerIPAddress: 10.0.0.3 + upperIPAddress: 10.0.0.254 + Enabled: Yes + OUTPUT + } + + + it "returns a list with one entry for each server" do + expect(subject.read_dhcp_servers).to eq([ + {network_name: 'HostInterfaceNetworking-vboxnet0', network: 'vboxnet0', ip: '172.28.128.2', lower: '172.28.128.3', upper: '172.28.128.254'}, + {network_name: 'HostInterfaceNetworking-vboxnet1', network: 'vboxnet1', ip: '10.0.0.2', lower: '10.0.0.3', upper: '10.0.0.254'}, + ]) + end + end + end + describe "read_guest_property" do it "reads the guest property of the machine referenced by the UUID" do key = "/Foo/Bar" @@ -50,4 +119,96 @@ shared_examples "a version 4.x virtualbox driver" do |options| to raise_error Vagrant::Errors::VirtualBoxGuestPropertyNotFound end end + + describe "read_host_only_interfaces" do + before { + expect(subprocess).to receive(:execute). + with("VBoxManage", "list", "hostonlyifs", an_instance_of(Hash)). + and_return(subprocess_result(stdout: output)) + } + + context "with empty output" do + let(:output) { "" } + + it "returns an empty list" do + expect(subject.read_host_only_interfaces).to eq([]) + end + end + + context "with a single host only interface" do + let(:output) { + <<-OUTPUT.gsub(/^ */, '') + Name: vboxnet0 + GUID: 786f6276-656e-4074-8000-0a0027000000 + DHCP: Disabled + IPAddress: 172.28.128.1 + NetworkMask: 255.255.255.0 + IPV6Address: + IPV6NetworkMaskPrefixLength: 0 + HardwareAddress: 0a:00:27:00:00:00 + MediumType: Ethernet + Status: Up + VBoxNetworkName: HostInterfaceNetworking-vboxnet0 + + OUTPUT + } + + it "returns a list with one entry describing that interface" do + expect(subject.read_host_only_interfaces).to eq([{ + name: 'vboxnet0', + ip: '172.28.128.1', + netmask: '255.255.255.0', + status: 'Up', + }]) + end + end + + context "with multiple host only interfaces" do + let(:output) { + <<-OUTPUT.gsub(/^ */, '') + Name: vboxnet0 + GUID: 786f6276-656e-4074-8000-0a0027000000 + DHCP: Disabled + IPAddress: 172.28.128.1 + NetworkMask: 255.255.255.0 + IPV6Address: + IPV6NetworkMaskPrefixLength: 0 + HardwareAddress: 0a:00:27:00:00:00 + MediumType: Ethernet + Status: Up + VBoxNetworkName: HostInterfaceNetworking-vboxnet0 + + Name: vboxnet1 + GUID: 5764a976-8479-8388-1245-8a0048080840 + DHCP: Disabled + IPAddress: 10.0.0.1 + NetworkMask: 255.255.255.0 + IPV6Address: + IPV6NetworkMaskPrefixLength: 0 + HardwareAddress: 0a:00:27:00:00:01 + MediumType: Ethernet + Status: Up + VBoxNetworkName: HostInterfaceNetworking-vboxnet1 + + OUTPUT + } + + it "returns a list with one entry for each interface" do + expect(subject.read_host_only_interfaces).to eq([ + {name: 'vboxnet0', ip: '172.28.128.1', netmask: '255.255.255.0', status: 'Up'}, + {name: 'vboxnet1', ip: '10.0.0.1', netmask: '255.255.255.0', status: 'Up'}, + ]) + end + end + end + + describe "remove_dhcp_server" do + it "removes the dhcp server with the specified network name" do + expect(subprocess).to receive(:execute). + with("VBoxManage", "dhcpserver", "remove", "--netname", "HostInterfaceNetworking-vboxnet0", an_instance_of(Hash)). + and_return(subprocess_result(stdout: '')) + + subject.remove_dhcp_server("HostInterfaceNetworking-vboxnet0") + end + end end