Skip to content

Commit

Permalink
Release v1.6.1
Browse files Browse the repository at this point in the history
* master:
  Bump version to 1.6.1
  Update changelog
  Fix detection of the next virtual network ID
  action/unregister: Add parallel-safe lock to prevent concurrent writes to box lease file
  Call fsync and flash before closing the box lease file
  Use temporary lock file for boxes to prevent early unregister in the race
  action/box_register: Remove curly brackets from the box UUID
  Pass "--regenerate-src-uuid" option to the command "clone"
  • Loading branch information
legal90 committed Jan 13, 2016
2 parents 1bc0640 + 127f0dc commit 65c31d8
Show file tree
Hide file tree
Showing 7 changed files with 92 additions and 72 deletions.
10 changes: 10 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,3 +1,13 @@
## 1.6.1 (January 13, 2016)

BUG FIXES:
- action/import: Fix `regenerate_src_uuid` option behavior in parallel run
[[GH-241](https://github.com/Parallels/vagrant-parallels/pull/241)]
- action/box_unregister: Use temporary lock file to prevent early unregister
in parallel run [[GH-244](https://github.com/Parallels/vagrant-parallels/pull/244)]
- action/network: Fix detection of the next virtual network ID [[GH-245](https://github.com/Parallels/vagrant-parallels/pull/245)]


## 1.6.0 (December 24, 2015)

BREAKING CHANGES:
Expand Down
34 changes: 26 additions & 8 deletions lib/vagrant-parallels/action/box_register.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
require 'fileutils'
require 'log4r'

module VagrantPlugins
Expand Down Expand Up @@ -58,13 +59,35 @@ def box_id(env, box_path)
config: tpl_config
end

id
id.delete('{}')
end

def lease_box_lock(env)
lease_file = env[:machine].box.directory.join('box_lease_count')

# If the temporary file, verify it is not too old. If its older than
# 1 hour, delete it first because previous run may be failed.
if lease_file.file? && lease_file.mtime.to_i < Time.now.to_i - 60 * 60
lease_file.delete
end

# Increment a counter in the file. Create the file if it doesn't exist
FileUtils.touch(lease_file)
File.open(lease_file ,'r+') do |file|
num = file.gets.to_i
file.rewind
file.puts num.next
file.fsync
file.flush
end
end

def register_box(env)
box_id_file = env[:machine].box.directory.join('box_id')
# Increment the lock counter in the temporary lease file
lease_box_lock(env)

# Read the master ID if we have it in the file.
# Read the box ID if we have it in the file.
box_id_file = env[:machine].box.directory.join('box_id')
env[:clone_id] = box_id_file.read.chomp if box_id_file.file?

# If we have the ID and the VM exists already, then we
Expand All @@ -84,11 +107,6 @@ def register_box(env)
# We need the box ID to be the same for all parallel runs
options = ['--preserve-uuid']

if env[:machine].provider_config.regen_src_uuid \
&& env[:machine].provider.pd_version_satisfies?('>= 10.1.2')
options << '--regenerate-src-uuid'
end

# Register the box VM image
env[:machine].provider.driver.register(pvm, options)
env[:clone_id] = box_id(env, pvm)
Expand Down
34 changes: 33 additions & 1 deletion lib/vagrant-parallels/action/box_unregister.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ module VagrantPlugins
module Parallels
module Action
class BoxUnregister
@@lock = Mutex.new

def initialize(app, env)
@app = app
@logger = Log4r::Logger.new('vagrant_parallels::action::box_unregister')
Expand All @@ -15,7 +17,12 @@ def call(env)
return @app.call(env)
end

unregister_box(env)
@@lock.synchronize do
lock_key = Digest::MD5.hexdigest(env[:machine].box.name)
env[:machine].env.lock(lock_key, retry: true) do
unregister_box(env)
end
end

# If we got interrupted, then the import could have been
# interrupted and its not a big deal. Just return out.
Expand All @@ -31,7 +38,32 @@ def recover(env)

private

def release_box_lock(lease_file)
return if !lease_file.file?

# Decrement the counter in the lease file
File.open(lease_file,'r+') do |file|
num = file.gets.to_i
file.rewind
file.puts(num - 1)
file.fsync
file.flush
end

# Delete the lease file if we are the last who need this box.
# Then the box image will be unregistered.
lease_file.delete if lease_file.read.chomp.to_i <= 1
end

def unregister_box(env)
# Release the box lock
lease_file = env[:machine].box.directory.join('box_lease_count')
release_box_lock(lease_file)

# Do not unregister the box image if the temporary lease file exists
# Most likely it is cloning to another Vagrant env (in parallel run)
return if lease_file.file?

if env[:clone_id] && env[:machine].provider.driver.vm_exists?(env[:clone_id])
env[:ui].info I18n.t('vagrant_parallels.actions.vm.box.unregister')
env[:machine].provider.driver.unregister(env[:clone_id])
Expand Down
39 changes: 16 additions & 23 deletions lib/vagrant-parallels/action/import.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,8 @@ def initialize(app, env)
end

def call(env)
options = {}

# Disable requiring password for register and clone actions [GH-67].
# It is available only since PD 10.
if env[:machine].provider.pd_version_satisfies?('>= 10')
Expand All @@ -22,18 +24,27 @@ def call(env)
env[:machine].provider.driver.disable_password_restrictions(acts)
end

if env[:machine].provider_config.regen_src_uuid \
&& env[:machine].provider.pd_version_satisfies?('>= 10.1.2')
options[:regenerate_src_uuid] = true
end

# Linked clones are supported only for PD 11 and higher
if env[:machine].provider_config.linked_clone \
&& env[:machine].provider.pd_version_satisfies?('>= 11')
# Linked clone creation should not be concurrent [GH-206]
options[:snapshot_id] = env[:clone_snapshot_id]
options[:linked] = true
@@lock.synchronize do
lock_key = Digest::MD5.hexdigest("#{env[:clone_id]}-linked-clone")
env[:machine].env.lock(lock_key, retry: true) do
clone_linked(env)
env[:ui].info I18n.t('vagrant_parallels.actions.vm.clone.linked')
clone(env, options)
end
end
else
clone_full(env)
env[:ui].info I18n.t('vagrant_parallels.actions.vm.clone.full')
clone(env, options)
end

# If we got interrupted, then the import could have been
Expand All @@ -44,7 +55,7 @@ def call(env)
raise Errors::VMCloneFailure if !env[:machine].id

if env[:machine].provider_config.regen_src_uuid \
&& env[:machine].provider.pd_version_satisfies?('< 11')
&& env[:machine].provider.pd_version_satisfies?('< 10.1.2')
@logger.info('Regenerate SourceVmUuid by editing config.pvs file')
env[:machine].provider.driver.regenerate_src_uuid
end
Expand Down Expand Up @@ -88,27 +99,9 @@ def recover(env)

protected

def clone_full(env)
env[:ui].info I18n.t('vagrant_parallels.actions.vm.clone.full')
env[:machine].id = env[:machine].provider.driver.clone_vm(
env[:clone_id]) do |progress|
env[:ui].clear_line
env[:ui].report_progress(progress, 100, false)
end

# Clear the line one last time since the progress meter doesn't disappear
# immediately.
env[:ui].clear_line
end

def clone_linked(env)
opts = {
snapshot_id: env[:clone_snapshot_id],
linked: true
}
env[:ui].info I18n.t('vagrant_parallels.actions.vm.clone.linked')
def clone(env, options)
env[:machine].id = env[:machine].provider.driver.clone_vm(
env[:clone_id], opts) do |progress|
env[:clone_id], options) do |progress|
env[:ui].clear_line
env[:ui].report_progress(progress, 100, false)
end
Expand Down
2 changes: 1 addition & 1 deletion lib/vagrant-parallels/action/network.rb
Original file line number Diff line number Diff line change
Expand Up @@ -415,7 +415,7 @@ def next_network_id
if net_nums.empty?
'vagrant-vnet0'
else
free_names = Array(0..net_nums.max) - net_nums
free_names = Array(0..net_nums.max.next) - net_nums
"vagrant-vnet#{free_names.first}"
end
end
Expand Down
43 changes: 5 additions & 38 deletions lib/vagrant-parallels/driver/base.rb
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ def clone_vm(src_name, options={})
args << '--linked' if options[:linked]
args.concat(['--id', options[:snapshot_id]]) if options[:snapshot_id]

# Regenerate SourceVmUuid of the cloned VM
args << '--regenerate-src-uuid' if options[:regenerate_src_uuid]

execute_prlctl(*args) do |_, data|
lines = data.split('\r')
# The progress of the clone will be in the last line. Do a greedy
Expand Down Expand Up @@ -526,25 +529,7 @@ def regenerate_src_uuid
# @param [String] pvm_file Path to the machine image (*.pvm)
# @param [Array<String>] opts List of options for "prlctl register"
def register(pvm_file, opts=[])
args = [@prlctl_path, 'register', pvm_file, *opts]

3.times do
result = raw(*args)
# Exit if everything is OK
return if result.exit_code == 0

# It may occur in the race condition with other Vagrant processes.
# It is OK, just exit.
return if result.stderr.include?('is already registered.')

# Sleep a bit though to give Parallels Desktop time to fix itself
sleep 2
end

# If we reach this point, it means that we consistently got the
# failure, do a standard execute now. This will raise an
# exception if it fails again.
execute(*args)
execute_prlctl('register', pvm_file, *opts)
end

# Switches the VM state to the specified snapshot
Expand Down Expand Up @@ -619,25 +604,7 @@ def suspend
# Virtual machine will be removed from the VM list, but its image will
# not be deleted from the disk. So, it can be registered again.
def unregister(uuid)
args = [@prlctl_path, 'unregister', uuid]
3.times do
result = raw(*args)
# Exit if everything is OK
return if result.exit_code == 0

# It may occur in the race condition with other Vagrant processes.
# Both are OK, just exit.
return if result.stderr.include?('is not registered')
return if result.stderr.include?('is being cloned')

# Sleep a bit though to give Parallels Desktop time to fix itself
sleep 2
end

# If we reach this point, it means that we consistently got the
# failure, do a standard execute now. This will raise an
# exception if it fails again.
execute(*args)
execute_prlctl('unregister', uuid)
end

# Unshare folders.
Expand Down
2 changes: 1 addition & 1 deletion lib/vagrant-parallels/version.rb
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
module VagrantPlugins
module Parallels
VERSION = '1.6.0'
VERSION = '1.6.1'
end
end

0 comments on commit 65c31d8

Please sign in to comment.