Class: VagrantPlugins::Parallels::Driver::Base
- Inherits:
-
Object
- Object
- VagrantPlugins::Parallels::Driver::Base
- Includes:
- Vagrant::Util::NetworkIP, Vagrant::Util::Retryable
- Defined in:
- lib/vagrant-parallels/driver/base.rb
Overview
Base class for all Parallels drivers.
This class provides useful tools for things such as executing PrlCtl and handling SIGINTs and so on.
Instance Method Summary collapse
-
#clear_forwarded_ports(ports) ⇒ Object
Removes the specified port forwarding rules for the virtual machine.
-
#clear_shared_folders ⇒ Object
Clears the shared folders that have been set on the virtual machine.
-
#clone_vm(src_name, options = {}) ⇒ String
Makes a clone of the virtual machine.
-
#compact_hdd(hdd_path) ⇒ Object
Compacts the specified virtual disk image.
-
#connect_network_interface(name) ⇒ Object
Connects the host machine to the specified virtual network interface Could be used for Parallels’ Shared and Host-Only interfaces only.
-
#create_host_only_network(options) ⇒ <Symbol => String>
Creates a host only network with the given options.
-
#create_snapshot(uuid, snapshot_name) ⇒ String
Creates a snapshot for the specified virtual machine.
-
#delete ⇒ Object
Deletes the virtual machine references by this driver.
-
#delete_disabled_adapters ⇒ Object
Deletes all disabled network adapters from the VM configuration.
-
#delete_snapshot(uuid, snapshot_id) ⇒ Object
Deletes the specified snapshot.
-
#delete_unused_host_only_networks ⇒ Object
Deletes host-only networks that aren’t being used by any virtual machine.
-
#disable_password_restrictions(acts) ⇒ Object
Disables requiring password on such operations as creating, adding, removing or cloning the virtual machine.
-
#enable_adapters(adapters) ⇒ Object
Enables network adapters on the VM.
-
#execute_prlctl(*command, &block) ⇒ Object
Wraps ‘execute’ and returns the output of given ‘prlctl’ subcommand.
-
#forward_ports(ports) ⇒ Object
Create a set of port forwarding rules for a virtual machine.
-
#halt(force = false) ⇒ Object
Halts the virtual machine (pulls the plug).
-
#initialize(uuid) ⇒ Base
constructor
A new instance of Base.
-
#list_snapshots(uuid) ⇒ <String => String>
Lists all snapshots for the specified VM.
-
#read_bridged_interfaces ⇒ Array<Symbol => String>
Returns a list of bridged interfaces.
-
#read_forwarded_ports(global = false) ⇒ Array<Symbol => String>
Returns the list of port forwarding rules.
-
#read_guest_ip_dhcp ⇒ String
Returns an IP of the virtual machine fetched from the DHCP lease file.
-
#read_guest_ip_prlctl ⇒ String
Returns an IP of the virtual machine fetched from prlctl.
-
#read_guest_tools_iso_path(guest_os, arch = nil) ⇒ String
Returns path to the Parallels Tools ISO file.
-
#read_guest_tools_state ⇒ Symbol
Returns the state of guest tools that is installed on this VM.
-
#read_host_info ⇒ <Symbol => String>
Returns Parallels Desktop properties and common information about the host machine.
-
#read_host_only_interfaces ⇒ Array<Symbol => String>
Returns a list of available host only interfaces.
-
#read_mac_address ⇒ String
Returns the MAC address of the first Shared network interface.
-
#read_mac_addresses ⇒ Array<String>
Returns the array of network interface card MAC addresses.
-
#read_network_interfaces ⇒ <Integer => Hash>
Returns a list of network interfaces of the VM.
-
#read_settings(uuid = @uuid) ⇒ <String => String, Hash>
Returns virtual machine settings.
-
#read_shared_folders ⇒ <String => String>
Returns a list of shared folders in format: { id => hostpath, … }.
-
#read_shared_interface ⇒ <Symbol => String, Hash>
Returns info about shared network interface.
-
#read_shared_network_id ⇒ String
Returns the unique name (e.q. “ID”) on the first Shared network in Parallels Desktop configuration.
-
#read_state ⇒ Symbol
Returns the current state of this VM.
-
#read_used_ports ⇒ Array
Returns a list of all forwarded ports in use by active virtual machines.
-
#read_virtual_networks ⇒ Array<String => String>
Returns the configuration of all virtual networks in Parallels Desktop.
-
#read_vm_option(option, uuid = @uuid) ⇒ String
Returns a value of specified VM option.
-
#read_vms ⇒ <String => String>
Returns names and ids of all virtual machines and templates.
-
#read_vms_info ⇒ Array <String => String>
Returns the configuration of all VMs and templates.
-
#register(pvm_file, opts = []) ⇒ Object
Registers the virtual machine.
-
#restore_snapshot(uuid, snapshot_id) ⇒ Object
Switches the VM state to the specified snapshot.
-
#resume ⇒ Object
Resumes the virtual machine.
-
#set_name(uuid, new_name) ⇒ Object
Sets the name of the virtual machine.
-
#set_power_consumption_mode(optimized) ⇒ Object
Sets Power Consumption method.
-
#share_folders(folders) ⇒ Object
Share a set of folders on this VM.
-
#ssh_ip ⇒ String
Reads the SSH IP of this VM from DHCP lease file or from ‘prlctl list` command - whatever returns a non-empty result first.
-
#ssh_port(expected) ⇒ Integer
Reads the SSH port of this VM.
-
#start ⇒ Object
Starts the virtual machine.
-
#suspend ⇒ Object
Suspends the virtual machine.
-
#unregister(uuid) ⇒ Object
Performs un-registeration of the specified VM in Parallels Desktop.
-
#unshare_folders(names) ⇒ Object
Unshare folders.
-
#vm_exists?(uuid) ⇒ Boolean
Checks if a VM with the given UUID exists.
Constructor Details
#initialize(uuid) ⇒ Base
Returns a new instance of Base.
23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 |
# File 'lib/vagrant-parallels/driver/base.rb', line 23 def initialize(uuid) @uuid = uuid @logger = Log4r::Logger.new('vagrant_parallels::driver::base') # This flag is used to keep track of interrupted state (SIGINT) @interrupted = false @prlctl_path = util_path('prlctl') @prlsrvctl_path = util_path('prlsrvctl') @prldisktool_path = util_path('prl_disk_tool') unless @prlctl_path # This means that Parallels Desktop was not found, so we raise this # error here. raise VagrantPlugins::Parallels::Errors::ParallelsNotDetected end @logger.info("prlctl path: #{@prlctl_path}") @logger.info("prlsrvctl path: #{@prlsrvctl_path}") end |
Instance Method Details
#clear_forwarded_ports(ports) ⇒ Object
Removes the specified port forwarding rules for the virtual machine.
Each port should be described as a hash with the following keys:
{
name: 'example',
protocol: 'tcp',
guest: 'target-vm-uuid',
hostport: '8080',
guestport: '80'
}
57 58 59 60 61 62 63 64 |
# File 'lib/vagrant-parallels/driver/base.rb', line 57 def clear_forwarded_ports(ports) args = [] ports.each do |r| args.concat(["--nat-#{r[:protocol]}-del", r[:name]]) end execute_prlsrvctl('net', 'set', read_shared_network_id, *args) unless args.empty? end |
#clear_shared_folders ⇒ Object
Clears the shared folders that have been set on the virtual machine.
67 68 69 70 71 72 |
# File 'lib/vagrant-parallels/driver/base.rb', line 67 def clear_shared_folders share_ids = read_shared_folders.keys share_ids.each do |id| execute_prlctl('set', @uuid, '--shf-host-del', id) end end |
#clone_vm(src_name, options = {}) ⇒ String
Makes a clone of the virtual machine.
79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 |
# File 'lib/vagrant-parallels/driver/base.rb', line 79 def clone_vm(src_name, = {}) dst_name = "vagrant_temp_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" args = ['clone', src_name, '--name', dst_name] args.concat(['--dst', [:dst]]) if [:dst] # Linked clone options args << '--linked' if [:linked] args.concat(['--id', [:snapshot_id]]) if [:snapshot_id] # Regenerate SourceVmUuid of the cloned VM args << '--regenerate-src-uuid' if [:regenerate_src_uuid] execute_prlctl(*args) do |_, data| lines = data.split('\r') # The progress of the clone will be in the last line. Do a greedy # regular expression to find what we're looking for. if lines.last =~ /Copying hard disk.+?(\d{,3}) ?%/ yield $1.to_i if block_given? end end read_vms[dst_name] end |
#compact_hdd(hdd_path) ⇒ Object
Compacts the specified virtual disk image
106 107 108 109 110 111 112 113 114 115 |
# File 'lib/vagrant-parallels/driver/base.rb', line 106 def compact_hdd(hdd_path) execute(@prldisktool_path, 'compact', '--hdd', hdd_path) do |_, data| lines = data.split('\r') # The progress of the compact will be in the last line. Do a greedy # regular expression to find what we're looking for. if lines.last =~ /.+?(\d{,3}) ?%/ yield $1.to_i if block_given? end end end |
#connect_network_interface(name) ⇒ Object
Connects the host machine to the specified virtual network interface Could be used for Parallels’ Shared and Host-Only interfaces only.
121 122 123 |
# File 'lib/vagrant-parallels/driver/base.rb', line 121 def connect_network_interface(name) raise NotImplementedError end |
#create_host_only_network(options) ⇒ <Symbol => String>
Creates a host only network with the given options.
including keys ‘:name`, `:ip`, `:netmask` and `:dhcp`
131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 |
# File 'lib/vagrant-parallels/driver/base.rb', line 131 def create_host_only_network() # Create the interface execute_prlsrvctl('net', 'add', [:network_id], '--type', 'host-only') # Configure it args = ['--ip', "#{[:adapter_ip]}/#{[:netmask]}"] if [:dhcp] args.concat(['--dhcp-ip', [:dhcp][:ip], '--ip-scope-start', [:dhcp][:lower], '--ip-scope-end', [:dhcp][:upper]]) end execute_prlsrvctl('net', 'set', [:network_id], *args) # Return the details { name: [:network_id], ip: [:adapter_ip], netmask: [:netmask], dhcp: [:dhcp] } end |
#create_snapshot(uuid, snapshot_name) ⇒ String
Creates a snapshot for the specified virtual machine.
159 160 161 162 163 164 |
# File 'lib/vagrant-parallels/driver/base.rb', line 159 def create_snapshot(uuid, snapshot_name) stdout = execute_prlctl('snapshot', uuid, '--name', snapshot_name) return Regexp.last_match(1) if stdout =~ /{([\w-]+)}/ raise Errors::SnapshotIdNotDetected, stdout: stdout end |
#delete ⇒ Object
Deletes the virtual machine references by this driver.
167 168 169 |
# File 'lib/vagrant-parallels/driver/base.rb', line 167 def delete execute_prlctl('delete', @uuid) end |
#delete_disabled_adapters ⇒ Object
Deletes all disabled network adapters from the VM configuration
172 173 174 175 176 177 178 |
# File 'lib/vagrant-parallels/driver/base.rb', line 172 def delete_disabled_adapters read_settings.fetch('Hardware', {}).each do |adapter, params| if adapter.start_with?('net') and !params.fetch('enabled', true) execute_prlctl('set', @uuid, '--device-del', adapter) end end end |
#delete_snapshot(uuid, snapshot_id) ⇒ Object
Deletes the specified snapshot
184 185 186 187 188 189 190 |
# File 'lib/vagrant-parallels/driver/base.rb', line 184 def delete_snapshot(uuid, snapshot_id) # Sometimes this command fails with 'Data synchronization is currently # in progress'. Just wait and retry. retryable(on: VagrantPlugins::Parallels::Errors::ExecutionError, tries: 2, sleep: 2) do execute_prlctl('snapshot-delete', uuid, '--id', snapshot_id) end end |
#delete_unused_host_only_networks ⇒ Object
Deletes host-only networks that aren’t being used by any virtual machine.
193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 |
# File 'lib/vagrant-parallels/driver/base.rb', line 193 def delete_unused_host_only_networks networks = read_virtual_networks # Exclude all host-only network interfaces which were not created by vagrant provider. networks.keep_if do |net| net['Type'] == 'host-only' && net['Network ID'] =~ /^vagrant-vnet(\d+)$/ end read_vms_info.each do |vm| used_nets = vm.fetch('Hardware', {}).select { |name, _| name.start_with? 'net' } used_nets.each_value do |net_params| networks.delete_if { |net| net['Network ID'] == net_params.fetch('iface', nil) } end end # Delete all unused network interfaces. networks.each do |net| execute_prlsrvctl('net', 'del', net['Network ID']) end end |
#disable_password_restrictions(acts) ⇒ Object
Disables requiring password on such operations as creating, adding, removing or cloning the virtual machine.
- ‘create-vm’, ‘add-vm’, ‘remove-vm’, ‘clone-vm’
219 220 221 222 223 224 |
# File 'lib/vagrant-parallels/driver/base.rb', line 219 def disable_password_restrictions(acts) server_info = json { execute_prlsrvctl('info', '--json') } server_info.fetch('Require password to', []).each do |act| execute_prlsrvctl('set', '--require-pwd', "#{act}:off") if acts.include? act end end |
#enable_adapters(adapters) ⇒ Object
Enables network adapters on the VM.
The format of each adapter specification should be like so:
:type => :hostonly,
:hostonly => "vagrant-vnet0",
:name => "vnic2",
:nic_type => "virtio"
This must support setting up both host only and bridged networks.
Array of adapters to be enabled.
241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 |
# File 'lib/vagrant-parallels/driver/base.rb', line 241 def enable_adapters(adapters) # Get adapters which have already configured for this VM # Such adapters will be just overridden existing_adapters = read_settings.fetch('Hardware', {}).keys.select do |name| name.start_with? 'net' end # Disable all previously existing adapters (except shared 'vnet0') existing_adapters.each do |adapter| execute_prlctl('set', @uuid, '--device-set', adapter, '--disable') if adapter != 'vnet0' end adapters.each do |adapter| args = [] if existing_adapters.include? "net#{adapter[:adapter]}" args.concat(['--device-set', "net#{adapter[:adapter]}", '--enable']) else args.concat(%w[--device-add net]) end case adapter[:type] when :hostonly args.concat(['--type', 'host', '--iface', adapter[:hostonly]]) when :bridged args.concat(['--type', 'bridged', '--iface', adapter[:bridge]]) when :shared args.concat(%w[--type shared]) end args.concat(['--mac', adapter[:mac_address]]) if adapter[:mac_address] args.concat(['--adapter-type', adapter[:nic_type].to_s]) if adapter[:nic_type] execute_prlctl('set', @uuid, *args) end end |
#execute_prlctl(*command, &block) ⇒ Object
Wraps ‘execute’ and returns the output of given ‘prlctl’ subcommand.
859 860 861 |
# File 'lib/vagrant-parallels/driver/base.rb', line 859 def execute_prlctl(*command, &block) execute(@prlctl_path, *command, &block) end |
#forward_ports(ports) ⇒ Object
Create a set of port forwarding rules for a virtual machine.
This will not affect any previously set forwarded ports, so be sure to delete those if you need to.
The format of each port hash should be the following:
{
guestport: 80,
hostport: 8500,
name: "foo",
protocol: "tcp"
}
Note that “protocol” is optional and will default to “tcp”.
for more information on the format.
296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 |
# File 'lib/vagrant-parallels/driver/base.rb', line 296 def forward_ports(ports) args = [] ports.each do || protocol = [:protocol] || 'tcp' pf_builder = [ [:name], [:host_port], @uuid, [:guest_port] ] args.concat(["--nat-#{protocol}-add", pf_builder.join(',')]) end execute_prlsrvctl('net', 'set', read_shared_network_id, *args) end |
#halt(force = false) ⇒ Object
Halts the virtual machine (pulls the plug).
343 344 345 346 347 |
# File 'lib/vagrant-parallels/driver/base.rb', line 343 def halt(force = false) args = ['stop', @uuid] args << '--kill' if force execute_prlctl(*args) end |
#list_snapshots(uuid) ⇒ <String => String>
Lists all snapshots for the specified VM. Returns an empty hash if there are no snapshots.
318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 |
# File 'lib/vagrant-parallels/driver/base.rb', line 318 def list_snapshots(uuid) settings = read_settings(uuid) snap_config = File.join(settings.fetch('Home'), 'Snapshots.xml') # There are no snapshots, exit return {} unless File.exist?(snap_config) xml = Nokogiri::XML(File.read(snap_config)) snapshots = {} # Loop over all 'SavedStateItem' and fetch 'Name' => 'ID' pairs xml.xpath('//SavedStateItem').each do |snap| snap_id = snap.attr('guid') # The first entry is always empty (the base sate) next if snap_id.empty? snap_name = snap.at('Name').text snapshots[snap_name] = snap_id end snapshots end |
#read_bridged_interfaces ⇒ Array<Symbol => String>
Returns a list of bridged interfaces.
352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 |
# File 'lib/vagrant-parallels/driver/base.rb', line 352 def read_bridged_interfaces host_hw_info = read_host_info.fetch('Hardware info', {}) net_list = host_hw_info.select do |name, attrs| # Get all network interfaces except 'vnicXXX' attrs.fetch('type') == 'net' and name !~ /^(vnic(.+?))$/ end bridged_ifaces = [] net_list.keys.each do |iface| info = {} ifconfig = execute('ifconfig', iface) # Assign default values info[:name] = iface info[:ip] = '0.0.0.0' info[:netmask] = '0.0.0.0' info[:status] = 'Down' info[:ip] = $1.to_s if ifconfig =~ /(?<=inet\s)(\S*)/ if ifconfig =~ /(?<=netmask\s)(\S*)/ # Netmask will be converted from hex to dec: # '0xffffff00' -> '255.255.255.0' info[:netmask] = $1.hex.to_s(16).scan(/../).each.map { |octet| octet.hex }.join('.') end info[:status] = 'Up' if ifconfig =~ /\W(UP)\W/ and ifconfig !~ /(?<=status:\s)inactive$/ bridged_ifaces << info end bridged_ifaces end |
#read_forwarded_ports(global = false) ⇒ Array<Symbol => String>
Returns the list of port forwarding rules. Each rule will be represented as a hash with the following keys:
{
name: 'example',
protocol: 'tcp',
guest: 'target-vm-uuid',
hostport: '8080',
guestport: '80'
}
Otherwise only rules related to the context VM will be returned.
396 397 398 399 400 401 402 403 404 |
# File 'lib/vagrant-parallels/driver/base.rb', line 396 def read_forwarded_ports(global = false) all_rules = read_shared_interface[:nat] if global all_rules else all_rules.select { |r| r[:guest].include?(@uuid) } end end |
#read_guest_ip_dhcp ⇒ String
Returns an IP of the virtual machine fetched from the DHCP lease file. It requires that Shared network adapter is configured for this VM and it obtains an IP via DHCP. Returns an empty string if the IP coudn’t be determined this way.
412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 |
# File 'lib/vagrant-parallels/driver/base.rb', line 412 def read_guest_ip_dhcp mac_addr = read_mac_address.downcase leases_file = '/Library/Preferences/Parallels/parallels_dhcp_leases' leases = {} begin File.open(leases_file).grep(/#{mac_addr}/) do |line| _, ip, exp, dur, = line.split /([\d.]*)="(\d*),(\d*),(\w*),(\w*)".*/ leases[ip] = exp.to_i - dur.to_i end rescue Errno::EACCES raise Errors::DhcpLeasesNotAccessible, leases_file: leases_file.to_s rescue Errno::ENOENT # File does not exist # Perhaps, it is the fist start of Parallels Desktop return '' end return '' if leases.empty? # Get the most resent lease and return an associated IP leases.max_by { |_ip, lease_time| lease_time }.first end |
#read_guest_ip_prlctl ⇒ String
Returns an IP of the virtual machine fetched from prlctl. Returns an empty string if the IP coudn’t be determined this way.
439 440 441 442 443 |
# File 'lib/vagrant-parallels/driver/base.rb', line 439 def read_guest_ip_prlctl vm_info = json { execute_prlctl('list', @uuid, '--full', '--json') } ip = vm_info.first.fetch('ip_configured', '') ip == '-' ? '' : ip end |
#read_guest_tools_iso_path(guest_os, arch = nil) ⇒ String
Returns path to the Parallels Tools ISO file.
449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 |
# File 'lib/vagrant-parallels/driver/base.rb', line 449 def read_guest_tools_iso_path(guest_os, arch=nil) guest_os = (guest_os + (['arm', 'arm64', 'aarch64'].include?(arch.to_s.strip) ? '_arm' : '')).to_sym iso_name = { linux: 'prl-tools-lin.iso', linux_arm: 'prl-tools-lin-arm.iso', darwin: 'prl-tools-mac.iso', darwin_arm: 'prl-tools-mac-arm.iso', windows: 'prl-tools-win.iso', windows_arm: 'prl-tools-win-arm.iso', } return nil unless iso_name[guest_os] bundle_id = 'com.parallels.desktop.console' bundle_path = execute('mdfind', "kMDItemCFBundleIdentifier == #{bundle_id}") iso_path = File.("./Contents/Resources/Tools/#{iso_name[guest_os]}", bundle_path.split("\n")[0]) unless File.exist?(iso_path) if guest_os == :windows # Fallback to exe tools package for older versions of Paralles iso_path = File.("./Contents/Resources/Tools/PTIAgent.exe", bundle_path.split("\n")[0]) end raise Errors::ParallelsToolsIsoNotFound, iso_path: iso_path unless File.exist?(iso_path) end iso_path end |
#read_guest_tools_state ⇒ Symbol
Returns the state of guest tools that is installed on this VM. Can be any of:
-
:installed
-
:not_installed
-
:possibly_installed
-
:outdated
486 487 488 489 490 |
# File 'lib/vagrant-parallels/driver/base.rb', line 486 def read_guest_tools_state state = read_settings.fetch('GuestTools', {}).fetch('state', nil) state ||= 'not_installed' state.to_sym end |
#read_host_info ⇒ <Symbol => String>
Returns Parallels Desktop properties and common information about the host machine.
496 497 498 |
# File 'lib/vagrant-parallels/driver/base.rb', line 496 def read_host_info json { execute_prlctl('server', 'info', '--json') } end |
#read_host_only_interfaces ⇒ Array<Symbol => String>
Returns a list of available host only interfaces. Each interface is represented as a Hash with the following details:
name: 'Host-Only', # Parallels Network ID
ip: '10.37.129.2', # IP address of the interface
netmask: '255.255.255.0', # netmask associated with the interface
status: 'Up' # status of the interface
511 512 513 514 515 516 517 518 519 520 521 522 523 524 525 526 527 528 529 530 531 532 533 534 535 536 537 538 539 540 541 |
# File 'lib/vagrant-parallels/driver/base.rb', line 511 def read_host_only_interfaces net_list = read_virtual_networks net_list.keep_if { |net| net['Type'] == 'host-only' } hostonly_ifaces = [] net_list.each do |iface| net_info = json do execute_prlsrvctl('net', 'info', iface['Network ID'], '--json') end iface = { name: net_info['Network ID'], status: 'Down' } adapter = net_info['Parallels adapter'] if adapter iface[:ip] = adapter['IPv4 address'] iface[:netmask] = adapter['IPv4 subnet mask'] iface[:status] = 'Up' if adapter['IPv6 address'] && adapter['IPv6 subnet mask'] iface[:ipv6] = adapter['IPv6 address'] iface[:ipv6_prefix] = adapter['IPv6 subnet mask'] end end hostonly_ifaces << iface end hostonly_ifaces end |
#read_mac_address ⇒ String
Returns the MAC address of the first Shared network interface.
546 547 548 549 550 551 552 553 554 555 |
# File 'lib/vagrant-parallels/driver/base.rb', line 546 def read_mac_address hw_info = read_settings.fetch('Hardware', {}) shared_ifaces = hw_info.select do |name, params| name.start_with?('net') && params['type'] == 'shared' end raise Errors::SharedInterfaceNotFound if shared_ifaces.empty? shared_ifaces.values.first.fetch('mac', nil) end |
#read_mac_addresses ⇒ Array<String>
Returns the array of network interface card MAC addresses
560 561 562 |
# File 'lib/vagrant-parallels/driver/base.rb', line 560 def read_mac_addresses read_vm_option('mac').strip.gsub(':', '').split(' ') end |
#read_network_interfaces ⇒ <Integer => Hash>
Returns a list of network interfaces of the VM.
567 568 569 570 571 572 573 574 575 576 577 578 579 580 581 582 583 584 585 586 587 588 589 590 |
# File 'lib/vagrant-parallels/driver/base.rb', line 567 def read_network_interfaces nics = {} # Get enabled VM's network interfaces ifaces = read_settings.fetch('Hardware', {}).keep_if do |dev, params| dev.start_with?('net') and params.fetch('enabled', true) end ifaces.each do |name, params| adapter = name.match(/^net(\d+)$/)[1].to_i nics[adapter] ||= {} case params['type'] when 'shared' nics[adapter][:type] = :shared when 'host' nics[adapter][:type] = :hostonly nics[adapter][:hostonly] = params.fetch('iface', '') when 'bridged' nics[adapter][:type] = :bridged nics[adapter][:bridge] = params.fetch('iface', '') end end nics end |
#read_settings(uuid = @uuid) ⇒ <String => String, Hash>
Returns virtual machine settings
595 596 597 598 |
# File 'lib/vagrant-parallels/driver/base.rb', line 595 def read_settings(uuid = @uuid) vm = json { execute_prlctl('list', uuid, '--info', '--no-header', '--json') } vm.last end |
#read_shared_folders ⇒ <String => String>
Returns a list of shared folders in format: { id => hostpath, … }
656 657 658 659 660 661 662 663 664 |
# File 'lib/vagrant-parallels/driver/base.rb', line 656 def read_shared_folders shf_info = read_settings.fetch('Host Shared Folders', {}) list = {} shf_info.delete_if { |k, _v| k == 'enabled' }.each do |id, data| list[id] = data.fetch('path') end list end |
#read_shared_interface ⇒ <Symbol => String, Hash>
Returns info about shared network interface.
612 613 614 615 616 617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 |
# File 'lib/vagrant-parallels/driver/base.rb', line 612 def read_shared_interface net_info = json do execute_prlsrvctl('net', 'info', read_shared_network_id, '--json') end iface = { nat: [], status: 'Down' } adapter = net_info['Parallels adapter'] if adapter iface[:ip] = adapter['IPv4 address'] iface[:netmask] = adapter['IPv4 subnet mask'] iface[:status] = 'Up' end if net_info.key?('DHCPv4 server') iface[:dhcp] = { ip: net_info['DHCPv4 server']['Server address'], lower: net_info['DHCPv4 server']['IP scope start address'], upper: net_info['DHCPv4 server']['IP scope end address'] } end net_info['NAT server'].each do |group, rules| rules.each do |name, params| iface[:nat] << { name: name, protocol: group == 'TCP rules' ? 'tcp' : 'udp', guest: params['destination IP/VM id'], hostport: params['source port'], guestport: params['destination port'] } end end iface end |
#read_shared_network_id ⇒ String
Returns the unique name (e.q. “ID”) on the first Shared network in Parallels Desktop configuration. By default there is only one called “Shared”.
605 606 607 |
# File 'lib/vagrant-parallels/driver/base.rb', line 605 def read_shared_network_id 'Shared' end |
#read_state ⇒ Symbol
Returns the current state of this VM.
669 670 671 |
# File 'lib/vagrant-parallels/driver/base.rb', line 669 def read_state read_vm_option('status').strip.to_sym end |
#read_used_ports ⇒ Array
Returns a list of all forwarded ports in use by active virtual machines.
677 678 679 680 |
# File 'lib/vagrant-parallels/driver/base.rb', line 677 def read_used_ports # Ignore our own used ports read_forwarded_ports(true).reject { |r| r[:guest].include?(@uuid) } end |
#read_virtual_networks ⇒ Array<String => String>
Returns the configuration of all virtual networks in Parallels Desktop.
685 686 687 |
# File 'lib/vagrant-parallels/driver/base.rb', line 685 def read_virtual_networks json { execute_prlsrvctl('net', 'list', '--json') } end |
#read_vm_option(option, uuid = @uuid) ⇒ String
Returns a value of specified VM option. Raises an exception if value is not available
695 696 697 698 699 700 |
# File 'lib/vagrant-parallels/driver/base.rb', line 695 def read_vm_option(option, uuid = @uuid) out = execute_prlctl('list', uuid, '--no-header', '-o', option).strip raise Errors::ParallelsVMOptionNotFound, vm_option: option if out.empty? out end |
#read_vms ⇒ <String => String>
Returns names and ids of all virtual machines and templates.
705 706 707 708 709 710 711 712 |
# File 'lib/vagrant-parallels/driver/base.rb', line 705 def read_vms args = %w[list --all --no-header --json -o name,uuid] vms_arr = json { execute_prlctl(*args) } templates_arr = json { execute_prlctl(*args, '--template') } vms = vms_arr | templates_arr Hash[vms.map { |i| [i.fetch('name'), i.fetch('uuid')] }] end |
#read_vms_info ⇒ Array <String => String>
Returns the configuration of all VMs and templates.
717 718 719 720 721 722 723 |
# File 'lib/vagrant-parallels/driver/base.rb', line 717 def read_vms_info args = %w[list --all --info --no-header --json] vms_arr = json { execute_prlctl(*args) } templates_arr = json { execute_prlctl(*args, '--template') } vms_arr | templates_arr end |
#register(pvm_file, opts = []) ⇒ Object
Registers the virtual machine
729 730 731 |
# File 'lib/vagrant-parallels/driver/base.rb', line 729 def register(pvm_file, opts = []) execute_prlctl('register', pvm_file, *opts) end |
#restore_snapshot(uuid, snapshot_id) ⇒ Object
Switches the VM state to the specified snapshot
737 738 739 740 741 742 743 |
# File 'lib/vagrant-parallels/driver/base.rb', line 737 def restore_snapshot(uuid, snapshot_id) # Sometimes this command fails with 'Data synchronization is currently # in progress'. Just wait and retry. retryable(on: VagrantPlugins::Parallels::Errors::ExecutionError, tries: 2, sleep: 2) do execute_prlctl('snapshot-switch', uuid, '-i', snapshot_id) end end |
#resume ⇒ Object
Resumes the virtual machine.
747 748 749 |
# File 'lib/vagrant-parallels/driver/base.rb', line 747 def resume execute_prlctl('resume', @uuid) end |
#set_name(uuid, new_name) ⇒ Object
Sets the name of the virtual machine.
755 756 757 |
# File 'lib/vagrant-parallels/driver/base.rb', line 755 def set_name(uuid, new_name) execute_prlctl('set', uuid, '--name', new_name) end |
#set_power_consumption_mode(optimized) ⇒ Object
Sets Power Consumption method.
instead “Better Performance”
763 764 765 766 |
# File 'lib/vagrant-parallels/driver/base.rb', line 763 def set_power_consumption_mode(optimized) state = optimized ? 'on' : 'off' execute_prlctl('set', @uuid, '--longer-battery-life', state) end |
#share_folders(folders) ⇒ Object
Share a set of folders on this VM.
771 772 773 774 775 776 777 778 |
# File 'lib/vagrant-parallels/driver/base.rb', line 771 def share_folders(folders) folders.each do |folder| # Add the shared folder execute_prlctl('set', @uuid, '--shf-host-add', folder[:name], '--path', folder[:hostpath]) end end |
#ssh_ip ⇒ String
Reads the SSH IP of this VM from DHCP lease file or from ‘prlctl list` command - whatever returns a non-empty result first. The method with DHCP does not work for *.macvm VMs on Apple M-series Macs, so we try both sources here.
786 787 788 789 790 791 792 793 794 795 796 797 798 799 800 |
# File 'lib/vagrant-parallels/driver/base.rb', line 786 def ssh_ip 5.times do ip = read_guest_ip_dhcp return ip unless ip.empty? ip = read_guest_ip_prlctl return ip unless ip.empty? sleep 2 end # We didn't manage to determine IP - return nil and # expect SSH client to do a retry return nil end |
#ssh_port(expected) ⇒ Integer
Reads the SSH port of this VM.
806 807 808 |
# File 'lib/vagrant-parallels/driver/base.rb', line 806 def ssh_port(expected) expected end |
#start ⇒ Object
Starts the virtual machine.
812 813 814 |
# File 'lib/vagrant-parallels/driver/base.rb', line 812 def start execute_prlctl('start', @uuid) end |
#suspend ⇒ Object
Suspends the virtual machine.
817 818 819 |
# File 'lib/vagrant-parallels/driver/base.rb', line 817 def suspend execute_prlctl('suspend', @uuid) end |
#unregister(uuid) ⇒ Object
Performs un-registeration of the specified VM in Parallels Desktop. Virtual machine will be removed from the VM list, but its image will not be deleted from the disk. So, it can be registered again.
824 825 826 |
# File 'lib/vagrant-parallels/driver/base.rb', line 824 def unregister(uuid) execute_prlctl('unregister', uuid) end |
#unshare_folders(names) ⇒ Object
Unshare folders.
829 830 831 832 833 |
# File 'lib/vagrant-parallels/driver/base.rb', line 829 def unshare_folders(names) names.each do |name| execute_prlctl('set', @uuid, '--shf-host-del', name) end end |
#vm_exists?(uuid) ⇒ Boolean
Checks if a VM with the given UUID exists.
838 839 840 841 842 843 844 845 846 847 848 849 850 851 852 853 854 855 856 |
# File 'lib/vagrant-parallels/driver/base.rb', line 838 def vm_exists?(uuid) 5.times do result = raw(@prlctl_path, 'list', uuid) return true if result.exit_code == 0 # Sometimes this happens. In this case, retry. # If we don't see this text, the VM really doesn't exist. return false unless result.stderr.include?('Login failed:') # Sleep a bit though to give Parallels Desktop time to fix itself sleep 2 end # If we reach this point, it means that we consistently got the # failure, do a standard prlctl now. This will raise an # exception if it fails again. execute_prlctl('list', uuid) true end |