diff --git a/.travis.yml b/.travis.yml index 68e5b594..487f5556 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,8 +1,6 @@ language: ruby before_install: - - sudo apt-get update -qq - - sudo apt-get install -qq -y bsdtar - - gem install bundler -v '1.5.2' + - gem install bundler -v '1.6.6' rvm: - 2.0.0 script: rake test:unit \ No newline at end of file diff --git a/LICENSE.txt b/LICENSE.txt index bc4b0b96..02fe1499 100644 --- a/LICENSE.txt +++ b/LICENSE.txt @@ -1,4 +1,5 @@ Copyright (c) 2013 Youssef Shahin +Copyright (c) 2013-2014 Parallels IP Holdings GmbH. MIT License diff --git a/lib/vagrant-parallels/action.rb b/lib/vagrant-parallels/action.rb index 6604e3d8..64015ab3 100644 --- a/lib/vagrant-parallels/action.rb +++ b/lib/vagrant-parallels/action.rb @@ -304,6 +304,31 @@ def self.action_simple_reboot end end + # This action sync folders on a running provider. It is used by the docker provider + # to link synced folders on the host machine as volumes into the docker containers + def self.action_sync_folders + Vagrant::Action::Builder.new.tap do |b| + b.use ConfigValidate + b.use Call, IsState, :not_created do |env1, b1| + if env1[:result] + b1.use Message, I18n.t("vagrant.commands.common.vm_not_created") + next + end + + b1.use Call, IsState, :running do |env2, b2| + if !env2[:result] + b2.use Message, I18n.t("vagrant.commands.common.vm_not_running") + next + end + + b2.use SyncedFolders + b2.use PrepareNFSSettings + end + end + end + end + + autoload :Boot, File.expand_path("../action/boot", __FILE__) autoload :HandleGuestTools, File.expand_path("../action/handle_guest_tools", __FILE__) autoload :HandleForwardedPortCollisions, File.expand_path("../action/handle_forwarded_port_collisions.rb", __FILE__) diff --git a/lib/vagrant-parallels/action/export.rb b/lib/vagrant-parallels/action/export.rb index 9d2c534a..ef4a35c9 100644 --- a/lib/vagrant-parallels/action/export.rb +++ b/lib/vagrant-parallels/action/export.rb @@ -13,17 +13,9 @@ def call(env) raise Vagrant::Errors::VMPowerOffToPackage end - name = "#{env[:root_path].basename.to_s}_#{env[:machine].name}" - name.gsub!(/[^-a-z0-9_]/i, "") + @tpl_name = gen_template_name - # Check the name is not in use - if @env[:machine].provider.driver.read_vms.has_key?(@template_name) - @template_name << rand(100000).to_s - end - - - @template_name = gen_template_name - @template_uuid = export + export compact_template unregister_template @@ -42,21 +34,24 @@ def gen_template_name name = @env[:machine].provider_config.name if !name name = "#{@env[:root_path].basename.to_s}_#{@env[:machine].name}" - name.gsub!(/[^-a-z0-9_]/i, "") + name.gsub!(/[^-a-z0-9_]/i, '') end + tpl_name = "#{name}_box" # Ensure that the name is not in use - if @env[:machine].provider.driver.read_vms.has_key?(tpl_name) - tpl_name << "_#{rand(1000)}" + ind = 0 + while @env[:machine].provider.driver.read_vms.has_key?(tpl_name) + ind += 1 + tpl_name = "#{name}_box_#{ind}" end tpl_name end def export - @env[:ui].info I18n.t("vagrant.actions.vm.export.exporting") - tpl_uuid = @env[:machine].provider.driver.export(@env["export.temp_dir"], @template_name) do |progress| + @env[:ui].info I18n.t('vagrant.actions.vm.export.exporting') + @env[:machine].provider.driver.export(@env['export.temp_dir'], @tpl_name) do |progress| @env[:ui].clear_line @env[:ui].report_progress(progress, 100, false) @@ -68,13 +63,11 @@ def export # Clear the line a final time so the next data can appear # alone on the line. @env[:ui].clear_line - - tpl_uuid end def compact_template - @env[:ui].info I18n.t("vagrant_parallels.actions.vm.export.compacting") - @env[:machine].provider.driver.compact(@template_uuid) do |progress| + @env[:ui].info I18n.t('vagrant_parallels.actions.vm.export.compacting') + @env[:machine].provider.driver.compact(@tpl_name) do |progress| @env[:ui].clear_line @env[:ui].report_progress(progress, 100, false) end @@ -85,10 +78,8 @@ def compact_template end def unregister_template - if @env[:machine].provider.driver.registered?(@template_uuid) - @logger.info("Unregister the box template: '#{@template_uuid}'") - @env[:machine].provider.driver.unregister(@template_uuid) - end + @logger.info("Unregister the box template: '#{@tpl_name}'") + @env[:machine].provider.driver.unregister(@tpl_name) end end end diff --git a/lib/vagrant-parallels/action/handle_forwarded_port_collisions.rb b/lib/vagrant-parallels/action/handle_forwarded_port_collisions.rb index e79629be..f96da729 100644 --- a/lib/vagrant-parallels/action/handle_forwarded_port_collisions.rb +++ b/lib/vagrant-parallels/action/handle_forwarded_port_collisions.rb @@ -13,7 +13,7 @@ def call(env) if env[:machine].provider.pd_version_satisfies?('>= 10') super else - # Just continue if port forwarding is not supporting + # Just continue if port forwarding is not supported @app.call(env) end end @@ -22,7 +22,7 @@ def recover(env) if env[:machine].provider.pd_version_satisfies?('>= 10') super end - # Do nothing if port forwarding is not supporting + # Do nothing if port forwarding is not supported end end end diff --git a/lib/vagrant-parallels/action/import.rb b/lib/vagrant-parallels/action/import.rb index 377af1eb..c95f7906 100644 --- a/lib/vagrant-parallels/action/import.rb +++ b/lib/vagrant-parallels/action/import.rb @@ -1,3 +1,5 @@ +require 'nokogiri' + module VagrantPlugins module Parallels module Action @@ -8,27 +10,36 @@ def initialize(app, env) end def call(env) - @env = env - @template_path = File.realpath(Pathname.glob(env[:machine].box.directory.join('*.pvm')).first) - @template_uuid = register_template - import - unregister_template + @machine = env[:machine] + + # Register template to be able to clone it further + register_template(template_path.to_s) + + # Get template name. It might be changed during registration if name + # collision has been occurred + tpl_name = template_name(template_path) + + # Import VM, e.q. clone it from registered template + import(env, tpl_name) + + # Hide template since we dont need it anymore + unregister_template(tpl_name) # Flag as erroneous and return if import failed - raise VagrantPlugins::Parallels::Errors::VMImportFailure if !env[:machine].id + raise Errors::VMImportFailure if !@machine.id # Import completed successfully. Continue the chain @app.call(env) end def recover(env) - @env = env # We should to unregister template - unregister_template + tpl_name = template_name(template_path) + unregister_template(tpl_name) - if env[:machine].state.id != :not_created + if @machine.state.id != :not_created return if env["vagrant.error"].is_a?(Vagrant::Errors::VagrantError) - return if env["vagrant_parallels.error"].is_a?(VagrantPlugins::Parallels::Errors::VagrantParallelsError) + return if env["vagrant_parallels.error"].is_a?(Errors::VagrantParallelsError) # If we're not supposed to destroy on error then just return return if !env[:destroy_on_error] @@ -45,42 +56,51 @@ def recover(env) protected - def register_template - if !@env[:machine].provider.driver.read_vms_paths.has_key?(@template_path) - @logger.info("Register the box template: '#{@template_path}'") - regen_uuid = @env[:machine].provider_config.regen_box_uuid + def register_template(tpl_path_s) + @logger.info("Register the box template: '#{tpl_path_s}'") + regen_uuid = @machine.provider_config.regen_box_uuid - @env[:machine].provider.driver.register(@template_path, regen_uuid) - end + @machine.provider.driver.register(tpl_path_s, regen_uuid) + end - # Return the uuid of registered template - @env[:machine].provider.driver.read_vms_paths[@template_path] + def template_path + Pathname.glob(@machine.box.directory.join('*.pvm')).first end - def import - @env[:ui].info I18n.t("vagrant.actions.vm.import.importing", - :name => @env[:machine].box.name) + def template_name(tpl_path) + # Get template name from XML-based configuration file + tpl_config = tpl_path.join('config.pvs') + xml = Nokogiri::XML(File.open(tpl_config)) + name = xml.xpath('//ParallelsVirtualMachine/Identification/VmName').text + + if !name + raise Errors::ParallelsTplNameNotFound, config_path: tpl_config + end + + name + end + def import(env, tpl_name) + env[:ui].info I18n.t("vagrant.actions.vm.import.importing", + :name => @machine.box.name) # Import the virtual machine - @env[:machine].id = @env[:machine].provider.driver.import(@template_uuid) do |progress| - @env[:ui].clear_line - @env[:ui].report_progress(progress, 100, false) + @machine.id = @machine.provider.driver.import(tpl_name) do |progress| + env[:ui].clear_line + env[:ui].report_progress(progress, 100, false) # # If we got interrupted, then the import could have been interrupted. # Just rise an exception and then 'recover' will be called to cleanup. - raise Vagrant::Errors::VagrantInterrupt if @env[:interrupted] + raise Vagrant::Errors::VagrantInterrupt if env[:interrupted] end # Clear the line one last time since the progress meter doesn't disappear # immediately. - @env[:ui].clear_line + env[:ui].clear_line end - def unregister_template - if @env[:machine].provider.driver.registered?(@template_uuid) - @logger.info("Unregister the box template: '#{@template_uuid}'") - @env[:machine].provider.driver.unregister(@template_uuid) - end + def unregister_template(tpl_name) + @logger.info("Unregister the box template: '#{tpl_name}'") + @machine.provider.driver.unregister(tpl_name) end end end diff --git a/lib/vagrant-parallels/action/network.rb b/lib/vagrant-parallels/action/network.rb index b5be41a3..cb9291f8 100644 --- a/lib/vagrant-parallels/action/network.rb +++ b/lib/vagrant-parallels/action/network.rb @@ -234,6 +234,7 @@ def hostonly_config(options) options = { auto_config: true, mac: nil, + name: nil, nic_type: nil, netmask: "255.255.255.0", type: :static @@ -293,6 +294,7 @@ def hostonly_config(options) auto_config: options[:auto_config], ip: options[:ip], mac: options[:mac], + name: options[:name], netmask: options[:netmask], nic_type: options[:nic_type], type: options[:type] @@ -306,12 +308,6 @@ def hostonly_adapter(config) if !interface @logger.info("Network not found. Creating if we can.") - # It is an error if a specific host only network name was specified - # but the network wasn't found. - if config[:name] - raise Vagrant::Errors::NetworkNotFound, :name => config[:name] - end - # Create a new network interface = hostonly_create_network(config) @logger.info("Created network: #{interface[:name]}") @@ -414,7 +410,7 @@ def next_network_id # This creates a host only network for the given configuration. def hostonly_create_network(config) options = { - network_id: next_network_id, + network_id: config[:name] || next_network_id, adapter_ip: config[:adapter_ip], netmask: config[:netmask], } @@ -432,15 +428,20 @@ def hostonly_create_network(config) # This finds a matching host only network for the given configuration. def hostonly_find_matching_network(config) - this_netaddr = network_address(config[:ip], config[:netmask]) + existing = @env[:machine].provider.driver.read_host_only_interfaces - @env[:machine].provider.driver.read_host_only_interfaces.each do |interface| - return interface if config[:name] && config[:name] == interface[:name] - return interface if this_netaddr == \ - network_address(interface[:ip], interface[:netmask]) + if config[:name] + # Search networks strongly by specified name + matched_iface = existing.detect { |i| config[:name] == i[:name] } + else + # Name is not specified - search by network address + this_netaddr = network_address(config[:ip], config[:netmask]) + matched_iface = existing.detect do |i| + this_netaddr == network_address(i[:ip], i[:netmask]) + end end - nil + matched_iface || nil end end end diff --git a/lib/vagrant-parallels/action/prepare_nfs_settings.rb b/lib/vagrant-parallels/action/prepare_nfs_settings.rb index fecf3821..a0057d60 100644 --- a/lib/vagrant-parallels/action/prepare_nfs_settings.rb +++ b/lib/vagrant-parallels/action/prepare_nfs_settings.rb @@ -11,7 +11,7 @@ def call(env) @machine = env[:machine] @app.call(env) - if using_nfs? + if using_nfs?(@machine.config.vm) || using_nfs?(env[:synced_folders_config]) @logger.info("Using NFS, preparing NFS settings by reading host IP and machine IP") add_ips_to_env!(env) end @@ -20,8 +20,8 @@ def call(env) # We're using NFS if we have any synced folder with NFS configured. If # we are not using NFS we don't need to do the extra work to # populate these fields in the environment. - def using_nfs? - @machine.config.vm.synced_folders.any? { |_, opts| opts[:type] == :nfs } + def using_nfs?(env) + env && env.synced_folders.any? { |_, opts| opts[:type] == :nfs } end # Extracts the proper host and guest IPs for NFS mounts and stores them @@ -30,9 +30,8 @@ def using_nfs? # # The ! indicates that this method modifies its argument. def add_ips_to_env!(env) - adapter, host_ip = find_host_only_adapter - machine_ip = nil - machine_ip = read_machine_ip if adapter + host_ip = find_host_only_adapter + machine_ip = read_machine_ip raise Vagrant::Errors::NFSNoHostonlyNetwork if !host_ip || !machine_ip @@ -40,18 +39,23 @@ def add_ips_to_env!(env) env[:nfs_machine_ip] = machine_ip end - # Finds first host only network adapter and returns its adapter number - # and IP address + # Finds first host only network adapter and returns its IP address # - # @return [Integer, String] adapter number, ip address of found host-only adapter + # @return [String] ip address of found host-only adapter def find_host_only_adapter - @machine.provider.driver.read_network_interfaces.each do |adapter, opts| - if opts[:type] == :hostonly - @machine.provider.driver.read_host_only_interfaces.each do |interface| - if interface[:name] == opts[:hostonly] - return adapter, interface[:ip] - end + host_only_all = @machine.provider.driver.read_host_only_interfaces + host_only_used = @machine.provider.driver.read_network_interfaces. + select { |_, opts| opts[:type] == :hostonly } + + host_only_used.each do |_, opts| + host_only_all.each do |interface| + if @machine.provider.pd_version_satisfies?('>= 10') + name_matched = interface[:name] == opts[:hostonly] + else + name_matched = interface[:bound_to] == opts[:hostonly] end + + return interface[:ip] if name_matched end end diff --git a/lib/vagrant-parallels/driver/base.rb b/lib/vagrant-parallels/driver/base.rb index e8aa078d..f006a24f 100644 --- a/lib/vagrant-parallels/driver/base.rb +++ b/lib/vagrant-parallels/driver/base.rb @@ -177,6 +177,14 @@ def read_shared_folders def read_state end + # Returns a value of specified VM option + # + # @param [String] option Name of option (See all: `prlctl list -L`) + # @param [String] uuid Virtual machine UUID + # @return [String] + def read_vm_option(option, uuid=@uuid) + end + # Returns a list of all registered # virtual machines and templates. # diff --git a/lib/vagrant-parallels/driver/meta.rb b/lib/vagrant-parallels/driver/meta.rb index 61c5f305..70d54d4f 100644 --- a/lib/vagrant-parallels/driver/meta.rb +++ b/lib/vagrant-parallels/driver/meta.rb @@ -105,7 +105,6 @@ def initialize(uuid=nil) :read_vms_info, :read_vms_paths, :register, - :registered?, :resume, :set_power_consumption_mode, :set_mac_address, diff --git a/lib/vagrant-parallels/driver/pd_10.rb b/lib/vagrant-parallels/driver/pd_10.rb index f4d8508d..cf248334 100644 --- a/lib/vagrant-parallels/driver/pd_10.rb +++ b/lib/vagrant-parallels/driver/pd_10.rb @@ -155,8 +155,8 @@ def read_host_only_interfaces net_list.each do |iface| info = {} net_info = json { execute_prlsrvctl('net', 'info', iface['Network ID'], '--json') } - # Really we need to work with bounded virtual interface info[:name] = net_info['Network ID'] + info[:bound_to] = net_info['Bound To'] info[:ip] = net_info['Parallels adapter']['IP address'] info[:netmask] = net_info['Parallels adapter']['Subnet mask'] # Such interfaces are always in 'Up' @@ -237,18 +237,6 @@ def read_shared_interface info end - # Parse the JSON from *all* VMs and templates. - # Then return an array of objects (without duplicates) - def read_vms_info - vms_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json') - end - templates_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json', '--template') - end - vms_arr | templates_arr - end - def read_used_ports # Ignore our own used ports read_forwarded_ports(true).reject { |r| r[:guest].include?(@uuid) } diff --git a/lib/vagrant-parallels/driver/pd_8.rb b/lib/vagrant-parallels/driver/pd_8.rb index c5805f12..56519502 100644 --- a/lib/vagrant-parallels/driver/pd_8.rb +++ b/lib/vagrant-parallels/driver/pd_8.rb @@ -59,7 +59,7 @@ def create_host_only_network(options) iface_name = net_info['Bound To'] # Return the details - return { + { name: iface_name, ip: options[:adapter_ip], netmask: options[:netmask], @@ -170,11 +170,10 @@ def halt(force=false) execute(*args) end - def import(template_uuid) - template_name = read_vms.key(template_uuid) - vm_name = "#{template_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" + def import(tpl_name) + vm_name = "#{tpl_name}_#{(Time.now.to_f * 1000.0).to_i}_#{rand(100000)}" - execute_prlctl('clone', template_uuid, '--name', vm_name) do |type, data| + execute_prlctl('clone', tpl_name, '--name', vm_name) do |type, data| lines = data.split("\r") # The progress of the import will be in the last line. Do a greedy # regular expression to find what we're looking for. @@ -277,7 +276,8 @@ def read_host_only_interfaces info = {} net_info = json { execute_prlsrvctl('net', 'info', iface['Network ID'], '--json') } # Really we need to work with bounded virtual interface - info[:name] = net_info['Bound To'] + info[:name] = net_info['Network ID'] + info[:bound_to] = net_info['Bound To'] info[:ip] = net_info['Parallels adapter']['IP address'] info[:netmask] = net_info['Parallels adapter']['Subnet mask'] # Such interfaces are always in 'Up' @@ -300,19 +300,13 @@ def read_host_only_interfaces end def read_mac_address - read_settings.fetch('Hardware', {}).fetch('net0', {}).fetch('mac', nil) + # Get MAC of Shared network interface (net0) + read_vm_option('mac').strip.split(' ').first.gsub(':', '') end def read_mac_addresses - macs = {} - read_settings.fetch('Hardware', {}).each do |device, params| - if device =~ /^net(\d+)$/ - adapter = $1 - mac = params.fetch('mac') - macs[adapter] = mac - end - end - macs + macs = read_vm_option('mac').strip.split(' ') + Hash[macs.map.with_index{ |mac, ind| [ind.to_s, mac.gsub(':', '')] }] end def read_network_interfaces @@ -345,7 +339,7 @@ def read_network_interfaces end def read_settings - vm = json { execute_prlctl('list', @uuid, '--info', '--json').gsub(/^INFO/, '') } + vm = json { execute_prlctl('list', @uuid, '--info', '--no-header', '--json') } vm.last end @@ -390,40 +384,33 @@ def read_shared_folders end def read_state - vm = json { execute_prlctl('list', @uuid, '--json').gsub(/^INFO/, '') } - return nil if !vm.last - vm.last.fetch('status').to_sym + read_vm_option('status').strip.to_sym end def read_virtual_networks json { execute_prlsrvctl('net', 'list', '--json') } end + def read_vm_option(option, uuid=@uuid) + execute_prlctl('list', uuid,'--no-header', '-o', option) + end + def read_vms - results = {} - vms_arr = json([]) do - execute_prlctl('list', '--all', '--json').gsub(/^INFO/, '') - end - templates_arr = json([]) do - execute_prlctl('list', '--all', '--json', '--template').gsub(/^INFO/, '') - end - vms = vms_arr | templates_arr - vms.each do |item| - results[item.fetch('name')] = item.fetch('uuid') - end + args = %w(list --all --no-header --json -o name,uuid) + vms_arr = json([]) { execute_prlctl(*args) } + templates_arr = json([]) { execute_prlctl(*args, '--template') } - results + vms = vms_arr | templates_arr + Hash[vms.map { |i| [i.fetch('name'), i.fetch('uuid')] }] end # Parse the JSON from *all* VMs and templates. # Then return an array of objects (without duplicates) def read_vms_info - vms_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json').gsub(/^INFO/, '') - end - templates_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json', '--template').gsub(/^INFO/, '') - end + 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 @@ -441,11 +428,31 @@ def read_vms_paths def register(pvm_file, regen_src_uuid=false) args = ['prlctl', 'register', pvm_file] args << '--regenerate-src-uuid' if regen_src_uuid + + 3.times do + result = raw(*args) + # Exit if everything is OK + return if result.exit_code == 0 + + # It may occur in the race condition with other Vagrant processes. + # It is OK, just exit. + return if result.stderr.include?('is already registered.') + + # 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 execute now. This will raise an + # exception if it fails again. execute(*args) end def registered?(uuid) - read_vms.has_value?(uuid) + args = %w(list --all --info --no-header -o uuid) + + execute_prlctl(*args).include?(uuid) || + execute_prlctl(*args, '--template').include?(uuid) end def resume @@ -489,7 +496,25 @@ def suspend end def unregister(uuid) - execute_prlctl('unregister', uuid) + args = ['prlctl', 'unregister', uuid] + 3.times do + result = raw(*args) + # Exit if everything is OK + return if result.exit_code == 0 + + # It may occur in the race condition with other Vagrant processes. + # Both are OK, just exit. + return if result.stderr.include?('is not registered') + return if result.stderr.include?('is being cloned') + + # 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 execute now. This will raise an + # exception if it fails again. + execute(*args) end def unshare_folders(names) @@ -499,7 +524,23 @@ def unshare_folders(names) end def vm_exists?(uuid) - raw('prlctl', 'list', uuid).exit_code == 0 + 5.times do |i| + result = raw('prlctl', '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 probably doesn't exist. + return false if !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 end end diff --git a/lib/vagrant-parallels/driver/pd_9.rb b/lib/vagrant-parallels/driver/pd_9.rb index d8baffe2..5d72437e 100644 --- a/lib/vagrant-parallels/driver/pd_9.rb +++ b/lib/vagrant-parallels/driver/pd_9.rb @@ -15,44 +15,6 @@ def initialize(uuid) @logger = Log4r::Logger.new('vagrant_parallels::driver::pd_9') end - def read_settings - vm = json { execute_prlctl('list', @uuid, '--info', '--json') } - vm.last - end - - def read_state - vm = json { execute_prlctl('list', @uuid, '--json') } - return nil if !vm.last - vm.last.fetch('status').to_sym - end - - def read_vms - results = {} - vms_arr = json([]) do - execute_prlctl('list', '--all', '--json') - end - templates_arr = json([]) do - execute_prlctl('list', '--all', '--json', '--template') - end - vms = vms_arr | templates_arr - vms.each do |item| - results[item.fetch('name')] = item.fetch('uuid') - end - - results - end - - # Parse the JSON from *all* VMs and templates. Then return an array of objects (without duplicates) - def read_vms_info - vms_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json') - end - templates_arr = json([]) do - execute_prlctl('list', '--all','--info', '--json', '--template') - end - vms_arr | templates_arr - end - def set_power_consumption_mode(optimized) state = optimized ? 'on' : 'off' execute_prlctl('set', @uuid, '--longer-battery-life', state) diff --git a/lib/vagrant-parallels/errors.rb b/lib/vagrant-parallels/errors.rb index 3ce9023c..cc690d0b 100644 --- a/lib/vagrant-parallels/errors.rb +++ b/lib/vagrant-parallels/errors.rb @@ -15,6 +15,10 @@ class LinuxMountFailed < VagrantParallelsError error_key(:linux_mount_failed) end + class LinuxPrlFsInvalidOptions < VagrantParallelsError + error_key(:linux_prl_fs_invalid_options) + end + class MacOSXRequired < VagrantParallelsError error_key(:mac_os_x_required) end @@ -43,6 +47,10 @@ class ParallelsToolsIsoNotFound < VagrantParallelsError error_key(:parallels_tools_iso_not_found) end + class ParallelsTplNameNotFound < VagrantParallelsError + error_key(:parallels_tpl_name_not_found) + end + class VMImportFailure < VagrantParallelsError error_key(:vm_import_failure) end diff --git a/lib/vagrant-parallels/guest_cap/linux/mount_parallels_shared_folder.rb b/lib/vagrant-parallels/guest_cap/linux/mount_parallels_shared_folder.rb index 1b93988b..0fefe47c 100644 --- a/lib/vagrant-parallels/guest_cap/linux/mount_parallels_shared_folder.rb +++ b/lib/vagrant-parallels/guest_cap/linux/mount_parallels_shared_folder.rb @@ -3,6 +3,18 @@ module Parallels module GuestLinuxCap class MountParallelsSharedFolder def self.mount_parallels_shared_folder(machine, name, guestpath, options) + # Sanity check for mount options: we are not supporting + # VirtualBox-specific 'fmode' and 'dmode' options + if options[:mount_options] + invalid_opts = options[:mount_options].select do |opt| + opt =~ /^(d|f)mode/ + end + + if !invalid_opts.empty? + raise Errors::LinuxPrlFsInvalidOptions, options: invalid_opts + end + end + expanded_guest_path = machine.guest.capability( :shell_expand_guest_path, guestpath) diff --git a/lib/vagrant-parallels/plugin.rb b/lib/vagrant-parallels/plugin.rb index 7b4a3cc5..2c8c3deb 100644 --- a/lib/vagrant-parallels/plugin.rb +++ b/lib/vagrant-parallels/plugin.rb @@ -20,7 +20,7 @@ class Plugin < Vagrant.plugin("2") Parallels-based virtual machines. EOF - provider(:parallels) do + provider(:parallels, parallel: true, priority: 7) do require File.expand_path("../provider", __FILE__) Provider end diff --git a/lib/vagrant-parallels/provider.rb b/lib/vagrant-parallels/provider.rb index cf665af8..a1abe714 100644 --- a/lib/vagrant-parallels/provider.rb +++ b/lib/vagrant-parallels/provider.rb @@ -7,6 +7,10 @@ class Provider < Vagrant.plugin("2", :provider) attr_reader :driver def self.usable?(raise_error=false) + if !Vagrant::Util::Platform.darwin? + raise Errors::MacOSXRequired + end + # Instantiate the driver, which will determine the Parallels Desktop # version and all that, which checks for Parallels Desktop being present Driver::Meta.new @@ -23,10 +27,6 @@ def initialize(machine) @logger = Log4r::Logger.new("vagrant::provider::parallels") @machine = machine - if !Vagrant::Util::Platform.darwin? - raise Errors::MacOSXRequired - end - # This method will load in our driver, so we call it now to # initialize it. machine_id_changed diff --git a/lib/vagrant-parallels/version.rb b/lib/vagrant-parallels/version.rb index 02c51a83..8f1d6bae 100644 --- a/lib/vagrant-parallels/version.rb +++ b/lib/vagrant-parallels/version.rb @@ -1,5 +1,5 @@ module VagrantPlugins module Parallels - VERSION = "1.2.2" + VERSION = "1.3.0" end end diff --git a/locales/en.yml b/locales/en.yml index c72025e7..ecbbc64e 100644 --- a/locales/en.yml +++ b/locales/en.yml @@ -31,7 +31,11 @@ en: The command attempted was: %{command} + linux_prl_fs_invalid_options: |- + Failed to mount folders in Linux guest. You've specified mount options + which are not supported by "prl_fs" file system. + Invalid mount options: %{options} mac_os_x_required: |- Parallels provider works only on OS X (Mac OS X) systems. parallels_install_incomplete: |- @@ -61,6 +65,11 @@ en: reinstall Parallels Desktop. Expected ISO path: "%{iso_path}" + parallels_tpl_name_not_found: |- + The VM import failed! Template name not found. Please verify that + the box you're using is not corrupted and try again. + + Template config path: "%{config_path}" vm_import_failure: |- The VM import failed! Please verify that the box you're using is not corrupted and try again. diff --git a/test/unit/driver/pd_10_test.rb b/test/unit/driver/pd_10_test.rb index 82bd01e3..dbd75744 100644 --- a/test/unit/driver/pd_10_test.rb +++ b/test/unit/driver/pd_10_test.rb @@ -4,201 +4,10 @@ include_context "parallels" let(:parallels_version) { "10" } - let(:vm_name) {'VM_Name'} - - let(:tpl_uuid) {'1234-some-template-uuid-5678'} - let(:tpl_name) {'Some_Template_Name'} - - let(:tools_state) {'installed'} - let(:tools_version) {'10.0.23062.123456'} - - let(:hostonly_iface) {'vnic10'} - - let(:vnic_options) do { - name: 'vagrant_vnic6', - adapter_ip: '11.11.11.11', - netmask: '255.255.252.0', - dhcp: { - ip: '11.11.11.11', - lower: '11.11.8.1', - upper: '11.11.11.254' - } - } end - subject { VagrantPlugins::Parallels::Driver::Meta.new(uuid) } it_behaves_like "parallels desktop driver" - before do - # Returns short info about all registered VMs - # `prlctl list --all --json` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", kind_of(Hash)) do - out = <<-eos -[ - { - "uuid": "#{uuid}", - "status": "stopped", - "name": "#{vm_name}" - } -] - eos - subprocess_result(stdout: out) - end - - # Returns short info about all registered templates - # `prlctl list --all --json --template` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", "--template", kind_of(Hash)) do - out = <<-eos -[ - { - "uuid": "1234-some-template-uuid-5678", - "name": "Some_Template_Name" - } -] - eos - subprocess_result(stdout: out) - end - - - # Returns detailed info about specified VM or all registered VMs - # `prlctl list --info --json` - # `prlctl list --all --info --json` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", - kind_of(Hash)) do - out = <<-eos -[ - { - "ID": "#{uuid}", - "Name": "#{vm_name}", - "State": "stopped", - "Home": "/path/to/#{vm_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/disk1.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42B4B074", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic2", - "mac": "001C42EC0068", - "card": "e1000", - "ips": "33.33.33.5/255.255.255.0 " - } - }, - "Host Shared Folders": { - "enabled": true, - "shared_folder_1": { - "enabled": true, - "path": "/path/to/shared/folder/1" - }, - "shared_folder_2": { - "enabled": true, - "path": "/path/to/shared/folder/2" - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about specified template or all registered templates - # `prlctl list --info --json --template` - # `prlctl list --all --info --json --template` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", "--template", - kind_of(Hash)) do - out = <<-eos -[ - { - "ID": "#{tpl_uuid}", - "Name": "#{tpl_name}", - "State": "stopped", - "Home": "/path/to/#{tpl_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/harddisk.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42F6E500", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic4", - "mac": "001C42AB0071", - "card": "e1000", - "ips": "33.33.33.10/255.255.255.0 " - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about virtual network interface - # `prlsrvctl net info , '--json', retryable: true) - subprocess.stub(:execute). - with("prlsrvctl", "net", "info", kind_of(String), "--json", - kind_of(Hash)) do - out = <<-eos -{ - "Network ID": "#{vnic_options[:name]}", - "Type": "host-only", - "Bound To": "#{hostonly_iface}", - "Parallels adapter": { - "IP address": "#{vnic_options[:adapter_ip]}", - "Subnet mask": "#{vnic_options[:netmask]}" - }, - "DHCPv4 server": { - "Server address": "#{vnic_options[:dhcp][:ip] || "10.37.132.1"}", - "IP scope start address": "#{vnic_options[:dhcp][:lower] || "10.37.132.1"}", - "IP scope end address": "#{vnic_options[:dhcp][:upper] || "10.37.132.254"}" - } -} - eos - subprocess_result(stdout: out) - end - - end - describe "set_power_consumption_mode" do it "turns 'longer-battery-life' on" do subprocess.should_receive(:execute). diff --git a/test/unit/driver/pd_8_test.rb b/test/unit/driver/pd_8_test.rb index 87e8c98b..de96c9c6 100644 --- a/test/unit/driver/pd_8_test.rb +++ b/test/unit/driver/pd_8_test.rb @@ -4,201 +4,10 @@ include_context "parallels" let(:parallels_version) { "8" } - let(:vm_name) {'VM_Name'} - - let(:tpl_uuid) {'1234-some-template-uuid-5678'} - let(:tpl_name) {'Some_Template_Name'} - - let(:tools_state) {'installed'} - let(:tools_version) {'8.0.18615.123456'} - - let(:hostonly_iface) {'vnic10'} - - let(:vnic_options) do { - name: 'vagrant_vnic6', - adapter_ip: '11.11.11.11', - netmask: '255.255.252.0', - dhcp: { - ip: '11.11.11.11', - lower: '11.11.8.1', - upper: '11.11.11.254' - } - } end - subject { VagrantPlugins::Parallels::Driver::Meta.new(uuid) } it_behaves_like "parallels desktop driver" - before do - # Returns short info about all registered VMs - # `prlctl list --all --json` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", kind_of(Hash)) do - out = <<-eos -INFO[ - { - "uuid": "#{uuid}", - "status": "stopped", - "name": "#{vm_name}" - } -] - eos - subprocess_result(stdout: out) - end - - # Returns short info about all registered templates - # `prlctl list --all --json --template` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", "--template", kind_of(Hash)) do - out = <<-eos -INFO[ - { - "uuid": "1234-some-template-uuid-5678", - "name": "Some_Template_Name" - } -] - eos - subprocess_result(stdout: out) - end - - - # Returns detailed info about specified VM or all registered VMs - # `prlctl list --info --json` - # `prlctl list --all --info --json` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", - kind_of(Hash)) do - out = <<-eos -INFO[ - { - "ID": "#{uuid}", - "Name": "#{vm_name}", - "State": "stopped", - "Home": "/path/to/#{vm_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/disk1.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42B4B074", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic2", - "mac": "001C42EC0068", - "card": "e1000", - "ips": "33.33.33.5/255.255.255.0 " - } - }, - "Host Shared Folders": { - "enabled": true, - "shared_folder_1": { - "enabled": true, - "path": "/path/to/shared/folder/1" - }, - "shared_folder_2": { - "enabled": true, - "path": "/path/to/shared/folder/2" - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about specified template or all registered templates - # `prlctl list --info --json --template` - # `prlctl list --all --info --json --template` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", "--template", - kind_of(Hash)) do - out = <<-eos -INFO[ - { - "ID": "#{tpl_uuid}", - "Name": "#{tpl_name}", - "State": "stopped", - "Home": "/path/to/#{tpl_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/harddisk.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42F6E500", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic4", - "mac": "001C42AB0071", - "card": "e1000", - "ips": "33.33.33.10/255.255.255.0 " - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about virtual network interface - # `prlsrvctl net info , '--json', retryable: true) - subprocess.stub(:execute). - with("prlsrvctl", "net", "info", kind_of(String), "--json", - kind_of(Hash)) do - out = <<-eos -{ - "Network ID": "#{vnic_options[:name]}", - "Type": "host-only", - "Bound To": "#{hostonly_iface}", - "Parallels adapter": { - "IP address": "#{vnic_options[:adapter_ip]}", - "Subnet mask": "#{vnic_options[:netmask]}" - }, - "DHCPv4 server": { - "Server address": "#{vnic_options[:dhcp][:ip] || "10.37.132.1"}", - "IP scope start address": "#{vnic_options[:dhcp][:lower] || "10.37.132.1"}", - "IP scope end address": "#{vnic_options[:dhcp][:upper] || "10.37.132.254"}" - } -} - eos - subprocess_result(stdout: out) - end - - end - describe "ssh_ip" do let(:content) {'10.200.0.99="1394547632,1800,001c420000ff,01001c420000ff"'} diff --git a/test/unit/driver/pd_9_test.rb b/test/unit/driver/pd_9_test.rb index ea9447e7..d80d3534 100644 --- a/test/unit/driver/pd_9_test.rb +++ b/test/unit/driver/pd_9_test.rb @@ -2,203 +2,11 @@ describe VagrantPlugins::Parallels::Driver::PD_9 do include_context "parallels" - let(:parallels_version) { "9" } - - let(:vm_name) {'VM_Name'} - - let(:tpl_uuid) {'1234-some-template-uuid-5678'} - let(:tpl_name) {'Some_Template_Name'} - - let(:tools_state) {'installed'} - let(:tools_version) {'9.0.23062.123456'} - - let(:hostonly_iface) {'vnic10'} - - let(:vnic_options) do { - name: 'vagrant_vnic6', - adapter_ip: '11.11.11.11', - netmask: '255.255.252.0', - dhcp: { - ip: '11.11.11.11', - lower: '11.11.8.1', - upper: '11.11.11.254' - } - } end subject { VagrantPlugins::Parallels::Driver::Meta.new(uuid) } it_behaves_like "parallels desktop driver" - before do - # Returns short info about all registered VMs - # `prlctl list --all --json` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", kind_of(Hash)) do - out = <<-eos -[ - { - "uuid": "#{uuid}", - "status": "stopped", - "name": "#{vm_name}" - } -] - eos - subprocess_result(stdout: out) - end - - # Returns short info about all registered templates - # `prlctl list --all --json --template` - subprocess.stub(:execute). - with("prlctl", "list", "--all", "--json", "--template", kind_of(Hash)) do - out = <<-eos -[ - { - "uuid": "1234-some-template-uuid-5678", - "name": "Some_Template_Name" - } -] - eos - subprocess_result(stdout: out) - end - - - # Returns detailed info about specified VM or all registered VMs - # `prlctl list --info --json` - # `prlctl list --all --info --json` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", - kind_of(Hash)) do - out = <<-eos -[ - { - "ID": "#{uuid}", - "Name": "#{vm_name}", - "State": "stopped", - "Home": "/path/to/#{vm_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/disk1.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42B4B074", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic2", - "mac": "001C42EC0068", - "card": "e1000", - "ips": "33.33.33.5/255.255.255.0 " - } - }, - "Host Shared Folders": { - "enabled": true, - "shared_folder_1": { - "enabled": true, - "path": "/path/to/shared/folder/1" - }, - "shared_folder_2": { - "enabled": true, - "path": "/path/to/shared/folder/2" - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about specified template or all registered templates - # `prlctl list --info --json --template` - # `prlctl list --all --info --json --template` - subprocess.stub(:execute). - with("prlctl", "list", kind_of(String), "--info", "--json", "--template", - kind_of(Hash)) do - out = <<-eos -[ - { - "ID": "#{tpl_uuid}", - "Name": "#{tpl_name}", - "State": "stopped", - "Home": "/path/to/#{tpl_name}.pvm/", - "GuestTools": { - "state": "#{tools_state}", - "version": "#{tools_version}" - }, - "Hardware": { - "cpu": { - "cpus": 1 - }, - "memory": { - "size": "512Mb" - }, - "hdd0": { - "enabled": true, - "image": "/path/to/harddisk.hdd" - }, - "net0": { - "enabled": true, - "type": "shared", - "mac": "001C42F6E500", - "card": "e1000", - "dhcp": "yes" - }, - "net1": { - "enabled": true, - "type": "bridged", - "iface": "vnic4", - "mac": "001C42AB0071", - "card": "e1000", - "ips": "33.33.33.10/255.255.255.0 " - } - } - } -] - eos - subprocess_result(stdout: out) - end - - # Returns detailed info about virtual network interface - # `prlsrvctl net info , '--json', retryable: true) - subprocess.stub(:execute). - with("prlsrvctl", "net", "info", kind_of(String), "--json", - kind_of(Hash)) do - out = <<-eos -{ - "Network ID": "#{vnic_options[:name]}", - "Type": "host-only", - "Bound To": "#{hostonly_iface}", - "Parallels adapter": { - "IP address": "#{vnic_options[:adapter_ip]}", - "Subnet mask": "#{vnic_options[:netmask]}" - }, - "DHCPv4 server": { - "Server address": "#{vnic_options[:dhcp][:ip] || "10.37.132.1"}", - "IP scope start address": "#{vnic_options[:dhcp][:lower] || "10.37.132.1"}", - "IP scope end address": "#{vnic_options[:dhcp][:upper] || "10.37.132.254"}" - } -} - eos - subprocess_result(stdout: out) - end - - end - describe "set_power_consumption_mode" do it "turns 'longer-battery-life' on" do subprocess.should_receive(:execute). diff --git a/test/unit/support/shared/parallels_context.rb b/test/unit/support/shared/parallels_context.rb index 5d333f76..630e559f 100644 --- a/test/unit/support/shared/parallels_context.rb +++ b/test/unit/support/shared/parallels_context.rb @@ -1,31 +1,225 @@ -shared_context "parallels" do +shared_context 'parallels' do let(:parallels_context) { true } - let(:uuid) { "1234-here-is-uuid-5678" } - let(:parallels_version) { "9" } - let(:subprocess) { double("Vagrant::Util::Subprocess") } - let(:driver) { subject.instance_variable_get("@driver") } + let(:uuid) { '1234-here-is-uuid-5678' } + let(:parallels_version) { '9' } + let(:subprocess) { double('Vagrant::Util::Subprocess') } + let(:driver) { subject.instance_variable_get('@driver') } + + let(:vm_name) {'VM_Name'} + let(:tpl_uuid) {'1234-some-template-uuid-5678'} + let(:tpl_name) {'Some_Template_Name'} + let(:tools_state) {'installed'} + let(:tools_version) {'8.0.18615.123456'} + let(:hostonly_iface) {'vnic10'} + + let(:vnic_options) do { + name: 'vagrant_vnic6', + adapter_ip: '11.11.11.11', + netmask: '255.255.252.0', + dhcp: { + ip: '11.11.11.11', + lower: '11.11.8.1', + upper: '11.11.11.254' + } + } end # this is a helper that returns a duck type suitable from a system command # execution; allows setting exit_code, stdout, and stderr in stubs. def subprocess_result(options={}) - defaults = {exit_code: 0, stdout: "", stderr: ""} - double("subprocess_result", defaults.merge(options)) + defaults = {exit_code: 0, stdout: '', stderr: ''} + double('subprocess_result', defaults.merge(options)) end before do # we don't want unit tests to ever run commands on the system; so we wire # in a double to ensure any unexpected messages raise exceptions - stub_const("Vagrant::Util::Subprocess", subprocess) + stub_const('Vagrant::Util::Subprocess', subprocess) # drivers will blow up on instantiation if they cannot determine the # Parallels Desktop version, so wire this stub in automatically subprocess.stub(:execute). - with("prlctl", "--version", an_instance_of(Hash)). + with('prlctl', '--version', an_instance_of(Hash)). and_return(subprocess_result(stdout: "prlctl version #{parallels_version}.0.98765")) # drivers also call vm_exists? during init; subprocess.stub(:execute). - with("prlctl", "list", uuid, kind_of(Hash)). + with('prlctl', 'list', uuid, kind_of(Hash)). and_return(subprocess_result(exit_code: 0)) + + # Returns detailed info about specified VM or all registered VMs + # `prlctl list --info --no-header --json` + # `prlctl list --all --info --no-header --json` + subprocess.stub(:execute). + with('prlctl', 'list', kind_of(String), '--info', '--no-header', '--json', + kind_of(Hash)) do + out = <<-eos +[ + { + "ID": "#{uuid}", + "Name": "#{vm_name}", + "State": "stopped", + "Home": "/path/to/#{vm_name}.pvm/", + "GuestTools": { + "state": "#{tools_state}", + "version": "#{tools_version}" + }, + "Hardware": { + "cpu": { + "cpus": 1 + }, + "memory": { + "size": "512Mb" + }, + "hdd0": { + "enabled": true, + "image": "/path/to/disk1.hdd" + }, + "net0": { + "enabled": true, + "type": "shared", + "mac": "001C42B4B074", + "card": "e1000", + "dhcp": "yes" + }, + "net1": { + "enabled": true, + "type": "bridged", + "iface": "vnic2", + "mac": "001C42EC0068", + "card": "e1000", + "ips": "33.33.33.5/255.255.255.0 " + } + }, + "Host Shared Folders": { + "enabled": true, + "shared_folder_1": { + "enabled": true, + "path": "/path/to/shared/folder/1" + }, + "shared_folder_2": { + "enabled": true, + "path": "/path/to/shared/folder/2" + } + } + } +] + eos + subprocess_result(stdout: out) + end + + # Returns detailed info about specified template or all registered templates + # `prlctl list --info --json --no-header --template` + # `prlctl list --all --info --no-header --json --template` + subprocess.stub(:execute). + with('prlctl', 'list', kind_of(String), '--info', '--no-header', '--json', + '--template', kind_of(Hash)) do + out = <<-eos +[ + { + "ID": "#{tpl_uuid}", + "Name": "#{tpl_name}", + "State": "stopped", + "Home": "/path/to/#{tpl_name}.pvm/", + "GuestTools": { + "state": "#{tools_state}", + "version": "#{tools_version}" + }, + "Hardware": { + "cpu": { + "cpus": 1 + }, + "memory": { + "size": "512Mb" + }, + "hdd0": { + "enabled": true, + "image": "/path/to/harddisk.hdd" + }, + "net0": { + "enabled": true, + "type": "shared", + "mac": "001C42F6E500", + "card": "e1000", + "dhcp": "yes" + }, + "net1": { + "enabled": true, + "type": "bridged", + "iface": "vnic4", + "mac": "001C42AB0071", + "card": "e1000", + "ips": "33.33.33.10/255.255.255.0 " + } + } + } +] + eos + subprocess_result(stdout: out) + end + + # Returns detailed info about virtual network interface + # `prlsrvctl net info , '--json'` + subprocess.stub(:execute). + with('prlsrvctl', 'net', 'info', kind_of(String), '--json', + kind_of(Hash)) do + out = <<-eos +{ + "Network ID": "#{vnic_options[:name]}", + "Type": "host-only", + "Bound To": "#{hostonly_iface}", + "Parallels adapter": { + "IP address": "#{vnic_options[:adapter_ip]}", + "Subnet mask": "#{vnic_options[:netmask]}" + }, + "DHCPv4 server": { + "Server address": "#{vnic_options[:dhcp][:ip] || '10.37.132.1'}", + "IP scope start address": "#{vnic_options[:dhcp][:lower] || '10.37.132.1'}", + "IP scope end address": "#{vnic_options[:dhcp][:upper] || '10.37.132.254'}" + } +} + eos + subprocess_result(stdout: out) + end + + # Returns values of 'name' and 'uuid' options for all registered VMs + # `prlctl list --all --no-header --json -o name,uuid` + subprocess.stub(:execute). + with('prlctl', 'list', '--all', '--no-header', '--json', '-o', 'name,uuid', + kind_of(Hash)) do + out = <<-eos +[ + { + "name": "#{vm_name}", + "uuid": "#{uuid}" + } +] + eos + subprocess_result(stdout: out) + end + + # Returns values of 'name' and 'uuid' options for all registered templates + # `prlctl list --all --no-header --json -o name,uuid --template` + subprocess.stub(:execute). + with('prlctl', 'list', '--all', '--no-header', '--json', '-o', 'name,uuid', + '--template', kind_of(Hash)) do + out = <<-eos +[ + { + "name": "#{tpl_name}", + "uuid": "#{tpl_uuid}" + } +] + eos + subprocess_result(stdout: out) + end + + # Returns value of 'mac' options for the specified VM + # `prlctl list --all --no-header -o mac` + subprocess.stub(:execute). + with('prlctl', 'list', kind_of(String), '--no-header', '-o', 'mac', + kind_of(Hash)) do + subprocess_result(stdout: "001C42B4B074\n") + end + end end diff --git a/vagrant-parallels.gemspec b/vagrant-parallels.gemspec index 98d9ea2a..a1d7dbf3 100644 --- a/vagrant-parallels.gemspec +++ b/vagrant-parallels.gemspec @@ -16,9 +16,10 @@ Gem::Specification.new do |spec| spec.required_rubygems_version = ">= 1.3.6" spec.rubyforge_project = "vagrant-parallels" - spec.add_development_dependency "bundler", ">= 1.5.2", "< 1.7.0" + spec.add_development_dependency "bundler", ">= 1.5.2" spec.add_development_dependency "rake" spec.add_development_dependency "minitest" + spec.add_development_dependency "nokogiri" spec.add_development_dependency "rspec", "~> 2.14.0" spec.add_development_dependency "i18n-tasks", "~> 0.3.9"