vagrant-parallels 0.1.2 → 0.1.3

Sign up to get free protection for your applications and to get access to all the features.
@@ -9,9 +9,16 @@ module VagrantPlugins
9
9
  def call(env)
10
10
  raise Vagrant::Errors::VMBaseMacNotSpecified if !env[:machine].config.vm.base_mac
11
11
 
12
- # Create the proc which we want to use to modify the virtual machine
13
- env[:ui].info I18n.t("vagrant.actions.vm.match_mac.matching")
14
- env[:machine].provider.driver.set_mac_address(env[:machine].config.vm.base_mac)
12
+ env[:ui].info I18n.t("vagrant_parallels.actions.vm.match_mac.matching")
13
+
14
+ base_mac = env[:machine].config.vm.base_mac
15
+ # Generate new base mac if the specified address is already in use
16
+ if env[:machine].provider.driver.mac_in_use?(base_mac)
17
+ env[:ui].info I18n.t("vagrant_parallels.actions.vm.match_mac.generate")
18
+ env[:machine].provider.driver.set_mac_address('auto')
19
+ else
20
+ env[:machine].provider.driver.set_mac_address(base_mac)
21
+ end
15
22
 
16
23
  @app.call(env)
17
24
  end
@@ -40,6 +40,7 @@ module VagrantPlugins
40
40
 
41
41
  def compact(uuid=nil)
42
42
  uuid ||= @uuid
43
+ # TODO: VM can have more than one hdd!
43
44
  path_to_hdd = read_settings(uuid).fetch("Hardware", {}).fetch("hdd0", {}).fetch("image", nil)
44
45
  raw('prl_disk_tool', 'compact', '--hdd', path_to_hdd) do |type, data|
45
46
  lines = data.split("\r")
@@ -80,7 +81,9 @@ module VagrantPlugins
80
81
  end
81
82
 
82
83
  def clear_shared_folders
83
- read_settings.fetch("Host Shared Folders", {}).keys.drop(1).each do |folder|
84
+ shf = read_settings.fetch("Host Shared Folders", {}).keys
85
+ shf.delete("enabled")
86
+ shf.each do |folder|
84
87
  execute("set", @uuid, "--shf-host-del", folder)
85
88
  end
86
89
  end
@@ -214,6 +217,18 @@ module VagrantPlugins
214
217
  end
215
218
  end
216
219
 
220
+
221
+ def mac_in_use?(mac)
222
+ all_macs_in_use = []
223
+ read_all_info.each do |vm|
224
+ all_macs_in_use << vm.fetch('Hardware', {}).fetch('net0',{}).fetch('mac', '')
225
+ end
226
+
227
+ valid_mac = mac.upcase.tr('^A-F0-9', '')
228
+
229
+ all_macs_in_use.include?(valid_mac)
230
+ end
231
+
217
232
  # Returns a hash of all UUIDs assigned to VMs and templates currently
218
233
  # known by Parallels. Keys are 'name' values
219
234
  #
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Parallels
3
- VERSION = "0.1.2"
3
+ VERSION = "0.1.3"
4
4
  end
5
5
  end
data/locales/en.yml CHANGED
@@ -890,7 +890,10 @@ en:
890
890
  The VM import failed! Try running `prlctl import` on the box file
891
891
  manually for more verbose error output.
892
892
  match_mac:
893
- matching: Matching MAC address for NAT networking...
893
+ generate: |-
894
+ The specified base MAC is already in use. Generating a new unique MAC
895
+ address for Shared network...
896
+ matching: Matching MAC address for Shared network...
894
897
  no_base_mac: |-
895
898
  No base MAC address was specified. This is required for the NAT networking
896
899
  to work properly (and hence port forwarding, SSH, etc.). Specifying this
