vagrant-parallels 1.3.13 → 1.4.0.rc1

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.
@@ -1,8 +1,7 @@
1
- require "forwardable"
1
+ require 'forwardable'
2
+ require 'log4r'
2
3
 
3
- require "log4r"
4
-
5
- require File.expand_path("../base", __FILE__)
4
+ require File.expand_path('../base', __FILE__)
6
5
 
7
6
  module VagrantPlugins
8
7
  module Parallels
@@ -23,7 +22,7 @@ module VagrantPlugins
23
22
 
24
23
  def initialize(uuid=nil)
25
24
  # Setup the base
26
- super()
25
+ super(uuid)
27
26
 
28
27
  @logger = Log4r::Logger.new('vagrant_parallels::driver::meta')
29
28
  @uuid = uuid
@@ -38,7 +37,7 @@ module VagrantPlugins
38
37
  '8' => PD_8,
39
38
  '9' => PD_9,
40
39
  '10' => PD_10,
41
- '11' => PD_10
40
+ '11' => PD_11
42
41
  }
43
42
 
44
43
  driver_klass = nil
@@ -71,16 +70,17 @@ module VagrantPlugins
71
70
  :clear_shared_folders,
72
71
  :compact,
73
72
  :create_host_only_network,
73
+ :create_snapshot,
74
74
  :delete,
75
75
  :delete_disabled_adapters,
76
76
  :delete_unused_host_only_networks,
77
77
  :disable_password_restrictions,
78
78
  :enable_adapters,
79
- :export,
80
79
  :forward_ports,
81
80
  :halt,
82
- :import,
81
+ :clone_vm,
83
82
  :read_bridged_interfaces,
83
+ :read_current_snapshot,
84
84
  :read_forwarded_ports,
85
85
  :read_guest_ip,
86
86
  :read_guest_tools_state,
@@ -98,12 +98,10 @@ module VagrantPlugins
98
98
  :read_vm_option,
99
99
  :read_vms,
100
100
  :read_vms_info,
101
- :read_vms_paths,
102
101
  :regenerate_src_uuid,
103
102
  :register,
104
103
  :resume,
105
104
  :set_power_consumption_mode,
106
- :set_mac_address,
107
105
  :set_name,
108
106
  :share_folders,
109
107
  :ssh_ip,
@@ -32,7 +32,7 @@ module VagrantPlugins
32
32
  # 'Shared'(vnic0) and 'Host-Only'(vnic1) are default in Parallels Desktop
33
33
  # They should not be deleted anyway.
34
34
  networks.keep_if do |net|
