vagrant-parallels 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -21,3 +21,4 @@ Vagrantfile
21
21
  .vagrant
22
22
  *.box
23
23
  *.pyc
24
+ sandi_meter
data/README.md CHANGED
@@ -1,14 +1,14 @@
1
1
  # Vagrant Parallels Provider
2
2
 
3
- This is a [Vagrant](http://www.vagrantup.com) 1.3+ plugin that adds an [Parallels Desktop](http://www.parallels.com/products/desktop/)
4
- provider to Vagrant, allowing Vagrant to control and provision machines using Parallels Desktop insead of the default Virtualbox.
3
+ This is a [Vagrant](http://www.vagrantup.com) 1.3+ plugin that adds a [Parallels Desktop](http://www.parallels.com/products/desktop/)
4
+ provider to Vagrant, allowing Vagrant to control and provision machines using Parallels Desktop instead of the default Virtualbox.
5
5
 
6
6
  ## Note
7
7
 
8
- This project is still in active development and not all vagrant features have been developed, please report any issues you might find.
9
- Almost all features are available except for exporting/packaging VM's this will be available soon.
8
+ This project is still in active development and not all vagrant features have been developed, so please report any issues you might find.
9
+ Almost all features are available except for exporting/packaging VM's. This will be available soon.
10
10
 
11
- We look forward to hearing from you with any issues or features, Thank you
11
+ We look forward to hearing from you with any issues or features. Thank you!
12
12
 
13
13
  ## Usage
14
14
  Install using standard Vagrant 1.1+ plugin installation methods. After installing, then do a `vagrant up` and specify the `parallels` provider. An example is shown below.
@@ -21,7 +21,27 @@ $ vagrant up --provider=parallels
21
21
  ...
22
22
  ```
23
23
 
24
- You need to have a paralles compatible box file installed before doing a `vagrnat up`, please refer to the coming section for instaructions.
24
+ You need to have a parallels compatible box file installed before doing a `vagrant up`, please refer to the coming section for instructions.
25
+
26
+ ### Default Provider
27
+
28
+ When using parallels as your vagrant provider after almost every command you will need to append `--provider=parallels`. To simplify this you can set your default vagrant provider as **parallels**
29
+
30
+ If you're using BASH
31
+
32
+ ```
33
+ # Append to bash
34
+ echo "export VAGRANT_DEFAULT_PROVIDER=parallels" | tee -a ~/.bashrc
35
+ source ~/.bashrc
36
+ ```
37
+
38
+ If you're using ZSH
39
+
40
+ ```
41
+ # Append to zsh
42
+ echo "export VAGRANT_DEFAULT_PROVIDER=parallels" | tee -a ~/.zshrc
43
+ source ~/.zshrc
44
+ ```
25
45
 
26
46
  ## Box Format
27
47
 
@@ -40,6 +60,29 @@ The box format is basically just the required `metadata.json` file
40
60
  along with a `Vagrantfile` that does default settings for the
41
61
  provider-specific configuration for this provider.
42
62
 
63
+ ## Networking
64
+ By default 'vagrant-parallels' uses the basic Vagrant networking approach. By default VM has one adapter assigned to the 'Shared' network in Parallels Desktop.
65
+ But you can also add one ore more `:private_network` adapters, as described below:
66
+
67
+ ### Private Network
68
+ It is fully compatible with basic Vagrant [Private Networking](http://docs.vagrantup.com/v2/networking/private_network.html).
69
+ #### Available arguments:
70
+ - `type` - IP configuration way: `:static` or `:dhcp`. Default is `:static`. If `:dchp` is set, such interface will get an IP dynamically from default subnet "10.37.129.1/255.255.255.0".
71
+ - `ip` - IP address which will be assigned to this network adapter. It is required only if type is `:static`.
72
+ - `netmask` - network mask. Default is `"255.255.255.0"`. It is required only if type is `:static`.
73
+ - `nic_type` - Unnecessary argument, means the type of network adapter. Can be any of `"virtio"`, `"e1000"` or `"rtl"`. Default is `"e1000"`.
74
+
75
+ #### Example:
76
+ ```ruby
77
+ Vagrant.configure("2") do |config|
78
+ config.vm.network :private_network, ip: "33.33.33.50", netmask: "255.255.0.0"
79
+ config.vm.network :private_network, type: :dhcp, nic_type: "rtl"
80
+ end
81
+ ```
82
+ It means that two private network adapters will be configured:
83
+ 1) The first will have static ip '33.33.33.50' and mask '255.255.0.0'. It will be represented as device `"e1000"` by default (e.g. 'Intel(R) PRO/1000 MT').
84
+ 2) The second adapter will be configured as `"rtl"` ('Realtek RTL8029AS') and get an IP from internal DHCP server, which is working on the default network "10.37.129.1/255.255.255.0".
85
+
43
86
  ## Development
44
87
 
45
88
  To work on the `vagrant-parallels` plugin, clone this repository out
@@ -70,7 +113,7 @@ and add the following line to your `Vagrantfile`
70
113
  Vagrant.require_plugin "vagrant-parallels"
71
114
  ```
72
115
 
73
- You need to have a compatable box file installed, refer to box file section
116
+ You need to have a compatible box file installed, refer to box file section
74
117
 
75
118
  Use bundler to execute Vagrant:
76
119
 
@@ -89,4 +132,16 @@ $ rake build
89
132
  $ vagrant plugin install ./pkg/vagrant-parallels-<version>.gem
90
133
  ...
91
134
  ```
92
- So, now you have your own plugin installed, check it by command `vagrant plugin list`
135
+ So, now that you have your own plugin installed, check it with the command `vagrant plugin list`
136
+
137
+ ## Contributors
138
+
139
+ A great thanks to the people who helping this project stand on its feet, thank you
140
+
141
+ * Kevin Kaland `@wizonesolutions`
142
+ * Mikhail Zholobov `@legal90`
143
+ * Dmytro Vasylenko `@odi-um`
144
+ * Thomas Koschate `@koschate`
145
+
146
+ and to all the people who are using and testing this tool
147
+
@@ -23,12 +23,14 @@ module VagrantPlugins
23
23
  #b.use PrepareNFSSettings
24
24
  b.use ClearSharedFolders
25
25
  b.use ShareFolders
26
+ b.use Network
26
27
  b.use ClearNetworkInterfaces
27
- # b.use Network
28
28
  # b.use ForwardPorts
29
29
  b.use SetHostname
30
30
  # b.use SaneDefaults
31
+ b.use Customize, "pre-boot"
31
32
  b.use Boot
33
+ b.use Customize, "post-boot"
32
34
  b.use WaitForCommunicator
33
35
  b.use CheckGuestTools
34
36
  end
@@ -53,6 +55,7 @@ module VagrantPlugins
53
55
  b3.use action_halt
54
56
  b3.use UnregisterTemplate
55
57
  b3.use Destroy
58
+ b3.use DestroyUnusedNetworkInterfaces
56
59
  else
57
60
  b3.use MessageWillNotDestroy
58
61
  end
@@ -255,6 +258,7 @@ module VagrantPlugins
255
258
  b2.use CheckAccessible
256
259
  b2.use HandleBoxUrl
257
260
  b2.use RegisterTemplate
261
+ b2.use Customize, "pre-import"
258
262
  b2.use Import
259
263
  b2.use UnregisterTemplate
260
264
  b2.use MatchMACAddress
@@ -274,7 +278,9 @@ module VagrantPlugins
274
278
  autoload :ClearNetworkInterfaces, File.expand_path("../action/clear_network_interfaces", __FILE__)
275
279
  autoload :ClearSharedFolders, File.expand_path("../action/clear_shared_folders", __FILE__)
276
280
  autoload :Created, File.expand_path("../action/created", __FILE__)
281
+ autoload :Customize, File.expand_path("../action/customize", __FILE__)
277
282
  autoload :Destroy, File.expand_path("../action/destroy", __FILE__)
283
+ autoload :DestroyUnusedNetworkInterfaces, File.expand_path("../action/destroy_unused_network_interfaces", __FILE__)
278
284
  autoload :Export, File.expand_path("../action/export", __FILE__)
279
285
  autoload :ForcedHalt, File.expand_path("../action/forced_halt", __FILE__)
280
286
  autoload :Import, File.expand_path("../action/import", __FILE__)
@@ -286,6 +292,7 @@ module VagrantPlugins
286
292
  autoload :MessageNotCreated, File.expand_path("../action/message_not_created", __FILE__)
287
293
  autoload :MessageNotRunning, File.expand_path("../action/message_not_running", __FILE__)
288
294
  autoload :MessageWillNotDestroy, File.expand_path("../action/message_will_not_destroy", __FILE__)
295
+ autoload :Network, File.expand_path("../action/network", __FILE__)
289
296
  autoload :Package, File.expand_path("../action/package", __FILE__)
290
297
  autoload :PackageConfigFiles, File.expand_path("../action/package_config_files", __FILE__)
291
298
  autoload :PruneNFSExports, File.expand_path("../action/prune_nfs_exports", __FILE__)
@@ -7,8 +7,6 @@ module VagrantPlugins
7
7
  end
8
8
 
9
9
  def call(env)
10
- # Use the raw interface for now, while the virtualbox gem
11
- # doesn't support guest properties (due to cross platform issues)
12
10
  tools_version = env[:machine].provider.driver.read_guest_tools_version
13
11
  if !tools_version
14
12
  env[:ui].warn I18n.t("vagrant.actions.vm.check_guest_tools.not_detected")
@@ -16,7 +14,7 @@ module VagrantPlugins
16
14
  env[:machine].provider.driver.verify! =~ /^[\w\s]+ ([\d.]+)$/
17
15
  os_version = $1
18
16
  unless os_version.start_with? tools_version
19
- env[:ui].warn(I18n.t("vagrant.actions.vm.check_guest_tools.version_mismatch",
17
+ env[:ui].warn(I18n.t("vagrant_parallels.actions.vm.check_guest_tools.version_mismatch",
20
18
  tools_version: tools_version,
21
19
  parallels_version: os_version))
22
20
  end
@@ -7,7 +7,7 @@ module VagrantPlugins
7
7
  end
8
8
 
9
9
  def call(env)
10
- # "Enable" all the adapters we setup.
10
+ # Delete all disabled network adapters
11
11
  env[:ui].info I18n.t("vagrant.actions.vm.clear_network_interfaces.deleting")
12
12
  env[:machine].provider.driver.delete_adapters
13
13
 
@@ -0,0 +1,43 @@
1
+ module VagrantPlugins
2
+ module Parallels
3
+ module Action
4
+ class Customize
5
+ def initialize(app, env, event)
6
+ @app = app
7
+ @event = event
8
+ end
9
+
10
+ def call(env)
11
+ customizations = []
12
+ env[:machine].provider_config.customizations.each do |event, command|
13
+ if event == @event
14
+ customizations << command
15
+ end
16
+ end
17
+
18
+ if !customizations.empty?
19
+ env[:ui].info I18n.t("vagrant.actions.vm.customize.running", event: @event)
20
+
21
+ # Execute each customization command.
22
+ customizations.each do |command|
23
+ processed_command = command.collect do |arg|
24
+ arg = env[:machine].id if arg == :id
25
+ arg.to_s
26
+ end
27
+
28
+ result = env[:machine].provider.driver.set_vm_settings(processed_command)
29
+ if result.exit_code != 0
30
+ raise Vagrant::Errors::VMCustomizationFailed, {
31
+ :command => processed_command.inspect,
32
+ :error => result.stderr
33
+ }
34
+ end
35
+ end
36
+ end
37
+
38
+ @app.call(env)
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ require "log4r"
2
+
3
+ module VagrantPlugins
4
+ module Parallels
5
+ module Action
6
+ class DestroyUnusedNetworkInterfaces
7
+ def initialize(app, env)
8
+ @app = app
9
+ end
10
+
11
+ def call(env)
12
+ if env[:machine].provider_config.destroy_unused_network_interfaces
13
+ env[:ui].info I18n.t("vagrant.actions.vm.destroy_network.destroying")
14
+ env[:machine].provider.driver.delete_unused_host_only_networks
15
+ end
16
+
17
+ @app.call(env)
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,272 @@
1
+ require "set"
2
+
3
+ require "log4r"
4
+
5
+ require "vagrant/util/network_ip"
6
+ require "vagrant/util/scoped_hash_override"
7
+
8
+ module VagrantPlugins
9
+ module Parallels
10
+ module Action
11
+ # This middleware class sets up all networking for the Parallels Desktop
12
+ # instance. This includes host only networks, bridged networking,
13
+ # forwarded ports, etc.
14
+ #
15
+ # This handles all the `config.vm.network` configurations.
16
+ class Network
17
+ include Vagrant::Util::NetworkIP
18
+ include Vagrant::Util::ScopedHashOverride
19
+
20
+ def initialize(app, env)
21
+ @app = app
22
+ @logger = Log4r::Logger.new("vagrant::plugins::parallels::network")
23
+ end
24
+
25
+ def call(env)
26
+ @env = env
27
+ # Get the list of network adapters from the configuration
28
+ network_adapters_config = env[:machine].provider_config.network_adapters.dup
29
+
30
+ # Assign the adapter slot for each high-level network
31
+ available_slots = Set.new(0..7)
32
+ network_adapters_config.each do |slot, _data|
33
+ available_slots.delete(slot)
34
+ end
35
+
36
+ @logger.debug("Available slots for high-level adapters: #{available_slots.inspect}")
37
+ @logger.info("Determining network adapters required for high-level configuration...")
38
+ available_slots = available_slots.to_a.sort
39
+ env[:machine].config.vm.networks.each do |type, options|
40
+ # We only handle private and public networks
41
+ next if type != :private_network && type != :public_network
42
+
43
+ options = scoped_hash_override(options, :parallels)
44
+
45
+ # Figure out the slot that this adapter will go into
46
+ slot = options[:adapter]
47
+ if !slot
48
+ if available_slots.empty?
49
+ raise VagrantPlugins::Parallels::Errors::ParallelsNoRoomForHighLevelNetwork
50
+ end
51
+
52
+ slot = available_slots.shift
53
+ end
54
+
55
+ # Configure it
56
+ data = nil
57
+ if type == :private_network
58
+ # private_network = hostonly
59
+ data = [:hostonly, options]
60
+ elsif type == :public_network
61
+ # public_network = bridged
62
+ data = [:bridged, options]
63
+ end
64
+
65
+ # Store it!
66
+ @logger.info(" -- Slot #{slot}: #{data[0]}")
67
+ network_adapters_config[slot] = data
68
+ end
69
+
70
+ @logger.info("Determining adapters and compiling network configuration...")
71
+ adapters = []
72
+ network_adapters_config.each do |slot, data|
73
+ type = data[0]
74
+ options = data[1]
75
+
76
+ @logger.info("Network slot #{slot}. Type: #{type}.")
77
+
78
+ # Get the normalized configuration for this type
79
+ config = send("#{type}_config", options)
80
+ config[:adapter] = slot
81
+ @logger.debug("Normalized configuration: #{config.inspect}")
82
+
83
+ # Get the virtual network adapter configuration
84
+ adapter = send("#{type}_adapter", config)
85
+ adapters << adapter
86
+ @logger.debug("Adapter configuration: #{adapter.inspect}")
87
+ end
88
+
89
+ if !adapters.empty?
90
+ # Enable the adapters
91
+ @logger.info("Enabling adapters...")
92
+ env[:ui].info I18n.t("vagrant.actions.vm.network.preparing")
93
+ env[:machine].provider.driver.enable_adapters(adapters)
94
+ end
95
+
96
+ # Continue the middleware chain.
97
+ @app.call(env)
98
+
99
+ end
100
+
101
+ def hostonly_config(options)
102
+ options = {
103
+ :mac => nil,
104
+ :nic_type => "e1000",
105
+ :netmask => "255.255.255.0",
106
+ :type => :static
107
+ }.merge(options)
108
+
109
+ # Make sure the type is a symbol
110
+ options[:type] = options[:type].to_sym
111
+
112
+ # Default IP is in the 20-bit private network block for DHCP based networks
113
+ options[:ip] = "10.37.129.1" if options[:type] == :dhcp && !options[:ip]
114
+
115
+ # Calculate our network address for the given IP/netmask
116
+ netaddr = network_address(options[:ip], options[:netmask])
117
+
118
+ # Verify that a host-only network subnet would not collide
119
+ # with a bridged networking interface.
120
+ #
121
+ # If the subnets overlap in any way then the host only network
122
+ # will not work because the routing tables will force the
123
+ # traffic onto the real interface rather than the virtual
124
+ # network interface.
125
+ @env[:machine].provider.driver.read_bridged_interfaces.each do |interface|
126
+ that_netaddr = network_address(interface[:ip], interface[:netmask])
127
+ raise Vagrant::Errors::NetworkCollision if \
128
+ netaddr == that_netaddr && interface[:status] != "Down"
129
+ end
130
+
131
+ # Split the IP address into its components
132
+ ip_parts = netaddr.split(".").map { |i| i.to_i }
133
+
134
+ # Calculate the adapter IP, which we assume is the IP ".1" at
135
+ # the end usually.
136
+ adapter_ip = ip_parts.dup
137
+ adapter_ip[3] += 1
138
+ options[:adapter_ip] ||= adapter_ip.join(".")
139
+
140
+ dhcp_options = {}
141
+ if options[:type] == :dhcp
142
+ # Calculate the DHCP server IP, which is the network address
143
+ # with the final octet + 2. So "172.28.0.0" turns into "172.28.0.2"
144
+ dhcp_ip = ip_parts.dup
145
+ dhcp_ip[3] += 2
146
+ dhcp_options[:dhcp_ip] ||= dhcp_ip.join(".")
147
+
148
+ # Calculate the lower and upper bound for the DHCP server
149
+ dhcp_lower = ip_parts.dup
150
+ dhcp_lower[3] += 3
151
+ dhcp_options[:dhcp_lower] ||= dhcp_lower.join(".")
152
+
153
+ dhcp_upper = ip_parts.dup
154
+ dhcp_upper[3] = 254
155
+ dhcp_options[:dhcp_upper] ||= dhcp_upper.join(".")
156
+ end
157
+
158
+ return {
159
+ :adapter_ip => options[:adapter_ip],
160
+ :ip => options[:ip],
161
+ :mac => options[:mac],
162
+ :netmask => options[:netmask],
163
+ :nic_type => options[:nic_type],
164
+ :type => options[:type],
165
+ }.merge(dhcp_options)
166
+ end
167
+
168
+ def hostonly_adapter(config)
169
+ @logger.info("Searching for matching hostonly network: #{config[:ip]}")
170
+ interface = hostonly_find_matching_network(config)
171
+
172
+ if !interface
173
+ @logger.info("Network not found. Creating if we can.")
174
+
175
+ # It is an error if a specific host only network name was specified
176
+ # but the network wasn't found.
177
+ if config[:name]
178
+ raise Vagrant::Errors::NetworkNotFound, :name => config[:name]
179
+ end
180
+
181
+ # Create a new network
182
+ interface = hostonly_create_network(config)
183
+ @logger.info("Created network: #{interface[:name]}")
184
+ end
185
+
186
+ return {
187
+ :adapter => config[:adapter],
188
+ :hostonly => interface[:name],
189
+ :bound_to => interface[:bound_to],
190
+ :mac => config[:mac],
191
+ :nic_type => config[:nic_type],
192
+ :type => :hostonly,
193
+ :dhcp => interface[:dhcp],
194
+ :ip => config[:ip],
195
+ :netmask => config[:netmask],
196
+ }
197
+ end
198
+
199
+ def shared_config(options)
200
+ return {}
201
+ end
202
+
203
+ def shared_adapter(config)
204
+ return {
205
+ :adapter => config[:adapter],
206
+ :shared => "Shared",
207
+ :type => :shared,
208
+ :dhcp => true,
209
+ :nic_type => "e1000"
210
+ }
211
+ end
212
+
213
+ #-----------------------------------------------------------------
214
+ # Misc. helpers
215
+ #-----------------------------------------------------------------
216
+ # This determines the next free network name
217
+ def next_network_name
218
+ # Get the list of numbers
219
+ net_nums = []
220
+ @env[:machine].provider.driver.read_virtual_networks.each do |net|
221
+ if net['Network ID'] =~ /^vagrant-vnet(\d+)$/
222
+ net_nums << $1.to_i
223
+ end
224
+ end
225
+
226
+ if net_nums.empty?
227
+ "vagrant-vnet0"
228
+ else
229
+ net_nums.sort! if net_nums
230
+ free_names = Array(0..net_nums.last.next) - net_nums
231
+ "vagrant-vnet#{free_names.first}"
232
+ end
233
+ end
234
+
235
+ #-----------------------------------------------------------------
236
+ # Hostonly Helper Functions
237
+ #-----------------------------------------------------------------
238
+ # This creates a host only network for the given configuration.
239
+ def hostonly_create_network(config)
240
+ options = {
241
+ :name => next_network_name,
242
+ :adapter_ip => config[:adapter_ip],
243
+ :netmask => config[:netmask],
244
+ }
245
+
246
+ if config[:type] == :dhcp
247
+ options[:dhcp] = {
248
+ :ip => config[:dhcp_ip],
249
+ :lower => config[:dhcp_lower],
250
+ :upper => config[:dhcp_upper]
251
+ }
252
+ end
253
+
254
+ @env[:machine].provider.driver.create_host_only_network(options)
255
+ end
256
+
257
+ # This finds a matching host only network for the given configuration.
258
+ def hostonly_find_matching_network(config)
259
+ this_netaddr = network_address(config[:ip], config[:netmask])
260
+
261
+ @env[:machine].provider.driver.read_host_only_interfaces.each do |interface|
262
+ return interface if config[:name] && config[:name] == interface[:name]
263
+ return interface if this_netaddr == \
264
+ network_address(interface[:ip], interface[:netmask])
265
+ end
266
+
267
+ nil
268
+ end
269
+ end
270
+ end
271
+ end
272
+ end