@@ -0,0 +1,3 @@
1
+ # This installs the tasks that help with gem creation and
2
+ # publishing.
3
+ Bundler::GemHelper.install_tasks
data/tasks/test.rake ADDED
@@ -0,0 +1,12 @@
1
+ require 'rake/testtask'
2
+ require 'rspec/core/rake_task'
3
+
4
+ namespace :test do
5
+ RSpec::Core::RakeTask.new(:unit) do |t|
6
+ t.pattern = "test/unit/**/*_test.rb"
7
+ end
8
+
9
+ RSpec::Core::RakeTask.new(:acceptance) do |t|
10
+ t.pattern = "test/acceptance/**/*_test.rb"
11
+ end
12
+ end
@@ -0,0 +1,46 @@
1
+ require "fileutils"
2
+ require "pathname"
3
+
4
+ require "log4r"
5
+
6
+ require "support/tempdir"
7
+
8
+ # This class manages an isolated environment for Vagrant to
9
+ # run in. It creates a temporary directory to act as the
10
+ # working directory as well as sets a custom home directory.
11
+ #
12
+ # This class also provides various helpers to create Vagrantfiles,
13
+ # boxes, etc.
14
+ class IsolatedEnvironment
15
+ attr_reader :homedir
16
+ attr_reader :workdir
17
+
18
+ # Initializes an isolated environment. You can pass in some
19
+ # options here to configure runing custom applications in place
20
+ # of others as well as specifying environmental variables.
21
+ #
22
+ # @param [Hash] apps A mapping of application name (such as "vagrant")
23
+ # to an alternate full path to the binary to run.
24
+ # @param [Hash] env Additional environmental variables to inject
25
+ # into the execution environments.
26
+ def initialize
27
+ @logger = Log4r::Logger.new("test::isolated_environment")
28
+
29
+ # Create a temporary directory for our work
30
+ @tempdir = Tempdir.new("vagrant")
31
+ @logger.info("Initialize isolated environment: #{@tempdir.path}")
32
+
33
+ # Setup the home and working directories
34
+ @homedir = Pathname.new(File.join(@tempdir.path, "home"))
35
+ @workdir = Pathname.new(File.join(@tempdir.path, "work"))
36
+
37
+ @homedir.mkdir
38
+ @workdir.mkdir
39
+ end
40
+
41
+ # This closes the environment by cleaning it up.
42
+ def close
43
+ @logger.info("Removing isolated environment: #{@tempdir.path}")
44
+ FileUtils.rm_rf(@tempdir.path)
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ require 'fileutils'
2
+ require 'tempfile'
3
+
4
+ # This class provides an easy way of creating a temporary
5
+ # directory and having it removed when the application exits.
6
+ class Tempdir
7
+ attr_reader :path
8
+
9
+ def initialize(basename="vagrant")
10
+ @path = nil
11
+
12
+ # Loop and attempt to create a temporary directory until
13
+ # it succeeds.
14
+ while @path.nil?
15
+ file = Tempfile.new(basename)
16
+ @path = file.path
17
+ file.unlink
18
+
19
+ begin
20
+ Dir.mkdir(@path)
21
+ rescue
22
+ @path = nil
23
+ end
24
+ end
25
+
26
+ # Setup a finalizer to delete the directory. This is the same way
27
+ # that Tempfile and friends do this...
28
+ @cleanup_proc = lambda do
29
+ FileUtils.rm_rf(@path) if File.directory?(@path)
30
+ end
31
+
32
+ ObjectSpace.define_finalizer(self, @cleanup_proc)
33
+ end
34
+
35
+ # This deletes the temporary directory.
36
+ def unlink
37
+ # Delete the directory
38
+ @cleanup_proc.call
39
+
40
+ # Undefine the finalizer since we're all cleaned up
41
+ ObjectSpace.undefine_finalizer(self)
42
+ end
43
+ end
data/test/unit/base.rb ADDED
@@ -0,0 +1,27 @@
1
+ require "rubygems"
2
+ require "rspec/autorun"
3
+
4
+ # Require Vagrant itself so we can reference the proper
5
+ # classes to test.
6
+ require "vagrant"
7
+ require 'vagrant-parallels'
8
+
9
+ # Add the test directory to the load path
10
+ $:.unshift File.expand_path("../../", __FILE__)
11
+
12
+ # Load in helpers
13
+ require "support/tempdir"
14
+ require "unit/support/shared/parallels_context"
15
+
16
+ # Do not buffer output
17
+ $stdout.sync = true
18
+ $stderr.sync = true
19
+
20
+ # Configure RSpec
21
+ RSpec.configure do |c|
22
+ c.expect_with :rspec, :stdlib
23
+ end
24
+
25
+ # Configure VAGRANT_CWD so that the tests never find an actual
26
+ # Vagrantfile anywhere, or at least this minimizes those chances.
27
+ ENV["VAGRANT_CWD"] = Tempdir.new.path
@@ -0,0 +1,138 @@
1
+ require_relative "../base"
2
+
3
+ describe VagrantPlugins::Parallels::Driver::PrlCtl do
4
+ include_context "parallels"
5
+
6
+ subject { VagrantPlugins::Parallels::Driver::PrlCtl.new(uuid) }
7
+
8
+ describe "compact" do
9
+ it "compacts the VM disk images" do
10
+ pending "Should have possibility to compact more than one hdd"
11
+ end
12
+ end
13
+
14
+ describe "create_host_only_network" do
15
+ it "creates host-only NIC"
16
+ end
17
+
18
+ describe "export" do
19
+ tpl_name = "new_template_name"
20
+ tpl_uuid = "12345-hfgs-3456-hste"
21
+
22
+ it "exports VM to template" do
23
+ subject.stub(:read_settings).with(tpl_name).
24
+ and_return({"ID" => tpl_uuid})
25
+
26
+ subprocess.should_receive(:execute).
27
+ with("prlctl", "clone", uuid, "--name", an_instance_of(String), "--template", "--dst",
28
+ an_instance_of(String), an_instance_of(Hash)).
29
+ and_return(subprocess_result(stdout: "The VM has been successfully cloned"))
30
+ subject.export("/path/to/template", tpl_name).should == tpl_uuid
31
+ end
32
+ end
33
+
34
+ describe "clear_shared_folders" do
35
+ shf_hash = {"enabled" => true, "shf_name_1" => {}, "shf_name_2" => {}}
36
+ it "deletes every shared folder assigned to the VM" do
37
+ subject.stub(:read_settings).and_return({"Host Shared Folders" => shf_hash})
38
+
39
+ subprocess.should_receive(:execute).exactly(2).times.
40
+ with("prlctl", "set", uuid, "--shf-host-del", an_instance_of(String), an_instance_of(Hash)).
41
+ and_return(subprocess_result(stdout: "Shared folder deleted"))
42
+ subject.clear_shared_folders
43
+ end
44
+ end
45
+
46
+ describe "halt" do
47
+ it "stops the VM" do
48
+ subprocess.should_receive(:execute).
49
+ with("prlctl", "stop", uuid, an_instance_of(Hash)).
50
+ and_return(subprocess_result(stdout: "VM has been halted gracefully"))
51
+ subject.halt
52
+ end
53
+
54
+ it "stops the VM force" do
55
+ subprocess.should_receive(:execute).
56
+ with("prlctl", "stop", uuid, "--kill", an_instance_of(Hash)).
57
+ and_return(subprocess_result(stdout: "VM has been halted forcibly"))
58
+ subject.halt(force=true)
59
+ end
60
+ end
61
+
62
+ describe "mac_in_use?" do
63
+ vm_1 = {
64
+ 'Hardware' => {
65
+ 'net0' => {'mac' => '001C42BB5901'},
66
+ 'net1' => {'mac' => '001C42BB5902'},
67
+ }
68
+ }
69
+ vm_2 = {
70
+ 'Hardware' => {
71
+ 'net0' => {'mac' => '001C42BB5903'},
72
+ 'net1' => {'mac' => '001C42BB5904'},
73
+ }
74
+ }
75
+
76
+ it "checks the MAC address is already in use" do
77
+ subject.stub(:read_all_info).and_return([vm_1, vm_2])
78
+
79
+ subject.mac_in_use?('00:1c:42:bb:59:01').should be_true
80
+ subject.mac_in_use?('00:1c:42:bb:59:02').should be_false
81
+ subject.mac_in_use?('00:1c:42:bb:59:03').should be_true
82
+ subject.mac_in_use?('00:1c:42:bb:59:04').should be_false
83
+ end
84
+ end
85
+
86
+ describe "set_mac_address" do
87
+ it "sets base MAC address to the Shared network adapter" do
88
+ subprocess.should_receive(:execute).exactly(2).times.
89
+ with("prlctl", "set", uuid, '--device-set', 'net0', '--type', 'shared', '--mac',
90
+ an_instance_of(String), an_instance_of(Hash)).
91
+ and_return(subprocess_result(stdout: "Settings applied"))
92
+
93
+ subject.set_mac_address('001C42DD5902')
94
+ subject.set_mac_address('auto')
95
+ end
96
+ end
97
+
98
+ describe "start" do
99
+ it "starts the VM" do
100
+ subprocess.should_receive(:execute).
101
+ with("prlctl", "start", uuid, an_instance_of(Hash)).
102
+ and_return(subprocess_result(stdout: "VM started"))
103
+ subject.start
104
+ end
105
+ end
106
+
107
+ describe "suspend" do
108
+ it "suspends the VM" do
109
+ subprocess.should_receive(:execute).
110
+ with("prlctl", "suspend", uuid, an_instance_of(Hash)).
111
+ and_return(subprocess_result(stdout: "VM suspended"))
112
+ subject.suspend
113
+ end
114
+ end
115
+
116
+ describe "unregister" do
117
+ it "suspends the VM" do
118
+ subprocess.should_receive(:execute).
119
+ with("prlctl", "unregister", an_instance_of(String), an_instance_of(Hash)).
120
+ and_return(subprocess_result(stdout: "Specified VM unregistered"))
121
+ subject.unregister("template_or_vm_uuid")
122
+ end
123
+ end
124
+
125
+ describe "version" do
126
+ it "parses the version from output" do
127
+ subject.version.should match(/(#{parallels_version}[\d\.]+)/)
128
+ end
129
+
130
+ it "rises ParallelsInstallIncomplete exception when output is invalid" do
131
+ subprocess.should_receive(:execute).
132
+ with("prlctl", "--version", an_instance_of(Hash)).
133
+ and_return(subprocess_result(stdout: "Some incorrect value has been returned!"))
134
+ expect { subject.version }.
135
+ to raise_error(VagrantPlugins::Parallels::Errors::ParallelsInstallIncomplete)
136
+ end
137
+ end
138
+ end
@@ -0,0 +1,30 @@
1
+ shared_context "parallels" do
2
+ let(:parallels_context) { true }
3
+ let(:uuid) { "9876-dcba-8765-hgfe" }
4
+ let(:parallels_version) { "9" }
5
+ let(:subprocess) { double("Vagrant::Util::Subprocess") }
6
+
7
+ # this is a helper that returns a duck type suitable from a system command
8
+ # execution; allows setting exit_code, stdout, and stderr in stubs.
9
+ def subprocess_result(options={})
10
+ defaults = {exit_code: 0, stdout: "", stderr: ""}
11
+ double("subprocess_result", defaults.merge(options))
12
+ end
13
+
14
+ before do
15
+ # we don't want unit tests to ever run commands on the system; so we wire
16
+ # in a double to ensure any unexpected messages raise exceptions
17
+ stub_const("Vagrant::Util::Subprocess", subprocess)
18
+
19
+ # drivers will blow up on instantiation if they cannot determine the
20
+ # Parallels Desktop version, so wire this stub in automatically
21
+ subprocess.stub(:execute).
22
+ with("prlctl", "--version", an_instance_of(Hash)).
23
+ and_return(subprocess_result(stdout: "prlctl version #{parallels_version}.23456.987654"))
24
+
25
+ # drivers also call vm_exists? during init;
26
+ subprocess.stub(:execute).
27
+ with("prlctl", "list", kind_of(String), "--info", "--json", kind_of(Hash)).
28
+ and_return(subprocess_result(exit_code: 0))
29
+ end
30
+ end
@@ -18,9 +18,7 @@ Gem::Specification.new do |spec|
18
18
 
19
19
  spec.add_development_dependency "bundler", "~> 1.3"
20
20
  spec.add_development_dependency "rake"
21
- spec.add_development_dependency "rspec-core", "~> 2.12.2"
22
- spec.add_development_dependency "rspec-expectations", "~> 2.12.1"
23
- spec.add_development_dependency "rspec-mocks", "~> 2.12.1"
21
+ spec.add_development_dependency "rspec", "~> 2.14.0"
24
22
 
25
23
  # The following block of code determines the files that should be included
26
24
  # in the gem. It does this by reading all the files in the directory where
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-parallels
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.2
4
+ version: 0.1.3
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-12-20 00:00:00.000000000 Z
12
+ date: 2013-12-25 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: bundler
@@ -44,13 +44,13 @@ dependencies:
44
44
  - !ruby/object:Gem::Version
45
45
  version: '0'
46
46
  - !ruby/object:Gem::Dependency
47
- name: rspec-core
47
+ name: rspec
48
48
  requirement: !ruby/object:Gem::Requirement
49
49
  none: false
50
50
  requirements:
51
51
  - - ~>
52
52
  - !ruby/object:Gem::Version
53
- version: 2.12.2
53
+ version: 2.14.0
54
54
  type: :development
55
55
  prerelease: false
56
56
  version_requirements: !ruby/object:Gem::Requirement
@@ -58,39 +58,7 @@ dependencies:
58
58
  requirements:
59
59
  - - ~>
60
60
  - !ruby/object:Gem::Version
61
- version: 2.12.2
62
- - !ruby/object:Gem::Dependency
63
- name: rspec-expectations
64
- requirement: !ruby/object:Gem::Requirement
65
- none: false
66
- requirements:
67
- - - ~>
68
- - !ruby/object:Gem::Version
69
- version: 2.12.1
70
- type: :development
71
- prerelease: false
72
- version_requirements: !ruby/object:Gem::Requirement
73
- none: false
74
- requirements:
75
- - - ~>
76
- - !ruby/object:Gem::Version
77
- version: 2.12.1
78
- - !ruby/object:Gem::Dependency
79
- name: rspec-mocks
80
- requirement: !ruby/object:Gem::Requirement
81
- none: false
82
- requirements:
83
- - - ~>
84
- - !ruby/object:Gem::Version
85
- version: 2.12.1
86
- type: :development
87
- prerelease: false
88
- version_requirements: !ruby/object:Gem::Requirement
89
- none: false
90
- requirements:
91
- - - ~>
92
- - !ruby/object:Gem::Version
93
- version: 2.12.1
61
+ version: 2.14.0
94
62
  description: Enables Vagrant to manage Parallels machines.
95
63
  email:
96
64
  - yshahin@gmail.com
@@ -148,10 +116,15 @@ files:
148
116
  - locales/en.yml
149
117
  - Rakefile
150
118
  - README.md
151
- - spec/vagrant-parallels/setup_spec.rb
119
+ - tasks/bundler.rake
120
+ - tasks/test.rake
121
+ - test/support/isolated_environment.rb
122
+ - test/support/tempdir.rb
123
+ - test/unit/base.rb
124
+ - test/unit/driver/prl_ctl_test.rb
125
+ - test/unit/support/shared/parallels_context.rb
152
126
  - vagrant-parallels.gemspec
153
127
  - .gitignore
154
- - .ruby-gemset
155
128
  - .travis.yml
156
129
  homepage: http://github.com/yshahin/vagrant-parallels
157
130
  licenses:
@@ -168,7 +141,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
168
141
  version: '0'
169
142
  segments:
170
143
  - 0
171
- hash: -845057650540824242
144
+ hash: 1018017984449213160
172
145
  required_rubygems_version: !ruby/object:Gem::Requirement
173
146
  none: false
174
147
  requirements: