2012-07-25 04:32:38 +00:00
|
|
|
require 'log4r'
|
|
|
|
|
|
|
|
require 'vagrant/util/busy'
|
|
|
|
require 'vagrant/util/platform'
|
|
|
|
require 'vagrant/util/retryable'
|
|
|
|
require 'vagrant/util/subprocess'
|
|
|
|
|
|
|
|
module VagrantPlugins
|
|
|
|
module ProviderVirtualBox
|
|
|
|
module Driver
|
|
|
|
# Base class for all VirtualBox drivers.
|
|
|
|
#
|
|
|
|
# This class provides useful tools for things such as executing
|
|
|
|
# VBoxManage and handling SIGINTs and so on.
|
|
|
|
class Base
|
|
|
|
# Include this so we can use `Subprocess` more easily.
|
|
|
|
include Vagrant::Util::Retryable
|
|
|
|
|
|
|
|
def initialize
|
|
|
|
@logger = Log4r::Logger.new("vagrant::provider::virtualbox::base")
|
|
|
|
|
|
|
|
# This flag is used to keep track of interrupted state (SIGINT)
|
|
|
|
@interrupted = false
|
|
|
|
|
|
|
|
# Set the path to VBoxManage
|
|
|
|
@vboxmanage_path = "VBoxManage"
|
|
|
|
|
2013-02-10 09:40:59 +00:00
|
|
|
if Vagrant::Util::Platform.windows? || Vagrant::Util::Platform.cygwin?
|
2012-07-25 04:32:38 +00:00
|
|
|
@logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage")
|
|
|
|
|
|
|
|
# On Windows, we use the VBOX_INSTALL_PATH environmental
|
|
|
|
# variable to find VBoxManage.
|
2014-05-21 02:37:18 +00:00
|
|
|
if ENV.has_key?("VBOX_INSTALL_PATH") ||
|
|
|
|
ENV.has_key?("VBOX_MSI_INSTALL_PATH")
|
2012-07-25 04:32:38 +00:00
|
|
|
# Get the path.
|
2014-05-21 02:37:18 +00:00
|
|
|
path = ENV["VBOX_INSTALL_PATH"] || ENV["VBOX_MSI_INSTALL_PATH"]
|
2012-07-25 04:32:38 +00:00
|
|
|
@logger.debug("VBOX_INSTALL_PATH value: #{path}")
|
|
|
|
|
|
|
|
# There can actually be multiple paths in here, so we need to
|
|
|
|
# split by the separator ";" and see which is a good one.
|
|
|
|
path.split(";").each do |single|
|
|
|
|
# Make sure it ends with a \
|
|
|
|
single += "\\" if !single.end_with?("\\")
|
|
|
|
|
|
|
|
# If the executable exists, then set it as the main path
|
|
|
|
# and break out
|
2014-04-26 08:05:32 +00:00
|
|
|
vboxmanage = "#{single}VBoxManage.exe"
|
2013-02-10 11:49:15 +00:00
|
|
|
if File.file?(vboxmanage)
|
2013-04-07 05:06:33 +00:00
|
|
|
@vboxmanage_path = Vagrant::Util::Platform.cygwin_windows_path(vboxmanage)
|
2012-07-25 04:32:38 +00:00
|
|
|
break
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
|
|
|
|
@logger.info("VBoxManage path: #{@vboxmanage_path}")
|
|
|
|
end
|
|
|
|
|
|
|
|
# Clears the forwarded ports that have been set on the virtual machine.
|
|
|
|
def clear_forwarded_ports
|
|
|
|
end
|
|
|
|
|
|
|
|
# Clears the shared folders that have been set on the virtual machine.
|
|
|
|
def clear_shared_folders
|
|
|
|
end
|
|
|
|
|
|
|
|
# Creates a DHCP server for a host only network.
|
|
|
|
#
|
|
|
|
# @param [String] network Name of the host-only network.
|
|
|
|
# @param [Hash] options Options for the DHCP server.
|
|
|
|
def create_dhcp_server(network, options)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Creates a host only network with the given options.
|
|
|
|
#
|
|
|
|
# @param [Hash] options Options to create the host only network.
|
|
|
|
# @return [Hash] The details of the host only network, including
|
|
|
|
# keys `:name`, `:ip`, and `:netmask`
|
|
|
|
def create_host_only_network(options)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Deletes the virtual machine references by this driver.
|
|
|
|
def delete
|
|
|
|
end
|
|
|
|
|
|
|
|
# Deletes any host only networks that aren't being used for anything.
|
|
|
|
def delete_unused_host_only_networks
|
|
|
|
end
|
|
|
|
|
|
|
|
# Discards any saved state associated with this VM.
|
|
|
|
def discard_saved_state
|
|
|
|
end
|
|
|
|
|
|
|
|
# Enables network adapters on the VM.
|
|
|
|
#
|
|
|
|
# The format of each adapter specification should be like so:
|
|
|
|
#
|
|
|
|
# {
|
2014-05-22 16:35:12 +00:00
|
|
|
# type: :hostonly,
|
|
|
|
# hostonly: "vboxnet0",
|
|
|
|
# mac_address: "tubes"
|
2012-07-25 04:32:38 +00:00
|
|
|
# }
|
|
|
|
#
|
|
|
|
# This must support setting up both host only and bridged networks.
|
|
|
|
#
|
|
|
|
# @param [Array<Hash>] adapters Array of adapters to enable.
|
|
|
|
def enable_adapters(adapters)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Execute a raw command straight through to VBoxManage.
|
|
|
|
#
|
2014-05-22 16:35:12 +00:00
|
|
|
# Accepts a retryable: true option if the command should be retried
|
2013-11-09 23:16:47 +00:00
|
|
|
# upon failure.
|
|
|
|
#
|
|
|
|
# Raises a VBoxManage error if it fails.
|
|
|
|
#
|
2012-07-25 04:32:38 +00:00
|
|
|
# @param [Array] command Command to execute.
|
|
|
|
def execute_command(command)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Exports the virtual machine to the given path.
|
|
|
|
#
|
|
|
|
# @param [String] path Path to the OVF file.
|
|
|
|
# @yield [progress] Yields the block with the progress of the export.
|
|
|
|
def export(path)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Forwards a set of ports for a VM.
|
|
|
|
#
|
|
|
|
# This will not affect any previously set forwarded ports,
|
|
|
|
# so be sure to delete those if you need to.
|
|
|
|
#
|
|
|
|
# The format of each port hash should be the following:
|
|
|
|
#
|
|
|
|
# {
|
2014-05-22 16:35:12 +00:00
|
|
|
# name: "foo",
|
|
|
|
# hostport: 8500,
|
|
|
|
# guestport: 80,
|
|
|
|
# adapter: 1,
|
|
|
|
# protocol: "tcp"
|
2012-07-25 04:32:38 +00:00
|
|
|
# }
|
|
|
|
#
|
|
|
|
# Note that "adapter" and "protocol" are optional and will default
|
|
|
|
# to 1 and "tcp" respectively.
|
|
|
|
#
|
|
|
|
# @param [Array<Hash>] ports An array of ports to set. See documentation
|
|
|
|
# for more information on the format.
|
|
|
|
def forward_ports(ports)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Halts the virtual machine (pulls the plug).
|
|
|
|
def halt
|
|
|
|
end
|
|
|
|
|
|
|
|
# Imports the VM from an OVF file.
|
|
|
|
#
|
|
|
|
# @param [String] ovf Path to the OVF file.
|
|
|
|
# @return [String] UUID of the imported VM.
|
|
|
|
def import(ovf)
|
|
|
|
end
|
|
|
|
|
2013-08-29 23:50:20 +00:00
|
|
|
# Returns the maximum number of network adapters.
|
|
|
|
def max_network_adapters
|
|
|
|
8
|
|
|
|
end
|
|
|
|
|
2012-07-25 04:32:38 +00:00
|
|
|
# Returns a list of forwarded ports for a VM.
|
|
|
|
#
|
|
|
|
# @param [String] uuid UUID of the VM to read from, or `nil` if this
|
|
|
|
# VM.
|
|
|
|
# @param [Boolean] active_only If true, only VMs that are running will
|
|
|
|
# be checked.
|
|
|
|
# @return [Array<Array>]
|
|
|
|
def read_forwarded_ports(uuid=nil, active_only=false)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a list of bridged interfaces.
|
|
|
|
#
|
|
|
|
# @return [Hash]
|
|
|
|
def read_bridged_interfaces
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the guest additions version that is installed on this VM.
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def read_guest_additions_version
|
|
|
|
end
|
|
|
|
|
2013-11-22 18:26:36 +00:00
|
|
|
# Returns the value of a guest property on the current VM.
|
|
|
|
#
|
|
|
|
# @param [String] property the name of the guest property to read
|
|
|
|
# @return [String] value of the guest property
|
|
|
|
# @raise [VirtualBoxGuestPropertyNotFound] if the guest property does not have a value
|
|
|
|
def read_guest_property(property)
|
|
|
|
end
|
|
|
|
|
2012-07-25 04:32:38 +00:00
|
|
|
# Returns a list of available host only interfaces.
|
|
|
|
#
|
|
|
|
# @return [Hash]
|
|
|
|
def read_host_only_interfaces
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the MAC address of the first network interface.
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def read_mac_address
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the folder where VirtualBox places it's VMs.
|
|
|
|
#
|
|
|
|
# @return [String]
|
|
|
|
def read_machine_folder
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a list of network interfaces of the VM.
|
|
|
|
#
|
|
|
|
# @return [Hash]
|
|
|
|
def read_network_interfaces
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns the current state of this VM.
|
|
|
|
#
|
|
|
|
# @return [Symbol]
|
|
|
|
def read_state
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a list of all forwarded ports in use by active
|
|
|
|
# virtual machines.
|
|
|
|
#
|
|
|
|
# @return [Array]
|
|
|
|
def read_used_ports
|
|
|
|
end
|
|
|
|
|
|
|
|
# Returns a list of all UUIDs of virtual machines currently
|
|
|
|
# known by VirtualBox.
|
|
|
|
#
|
|
|
|
# @return [Array<String>]
|
|
|
|
def read_vms
|
|
|
|
end
|
|
|
|
|
|
|
|
# Sets the MAC address of the first network adapter.
|
|
|
|
#
|
|
|
|
# @param [String] mac MAC address without any spaces/hyphens.
|
|
|
|
def set_mac_address(mac)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Share a set of folders on this VM.
|
|
|
|
#
|
|
|
|
# @param [Array<Hash>] folders
|
|
|
|
def share_folders(folders)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Reads the SSH port of this VM.
|
|
|
|
#
|
|
|
|
# @param [Integer] expected Expected guest port of SSH.
|
|
|
|
def ssh_port(expected)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Starts the virtual machine.
|
|
|
|
#
|
|
|
|
# @param [String] mode Mode to boot the VM. Either "headless"
|
|
|
|
# or "gui"
|
|
|
|
def start(mode)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Suspend the virtual machine.
|
|
|
|
def suspend
|
|
|
|
end
|
|
|
|
|
2014-04-16 17:24:33 +00:00
|
|
|
# Unshare folders.
|
|
|
|
def unshare_folders(names)
|
|
|
|
end
|
|
|
|
|
2012-07-25 04:32:38 +00:00
|
|
|
# Verifies that the driver is ready to accept work.
|
|
|
|
#
|
|
|
|
# This should raise a VagrantError if things are not ready.
|
|
|
|
def verify!
|
|
|
|
end
|
|
|
|
|
|
|
|
# Verifies that an image can be imported properly.
|
|
|
|
#
|
|
|
|
# @param [String] path Path to an OVF file.
|
|
|
|
# @return [Boolean]
|
|
|
|
def verify_image(path)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Checks if a VM with the given UUID exists.
|
|
|
|
#
|
|
|
|
# @return [Boolean]
|
|
|
|
def vm_exists?(uuid)
|
|
|
|
end
|
|
|
|
|
|
|
|
# Execute the given subcommand for VBoxManage and return the output.
|
|
|
|
def execute(*command, &block)
|
|
|
|
# Get the options hash if it exists
|
|
|
|
opts = {}
|
|
|
|
opts = command.pop if command.last.is_a?(Hash)
|
|
|
|
|
|
|
|
tries = 0
|
|
|
|
tries = 3 if opts[:retryable]
|
|
|
|
|
|
|
|
# Variable to store our execution result
|
|
|
|
r = nil
|
|
|
|
|
2014-05-22 16:35:12 +00:00
|
|
|
retryable(on: Vagrant::Errors::VBoxManageError, tries: tries, sleep: 1) do
|
2013-10-11 17:32:54 +00:00
|
|
|
# If there is an error with VBoxManage, this gets set to true
|
|
|
|
errored = false
|
2013-11-22 18:26:36 +00:00
|
|
|
|
2012-07-25 04:32:38 +00:00
|
|
|
# Execute the command
|
|
|
|
r = raw(*command, &block)
|
|
|
|
|
|
|
|
# If the command was a failure, then raise an exception that is
|
|
|
|
# nicely handled by Vagrant.
|
|
|
|
if r.exit_code != 0
|
|
|
|
if @interrupted
|
|
|
|
@logger.info("Exit code != 0, but interrupted. Ignoring.")
|
2013-03-29 20:14:00 +00:00
|
|
|
elsif r.exit_code == 126
|
2013-03-29 20:15:11 +00:00
|
|
|
# This exit code happens if VBoxManage is on the PATH,
|
|
|
|
# but another executable it tries to execute is missing.
|
|
|
|
# This is usually indicative of a corrupted VirtualBox install.
|
2013-03-29 20:14:00 +00:00
|
|
|
raise Vagrant::Errors::VBoxManageNotFoundError
|
2012-07-25 04:32:38 +00:00
|
|
|
else
|
2013-04-01 03:45:54 +00:00
|
|
|
errored = true
|
2012-07-25 04:32:38 +00:00
|
|
|
end
|
|
|
|
else
|
|
|
|
# Sometimes, VBoxManage fails but doesn't actual return a non-zero
|
|
|
|
# exit code. For this we inspect the output and determine if an error
|
|
|
|
# occurred.
|
2013-04-30 16:45:00 +00:00
|
|
|
|
|
|
|
if r.stderr =~ /failed to open \/dev\/vboxnetctl/i
|
|
|
|
# This catches an error message that only shows when kernel
|
|
|
|
# drivers aren't properly installed.
|
|
|
|
@logger.error("Error message about unable to open vboxnetctl")
|
|
|
|
raise Vagrant::Errors::VirtualBoxKernelModuleNotLoaded
|
|
|
|
end
|
|
|
|
|
2013-04-29 17:11:20 +00:00
|
|
|
if r.stderr =~ /VBoxManage([.a-z]+?): error:/
|
2013-04-30 16:45:00 +00:00
|
|
|
# This catches the generic VBoxManage error case.
|
2012-07-25 04:32:38 +00:00
|
|
|
@logger.info("VBoxManage error text found, assuming error.")
|
2013-04-01 03:45:54 +00:00
|
|
|
errored = true
|
2012-07-25 04:32:38 +00:00
|
|
|
end
|
|
|
|
end
|
2013-11-22 18:26:36 +00:00
|
|
|
|
2013-10-11 17:32:54 +00:00
|
|
|
# If there was an error running VBoxManage, show the error and the
|
|
|
|
# output.
|
|
|
|
if errored
|
|
|
|
raise Vagrant::Errors::VBoxManageError,
|
2014-05-22 16:35:12 +00:00
|
|
|
command: command.inspect,
|
|
|
|
stderr: r.stderr
|
2013-10-11 17:32:54 +00:00
|
|
|
end
|
2013-04-01 03:45:54 +00:00
|
|
|
end
|
|
|
|
|
2012-07-25 04:32:38 +00:00
|
|
|
# Return the output, making sure to replace any Windows-style
|
|
|
|
# newlines with Unix-style.
|
|
|
|
r.stdout.gsub("\r\n", "\n")
|
|
|
|
end
|
|
|
|
|
|
|
|
# Executes a command and returns the raw result object.
|
|
|
|
def raw(*command, &block)
|
|
|
|
int_callback = lambda do
|
|
|
|
@interrupted = true
|
2014-02-24 06:09:15 +00:00
|
|
|
|
|
|
|
# We have to execute this in a thread due to trap contexts
|
|
|
|
# and locks.
|
|
|
|
Thread.new { @logger.info("Interrupted.") }.join
|
2012-07-25 04:32:38 +00:00
|
|
|
end
|
|
|
|
|
|
|
|
# Append in the options for subprocess
|
2014-05-22 16:35:12 +00:00
|
|
|
command << { notify: [:stdout, :stderr] }
|
2012-07-25 04:32:38 +00:00
|
|
|
|
|
|
|
Vagrant::Util::Busy.busy(int_callback) do
|
|
|
|
Vagrant::Util::Subprocess.execute(@vboxmanage_path, *command, &block)
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|
|
|
|
end
|