Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 1 addition & 5 deletions lib/vagrant/environment.rb
Original file line number Diff line number Diff line change
Expand Up @@ -526,11 +526,7 @@ def lock(name="global", **opts)
if name != "dotlock"
lock("dotlock", retry: true) do
f.close
begin
File.delete(lock_path)
rescue
@logger.debug("Failed to delete lock file #{lock_path} - some other thread might be trying to acquire it -> ignoring this error")
end
File.delete(lock_path)
end
end

Expand Down
8 changes: 0 additions & 8 deletions lib/vagrant/errors.rb
Original file line number Diff line number Diff line change
Expand Up @@ -784,14 +784,6 @@ class VMBootTimeout < VagrantError
error_key(:boot_timeout)
end

class VMCloneFailure < VagrantError
error_key(:failure, "vagrant.actions.vm.clone")
end

class VMCreateMasterFailure < VagrantError
error_key(:failure, "vagrant.actions.vm.clone.create_master")
end

class VMCustomizationFailed < VagrantError
error_key(:failure, "vagrant.actions.vm.customize")
end
Expand Down
10 changes: 1 addition & 9 deletions plugins/providers/virtualbox/action.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@ module Action
autoload :CleanMachineFolder, File.expand_path("../action/clean_machine_folder", __FILE__)
autoload :ClearForwardedPorts, File.expand_path("../action/clear_forwarded_ports", __FILE__)
autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__)
autoload :CreateClone, File.expand_path("../action/create_clone", __FILE__)
autoload :Created, File.expand_path("../action/created", __FILE__)
autoload :Customize, File.expand_path("../action/customize", __FILE__)
autoload :Destroy, File.expand_path("../action/destroy", __FILE__)
Expand All @@ -22,7 +21,6 @@ module Action
autoload :ForcedHalt, File.expand_path("../action/forced_halt", __FILE__)
autoload :ForwardPorts, File.expand_path("../action/forward_ports", __FILE__)
autoload :Import, File.expand_path("../action/import", __FILE__)
autoload :ImportMaster, File.expand_path("../action/import_master", __FILE__)
autoload :IsPaused, File.expand_path("../action/is_paused", __FILE__)
autoload :IsRunning, File.expand_path("../action/is_running", __FILE__)
autoload :IsSaved, File.expand_path("../action/is_saved", __FILE__)
Expand Down Expand Up @@ -325,13 +323,7 @@ def self.action_up
if !env[:result]
b2.use CheckAccessible
b2.use Customize, "pre-import"

if env[:machine].provider_config.use_linked_clone
b2.use ImportMaster
b2.use CreateClone
else
b2.use Import
end
b2.use Import
b2.use MatchMACAddress
end
end
Expand Down
51 changes: 0 additions & 51 deletions plugins/providers/virtualbox/action/create_clone.rb

This file was deleted.

3 changes: 2 additions & 1 deletion plugins/providers/virtualbox/action/import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,8 @@ def call(env)

# Import the virtual machine
ovf_file = env[:machine].box.directory.join("box.ovf").to_s
env[:machine].id = env[:machine].provider.driver.import(ovf_file) do |progress|
method = (env[:machine].provider_config.use_linked_clone) ? :import_multiattach : :import
env[:machine].id = env[:machine].provider.driver.send(method, ovf_file) do |progress|
env[:ui].clear_line
env[:ui].report_progress(progress, 100, false)
end
Expand Down
66 changes: 0 additions & 66 deletions plugins/providers/virtualbox/action/import_master.rb

This file was deleted.

6 changes: 3 additions & 3 deletions plugins/providers/virtualbox/config.rb
Original file line number Diff line number Diff line change
Expand Up @@ -32,12 +32,12 @@ class Config < Vagrant.plugin("2", :config)
# @return [Boolean]
attr_accessor :gui

# If set to `true`, then a linked clone is created from a master
# VM generated from the specified box.
# If set to `true`, then a linked clone is created from the
# specified box and a differencing disk image for every linked clone.
#
# @return [Boolean]
attr_accessor :use_linked_clone

# This should be set to the name of the machine in the VirtualBox
# GUI.
#
Expand Down
156 changes: 156 additions & 0 deletions plugins/providers/virtualbox/driver/base.rb
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
require 'log4r'
require 'nokogiri'

require 'vagrant/util/busy'
require 'vagrant/util/platform'
Expand Down Expand Up @@ -56,6 +57,44 @@ def initialize
@logger.info("VBoxManage path: #{@vboxmanage_path}")
end

# Attaches the specified disk images to the specified VM.
#
# The format of each virtual disk should be:
#
# {
# :controller => "SATA Controller", # Name of the controller to attach disk image
# :port => "0", # Port to attach disk image
# :device => "0", # Device to attach disk image
# :file => "/path/to/image.vmdk" # Path to the disk image file
# }
#
# @param [String] machine_id The id of the VM.
# @param [Array<Hash>] virtual_disks Array of disk images to attach.
def attach_virtual_disks(machine_id, virtual_disks)
# Find existing disks known to VirtualBox
output = execute("list", "-l", "hdds")
lines = output.split("\n")
known_files = lines.select() { |line| line.start_with?('Location:') }
.map() { |line| File.absolute_path(line.sub(/^Location:\s*([^\s](.*))$/, '\1')) }

