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.
Files changed (38) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +7 -4
  3. data/CHANGELOG.md +33 -0
  4. data/Gemfile +2 -2
  5. data/README.md +1 -1
  6. data/debug.log +1237 -1
  7. data/lib/vagrant-parallels/action.rb +8 -0
  8. data/lib/vagrant-parallels/action/box_register.rb +113 -0
  9. data/lib/vagrant-parallels/action/box_unregister.rb +43 -0
  10. data/lib/vagrant-parallels/action/export.rb +36 -35
  11. data/lib/vagrant-parallels/action/forward_ports.rb +32 -18
  12. data/lib/vagrant-parallels/action/import.rb +59 -106
  13. data/lib/vagrant-parallels/action/network.rb +17 -13
  14. data/lib/vagrant-parallels/action/prepare_clone_snapshot.rb +67 -0
  15. data/lib/vagrant-parallels/action/set_name.rb +1 -1
  16. data/lib/vagrant-parallels/action/snapshot_delete.rb +25 -0
  17. data/lib/vagrant-parallels/action/snapshot_restore.rb +23 -0
  18. data/lib/vagrant-parallels/action/snapshot_save.rb +23 -0
  19. data/lib/vagrant-parallels/cap.rb +63 -0
  20. data/lib/vagrant-parallels/config.rb +7 -3
  21. data/lib/vagrant-parallels/driver/base.rb +61 -28
  22. data/lib/vagrant-parallels/driver/meta.rb +20 -10
  23. data/lib/vagrant-parallels/driver/pd_11.rb +0 -21
  24. data/lib/vagrant-parallels/errors.rb +14 -6
  25. data/lib/vagrant-parallels/guest_cap/darwin/install_parallels_tools.rb +34 -0
  26. data/lib/vagrant-parallels/plugin.rb +23 -8
  27. data/lib/vagrant-parallels/version.rb +1 -1
  28. data/locales/en.yml +31 -17
  29. data/test/acceptance/provider/linked_clone_spec.rb +6 -2
  30. data/test/acceptance/skeletons/linked_clone/Vagrantfile +1 -1
  31. data/test/unit/base.rb +1 -0
  32. data/test/unit/cap_test.rb +96 -0
  33. data/test/unit/support/shared/pd_driver_examples.rb +7 -10
  34. metadata +12 -6
  35. data/lib/vagrant-parallels/cap/forwarded_ports.rb +0 -22
  36. data/lib/vagrant-parallels/cap/host_address.rb +0 -15
  37. data/lib/vagrant-parallels/cap/nic_mac_addresses.rb +0 -17
  38. 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
- bridgedifs.each do |interface|
165
- if interface[:name].downcase == config[:bridge].downcase
166
- @logger.debug('Specific bridge found as configured in the Vagrantfile. Using it.')
167
- chosen_bridge = interface[:name]
168
- break
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('vagrant.actions.vm.bridged_networking.specific_not_found',
175
- :bridge => config[:bridge])
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('vagrant.actions.vm.bridged_networking.available',
192
- :prefix => false)
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]}", :prefix => false)
200
+ @env[:ui].info("#{index + 1}) #{interface[:name]}", prefix: false)
196
201
  end
197
202
  @env[:ui].info(I18n.t(
198
- 'vagrant.actions.vm.bridged_networking.choice_help')+"\n")
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.sort! if 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 :use_linked_clone
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
- @use_linked_clone = UNSET_VALUE
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
- @use_linked_clone = false if @use_linked_clone == UNSET_VALUE
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, dst_name, options={})
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 [<Symbol => String, Boolean>] options Snapshot options.
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, options)
134
- raise NotImplementedError
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
- def register(pvm_file)
495
- args = [@prlctl_path, 'register', pvm_file]
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
- # Checks that specified virtual machine is registered
550
+ # Switches the VM state to the specified snapshot
517
551
  #
518
- # @return [Boolean]
519
- def registered?(uuid)
520
- args = %w(list --all --info --no-header -o uuid)
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] name New VM name.
535
- def set_name(name)
536
- execute_prlctl('set', @uuid, '--name', name)
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.