vagrant-parallels 1.5.1 → 1.6.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +7 -4
- data/CHANGELOG.md +33 -0
- data/Gemfile +2 -2
- data/README.md +1 -1
- data/debug.log +1237 -1
- data/lib/vagrant-parallels/action.rb +8 -0
- data/lib/vagrant-parallels/action/box_register.rb +113 -0
- data/lib/vagrant-parallels/action/box_unregister.rb +43 -0
- data/lib/vagrant-parallels/action/export.rb +36 -35
- data/lib/vagrant-parallels/action/forward_ports.rb +32 -18
- data/lib/vagrant-parallels/action/import.rb +59 -106
- data/lib/vagrant-parallels/action/network.rb +17 -13
- data/lib/vagrant-parallels/action/prepare_clone_snapshot.rb +67 -0
- data/lib/vagrant-parallels/action/set_name.rb +1 -1
- data/lib/vagrant-parallels/action/snapshot_delete.rb +25 -0
- data/lib/vagrant-parallels/action/snapshot_restore.rb +23 -0
- data/lib/vagrant-parallels/action/snapshot_save.rb +23 -0
- data/lib/vagrant-parallels/cap.rb +63 -0
- data/lib/vagrant-parallels/config.rb +7 -3
- data/lib/vagrant-parallels/driver/base.rb +61 -28
- data/lib/vagrant-parallels/driver/meta.rb +20 -10
- data/lib/vagrant-parallels/driver/pd_11.rb +0 -21
- data/lib/vagrant-parallels/errors.rb +14 -6
- data/lib/vagrant-parallels/guest_cap/darwin/install_parallels_tools.rb +34 -0
- data/lib/vagrant-parallels/plugin.rb +23 -8
- data/lib/vagrant-parallels/version.rb +1 -1
- data/locales/en.yml +31 -17
- data/test/acceptance/provider/linked_clone_spec.rb +6 -2
- data/test/acceptance/skeletons/linked_clone/Vagrantfile +1 -1
- data/test/unit/base.rb +1 -0
- data/test/unit/cap_test.rb +96 -0
- data/test/unit/support/shared/pd_driver_examples.rb +7 -10
- metadata +12 -6
- data/lib/vagrant-parallels/cap/forwarded_ports.rb +0 -22
- data/lib/vagrant-parallels/cap/host_address.rb +0 -15
- data/lib/vagrant-parallels/cap/nic_mac_addresses.rb +0 -17
- data/lib/vagrant-parallels/cap/public_address.rb +0 -15
@@ -161,18 +161,23 @@ module VagrantPlugins
|
|
161
161
|
@logger.debug("Bridge was directly specified in config, searching for: #{config[:bridge]}")
|
162
162
|
|
163
163
|
# Search for a matching bridged interface
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
164
|
+
Array(config[:bridge]).each do |bridge|
|
165
|
+
bridge = bridge.downcase if bridge.respond_to?(:downcase)
|
166
|
+
bridgedifs.each do |interface|
|
167
|
+
if bridge === interface[:name].downcase
|
168
|
+
@logger.debug('Specific bridge found as configured in the Vagrantfile. Using it.')
|
169
|
+
chosen_bridge = interface[:name]
|
170
|
+
break
|
171
|
+
end
|
169
172
|
end
|
173
|
+
break if chosen_bridge
|
170
174
|
end
|
171
175
|
|
172
176
|
# If one wasn't found, then we notify the user here.
|
173
177
|
if !chosen_bridge
|
174
|
-
@env[:ui].info I18n.t(
|
175
|
-
|
178
|
+
@env[:ui].info I18n.t(
|
179
|
+
'vagrant.actions.vm.bridged_networking.specific_not_found',
|
180
|
+
bridge: config[:bridge])
|
176
181
|
end
|
177
182
|
end
|
178
183
|
|
@@ -188,14 +193,14 @@ module VagrantPlugins
|
|
188
193
|
else
|
189
194
|
# More than one bridgable interface requires a user decision, so
|
190
195
|
# show options to choose from.
|
191
|
-
@env[:ui].info I18n.t(
|
192
|
-
|
196
|
+
@env[:ui].info I18n.t(
|
197
|
+
'vagrant.actions.vm.bridged_networking.available', prefix: false)
|
193
198
|
bridgedifs.each_index do |index|
|
194
199
|
interface = bridgedifs[index]
|
195
|
-
@env[:ui].info("#{index + 1}) #{interface[:name]}", :
|
200
|
+
@env[:ui].info("#{index + 1}) #{interface[:name]}", prefix: false)
|
196
201
|
end
|
197
202
|
@env[:ui].info(I18n.t(
|
198
|
-
|
203
|
+
'vagrant.actions.vm.bridged_networking.choice_help')+"\n")
|
199
204
|
|
200
205
|
# The range of valid choices
|
201
206
|
valid = Range.new(1, bridgedifs.length)
|
@@ -410,8 +415,7 @@ module VagrantPlugins
|
|
410
415
|
if net_nums.empty?
|
411
416
|
'vagrant-vnet0'
|
412
417
|
else
|
413
|
-
net_nums.
|
414
|
-
free_names = Array(0..net_nums.last.next) - net_nums
|
418
|
+
free_names = Array(0..net_nums.max) - net_nums
|
415
419
|
"vagrant-vnet#{free_names.first}"
|
416
420
|
end
|
417
421
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
require 'digest/md5'
|
4
|
+
|
5
|
+
module VagrantPlugins
|
6
|
+
module Parallels
|
7
|
+
module Action
|
8
|
+
class PrepareCloneSnapshot
|
9
|
+
@@lock = Mutex.new
|
10
|
+
|
11
|
+
def initialize(app, env)
|
12
|
+
@app = app
|
13
|
+
@logger = Log4r::Logger.new('vagrant_parallels::action::prepare_clone_snapshot')
|
14
|
+
end
|
15
|
+
|
16
|
+
def call(env)
|
17
|
+
if !env[:clone_id]
|
18
|
+
@logger.info('No source VM for cloning, skip snapshot preparing')
|
19
|
+
return @app.call(env)
|
20
|
+
end
|
21
|
+
|
22
|
+
# If we're not doing a linked clone, snapshots don't matter
|
23
|
+
if !env[:machine].provider_config.linked_clone \
|
24
|
+
|| env[:machine].provider.pd_version_satisfies?('< 11')
|
25
|
+
return @app.call(env)
|
26
|
+
end
|
27
|
+
|
28
|
+
# We lock so that we don't snapshot in parallel
|
29
|
+
@@lock.synchronize do
|
30
|
+
lock_key = Digest::MD5.hexdigest("#{env[:clone_id]}-snapshot")
|
31
|
+
env[:machine].env.lock(lock_key, retry: true) do
|
32
|
+
prepare_snapshot(env)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
# Continue
|
37
|
+
@app.call(env)
|
38
|
+
end
|
39
|
+
|
40
|
+
protected
|
41
|
+
|
42
|
+
def prepare_snapshot(env)
|
43
|
+
set_snapshot = env[:machine].provider_config.linked_clone_snapshot
|
44
|
+
env[:clone_snapshot] = set_snapshot || 'vagrant_linked_clone'
|
45
|
+
|
46
|
+
# Get the snapshots. We're done if it already exists
|
47
|
+
snapshots = env[:machine].provider.driver.list_snapshots(env[:clone_id])
|
48
|
+
|
49
|
+
if snapshots.include?(env[:clone_snapshot])
|
50
|
+
env[:clone_snapshot_id] = snapshots[env[:clone_snapshot]]
|
51
|
+
@logger.info('Linked clone snapshot already exists, doing nothing')
|
52
|
+
return
|
53
|
+
end
|
54
|
+
|
55
|
+
# We've specified the snapshot name but it doesn't exist
|
56
|
+
if set_snapshot
|
57
|
+
raise Errors::SnapshotNotFound, snapshot: set_snapshot
|
58
|
+
end
|
59
|
+
|
60
|
+
@logger.info('Creating a new snapshot for linked clone')
|
61
|
+
env[:clone_snapshot_id] = env[:machine].provider.driver.create_snapshot(
|
62
|
+
env[:clone_id], env[:clone_snapshot])
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
@@ -37,7 +37,7 @@ module VagrantPlugins
|
|
37
37
|
else
|
38
38
|
env[:ui].info(I18n.t(
|
39
39
|
'vagrant.actions.vm.set_name.setting_name', name: name))
|
40
|
-
env[:machine].provider.driver.set_name(name)
|
40
|
+
env[:machine].provider.driver.set_name(env[:machine].id, name)
|
41
41
|
end
|
42
42
|
|
43
43
|
# Create the sentinel
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Parallels
|
3
|
+
module Action
|
4
|
+
class SnapshotDelete
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
snapshots = env[:machine].provider.driver.list_snapshots(env[:machine].id)
|
11
|
+
snapshot_id = snapshots[env[:snapshot_name]]
|
12
|
+
|
13
|
+
env[:ui].info I18n.t('vagrant.actions.vm.snapshot.deleting',
|
14
|
+
name: env[:snapshot_name])
|
15
|
+
env[:machine].provider.driver.delete_snapshot(
|
16
|
+
env[:machine].id, snapshot_id)
|
17
|
+
|
18
|
+
env[:ui].success I18n.t('vagrant.actions.vm.snapshot.deleted',
|
19
|
+
name: env[:snapshot_name])
|
20
|
+
@app.call(env)
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Parallels
|
3
|
+
module Action
|
4
|
+
class SnapshotRestore
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
snapshots = env[:machine].provider.driver.list_snapshots(env[:machine].id)
|
11
|
+
snapshot_id = snapshots[env[:snapshot_name]]
|
12
|
+
|
13
|
+
env[:ui].info I18n.t('vagrant.actions.vm.snapshot.restoring',
|
14
|
+
name: env[:snapshot_name])
|
15
|
+
env[:machine].provider.driver.restore_snapshot(
|
16
|
+
env[:machine].id, snapshot_id)
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Parallels
|
3
|
+
module Action
|
4
|
+
class SnapshotSave
|
5
|
+
def initialize(app, env)
|
6
|
+
@app = app
|
7
|
+
end
|
8
|
+
|
9
|
+
def call(env)
|
10
|
+
env[:ui].info I18n.t('vagrant.actions.vm.snapshot.saving',
|
11
|
+
name: env[:snapshot_name])
|
12
|
+
env[:machine].provider.driver.create_snapshot(
|
13
|
+
env[:machine].id, env[:snapshot_name])
|
14
|
+
|
15
|
+
env[:ui].success I18n.t('vagrant.actions.vm.snapshot.saved',
|
16
|
+
name: env[:snapshot_name])
|
17
|
+
|
18
|
+
@app.call(env)
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,63 @@
|
|
1
|
+
module VagrantPlugins
|
2
|
+
module Parallels
|
3
|
+
module Cap
|
4
|
+
# Reads the forwarded ports that currently exist on the machine
|
5
|
+
# itself.
|
6
|
+
#
|
7
|
+
# This also may not match up with configured forwarded ports, because
|
8
|
+
# Vagrant auto port collision fixing may have taken place.
|
9
|
+
#
|
10
|
+
# @return [Hash<Integer, Integer>] Host => Guest port mappings.
|
11
|
+
def self.forwarded_ports(machine)
|
12
|
+
return nil if machine.state.id != :running
|
13
|
+
|
14
|
+
{}.tap do |result|
|
15
|
+
machine.provider.driver.read_forwarded_ports.each do |fp|
|
16
|
+
result[fp[:hostport]] = fp[:guestport]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
# Returns host's IP address that can be used to access the host machine
|
22
|
+
# from the VM.
|
23
|
+
#
|
24
|
+
# @return [String] Host's IP address
|
25
|
+
def self.host_address(machine)
|
26
|
+
|
27
|
+
shared_iface = machine.provider.driver.read_shared_interface
|
28
|
+
return shared_iface[:ip] if shared_iface
|
29
|
+
|
30
|
+
nil
|
31
|
+
end
|
32
|
+
|
33
|
+
# Reads the network interface card MAC addresses and returns them.
|
34
|
+
#
|
35
|
+
# @return [Hash<Integer, String>] Adapter => MAC address
|
36
|
+
def self.nic_mac_addresses(machine)
|
37
|
+
nic_macs = machine.provider.driver.read_mac_addresses
|
38
|
+
|
39
|
+
# Make numeration starting from 1, as it is expected in Vagrant.
|
40
|
+
Hash[nic_macs.map.with_index{ |mac, index| [index+1, mac] }]
|
41
|
+
end
|
42
|
+
|
43
|
+
# Returns guest's IP address that can be used to access the VM from the
|
44
|
+
# host machine.
|
45
|
+
#
|
46
|
+
# @return [String] Guest's IP address
|
47
|
+
def self.public_address(machine)
|
48
|
+
return nil if machine.state.id != :running
|
49
|
+
|
50
|
+
ssh_info = machine.ssh_info
|
51
|
+
return nil if !ssh_info
|
52
|
+
ssh_info[:host]
|
53
|
+
end
|
54
|
+
|
55
|
+
# Returns a list of the snapshots that are taken on this machine.
|
56
|
+
#
|
57
|
+
# @return [Array<String>] Snapshot Name
|
58
|
+
def self.snapshot_list(machine)
|
59
|
+
machine.provider.driver.list_snapshots(machine.id).keys
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
@@ -6,7 +6,8 @@ module VagrantPlugins
|
|
6
6
|
attr_accessor :destroy_unused_network_interfaces
|
7
7
|
attr_accessor :functional_psf
|
8
8
|
attr_accessor :optimize_power_consumption
|
9
|
-
attr_accessor :
|
9
|
+
attr_accessor :linked_clone
|
10
|
+
attr_accessor :linked_clone_snapshot
|
10
11
|
attr_accessor :name
|
11
12
|
attr_reader :network_adapters
|
12
13
|
attr_accessor :regen_src_uuid
|
@@ -17,13 +18,15 @@ module VagrantPlugins
|
|
17
18
|
|
18
19
|
# Compatibility with old names
|
19
20
|
alias :regen_box_uuid= :regen_src_uuid=
|
21
|
+
alias :use_linked_clone= :linked_clone=
|
20
22
|
|
21
23
|
def initialize
|
22
24
|
@check_guest_tools = UNSET_VALUE
|
23
25
|
@customizations = []
|
24
26
|
@destroy_unused_network_interfaces = UNSET_VALUE
|
25
27
|
@functional_psf = UNSET_VALUE
|
26
|
-
@
|
28
|
+
@linked_clone = UNSET_VALUE
|
29
|
+
@linked_clone_snapshot = UNSET_VALUE
|
27
30
|
@network_adapters = {}
|
28
31
|
@name = UNSET_VALUE
|
29
32
|
@optimize_power_consumption = UNSET_VALUE
|
@@ -77,7 +80,8 @@ module VagrantPlugins
|
|
77
80
|
@functional_psf = true
|
78
81
|
end
|
79
82
|
|
80
|
-
@
|
83
|
+
@linked_clone = false if @linked_clone == UNSET_VALUE
|
84
|
+
@linked_clone_snapshot = nil if @linked_clone_snapshot == UNSET_VALUE
|
81
85
|
|
82
86
|
@name = nil if @name == UNSET_VALUE
|
83
87
|
|
@@ -55,12 +55,12 @@ module VagrantPlugins
|
|
55
55
|
# Makes a clone of the virtual machine.
|
56
56
|
#
|
57
57
|
# @param [String] src_name Name or UUID of the source VM or template.
|
58
|
-
# @param [String] dst_name Name of the destination VM.
|
59
58
|
# @param [<String => String>] options Options to clone virtual machine.
|
60
59
|
# @return [String] UUID of the new VM.
|
61
|
-
def clone_vm(src_name,
|
60
|
+
def clone_vm(src_name, options={})
|
61
|
+
dst_name = "vagrant_temp_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
|
62
|
+
|
62
63
|
args = ['clone', src_name, '--name', dst_name]
|
63
|
-
args << '--template' if options[:template]
|
64
64
|
args.concat(['--dst', options[:dst]]) if options[:dst]
|
65
65
|
|
66
66
|
# Linked clone options
|
@@ -127,11 +127,16 @@ module VagrantPlugins
|
|
127
127
|
|
128
128
|
# Creates a snapshot for the specified virtual machine.
|
129
129
|
#
|
130
|
-
# @param [String] uuid Name or UUID of the target VM
|
131
|
-
# @param [
|
130
|
+
# @param [String] uuid Name or UUID of the target VM
|
131
|
+
# @param [String] snapshot_name Snapshot name
|
132
132
|
# @return [String] ID of the created snapshot.
|
133
|
-
def create_snapshot(uuid,
|
134
|
-
|
133
|
+
def create_snapshot(uuid, snapshot_name)
|
134
|
+
stdout = execute_prlctl('snapshot', uuid, '--name', snapshot_name)
|
135
|
+
if stdout =~ /\{([\w-]+)\}/
|
136
|
+
return Regexp.last_match(1)
|
137
|
+
end
|
138
|
+
|
139
|
+
raise Errors::SnapshotIdNotDetected, stdout: stdout
|
135
140
|
end
|
136
141
|
|
137
142
|
# Deletes the virtual machine references by this driver.
|
@@ -148,6 +153,14 @@ module VagrantPlugins
|
|
148
153
|
end
|
149
154
|
end
|
150
155
|
|
156
|
+
# Deletes the specified snapshot
|
157
|
+
#
|
158
|
+
# @param [String] uuid Name or UUID of the target VM
|
159
|
+
# @param [String] snapshot_id Snapshot ID
|
160
|
+
def delete_snapshot(uuid, snapshot_id)
|
161
|
+
execute_prlctl('snapshot-delete', uuid, '--id', snapshot_id)
|
162
|
+
end
|
163
|
+
|
151
164
|
# Deletes any host only networks that aren't being used for anything.
|
152
165
|
def delete_unused_host_only_networks
|
153
166
|
raise NotImplementedError
|
@@ -203,6 +216,35 @@ module VagrantPlugins
|
|
203
216
|
raise NotImplementedError
|
204
217
|
end
|
205
218
|
|
219
|
+
# Lists all snapshots for the specified VM. Returns an empty hash if
|
220
|
+
# there are no snapshots.
|
221
|
+
#
|
222
|
+
# @param [String] uuid Name or UUID of the target VM.
|
223
|
+
# @return [<String => String>] {'Snapshot Name' => 'Snapshot UUID'}
|
224
|
+
def list_snapshots(uuid)
|
225
|
+
settings = read_settings(uuid)
|
226
|
+
snap_config = File.join(settings.fetch('Home'), 'Snapshots.xml')
|
227
|
+
|
228
|
+
# There are no snapshots, exit
|
229
|
+
return {} if !File.exist?(snap_config)
|
230
|
+
|
231
|
+
xml = Nokogiri::XML(File.read(snap_config))
|
232
|
+
snapshots = {}
|
233
|
+
|
234
|
+
# Loop over all 'SavedStateItem' and fetch 'Name' => 'ID' pairs
|
235
|
+
xml.xpath('//SavedStateItem').each do |snap|
|
236
|
+
snap_id = snap.attr('guid')
|
237
|
+
|
238
|
+
# The first entry is always empty (the base sate)
|
239
|
+
next if snap_id.empty?
|
240
|
+
|
241
|
+
snap_name = snap.at('Name').text
|
242
|
+
snapshots[snap_name] = snap_id
|
243
|
+
end
|
244
|
+
|
245
|
+
snapshots
|
246
|
+
end
|
247
|
+
|
206
248
|
# Halts the virtual machine (pulls the plug).
|
207
249
|
def halt(force=false)
|
208
250
|
args = ['stop', @uuid]
|
@@ -247,15 +289,6 @@ module VagrantPlugins
|
|
247
289
|
bridged_ifaces
|
248
290
|
end
|
249
291
|
|
250
|
-
# Returns current snapshot ID for the specified VM. Returns nil if
|
251
|
-
# the VM doesn't have any snapshot.
|
252
|
-
#
|
253
|
-
# @param [String] uuid Name or UUID of the target VM.
|
254
|
-
# @return [String]
|
255
|
-
def read_current_snapshot(uuid)
|
256
|
-
raise NotImplementedError
|
257
|
-
end
|
258
|
-
|
259
292
|
def read_forwarded_ports(global=false)
|
260
293
|
raise NotImplementedError
|
261
294
|
end
|
@@ -491,8 +524,9 @@ module VagrantPlugins
|
|
491
524
|
# Registers the virtual machine
|
492
525
|
#
|
493
526
|
# @param [String] pvm_file Path to the machine image (*.pvm)
|
494
|
-
|
495
|
-
|
527
|
+
# @param [Array<String>] opts List of options for "prlctl register"
|
528
|
+
def register(pvm_file, opts=[])
|
529
|
+
args = [@prlctl_path, 'register', pvm_file, *opts]
|
496
530
|
|
497
531
|
3.times do
|
498
532
|
result = raw(*args)
|
@@ -513,14 +547,12 @@ module VagrantPlugins
|
|
513
547
|
execute(*args)
|
514
548
|
end
|
515
549
|
|
516
|
-
#
|
550
|
+
# Switches the VM state to the specified snapshot
|
517
551
|
#
|
518
|
-
# @
|
519
|
-
|
520
|
-
|
521
|
-
|
522
|
-
execute_prlctl(*args).include?(uuid) ||
|
523
|
-
execute_prlctl(*args, '--template').include?(uuid)
|
552
|
+
# @param [String] uuid Name or UUID of the target VM
|
553
|
+
# @param [String] snapshot_id Snapshot ID
|
554
|
+
def restore_snapshot(uuid, snapshot_id)
|
555
|
+
execute_prlctl('snapshot-switch', uuid, '-i', snapshot_id)
|
524
556
|
end
|
525
557
|
|
526
558
|
# Resumes the virtual machine.
|
@@ -531,9 +563,10 @@ module VagrantPlugins
|
|
531
563
|
|
532
564
|
# Sets the name of the virtual machine.
|
533
565
|
#
|
534
|
-
# @param [String]
|
535
|
-
|
536
|
-
|
566
|
+
# @param [String] uuid VM name or UUID
|
567
|
+
# @param [String] new_name New VM name
|
568
|
+
def set_name(uuid, new_name)
|
569
|
+
execute_prlctl('set', uuid, '--name', new_name)
|
537
570
|
end
|
538
571
|
|
539
572
|
# Sets Power Consumption method.
|