35
- net['Type'] == "host-only" &&
35
+ net['Type'] == 'host-only' &&
36
36
  net['Bound To'].match(/^(?>vnic|Parallels Host-Only #)(\d+)$/)[1].to_i >= 2
37
37
  end
38
38
 
@@ -73,28 +73,28 @@ module VagrantPlugins
73
73
  adapters.each do |adapter|
74
74
  args = []
75
75
  if existing_adapters.include? "net#{adapter[:adapter]}"
76
- args.concat(["--device-set","net#{adapter[:adapter]}", "--enable"])
76
+ args.concat(['--device-set',"net#{adapter[:adapter]}", '--enable'])
77
77
  else
78
- args.concat(["--device-add", "net"])
78
+ args.concat(['--device-add', 'net'])
79
79
  end
80
80
 
81
81
  if adapter[:type] == :hostonly
82
- args.concat(["--type", "host", "--iface", adapter[:hostonly]])
82
+ args.concat(['--type', 'host', '--iface', adapter[:hostonly]])
83
83
  elsif adapter[:type] == :bridged
84
- args.concat(["--type", "bridged", "--iface", adapter[:bridge]])
84
+ args.concat(['--type', 'bridged', '--iface', adapter[:bridge]])
85
85
  elsif adapter[:type] == :shared
86
- args.concat(["--type", "shared"])
86
+ args.concat(['--type', 'shared'])
87
87
  end
88
88
 
89
89
  if adapter[:mac_address]
90
- args.concat(["--mac", adapter[:mac_address]])
90
+ args.concat(['--mac', adapter[:mac_address]])
91
91
  end
92
92
 
93
93
  if adapter[:nic_type]
94
- args.concat(["--adapter-type", adapter[:nic_type].to_s])
94
+ args.concat(['--adapter-type', adapter[:nic_type].to_s])
95
95
  end
96
96
 
97
- execute_prlctl("set", @uuid, *args)
97
+ execute_prlctl('set', @uuid, *args)
98
98
  end
99
99
  end
100
100
 
@@ -127,7 +127,7 @@ module VagrantPlugins
127
127
 
128
128
  def read_host_only_interfaces
129
129
  net_list = read_virtual_networks
130
- net_list.keep_if { |net| net['Type'] == "host-only" }
130
+ net_list.keep_if { |net| net['Type'] == 'host-only' }
131
131
 
132
132
  hostonly_ifaces = []
133
133
  net_list.each do |iface|
@@ -142,7 +142,7 @@ module VagrantPlugins
142
142
  info[:netmask] = adapter['Subnet mask'] || adapter['IPv4 subnet mask']
143
143
 
144
144
  # Such interfaces are always in 'Up'
145
- info[:status] = "Up"
145
+ info[:status] = 'Up'
146
146
 
147
147
  # There may be a fake DHCPv4 parameters
148
148
  # We can trust them only if adapter IP and DHCP IP are in the same subnet
@@ -165,18 +165,18 @@ module VagrantPlugins
165
165
 
166
166
  # Get enabled VM's network interfaces
167
167
  ifaces = read_settings.fetch('Hardware', {}).keep_if do |dev, params|
168
- dev.start_with?('net') and params.fetch("enabled", true)
168
+ dev.start_with?('net') and params.fetch('enabled', true)
169
169
  end
170
170
  ifaces.each do |name, params|
171
171
  adapter = name.match(/^net(\d+)$/)[1].to_i
172
172
  nics[adapter] ||= {}
173
173
 
174
- if params['type'] == "shared"
174
+ if params['type'] == 'shared'
175
175
  nics[adapter][:type] = :shared
176
- elsif params['type'] == "host"
176
+ elsif params['type'] == 'host'
177
177
  nics[adapter][:type] = :hostonly
178
178
  nics[adapter][:hostonly] = params.fetch('iface','')
179
- elsif params['type'] == "bridged"
179
+ elsif params['type'] == 'bridged'
180
180
  nics[adapter][:type] = :bridged
181
181
  nics[adapter][:bridge] = params.fetch('iface','')
182
182
  end
@@ -227,12 +227,6 @@ module VagrantPlugins
227
227
  # Ignore our own used ports
228
228
  read_forwarded_ports(true).reject { |r| r[:guest].include?(@uuid) }
229
229
  end
230
-
231
- def set_power_consumption_mode(optimized)
232
- state = optimized ? 'on' : 'off'
233
- execute_prlctl('set', @uuid, '--longer-battery-life', state)
234
- end
235
-
236
230
  end
237
231
  end
238
232
  end
@@ -0,0 +1,41 @@
1
+ require 'log4r'
2
+
3
+ require 'vagrant/util/platform'
4
+
5
+ require File.expand_path('../pd_10', __FILE__)
6
+
7
+ module VagrantPlugins
8
+ module Parallels
9
+ module Driver
10
+ # Driver for Parallels Desktop 11.
11
+ class PD_11 < PD_10
12
+ def initialize(uuid)
13
+ super(uuid)
14
+
15
+ @logger = Log4r::Logger.new('vagrant_parallels::driver::pd_11')
16
+ end
17
+
18
+ def create_snapshot(uuid, options)
19
+ args = ['snapshot', uuid]
20
+ args.concat(['--name', options[:name]]) if options[:name]
21
+ args.concat(['--description', options[:desc]]) if options[:desc]
22
+
23
+ stdout = execute_prlctl(*args)
24
+ if stdout =~ /\{([\w-]+)\}/
25
+ return $1
26
+ end
27
+
28
+ raise Errors::SnapshotIdNotDetected, stdout: stdout
29
+ end
30
+
31
+ def read_current_snapshot(uuid)
32
+ if execute_prlctl('snapshot-list', uuid) =~ /\*\{([\w-]+)\}/
33
+ return $1
34
+ end
35
+
36
+ nil
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -12,69 +12,9 @@ module VagrantPlugins
12
12
  # Driver for Parallels Desktop 8.
13
13
  class PD_8 < Base
14
14
  def initialize(uuid)
15
- super()
15
+ super(uuid)
16
16
 
17
17
  @logger = Log4r::Logger.new('vagrant_parallels::driver::pd_8')
18
- @uuid = uuid
19
- end
20
-
21
-
22
- def compact(uuid)
23
- used_drives = read_settings.fetch('Hardware', {}).select do |name, _|
24
- name.start_with? 'hdd'
25
- end
26
- used_drives.each_value do |drive_params|
27
- execute(@prldisktool_path, 'compact', '--hdd', drive_params['image']) do |type, data|
28
- lines = data.split("\r")
29
- # The progress of the compact will be in the last line. Do a greedy
30
- # regular expression to find what we're looking for.
31
- if lines.last =~ /.+?(\d{,3}) ?%/
32
- yield $1.to_i if block_given?
33
- end
34
- end
35
- end
36
- end
37
-
38
- def clear_shared_folders
39
- share_ids = read_shared_folders.keys
40
- share_ids.each do |id|
41
- execute_prlctl('set', @uuid, '--shf-host-del', id)
42
- end
43
- end
44
-
45
- def create_host_only_network(options)
46
- # Create the interface
47
- execute_prlsrvctl('net', 'add', options[:network_id], '--type', 'host-only')
48
-
49
- # Configure it
50
- args = ["--ip", "#{options[:adapter_ip]}/#{options[:netmask]}"]
51
- if options[:dhcp]
52
- args.concat(["--dhcp-ip", options[:dhcp][:ip],
53
- "--ip-scope-start", options[:dhcp][:lower],
54
- "--ip-scope-end", options[:dhcp][:upper]])
55
- end
56
-
57
- execute_prlsrvctl('net', 'set', options[:network_id], *args)
58
-
59
- # Return the details
60
- {
61
- name: options[:network_id],
62
- ip: options[:adapter_ip],
63
- netmask: options[:netmask],
64
- dhcp: options[:dhcp]
65
- }
66
- end
67
-
68
- def delete
69
- execute_prlctl('delete', @uuid)
70
- end
71
-
72
- def delete_disabled_adapters
73
- read_settings.fetch('Hardware', {}).each do |adapter, params|
74
- if adapter.start_with?('net') and !params.fetch('enabled', true)
75
- execute_prlctl('set', @uuid, '--device-del', adapter)
76
- end
77
- end
78
18
  end
79
19
 
80
20
  def delete_unused_host_only_networks
@@ -152,124 +92,6 @@ module VagrantPlugins
152
92
  end
153
93
  end
154
94
 
155
- def export(path, tpl_name)
156
- execute_prlctl('clone', @uuid,
157
- '--name', tpl_name,
158
- '--template',
159
- '--dst', path.to_s) do |type, data|
160
- lines = data.split("\r")
161
- # The progress of the export will be in the last line. Do a greedy
162
- # regular expression to find what we're looking for.
163
- if lines.last =~ /.+?(\d{,3}) ?%/
164
- yield $1.to_i if block_given?
165
- end
166
- end
167
- read_vms[tpl_name]
168
- end
169
-
170
- def halt(force=false)
171
- args = ['stop', @uuid]
172
- args << '--kill' if force
173
- execute_prlctl(*args)
174
- end
175
-
176
- def import(tpl_name)
177
- vm_name = "#{tpl_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}"
178
-
179
- execute_prlctl('clone', tpl_name, '--name', vm_name) do |type, data|
180
- lines = data.split("\r")
181
- # The progress of the import will be in the last line. Do a greedy
182
- # regular expression to find what we're looking for.
183
- if lines.last =~ /.+?(\d{,3}) ?%/
184
- yield $1.to_i if block_given?
185
- end
186
- end
187
- read_vms[vm_name]
188
- end
189
-
190
- def read_bridged_interfaces
191
- host_hw_info = read_host_info.fetch("Hardware info")
192
- net_list = host_hw_info.select do |name, attrs|
193
- # Get all network interfaces except 'vnicXXX'
194
- attrs.fetch("type") == "net" and name !~ /^(vnic(.+?))$/
195
- end
196
-
197
- bridged_ifaces = []
198
- net_list.keys.each do |iface|
199
- info = {}
200
- ifconfig = execute('ifconfig', iface)
201
- # Assign default values
202
- info[:name] = iface
203
- info[:ip] = "0.0.0.0"
204
- info[:netmask] = "0.0.0.0"
205
- info[:status] = "Down"
206
-
207
- if ifconfig =~ /(?<=inet\s)(\S*)/
208
- info[:ip] = $1.to_s
209
- end
210
- if ifconfig =~ /(?<=netmask\s)(\S*)/
211
- # Netmask will be converted from hex to dec:
212
- # '0xffffff00' -> '255.255.255.0'
213
- info[:netmask] = $1.hex.to_s(16).scan(/../).each.map{|octet| octet.hex}.join(".")
214
- end
215
- if ifconfig =~ /\W(UP)\W/ and ifconfig !~ /(?<=status:\s)inactive$/
216
- info[:status] = "Up"
217
- end
218
-
219
- bridged_ifaces << info
220
- end
221
- bridged_ifaces
222
- end
223
-
224
- def read_guest_ip
225
- mac_addr = read_mac_address.downcase
226
- leases_file = "/Library/Preferences/Parallels/parallels_dhcp_leases"
227
- begin
228
- File.open(leases_file).grep(/#{mac_addr}/) do |line|
229
- return line[/^(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})/]
230
- end
231
- rescue Errno::EACCES
232
- raise Errors::DhcpLeasesNotAccessible, :leases_file => leases_file.to_s
233
- rescue Errno::ENOENT
234
- # File does not exist
235
- # Perhaps, it is the fist start of Parallels Desktop
236
- return nil
237
- end
238
-
239
- nil
240
- end
241
-
242
- def read_guest_tools_iso_path(guest_os)
243
- guest_os = guest_os.to_sym
244
- iso_name ={
245
- linux: "prl-tools-lin.iso",
246
- darwin: "prl-tools-mac.iso",
247
- windows: "prl-tools-win.iso"
248
- }
249
- return nil if !iso_name[guest_os]
250
-
251
- bundle_id = 'com.parallels.desktop.console'
252
- bundle_path = execute('mdfind', "kMDItemCFBundleIdentifier == #{bundle_id}")
253
- iso_path = File.expand_path("./Contents/Resources/Tools/#{iso_name[guest_os]}",
254
- bundle_path.split("\n")[0])
255
-
256
- if !File.exist?(iso_path)
257
- raise Errors::ParallelsToolsIsoNotFound, :iso_path => iso_path
258
- end
259
-
260
- iso_path
261
- end
262
-
263
- def read_guest_tools_state
264
- state = read_settings.fetch('GuestTools', {}).fetch('state', nil)
265
- state = "not_installed" if !state
266
- state.to_sym
267
- end
268
-
269
- def read_host_info
270
- json { execute_prlctl('server', 'info', '--json') }
271
- end
272
-
273
95
  def read_host_only_interfaces
274
96
  net_list = read_virtual_networks
275
97
  net_list.keep_if { |net| net['Type'] == "host-only" }
@@ -301,23 +123,6 @@ module VagrantPlugins
301
123
  hostonly_ifaces
302
124
  end
303
125
 
304
- def read_mac_address
305
- hw_info = read_settings.fetch('Hardware', {})
306
- shared_ifaces = hw_info.select do |name, params|
307
- name.start_with?('net') && params['type'] == 'shared'
308
- end
309
-
310
- if shared_ifaces.empty?
311
- raise Errors::SharedAdapterNotFound
312
- end
313
-
314
- shared_ifaces.values.first.fetch('mac', nil)
315
- end
316
-
317
- def read_mac_addresses
318
- read_vm_option('mac').strip.gsub(':', '').split(' ')
319
- end
320
-
321
126
  def read_network_interfaces
322
127
  nics = {}
323
128
 
@@ -347,19 +152,6 @@ module VagrantPlugins
347
152
  nics
348
153
  end
349
154
 
350
- def read_settings
351
- vm = json { execute_prlctl('list', @uuid, '--info', '--no-header', '--json') }
352
- vm.last
353
- end
354
-
355
- def read_shared_network_id
356
- # There should be only one Shared interface
357
- shared_net = read_virtual_networks.detect do |net|
358
- net['Type'] == 'shared'
359
- end
360
- shared_net.fetch('Network ID')
361
- end
362
-
363
155
  def read_shared_interface
364
156
  net_info = json do
365
157
  execute_prlsrvctl('net', 'info', read_shared_network_id, '--json')
@@ -381,194 +173,6 @@ module VagrantPlugins
381
173
 
382
174
  info
383
175
  end
384
-
385
- def read_shared_folders
386
- shf_info = read_settings.fetch("Host Shared Folders", {})
387
- list = {}
388
- shf_info.delete_if {|k,v| k == "enabled"}.each do |id, data|
389
- list[id] = data.fetch("path")
390
- end
391
-
392
- list
393
- end
394
-
395
- def read_state
396
- read_vm_option('status').strip.to_sym
397
- end
398
-
399
- def read_virtual_networks
400
- json { execute_prlsrvctl('net', 'list', '--json') }
401
- end
402
-
403
- def read_vm_option(option, uuid=@uuid)
404
- out = execute_prlctl('list', uuid,'--no-header', '-o', option).strip
405
- if out.empty?
406
- raise Errors::ParallelsVMOptionNotFound, vm_option: option
407
- end
408
-
409
- out
410
- end
411
-
412
- def read_vms
413
- args = %w(list --all --no-header --json -o name,uuid)
414
- vms_arr = json([]) { execute_prlctl(*args) }
415
- templates_arr = json([]) { execute_prlctl(*args, '--template') }
416
-
417
- vms = vms_arr | templates_arr
418
- Hash[vms.map { |i| [i.fetch('name'), i.fetch('uuid')] }]
419
- end
420
-
421
- # Parse the JSON from *all* VMs and templates.
422
- # Then return an array of objects (without duplicates)
423
- def read_vms_info
424
- args = %w(list --all --info --no-header --json)
425
- vms_arr = json([]) { execute_prlctl(*args) }
426
- templates_arr = json([]) { execute_prlctl(*args, '--template') }
427
-
428
- vms_arr | templates_arr
429
- end
430
-
431
- def read_vms_paths
432
- list = {}
433
- read_vms_info.each do |item|
434
- if Dir.exists? item.fetch('Home')
435
- list[File.realpath item.fetch('Home')] = item.fetch('ID')
436
- end
437
- end
438
-
439
- list
440
- end
441
-
442
- def regenerate_src_uuid
443
- settings = read_settings
444
- vm_config = File.join(settings.fetch('Home'), 'config.pvs')
445
-
446
- # Generate and put new SourceVmUuid
447
- xml = Nokogiri::XML(File.open(vm_config))
448
- p = '//ParallelsVirtualMachine/Identification/SourceVmUuid'
449
- xml.xpath(p).first.content = "{#{SecureRandom.uuid}}"
450
-
451
- File.open(vm_config, 'w') do |f|
452
- f.write xml.to_xml
453
- end
454
- end
455
-
456
- def register(pvm_file)
457
- args = [@prlctl_path, 'register', pvm_file]
458
-
459
- 3.times do
460
- result = raw(*args)
461
- # Exit if everything is OK
462
- return if result.exit_code == 0
463
-
464
- # It may occur in the race condition with other Vagrant processes.
465
- # It is OK, just exit.
466
- return if result.stderr.include?('is already registered.')
467
-
468
- # Sleep a bit though to give Parallels Desktop time to fix itself
469
- sleep 2
470
- end
471
-
472
- # If we reach this point, it means that we consistently got the
473
- # failure, do a standard execute now. This will raise an
474
- # exception if it fails again.
475
- execute(*args)
476
- end
477
-
478
- def registered?(uuid)
479
- args = %w(list --all --info --no-header -o uuid)
480
-
481
- execute_prlctl(*args).include?(uuid) ||
482
- execute_prlctl(*args, '--template').include?(uuid)
483
- end
484
-
485
- def resume
486
- execute_prlctl('resume', @uuid)
487
- end
488
-
489
- def set_mac_address(mac)
490
- execute_prlctl('set', @uuid,
491
- '--device-set', 'net0',
492
- '--type', 'shared',
493
- '--mac', mac)
494
- end
495
-
496
- def set_name(name)
497
- execute_prlctl('set', @uuid, '--name', name)
498
- end
499
-
500
- def share_folders(folders)
501
- folders.each do |folder|
502
- # Add the shared folder
503
- execute_prlctl('set', @uuid,
504
- '--shf-host-add', folder[:name],
505
- '--path', folder[:hostpath])
506
- end
507
- end
508
-
509
- def ssh_ip
510
- read_guest_ip
511
- end
512
-
513
- def ssh_port(expected_port)
514
- expected_port
515
- end
516
-
517
- def start
518
- execute_prlctl('start', @uuid)
519
- end
520
-
521
- def suspend
522
- execute_prlctl('suspend', @uuid)
523
- end
524
-
525
- def unregister(uuid)
526
- args = [@prlctl_path, 'unregister', uuid]
527
- 3.times do
528
- result = raw(*args)
529
- # Exit if everything is OK
530
- return if result.exit_code == 0
531
-
532
- # It may occur in the race condition with other Vagrant processes.
533
- # Both are OK, just exit.
534
- return if result.stderr.include?('is not registered')
535
- return if result.stderr.include?('is being cloned')
536
-
537
- # Sleep a bit though to give Parallels Desktop time to fix itself
538
- sleep 2
539
- end
540
-
541
- # If we reach this point, it means that we consistently got the
542
- # failure, do a standard execute now. This will raise an
543
- # exception if it fails again.
544
- execute(*args)
545
- end
546
-
547
- def unshare_folders(names)
548
- names.each do |name|
549
- execute_prlctl('set', @uuid, '--shf-host-del', name)
550
- end
551
- end
552
-
553
- def vm_exists?(uuid)
554
- 5.times do |i|
555
- result = raw(@prlctl_path, 'list', uuid)
556
- return true if result.exit_code == 0
557
-
558
- # Sometimes this happens. In this case, retry. If
559
- # we don't see this text, the VM really probably doesn't exist.
560
- return false if !result.stderr.include?('Login failed:')
561
-
562
- # Sleep a bit though to give Parallels Desktop time to fix itself
563
- sleep 2
564
- end
565
-
566
- # If we reach this point, it means that we consistently got the
567
- # failure, do a standard prlctl now. This will raise an
568
- # exception if it fails again.
569
- execute_prlctl('list', uuid)
570
- true
571
- end
572
176
  end
573
177
  end
574
178
  end
@@ -2,7 +2,7 @@ require 'log4r'
2
2
 
3
3
  require 'vagrant/util/platform'
4
4
 
5
- require File.expand_path("../pd_8", __FILE__)
5
+ require File.expand_path('../pd_8', __FILE__)
6
6
 
7
7
  module VagrantPlugins
8
8
  module Parallels
@@ -59,6 +59,10 @@ module VagrantPlugins
59
59
  error_key(:shared_adapter_not_found)
60
60
  end
61
61
 
62
+ class SnapshotIdNotDetected < VagrantParallelsError
63
+ error_key(:snapshot_id_not_detected)
64
+ end
65
+
62
66
  class VMImportFailure < VagrantParallelsError
63
67
  error_key(:vm_import_failure)
64
68
  end
@@ -91,6 +91,7 @@ module VagrantPlugins
91
91
  autoload :PD_8, File.expand_path("../driver/pd_8", __FILE__)
92
92
  autoload :PD_9, File.expand_path("../driver/pd_9", __FILE__)
93
93
  autoload :PD_10, File.expand_path("../driver/pd_10", __FILE__)
94
+ autoload :PD_11, File.expand_path("../driver/pd_11", __FILE__)
94
95
  end
95
96
 
96
97
  module Model
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Parallels
3
- VERSION = '1.3.13'
3
+ VERSION = '1.4.0.rc1'
4
4
  end
5
5
  end