vagrant-parallels 1.2.2 → 1.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -59,7 +59,7 @@ module VagrantPlugins
59
59
  iface_name = net_info['Bound To']
60
60
 
61
61
  # Return the details
62
- return {
62
+ {
63
63
  name: iface_name,
64
64
  ip: options[:adapter_ip],
65
65
  netmask: options[:netmask],
@@ -170,11 +170,10 @@ module VagrantPlugins
170
170
  execute(*args)
171
171
  end
172
172
 
173
- def import(template_uuid)
174
- template_name = read_vms.key(template_uuid)
175
- vm_name = "#{template_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
173
+ def import(tpl_name)
174
+ vm_name = "#{tpl_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
176
175
 
177
- execute_prlctl('clone', template_uuid, '--name', vm_name) do |type, data|
176
+ execute_prlctl('clone', tpl_name, '--name', vm_name) do |type, data|
178
177
  lines = data.split("\r")
179
178
  # The progress of the import will be in the last line. Do a greedy
180
179
  # regular expression to find what we're looking for.
@@ -277,7 +276,8 @@ module VagrantPlugins
277
276
  info = {}
278
277
  net_info = json { execute_prlsrvctl('net', 'info', iface['Network ID'], '--json') }
279
278
  # Really we need to work with bounded virtual interface
280
- info[:name] = net_info['Bound To']
279
+ info[:name] = net_info['Network ID']
280
+ info[:bound_to] = net_info['Bound To']
281
281
  info[:ip] = net_info['Parallels adapter']['IP address']
282
282
  info[:netmask] = net_info['Parallels adapter']['Subnet mask']
283
283
  # Such interfaces are always in 'Up'
@@ -300,19 +300,13 @@ module VagrantPlugins
300
300
  end
301
301
 
302
302
  def read_mac_address
303
- read_settings.fetch('Hardware', {}).fetch('net0', {}).fetch('mac', nil)
303
+ # Get MAC of Shared network interface (net0)
304
+ read_vm_option('mac').strip.split(' ').first.gsub(':', '')
304
305
  end
305
306
 
306
307
  def read_mac_addresses
307
- macs = {}
308
- read_settings.fetch('Hardware', {}).each do |device, params|
309
- if device =~ /^net(\d+)$/
310
- adapter = $1
311
- mac = params.fetch('mac')
312
- macs[adapter] = mac
313
- end
314
- end
315
- macs
308
+ macs = read_vm_option('mac').strip.split(' ')
309
+ Hash[macs.map.with_index{ |mac, ind| [ind.to_s, mac.gsub(':', '')] }]
316
310
  end
317
311
 
318
312
  def read_network_interfaces
@@ -345,7 +339,7 @@ module VagrantPlugins
345
339
  end
346
340
 
347
341
  def read_settings
348
- vm = json { execute_prlctl('list', @uuid, '--info', '--json').gsub(/^INFO/, '') }
342
+ vm = json { execute_prlctl('list', @uuid, '--info', '--no-header', '--json') }
349
343
  vm.last
350
344
  end
351
345
 
@@ -390,40 +384,33 @@ module VagrantPlugins
390
384
  end
391
385
 
392
386
  def read_state
393
- vm = json { execute_prlctl('list', @uuid, '--json').gsub(/^INFO/, '') }
394
- return nil if !vm.last
395
- vm.last.fetch('status').to_sym
387
+ read_vm_option('status').strip.to_sym
396
388
  end
397
389
 
398
390
  def read_virtual_networks
399
391
  json { execute_prlsrvctl('net', 'list', '--json') }
400
392
  end
401
393
 
394
+ def read_vm_option(option, uuid=@uuid)
395
+ execute_prlctl('list', uuid,'--no-header', '-o', option)
396
+ end
397
+
402
398
  def read_vms
403
- results = {}
404
- vms_arr = json([]) do
405
- execute_prlctl('list', '--all', '--json').gsub(/^INFO/, '')
406
- end
407
- templates_arr = json([]) do
408
- execute_prlctl('list', '--all', '--json', '--template').gsub(/^INFO/, '')
409
- end
410
- vms = vms_arr | templates_arr
411
- vms.each do |item|
412
- results[item.fetch('name')] = item.fetch('uuid')
413
- end
399
+ args = %w(list --all --no-header --json -o name,uuid)
400
+ vms_arr = json([]) { execute_prlctl(*args) }
401
+ templates_arr = json([]) { execute_prlctl(*args, '--template') }
414
402
 
415
- results
403
+ vms = vms_arr | templates_arr
404
+ Hash[vms.map { |i| [i.fetch('name'), i.fetch('uuid')] }]
416
405
  end
417
406
 
418
407
  # Parse the JSON from *all* VMs and templates.
419
408
  # Then return an array of objects (without duplicates)
420
409
  def read_vms_info
421
- vms_arr = json([]) do
422
- execute_prlctl('list', '--all','--info', '--json').gsub(/^INFO/, '')
423
- end
424
- templates_arr = json([]) do
425
- execute_prlctl('list', '--all','--info', '--json', '--template').gsub(/^INFO/, '')
426
- end
410
+ args = %w(list --all --info --no-header --json)
411
+ vms_arr = json([]) { execute_prlctl(*args) }
412
+ templates_arr = json([]) { execute_prlctl(*args, '--template') }
413
+
427
414
  vms_arr | templates_arr
428
415
  end
429
416
 
@@ -441,11 +428,31 @@ module VagrantPlugins
441
428
  def register(pvm_file, regen_src_uuid=false)
442
429
  args = ['prlctl', 'register', pvm_file]
443
430
  args << '--regenerate-src-uuid' if regen_src_uuid
431
+
432
+ 3.times do
433
+ result = raw(*args)
434
+ # Exit if everything is OK
435
+ return if result.exit_code == 0
436
+
437
+ # It may occur in the race condition with other Vagrant processes.
438
+ # It is OK, just exit.
439
+ return if result.stderr.include?('is already registered.')
440
+
441
+ # Sleep a bit though to give Parallels Desktop time to fix itself
442
+ sleep 2
443
+ end
444
+
445
+ # If we reach this point, it means that we consistently got the
446
+ # failure, do a standard execute now. This will raise an
447
+ # exception if it fails again.
444
448
  execute(*args)
445
449
  end
446
450
 
447
451
  def registered?(uuid)
448
- read_vms.has_value?(uuid)
452
+ args = %w(list --all --info --no-header -o uuid)
453
+
454
+ execute_prlctl(*args).include?(uuid) ||
455
+ execute_prlctl(*args, '--template').include?(uuid)
449
456
  end
450
457
 
451
458
  def resume
@@ -489,7 +496,25 @@ module VagrantPlugins
489
496
  end
490
497
 
491
498
  def unregister(uuid)
492
- execute_prlctl('unregister', uuid)
499
+ args = ['prlctl', 'unregister', uuid]
500
+ 3.times do
501
+ result = raw(*args)
502
+ # Exit if everything is OK
503
+ return if result.exit_code == 0
504
+
505
+ # It may occur in the race condition with other Vagrant processes.
506
+ # Both are OK, just exit.
507
+ return if result.stderr.include?('is not registered')
508
+ return if result.stderr.include?('is being cloned')
509
+
510
+ # Sleep a bit though to give Parallels Desktop time to fix itself
511
+ sleep 2
512
+ end
513
+
514
+ # If we reach this point, it means that we consistently got the
515
+ # failure, do a standard execute now. This will raise an
516
+ # exception if it fails again.
517
+ execute(*args)
493
518
  end
494
519
 
495
520
  def unshare_folders(names)
@@ -499,7 +524,23 @@ module VagrantPlugins
499
524
  end
500
525
 
501
526
  def vm_exists?(uuid)
502
- raw('prlctl', 'list', uuid).exit_code == 0
527
+ 5.times do |i|
528
+ result = raw('prlctl', 'list', uuid)
529
+ return true if result.exit_code == 0
530
+
531
+ # Sometimes this happens. In this case, retry. If
532
+ # we don't see this text, the VM really probably doesn't exist.
533
+ return false if !result.stderr.include?('Login failed:')
534
+
535
+ # Sleep a bit though to give Parallels Desktop time to fix itself
536
+ sleep 2
537
+ end
538
+
539
+ # If we reach this point, it means that we consistently got the
540
+ # failure, do a standard prlctl now. This will raise an
541
+ # exception if it fails again.
542
+ execute_prlctl('list', uuid)
543
+ true
503
544
  end
504
545
  end
505
546
  end
@@ -15,44 +15,6 @@ module VagrantPlugins
15
15
  @logger = Log4r::Logger.new('vagrant_parallels::driver::pd_9')
16
16
  end
17
17
 
18
- def read_settings
19
- vm = json { execute_prlctl('list', @uuid, '--info', '--json') }
20
- vm.last
21
- end
22
-
23
- def read_state
24
- vm = json { execute_prlctl('list', @uuid, '--json') }
25
- return nil if !vm.last
26
- vm.last.fetch('status').to_sym
27
- end
28
-
29
- def read_vms
30
- results = {}
31
- vms_arr = json([]) do
32
- execute_prlctl('list', '--all', '--json')
33
- end
34
- templates_arr = json([]) do
35
- execute_prlctl('list', '--all', '--json', '--template')
36
- end
37
- vms = vms_arr | templates_arr
38
- vms.each do |item|
39
- results[item.fetch('name')] = item.fetch('uuid')
40
- end
41
-
42
- results
43
- end
44
-
45
- # Parse the JSON from *all* VMs and templates. Then return an array of objects (without duplicates)
46
- def read_vms_info
47
- vms_arr = json([]) do
48
- execute_prlctl('list', '--all','--info', '--json')
49
- end
50
- templates_arr = json([]) do
51
- execute_prlctl('list', '--all','--info', '--json', '--template')
52
- end
53
- vms_arr | templates_arr
54
- end
55
-
56
18
  def set_power_consumption_mode(optimized)
57
19
  state = optimized ? 'on' : 'off'
58
20
  execute_prlctl('set', @uuid, '--longer-battery-life', state)
@@ -15,6 +15,10 @@ module VagrantPlugins
15
15
  error_key(:linux_mount_failed)
16
16
  end
17
17
 
18
+ class LinuxPrlFsInvalidOptions < VagrantParallelsError
19
+ error_key(:linux_prl_fs_invalid_options)
20
+ end
21
+
18
22
  class MacOSXRequired < VagrantParallelsError
19
23
  error_key(:mac_os_x_required)
20
24
  end
@@ -43,6 +47,10 @@ module VagrantPlugins
43
47
  error_key(:parallels_tools_iso_not_found)
44
48
  end
45
49
 
50
+ class ParallelsTplNameNotFound < VagrantParallelsError
51
+ error_key(:parallels_tpl_name_not_found)
52
+ end
53
+
46
54
  class VMImportFailure < VagrantParallelsError
47
55
  error_key(:vm_import_failure)
48
56
  end
@@ -3,6 +3,18 @@ module VagrantPlugins
3
3
  module GuestLinuxCap
4
4
  class MountParallelsSharedFolder
5
5
  def self.mount_parallels_shared_folder(machine, name, guestpath, options)
6
+ # Sanity check for mount options: we are not supporting
7
+ # VirtualBox-specific 'fmode' and 'dmode' options
8
+ if options[:mount_options]
9
+ invalid_opts = options[:mount_options].select do |opt|
10
+ opt =~ /^(d|f)mode/
11
+ end
12
+
13
+ if !invalid_opts.empty?
14
+ raise Errors::LinuxPrlFsInvalidOptions, options: invalid_opts
15
+ end
16
+ end
17
+
6
18
  expanded_guest_path = machine.guest.capability(
7
19
  :shell_expand_guest_path, guestpath)
8
20
 
@@ -20,7 +20,7 @@ module VagrantPlugins
20
20
  Parallels-based virtual machines.
21
21
  EOF
22
22
 
23
- provider(:parallels) do
23
+ provider(:parallels, parallel: true, priority: 7) do
24
24
  require File.expand_path("../provider", __FILE__)
25
25
  Provider
26
26
  end
@@ -7,6 +7,10 @@ module VagrantPlugins
7
7
  attr_reader :driver
8
8
 
9
9
  def self.usable?(raise_error=false)
10
+ if !Vagrant::Util::Platform.darwin?
11
+ raise Errors::MacOSXRequired
12
+ end
13
+
10
14
  # Instantiate the driver, which will determine the Parallels Desktop
11
15
  # version and all that, which checks for Parallels Desktop being present
12
16
  Driver::Meta.new
@@ -23,10 +27,6 @@ module VagrantPlugins
23
27
  @logger = Log4r::Logger.new("vagrant::provider::parallels")
24
28
  @machine = machine
25
29
 
26
- if !Vagrant::Util::Platform.darwin?
27
- raise Errors::MacOSXRequired
28
- end
29
-
30
30
  # This method will load in our driver, so we call it now to
31
31
  # initialize it.
32
32
  machine_id_changed
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Parallels
3
- VERSION = "1.2.2"
3
+ VERSION = "1.3.0"
4
4
  end
5
5
  end
data/locales/en.yml CHANGED
@@ -31,7 +31,11 @@ en:
31
31
  The command attempted was:
32
32
 
33
33
  %{command}
34
+ linux_prl_fs_invalid_options: |-
35
+ Failed to mount folders in Linux guest. You've specified mount options
36
+ which are not supported by "prl_fs" file system.
34
37
 
38
+ Invalid mount options: %{options}
35
39
  mac_os_x_required: |-
36
40
  Parallels provider works only on OS X (Mac OS X) systems.
37
41
  parallels_install_incomplete: |-
@@ -61,6 +65,11 @@ en:
61
65
  reinstall Parallels Desktop.
62
66
 
63
67
  Expected ISO path: "%{iso_path}"
68
+ parallels_tpl_name_not_found: |-
69
+ The VM import failed! Template name not found. Please verify that
70
+ the box you're using is not corrupted and try again.
71
+
72
+ Template config path: "%{config_path}"
64
73
  vm_import_failure: |-
65
74
  The VM import failed! Please verify that the box you're using is not
66
75
  corrupted and try again.
@@ -4,201 +4,10 @@ describe VagrantPlugins::Parallels::Driver::PD_10 do
4
4
  include_context "parallels"
5
5
  let(:parallels_version) { "10" }
6
6
 
7
- let(:vm_name) {'VM_Name'}
8
-
9
- let(:tpl_uuid) {'1234-some-template-uuid-5678'}
10
- let(:tpl_name) {'Some_Template_Name'}
11
-
12
- let(:tools_state) {'installed'}
13
- let(:tools_version) {'10.0.23062.123456'}
14
-
15
- let(:hostonly_iface) {'vnic10'}
16
-
17
- let(:vnic_options) do {
18
- name: 'vagrant_vnic6',
19
- adapter_ip: '11.11.11.11',
20
- netmask: '255.255.252.0',
21
- dhcp: {
22
- ip: '11.11.11.11',
23
- lower: '11.11.8.1',
24
- upper: '11.11.11.254'
25
- }
26
- } end
27
-
28
7
  subject { VagrantPlugins::Parallels::Driver::Meta.new(uuid) }
29
8
 
30
9
  it_behaves_like "parallels desktop driver"
31
10
 
32
- before do
33
- # Returns short info about all registered VMs
34
- # `prlctl list --all --json`
35
- subprocess.stub(:execute).
36
- with("prlctl", "list", "--all", "--json", kind_of(Hash)) do
37
- out = <<-eos
38
- [
39
- {
40
- "uuid": "#{uuid}",
41
- "status": "stopped",
42
- "name": "#{vm_name}"
43
- }
44
- ]
45
- eos
46
- subprocess_result(stdout: out)
47
- end
48
-
49
- # Returns short info about all registered templates
50
- # `prlctl list --all --json --template`
51
- subprocess.stub(:execute).
52
- with("prlctl", "list", "--all", "--json", "--template", kind_of(Hash)) do
53
- out = <<-eos
54
- [
55
- {
56
- "uuid": "1234-some-template-uuid-5678",
57
- "name": "Some_Template_Name"
58
- }
59
- ]
60
- eos
61
- subprocess_result(stdout: out)
62
- end
63
-
64
-
65
- # Returns detailed info about specified VM or all registered VMs
66
- # `prlctl list <vm_uuid> --info --json`
67
- # `prlctl list --all --info --json`
68
- subprocess.stub(:execute).
69
- with("prlctl", "list", kind_of(String), "--info", "--json",
70
- kind_of(Hash)) do
71
- out = <<-eos
72
- [
73
- {
74
- "ID": "#{uuid}",
75
- "Name": "#{vm_name}",
76
- "State": "stopped",
77
- "Home": "/path/to/#{vm_name}.pvm/",
78
- "GuestTools": {
79
- "state": "#{tools_state}",
80
- "version": "#{tools_version}"
81
- },
82
- "Hardware": {
83
- "cpu": {
84
- "cpus": 1
85
- },
86
- "memory": {
87
- "size": "512Mb"
88
- },
89
- "hdd0": {
90
- "enabled": true,
91
- "image": "/path/to/disk1.hdd"
92
- },
93
- "net0": {
94
- "enabled": true,
95
- "type": "shared",
96
- "mac": "001C42B4B074",
97
- "card": "e1000",
98
- "dhcp": "yes"
99
- },
100
- "net1": {
101
- "enabled": true,
102
- "type": "bridged",
103
- "iface": "vnic2",
104
- "mac": "001C42EC0068",
105
- "card": "e1000",
106
- "ips": "33.33.33.5/255.255.255.0 "
107
- }
108
- },
109
- "Host Shared Folders": {
110
- "enabled": true,
111
- "shared_folder_1": {
112
- "enabled": true,
113
- "path": "/path/to/shared/folder/1"
114
- },
115
- "shared_folder_2": {
116
- "enabled": true,
117
- "path": "/path/to/shared/folder/2"
118
- }
119
- }
120
- }
121
- ]
122
- eos
123
- subprocess_result(stdout: out)
124
- end
125
-
126
- # Returns detailed info about specified template or all registered templates
127
- # `prlctl list <tpl_uuid> --info --json --template`
128
- # `prlctl list --all --info --json --template`
129
- subprocess.stub(:execute).
130
- with("prlctl", "list", kind_of(String), "--info", "--json", "--template",
131
- kind_of(Hash)) do
132
- out = <<-eos
133
- [
134
- {
135
- "ID": "#{tpl_uuid}",
136
- "Name": "#{tpl_name}",
137
- "State": "stopped",
138
- "Home": "/path/to/#{tpl_name}.pvm/",
139
- "GuestTools": {
140
- "state": "#{tools_state}",
141
- "version": "#{tools_version}"
142
- },
143
- "Hardware": {
144
- "cpu": {
145
- "cpus": 1
146
- },
147
- "memory": {
148
- "size": "512Mb"
149
- },
150
- "hdd0": {
151
- "enabled": true,
152
- "image": "/path/to/harddisk.hdd"
153
- },
154
- "net0": {
155
- "enabled": true,
156
- "type": "shared",
157
- "mac": "001C42F6E500",
158
- "card": "e1000",
159
- "dhcp": "yes"
160
- },
161
- "net1": {
162
- "enabled": true,
163
- "type": "bridged",
164
- "iface": "vnic4",
165
- "mac": "001C42AB0071",
166
- "card": "e1000",
167
- "ips": "33.33.33.10/255.255.255.0 "
168
- }
169
- }
170
- }
171
- ]
172
- eos
173
- subprocess_result(stdout: out)
174
- end
175
-
176
- # Returns detailed info about virtual network interface
177
- # `prlsrvctl net info <net_name>, '--json', retryable: true)
178
- subprocess.stub(:execute).
179
- with("prlsrvctl", "net", "info", kind_of(String), "--json",
180
- kind_of(Hash)) do
181
- out = <<-eos
182
- {
183
- "Network ID": "#{vnic_options[:name]}",
184
- "Type": "host-only",
185
- "Bound To": "#{hostonly_iface}",
186
- "Parallels adapter": {
187
- "IP address": "#{vnic_options[:adapter_ip]}",
188
- "Subnet mask": "#{vnic_options[:netmask]}"
189
- },
190
- "DHCPv4 server": {
191
- "Server address": "#{vnic_options[:dhcp][:ip] || "10.37.132.1"}",
192
- "IP scope start address": "#{vnic_options[:dhcp][:lower] || "10.37.132.1"}",
193
- "IP scope end address": "#{vnic_options[:dhcp][:upper] || "10.37.132.254"}"
194
- }
195
- }
196
- eos
197
- subprocess_result(stdout: out)
198
- end
199
-
200
- end
201
-
202
11
  describe "set_power_consumption_mode" do
203
12
  it "turns 'longer-battery-life' on" do
204
13
  subprocess.should_receive(:execute).