# Attach disks to the VM
virtual_disks.each do |disk|
params = [
"--storagectl", disk[:controller],
"--port", disk[:port],
"--device", disk[:device],
"--type", "hdd",
"--medium", disk[:file]
]

if known_files.none? { |known_file| File.identical?(known_file, disk[:file]) }
params << "--mtype" << "multiattach"
end

execute("storageattach", machine_id, *params)
end
end

# Clears the forwarded ports that have been set on the virtual machine.
def clear_forwarded_ports
end
Expand Down Expand Up @@ -148,6 +187,17 @@ def export(path)
def forward_ports(ports)
end

# Gets the UUID of the VM with the specified name.
#
# @param [String] machine_name The name of the VM.
# @return [String] UUID of the VM.
def get_machine_id(machine_name)
output = execute("list", "vms", retryable: true)
match = /^"#{Regexp.escape(machine_name)}" \{(.+?)\}$/.match(output)
return match[1].to_s if match
nil
end

# Halts the virtual machine (pulls the plug).
def halt
end
Expand All @@ -159,11 +209,117 @@ def halt
def import(ovf)
end

# Imports the VM from an OVF file. The disk images in the box are used
# in multiattach mode, speeding the import process.
#
# @param [String] ovf Path to the OVF file.
# @return [String] UUID of the imported VM.
def import_multiattach(ovf)
virtual_disks, doc = parse_ovf(ovf)

# Write out a new OVF with no references to disk images
ovf = File.absolute_path("box-linked-clone.ovf", File.dirname(ovf))
f = File.open(ovf, 'w')
doc.write_xml_to(f)
f.close()

# Import the OVF that has no disks
machine_id = import(ovf)

attach_virtual_disks(machine_id, virtual_disks)

return machine_id
end

# Returns the maximum number of network adapters.
def max_network_adapters
8
end

# Parses the OVF. The disk images referenced by the OVF are removed
# from the returned DOM and returned as a separate return value.
#
# @param [String] ovf_file Path to the OVF file.
def parse_ovf(ovf_file)
# Parse the OVF
f = File.open(ovf_file)
doc = Nokogiri::XML(f)
f.close()

# Extract prefixes for the namespaces we care about
ovf = rasd = vbox = ""
doc.namespaces.each do |prefix, namespace|
prefix_start = prefix.index(":")
if prefix_start != nil
prefix_start += 1
case namespace
when "http://schemas.dmtf.org/ovf/envelope/1"
ovf = prefix[prefix_start..-1]
when "http://schemas.dmtf.org/wbem/wscim/1/cim-schema/2/CIM_ResourceAllocationSettingData"
rasd = prefix[prefix_start..-1]
when "http://www.virtualbox.org/ovf/machine"
vbox = prefix[prefix_start..-1]
end
end
end

if ovf == "" or rasd == "" or vbox == ""
raise Vagrant::Errors::VMImportFailure
end

# Create a map of disk UUIDs to the disk file paths
# Also remove the Disk, File, and VirtalHardwareSection/Item nodes associated with the disks
ovf_base_dir = File.dirname(ovf_file)
uuid_path_map = {}
disks = doc.xpath("//#{ovf}:Disk")
disks.each do |disk|
fileRef = disk["#{ovf}:fileRef"]

# Skip empty disks
next if fileRef == nil

uuid = disk["#{vbox}:uuid"]
diskId = disk["#{ovf}:diskId"]

# Compute the path to the disk image and remove the associated File node
files = doc.xpath("//#{ovf}:References/#{ovf}:File[@#{ovf}:id='#{fileRef}']")
file = files.first
href = file["#{ovf}:href"]
uuid_path_map[uuid] = File.absolute_path(href, ovf_base_dir)
files.remove

# Remove the associated VirtualHardwareSection/Item
items = doc.xpath("//#{ovf}:Item[#{rasd}:HostResource/.='/disk/#{diskId}']")
items.remove

disk.remove
end

# Find all StorageControllers/StorageController/AttachedDevice that point to Disks and
# remove them, taking note of the controller name to which they are attached and the
# port and device numbers where they are attached.
virtual_disks = []
controllers = doc.xpath("//#{ovf}:StorageController")
controllers.each do |controller|
name = controller["name"]
devices = controller.xpath("#{ovf}:AttachedDevice")
devices.each do |device|
portIndex = device["port"]
deviceIndex = device["device"]
uuidNode = device.xpath("#{ovf}:Image/@uuid").first

if uuidNode != nil
uuid = uuidNode.content[1..-2]
@logger.debug("Found virtual disk #{uuid} on '#{name}':#{portIndex}:#{deviceIndex} at #{uuid_path_map[uuid]}")
virtual_disks.push({:controller => name, :file => uuid_path_map[uuid], :port => portIndex, :device => deviceIndex})
device.remove
end
end
end

return virtual_disks, doc
end

# Returns a list of forwarded ports for a VM.
#
# @param [String] uuid UUID of the VM to read from, or `nil` if this
Expand Down
Loading