vagrant-parallels 1.2.2 → 1.3.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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).