VirtualBox base class for the driver.
This commit is contained in:
parent
cab625c06c
commit
767ee2509e
|
@ -1,19 +1,18 @@
|
|||
require 'log4r'
|
||||
require 'vagrant/util/busy'
|
||||
require 'vagrant/util/platform'
|
||||
|
||||
require 'vagrant/driver/virtualbox_base'
|
||||
require 'vagrant/util/subprocess'
|
||||
|
||||
module Vagrant
|
||||
module Driver
|
||||
# This class contains the logic to drive VirtualBox.
|
||||
class VirtualBox
|
||||
#
|
||||
# Read the VirtualBoxBase source for documentation on each method.
|
||||
class VirtualBox < VirtualBoxBase
|
||||
# This is raised if the VM is not found when initializing a driver
|
||||
# with a UUID.
|
||||
class VMNotFound < StandardError; end
|
||||
|
||||
# Include this so we can use `Subprocess` more easily.
|
||||
include Vagrant::Util
|
||||
|
||||
# The UUID of the virtual machine we represent
|
||||
attr_reader :uuid
|
||||
|
||||
|
@ -21,30 +20,12 @@ module Vagrant
|
|||
attr_reader :version
|
||||
|
||||
def initialize(uuid)
|
||||
# Setup the base
|
||||
super()
|
||||
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox")
|
||||
@uuid = uuid
|
||||
|
||||
# This flag is used to keep track of interrupted state (SIGINT)
|
||||
@interrupted = false
|
||||
|
||||
# Set the path to VBoxManage
|
||||
@vboxmanage_path = "VBoxManage"
|
||||
|
||||
if Util::Platform.windows?
|
||||
@logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage")
|
||||
|
||||
# On Windows, we use the VBOX_INSTALL_PATH environmental
|
||||
# variable to find VBoxManage.
|
||||
if ENV.has_key?("VBOX_INSTALL_PATH")
|
||||
# The path usually ends with a \ but we make sure here
|
||||
path = ENV["VBOX_INSTALL_PATH"]
|
||||
path += "\\" if !path.end_with?("\\")
|
||||
@vboxmanage_path = "#{path}VBoxManage.exe"
|
||||
end
|
||||
end
|
||||
|
||||
@logger.info("VBoxManage path: #{@vboxmanage_path}")
|
||||
|
||||
# Read and assign the version of VirtualBox we know which
|
||||
# specific driver to instantiate.
|
||||
begin
|
||||
|
@ -63,8 +44,6 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This clears the forwarded ports that have been set on the
|
||||
# virtual machine.
|
||||
def clear_forwarded_ports
|
||||
args = []
|
||||
read_forwarded_ports(@uuid).each do |nic, name, _, _|
|
||||
|
@ -74,8 +53,6 @@ module Vagrant
|
|||
execute("modifyvm", @uuid, *args) if !args.empty?
|
||||
end
|
||||
|
||||
# This clears all the shared folders that have been set
|
||||
# on the virtual machine.
|
||||
def clear_shared_folders
|
||||
execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line|
|
||||
if line =~ /^SharedFolderNameMachineMapping\d+="(.+?)"$/
|
||||
|
@ -84,7 +61,6 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# Creates a host only network with the given options.
|
||||
def create_host_only_network(options)
|
||||
# Create the interface
|
||||
execute("hostonlyif", "create") =~ /^Interface '(.+?)' was successfully created$/
|
||||
|
@ -103,12 +79,10 @@ module Vagrant
|
|||
}
|
||||
end
|
||||
|
||||
# This deletes the VM with the given name.
|
||||
def delete
|
||||
execute("unregistervm", @uuid, "--delete")
|
||||
end
|
||||
|
||||
# Deletes any host only networks that aren't being used for anything.
|
||||
def delete_unused_host_only_networks
|
||||
networks = []
|
||||
execute("list", "hostonlyifs").split("\n").each do |line|
|
||||
|
@ -130,12 +104,10 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# Discards any saved state associated with this VM.
|
||||
def discard_saved_state
|
||||
execute("discardstate", @uuid)
|
||||
end
|
||||
|
||||
# Enables network adapters on this virtual machine.
|
||||
def enable_adapters(adapters)
|
||||
args = []
|
||||
adapters.each do |adapter|
|
||||
|
@ -160,39 +132,15 @@ module Vagrant
|
|||
execute("modifyvm", @uuid, *args)
|
||||
end
|
||||
|
||||
# Executes a raw command.
|
||||
def execute_command(command)
|
||||
raw(*command)
|
||||
end
|
||||
|
||||
# Exports the virtual machine to the given path.
|
||||
#
|
||||
# @param [String] path Path to the OVF file.
|
||||
def export(path)
|
||||
# TODO: Progress
|
||||
execute("export", @uuid, "--output", path.to_s)
|
||||
end
|
||||
|
||||
# Forwards a set of ports for a VM.
|
||||
#
|
||||
# This will not affect any previously set forwarded ports,
|
||||
# so be sure to delete those if you need to.
|
||||
#
|
||||
# The format of each port hash should be the following:
|
||||
#
|
||||
# {
|
||||
# :name => "foo",
|
||||
# :hostport => 8500,
|
||||
# :guestport => 80,
|
||||
# :adapter => 1,
|
||||
# :protocol => "tcp"
|
||||
# }
|
||||
#
|
||||
# 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)
|
||||
args = []
|
||||
ports.each do |options|
|
||||
|
@ -210,13 +158,10 @@ module Vagrant
|
|||
execute("modifyvm", @uuid, *args)
|
||||
end
|
||||
|
||||
# Halts the virtual machine.
|
||||
def halt
|
||||
execute("controlvm", @uuid, "poweroff")
|
||||
end
|
||||
|
||||
# Imports the VM with the given path to the OVF file. It returns
|
||||
# the UUID as a string.
|
||||
def import(ovf, name)
|
||||
total = ""
|
||||
last = 0
|
||||
|
@ -249,10 +194,6 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# This returns a list of the forwarded ports in the form
|
||||
# of `[nic, name, hostport, guestport]`.
|
||||
#
|
||||
# @return [Array<Array>]
|
||||
def read_forwarded_ports(uuid=nil, active_only=false)
|
||||
uuid ||= @uuid
|
||||
|
||||
|
@ -282,7 +223,6 @@ module Vagrant
|
|||
results
|
||||
end
|
||||
|
||||
# This reads the list of host only networks.
|
||||
def read_bridged_interfaces
|
||||
execute("list", "bridgedifs").split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
@ -304,14 +244,12 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This reads the guest additions version for a VM.
|
||||
def read_guest_additions_version
|
||||
output = execute("guestproperty", "get", @uuid, "/VirtualBox/GuestAdd/Version")
|
||||
return $1.to_s if output =~ /^Value: (.+?)$/
|
||||
return nil
|
||||
end
|
||||
|
||||
# Reads and returns the available host only interfaces.
|
||||
def read_host_only_interfaces
|
||||
execute("list", "hostonlyifs").split("\n\n").collect do |block|
|
||||
info = {}
|
||||
|
@ -330,7 +268,6 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# Reads the MAC address of the first network interface.
|
||||
def read_mac_address
|
||||
execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line|
|
||||
return $1.to_s if line =~ /^macaddress1="(.+?)"$/
|
||||
|
@ -339,7 +276,6 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# This reads the folder where VirtualBox places it's VMs.
|
||||
def read_machine_folder
|
||||
execute("list", "systemproperties").split("\n").each do |line|
|
||||
if line =~ /^Default machine folder:\s+(.+?)$/i
|
||||
|
@ -350,10 +286,6 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# This reads the network interfaces and returns various information
|
||||
# about them.
|
||||
#
|
||||
# @return [Hash]
|
||||
def read_network_interfaces
|
||||
nics = {}
|
||||
execute("showvminfo", @uuid, "--machinereadable").split("\n").each do |line|
|
||||
|
@ -381,8 +313,6 @@ module Vagrant
|
|||
nics
|
||||
end
|
||||
|
||||
# This reads the state for the given UUID. The state of the VM
|
||||
# will be returned as a symbol.
|
||||
def read_state
|
||||
output = execute("showvminfo", @uuid, "--machinereadable")
|
||||
if output =~ /^name="<inaccessible>"$/
|
||||
|
@ -394,8 +324,6 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# This will read all the used ports for port forwarding by
|
||||
# all virtual machines.
|
||||
def read_used_ports
|
||||
ports = []
|
||||
execute("list", "vms").split("\n").each do |line|
|
||||
|
@ -414,22 +342,10 @@ module Vagrant
|
|||
ports
|
||||
end
|
||||
|
||||
# This sets the MAC address for a network adapter.
|
||||
def set_mac_address(mac)
|
||||
execute("modifyvm", @uuid, "--macaddress1", mac)
|
||||
end
|
||||
|
||||
# Sets up the shared folder metadata for a virtual machine.
|
||||
#
|
||||
# The structure of a folder definition should be the following:
|
||||
#
|
||||
# {
|
||||
# :name => "foo",
|
||||
# :hostpath => "/foo/bar"
|
||||
# }
|
||||
#
|
||||
# @param [Array<Hash>] folders An array of folder definitions to
|
||||
# setup.
|
||||
def share_folders(folders)
|
||||
folders.each do |folder|
|
||||
execute("sharedfolder", "add", @uuid, "--name",
|
||||
|
@ -437,7 +353,6 @@ module Vagrant
|
|||
end
|
||||
end
|
||||
|
||||
# This reads the SSH port from the VM.
|
||||
def ssh_port(expected_port)
|
||||
# Look for the forwarded port only by comparing the guest port
|
||||
read_forwarded_ports.each do |_, _, hostport, guestport|
|
||||
|
@ -447,21 +362,14 @@ module Vagrant
|
|||
nil
|
||||
end
|
||||
|
||||
# Starts the virtual machine in the given mode.
|
||||
#
|
||||
# @param [String] mode Mode to boot the VM: either "headless" or "gui"
|
||||
def start(mode)
|
||||
execute("startvm", @uuid, "--type", mode.to_s)
|
||||
end
|
||||
|
||||
# Suspends the virtual machine.
|
||||
def suspend
|
||||
execute("controlvm", @uuid, "savestate")
|
||||
end
|
||||
|
||||
# Verifies that an image can be imported properly.
|
||||
#
|
||||
# @return [Boolean]
|
||||
def verify_image(path)
|
||||
r = raw("import", path.to_s, "--dry-run")
|
||||
return r.exit_code == 0
|
||||
|
@ -482,38 +390,6 @@ module Vagrant
|
|||
# Below accounts for all of these:
|
||||
execute("--version").split("_")[0].split("r")[0]
|
||||
end
|
||||
|
||||
# Execute the given subcommand for VBoxManage and return the output.
|
||||
def execute(*command, &block)
|
||||
# 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.")
|
||||
else
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
end
|
||||
|
||||
# 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
|
||||
@logger.info("Interrupted.")
|
||||
end
|
||||
|
||||
Util::Busy.busy(int_callback) do
|
||||
Subprocess.execute(@vboxmanage_path, *command, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
|
@ -0,0 +1,8 @@
|
|||
module Vagrant
|
||||
module Driver
|
||||
# Driver to communicate with VirtualBox 4.0.x
|
||||
class VirtualBox_4_0
|
||||
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,10 @@
|
|||
module Vagrant
|
||||
module Driver
|
||||
# Driver to communicate with VirtualBox 4.1.x
|
||||
class VirtualBox_4_1
|
||||
def initialize(uuid)
|
||||
@uuid = uuid
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
|
@ -0,0 +1,264 @@
|
|||
require 'log4r'
|
||||
|
||||
require 'vagrant/util/busy'
|
||||
require 'vagrant/util/platform'
|
||||
require 'vagrant/util/subprocess'
|
||||
|
||||
module Vagrant
|
||||
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 VirtualBoxBase
|
||||
# Include this so we can use `Subprocess` more easily.
|
||||
include Vagrant::Util
|
||||
|
||||
def initialize
|
||||
@logger = Log4r::Logger.new("vagrant::driver::virtualbox_base")
|
||||
|
||||
# This flag is used to keep track of interrupted state (SIGINT)
|
||||
@interrupted = false
|
||||
|
||||
# Set the path to VBoxManage
|
||||
@vboxmanage_path = "VBoxManage"
|
||||
|
||||
if Util::Platform.windows?
|
||||
@logger.debug("Windows. Trying VBOX_INSTALL_PATH for VBoxManage")
|
||||
|
||||
# On Windows, we use the VBOX_INSTALL_PATH environmental
|
||||
# variable to find VBoxManage.
|
||||
if ENV.has_key?("VBOX_INSTALL_PATH")
|
||||
# The path usually ends with a \ but we make sure here
|
||||
path = ENV["VBOX_INSTALL_PATH"]
|
||||
path += "\\" if !path.end_with?("\\")
|
||||
@vboxmanage_path = "#{path}VBoxManage.exe"
|
||||
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 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:
|
||||
#
|
||||
# {
|
||||
# :type => :hostonly,
|
||||
# :hostonly => "vboxnet0",
|
||||
# :mac_address => "tubes"
|
||||
# }
|
||||
#
|
||||
# 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.
|
||||
#
|
||||
# @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:
|
||||
#
|
||||
# {
|
||||
# :name => "foo",
|
||||
# :hostport => 8500,
|
||||
# :guestport => 80,
|
||||
# :adapter => 1,
|
||||
# :protocol => "tcp"
|
||||
# }
|
||||
#
|
||||
# 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.
|
||||
# @param [String] name Name of the VM.
|
||||
# @return [String] UUID of the imported VM.
|
||||
def import
|
||||
end
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# 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
|
||||
|
||||
# Verifies that an image can be imported properly.
|
||||
#
|
||||
# @param [String] path Path to an OVF file.
|
||||
# @return [Boolean]
|
||||
def verify_image(path)
|
||||
end
|
||||
|
||||
protected
|
||||
|
||||
# Execute the given subcommand for VBoxManage and return the output.
|
||||
def execute(*command, &block)
|
||||
# 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.")
|
||||
else
|
||||
raise Errors::VBoxManageError, :command => command.inspect
|
||||
end
|
||||
end
|
||||
|
||||
# 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
|
||||
@logger.info("Interrupted.")
|
||||
end
|
||||
|
||||
Util::Busy.busy(int_callback) do
|
||||
Subprocess.execute(@vboxmanage_path, *command, &block)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue