vagrant-libvirt 0.0.4 → 0.0.5

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ # 0.0.5 (May 10, 2013)
2
+
3
+ * Private networks support.
4
+ * Creating new private networks if ip is specified and network is not
5
+ available.
6
+ * Removing previously created networks, if there are no active connections.
7
+ * Guest interfaces configuration.
8
+ * Setting guest hostname (via `config.vm.hostname`).
9
+
1
10
  # 0.0.4 (May 5, 2013)
2
11
 
3
12
  * Bug fix in number of parameters for provisioner.
data/README.md CHANGED
@@ -4,16 +4,19 @@ This is a [Vagrant](http://www.vagrantup.com) 1.1+ plugin that adds an
4
4
  [Libvirt](http://libvirt.org) provider to Vagrant, allowing Vagrant to
5
5
  control and provision machines via Libvirt toolkit.
6
6
 
7
- **Note:** Actual version (0.0.4) is still a development one. Feedback is
7
+ **Note:** Actual version (0.0.5) is still a development one. Feedback is
8
8
  welcome and can help a lot :-)
9
9
 
10
- ## Features (Version 0.0.4)
10
+ ## Features (Version 0.0.5)
11
11
 
12
+ * Controll local or remote Libvirt hypervisors.
12
13
  * Vagrant `up`, `destroy`, `suspend`, `resume`, `halt`, `ssh` and `provision` commands.
13
14
  * Upload box image (qcow2 format) to Libvirt storage pool.
14
15
  * Create volume as COW diff image for domains.
16
+ * Create private networks.
15
17
  * Create and boot Libvirt domains.
16
18
  * SSH into domains.
19
+ * Setup hostname and network interfaces.
17
20
  * Provision domains with any built-in Vagrant provisioner.
18
21
  * Minimal synced folder support via `rsync`.
19
22
 
@@ -31,7 +34,7 @@ installing, `vagrant up` and specify the `libvirt` provider. An example is shown
31
34
  $ vagrant plugin install vagrant-libvirt
32
35
  ```
33
36
 
34
- ### Possible problems with plugin installation
37
+ ### Possible problems with plugin installation on Linux
35
38
 
36
39
  In case of problems with building nokogiri and ruby-libvirt gem, install
37
40
  missing development libraries for libxslt, libxml2 and libvirt.
@@ -57,24 +60,25 @@ want. This is just an example of Libvirt CentOS 6.4 box available:
57
60
  $ vagrant box add centos64 http://kwok.cz/centos64.box
58
61
  ```
59
62
 
60
- And then make a Vagrantfile that looks like the following, filling in
61
- your information where necessary.
63
+ And then make a Vagrantfile that looks like the following, filling in your
64
+ information where necessary. In example below, VM named test_vm is created from
65
+ centos64 box and setup with 10.20.30.40 IP address.
62
66
 
63
67
  ```ruby
64
68
  Vagrant.configure("2") do |config|
65
69
  config.vm.define :test_vm do |test_vm|
66
70
  test_vm.vm.box = "centos64"
71
+ test_vm.vm.network :private_network, :ip => '10.20.30.40'
67
72
  end
68
73
 
69
74
  config.vm.provider :libvirt do |libvirt|
70
75
  libvirt.driver = "qemu"
71
- libvirt.host = "example.com"
76
+ libvirt.host = "localhost"
72
77
  libvirt.connect_via_ssh = true
73
78
  libvirt.username = "root"
74
79
  libvirt.storage_pool_name = "default"
75
80
  end
76
81
  end
77
-
78
82
  ```
79
83
 
80
84
  ### Libvirt Configuration Options
@@ -93,6 +97,7 @@ This provider exposes quite a few provider-specific configuration options:
93
97
 
94
98
  * `memory` - Amount of memory in MBytes. Defaults to 512 if not set.
95
99
  * `cpus` - Number of virtual cpus. Defaults to 1 if not set.
100
+ * `nested` - [Enable nested virtualization](https://github.com/torvalds/linux/blob/master/Documentation/virtual/kvm/nested-vmx.txt). Default is false.
96
101
 
97
102
  Specific domain settings can be set for each domain separately in multi-VM
98
103
  environment. Example below shows a part of Vagrantfile, where specific options
@@ -108,7 +113,7 @@ Vagrant.configure("2") do |config|
108
113
  end
109
114
  end
110
115
 
111
- ...
116
+ # ...
112
117
  ```
113
118
 
114
119
  ## Create Project - Vagrant up
@@ -130,7 +135,7 @@ Vagrant goes through steps below when creating new project:
130
135
 
131
136
  1. Connect to Libvirt localy or remotely via SSH.
132
137
  2. Check if box image is available in Libvirt storage pool. If not, upload it to
133
- remote Libvirt storage pool as new volume.
138
+ remote Libvirt storage pool as new volume.
134
139
  3. Create COW diff image of base box image for new Libvirt domain.
135
140
  4. Create and start new domain on Libvirt host.
136
141
  5. Check for DHCP lease from dnsmasq server.
@@ -140,9 +145,47 @@ Vagrant goes through steps below when creating new project:
140
145
 
141
146
  ## Networks
142
147
 
143
- Networking features in the form of `config.vm.network` are not supported right
144
- now. Support for private network is planned to be added in next release of
145
- provider.
148
+ Networking features in the form of `config.vm.network` support private networks
149
+ concept. No public network or port forwarding are supported in current version
150
+ of provider.
151
+
152
+ An examples of network interface definitions:
153
+
154
+ ```ruby
155
+ config.vm.define :test_vm1 do |test_vm1|
156
+ test_vm1.vm.network :private_network, :ip => '10.20.30.40'
157
+ end
158
+ ```
159
+
160
+ In example below, one network interface is configured for VM test_vm1. After
161
+ you run `vagrant up`, VM will be accessible on IP address 10.20.30.40. So if
162
+ you install a web server via provisioner, you will be able to access your
163
+ testing server on http://10.20.30.40 URL. But beware that this address is
164
+ private to libvirt host only. It's not visible outside of the hypervisor box.
165
+
166
+ If network 10.20.30.0/24 doesn't exist, provider will create it. By default
167
+ created networks are NATed to outside world, so your VM will be able to connect
168
+ to the internet (if hypervisor can). And by default, DHCP is offering addresses
169
+ on newly created networks.
170
+
171
+ ### Private Network Options
172
+
173
+ There is a way to pass specific options for libvirt provider when using
174
+ `config.vm.network` to configure new network interface. Each parameter name
175
+ starts with 'libvirt__' string. Here is a list of those options:
176
+
177
+ * `:libvirt__network_name` - Name of libvirt network to connect to. By default,
178
+ network 'default' is used.
179
+ * `:libvirt__netmask` - Used only together with `:ip` option. Default is
180
+ '255.255.255.0'.
181
+ * `:libvirt__nat_interface` - Name of interface, where should network be
182
+ NATed. Used only when creating new network. By default, all physical
183
+ interfaces are used.
184
+ * `:libvirt__isolated` - If network should be isolated - without NAT to outside.
185
+ Used only when creating new network. Default is set to false.
186
+ * `:libvirt__dhcp_enabled` - If DHCP will offer addresses, or not. Used only
187
+ when creating new network. Default is true.
188
+ * `:libvirt__adapter` - Number specifiyng sequence number of interface.
146
189
 
147
190
  ## Obtaining Domain IP Address
148
191
 
@@ -19,6 +19,7 @@ module VagrantPlugins
19
19
  # Gather some info about domain
20
20
  @name = env[:domain_name]
21
21
  @cpus = config.cpus
22
+ @nested = config.nested
22
23
  @memory_size = config.memory*1024
23
24
 
24
25
  # TODO get type from driver config option
@@ -1,12 +1,18 @@
1
1
  require 'log4r'
2
+ require 'vagrant/util/network_ip'
3
+ require 'vagrant/util/scoped_hash_override'
2
4
 
3
5
  module VagrantPlugins
4
6
  module Libvirt
5
7
  module Action
6
8
 
7
9
  # Create network interfaces for domain, before domain is running.
10
+ # Networks for connecting those interfaces should be already prepared.
8
11
  class CreateNetworkInterfaces
9
12
  include VagrantPlugins::Libvirt::Util::ErbTemplate
13
+ include VagrantPlugins::Libvirt::Util::LibvirtUtil
14
+ include Vagrant::Util::NetworkIP
15
+ include Vagrant::Util::ScopedHashOverride
10
16
 
11
17
  def initialize(app, env)
12
18
  @logger = Log4r::Logger.new("vagrant_libvirt::action::create_network_interfaces")
@@ -23,41 +29,59 @@ module VagrantPlugins
23
29
  :error_message => e.message
24
30
  end
25
31
 
26
- # Setup list of interfaces before creating them
32
+ # Setup list of interfaces before creating them.
27
33
  adapters = []
28
34
 
29
35
  # Assign main interface for provisioning to first slot.
30
36
  # Use network 'default' as network for ssh connecting and
31
- # machine provisioning. This should be maybe configurable in
32
- # Vagrantfile in future.
33
- adapters[0] = 'default'
34
-
37
+ # machine provisioning.
38
+ #
39
+ # TODO Network name with DHCP for first interface should be
40
+ # configurable.
41
+ adapters[0] = {
42
+ :network_name => 'default'
43
+ }
44
+
45
+ # Assign interfaces to slots.
35
46
  env[:machine].config.vm.networks.each do |type, options|
36
- # Other types than bridged are not supported for now.
37
- next if type != :bridged
38
-
39
- network_name = 'default'
40
- network_name = options[:bridge] if options[:bridge]
41
-
47
+ # Only private network is supported now. Port forwarding and public
48
+ # network are not supported via libvirt API, so they are not
49
+ # implemented in this provider.
50
+ next if type != :private_network
51
+
52
+ # Get options for this interface. Options can be specified in
53
+ # Vagrantfile in short format (:ip => ...), or provider format
54
+ # (:libvirt__network_name => ...).
55
+ options = scoped_hash_override(options, :libvirt)
56
+ options = { :netmask => '255.255.255.0' }.merge(options)
57
+
58
+ # TODO fill first ifaces with adapter option specified.
42
59
  if options[:adapter]
43
60
  if adapters[options[:adapter]]
44
61
  raise Errors::InterfaceSlotNotAvailable
45
62
  end
46
63
 
47
- adapters[options[:adapter].to_i] = network_name
64
+ free_slot = options[:adapter].to_i
48
65
  else
49
- empty_slot = find_empty(adapters, start=1)
50
- raise Errors::InterfaceSlotNotAvailable if empty_slot == nil
66
+ free_slot = find_empty(adapters, start=1)
67
+ raise Errors::InterfaceSlotNotAvailable if free_slot == nil
68
+ end
51
69
 
52
- adapters[empty_slot] = network_name
53
- end
70
+ # We have slot for interface, fill it with interface configuration.
71
+ adapters[free_slot] = options
72
+ adapters[free_slot][:network_name] = interface_network(
73
+ env[:libvirt_compute].client, adapters[free_slot])
54
74
  end
55
75
 
56
- # Create each interface as new domain device
57
- adapters.each_with_index do |network_name, slot_number|
76
+ # Create each interface as new domain device.
77
+ adapters.each_with_index do |iface_configuration, slot_number|
58
78
  @iface_number = slot_number
59
- @network_name = network_name
60
- @logger.info("Creating network interface eth#{@iface_number}")
79
+ @network_name = iface_configuration[:network_name]
80
+
81
+ message = "Creating network interface eth#{@iface_number}"
82
+ message << " connected to network #{@network_name}."
83
+ @logger.info(message)
84
+
61
85
  begin
62
86
  domain.attach_device(to_xml('interface'))
63
87
  rescue => e
@@ -66,7 +90,40 @@ module VagrantPlugins
66
90
  end
67
91
  end
68
92
 
93
+ # Continue the middleware chain.
69
94
  @app.call(env)
95
+
96
+ # Configure interfaces that user requested. Machine should be up and
97
+ # running now.
98
+ networks_to_configure = []
99
+
100
+ adapters.each_with_index do |options, slot_number|
101
+ # Skip configuring first interface. It's used for provisioning and
102
+ # it has to be available during provisioning - ifdown command is
103
+ # not acceptable here.
104
+ next if slot_number == 0
105
+
106
+ network = {
107
+ :interface => slot_number,
108
+ #:mac => ...,
109
+ }
110
+
111
+ if options[:ip]
112
+ network = {
113
+ :type => :static,
114
+ :ip => options[:ip],
115
+ :netmask => options[:netmask],
116
+ }.merge(network)
117
+ else
118
+ network[:type] = :dhcp
119
+ end
120
+
121
+ networks_to_configure << network
122
+ end
123
+
124
+ env[:ui].info I18n.t("vagrant.actions.vm.network.configuring")
125
+ env[:machine].guest.capability(
126
+ :configure_networks, networks_to_configure)
70
127
  end
71
128
 
72
129
  private
@@ -77,9 +134,25 @@ module VagrantPlugins
77
134
  end
78
135
  return nil
79
136
  end
80
- end
81
137
 
138
+ # Return network name according to interface options.
139
+ def interface_network(libvirt_client, options)
140
+ return options[:network_name] if options[:network_name]
141
+
142
+ # Get list of all (active and inactive) libvirt networks.
143
+ available_networks = libvirt_networks(libvirt_client)
144
+
145
+ if options[:ip]
146
+ address = network_address(options[:ip], options[:netmask])
147
+ available_networks.each do |network|
148
+ return network[:name] if address == network[:network_address]
149
+ end
150
+ end
151
+
152
+ # TODO Network default can be missing or named different.
153
+ return 'default'
154
+ end
155
+ end
82
156
  end
83
157
  end
84
158
  end
85
-
@@ -0,0 +1,277 @@
1
+ require 'log4r'
2
+ require 'vagrant/util/network_ip'
3
+ require 'vagrant/util/scoped_hash_override'
4
+ require 'ipaddr'
5
+
6
+ module VagrantPlugins
7
+ module Libvirt
8
+ module Action
9
+ # Prepare all networks needed for domain connections.
10
+ class CreateNetworks
11
+ include Vagrant::Util::NetworkIP
12
+ include Vagrant::Util::ScopedHashOverride
13
+ include VagrantPlugins::Libvirt::Util::ErbTemplate
14
+ include VagrantPlugins::Libvirt::Util::LibvirtUtil
15
+
16
+ def initialize(app, env)
17
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::create_networks")
18
+ @app = app
19
+
20
+ @available_networks = []
21
+ @options = {}
22
+ @libvirt_client = env[:libvirt_compute].client
23
+ end
24
+
25
+ def call(env)
26
+
27
+ # Iterate over networks requested from config. If some network is not
28
+ # available, create it if possible. Otherwise raise an error.
29
+ env[:machine].config.vm.networks.each do |type, options|
30
+
31
+ # Get a list of all (active and inactive) libvirt networks. This
32
+ # list is used throughout this class and should be easier to
33
+ # process than libvirt API calls.
34
+ @available_networks = libvirt_networks(env[:libvirt_compute].client)
35
+
36
+ # Now, we support private networks only. There are two other types
37
+ # public network and port forwarding, but there are problems with
38
+ # creating them via libvirt API, so this provider doesn't implement
39
+ # them.
40
+ next if type != :private_network
41
+
42
+ # Get options for this interface network. Options can be specified
43
+ # in Vagrantfile in short format (:ip => ...), or provider format
44
+ # (:libvirt__network_name => ...).
45
+ @options = scoped_hash_override(options, :libvirt)
46
+ @options = {
47
+ :netmask => '255.255.255.0',
48
+ }.merge(@options)
49
+
50
+ # Prepare a hash describing network for this specific interface.
51
+ @interface_network = {
52
+ :name => nil,
53
+ :ip_address => nil,
54
+ :netmask => @options[:netmask],
55
+ :network_address => nil,
56
+ :bridge_name => nil,
57
+ :created => false,
58
+ :active => false,
59
+ :autostart => false,
60
+ :libvirt_network => nil,
61
+ }
62
+
63
+ if @options[:ip]
64
+ handle_ip_option(env)
65
+ elsif @options[:network_name]
66
+ handle_network_name_option
67
+ else
68
+ # TODO Should be smarter than just using fixed 'default' string.
69
+ @interface_network = lookup_network_by_name('default')
70
+ if not @interface_network
71
+ raise Errors::NetworkNotAvailableError,
72
+ :network_name => 'default'
73
+ end
74
+ end
75
+
76
+ autostart_network if not @interface_network[:autostart]
77
+ activate_network if not @interface_network[:active]
78
+ end
79
+
80
+ @app.call(env)
81
+ end
82
+
83
+ private
84
+
85
+ # Return hash of network for specified name, or nil if not found.
86
+ def lookup_network_by_name(network_name)
87
+ @available_networks.each do |network|
88
+ return network if network[:name] == network_name
89
+ end
90
+ nil
91
+ end
92
+
93
+ # Return hash of network for specified bridge, or nil if not found.
94
+ def lookup_bridge_by_name(bridge_name)
95
+ @available_networks.each do |network|
96
+ return network if network[:bridge_name] == bridge_name
97
+ end
98
+ nil
99
+ end
100
+
101
+ # Handle only situations, when ip is specified. Variables @options and
102
+ # @available_networks should be filled before calling this function.
103
+ def handle_ip_option(env)
104
+ return if not @options[:ip]
105
+
106
+ net_address = network_address(@options[:ip], @options[:netmask])
107
+ @interface_network[:network_address] = net_address
108
+
109
+ # Set IP address of network (actually bridge). It will be used as
110
+ # gateway address for machines connected to this network.
111
+ net = IPAddr.new(net_address)
112
+ @interface_network[:ip_address] = net.to_range.begin.succ
113
+
114
+ # Is there an available network matching to configured ip
115
+ # address?
116
+ @available_networks.each do |available_network|
117
+ if available_network[:network_address] == \
118
+ @interface_network[:network_address]
119
+ @interface_network = available_network
120
+ break
121
+ end
122
+ end
123
+
124
+ if @options[:network_name]
125
+ if @interface_network[:created]
126
+ # Just check for mismatch error here - if name and ip from
127
+ # config match together.
128
+ if @options[:network_name] != @interface_network[:name]
129
+ raise Errors::NetworkNameAndAddressMismatch,
130
+ :ip_address => @options[:ip],
131
+ :network_name => @options[:network_name]
132
+ end
133
+ else
134
+ # Network is not created, but name is set. We need to check,
135
+ # whether network name from config doesn't already exist.
136
+ if lookup_network_by_name @options[:network_name]
137
+ raise Errors::NetworkNameAndAddressMismatch,
138
+ :ip_address => @options[:ip],
139
+ :network_name => @options[:network_name]
140
+ end
141
+
142
+ # Network with 'name' doesn't exist. Set it as name for new
143
+ # network.
144
+ @interface_network[:name] = @options[:network_name]
145
+ end
146
+ end
147
+
148
+ # Do we need to create new network?
149
+ if not @interface_network[:created]
150
+
151
+ # TODO stop after some loops. Don't create infinite loops.
152
+
153
+ # Is name for new network set? If not, generate a unique one.
154
+ count = 0
155
+ while @interface_network[:name] == nil do
156
+
157
+ # Generate a network name.
158
+ network_name = env[:root_path].basename.to_s.dup
159
+ network_name << count.to_s
160
+ count += 1
161
+
162
+ # Check if network name is unique.
163
+ next if lookup_network_by_name(network_name)
164
+
165
+ @interface_network[:name] = network_name
166
+ end
167
+
168
+ # Generate a unique name for network bridge.
169
+ count = 0
170
+ while @interface_network[:bridge_name] == nil do
171
+ bridge_name = 'virbr'
172
+ bridge_name << count.to_s
173
+ count += 1
174
+
175
+ next if lookup_bridge_by_name(bridge_name)
176
+
177
+ @interface_network[:bridge_name] = bridge_name
178
+ end
179
+
180
+ # Create a private network.
181
+ create_private_network(env)
182
+ end
183
+ end
184
+
185
+ # Handle network_name option, if ip was not specified. Variables
186
+ # @options and @available_networks should be filled before calling this
187
+ # function.
188
+ def handle_network_name_option
189
+ return if @options[:ip] or not @options[:network_name]
190
+
191
+ @interface_network = lookup_network_by_name(@options[:network_name])
192
+ if not @interface_network
193
+ raise Errors::NetworkNotAvailableError,
194
+ :network_name => @options[:network_name]
195
+ end
196
+ end
197
+
198
+ def create_private_network(env)
199
+ @network_name = @interface_network[:name]
200
+ @network_bridge_name = @interface_network[:bridge_name]
201
+ @network_address = @interface_network[:ip_address]
202
+ @network_netmask = @interface_network[:netmask]
203
+
204
+ if @options[:isolated]
205
+ @network_forward_mode = false
206
+ else
207
+ @network_forward_mode = 'nat'
208
+
209
+ if @options[:nat_interface]
210
+ @network_nat_interface = @options[:nat_interface]
211
+ end
212
+ end
213
+
214
+ if @options[:dhcp_enabled]
215
+ # Find out DHCP addresses pool range.
216
+ network_address = "#{@interface_network[:network_address]}/"
217
+ network_address << "#{@interface_network[:netmask]}"
218
+ net = IPAddr.new(network_address)
219
+
220
+ # First is address of network, second is gateway. Start the range two
221
+ # addresses after network address.
222
+ # TODO Detect if this IP is not set on the interface.
223
+ start_address = net.to_range.begin.succ.succ
224
+
225
+ # Stop address must not be broadcast.
226
+ stop_address = net.to_range.end & IPAddr.new('255.255.255.254')
227
+
228
+ @network_dhcp_enabled = true
229
+ @network_range_start = start_address
230
+ @network_range_stop = stop_address
231
+ else
232
+ @network_dhcp_enabled = false
233
+ end
234
+
235
+ begin
236
+ @interface_network[:libvirt_network] = \
237
+ @libvirt_client.define_network_xml(to_xml('private_network'))
238
+ rescue => e
239
+ raise Errors::CreateNetworkError,
240
+ :error_message => e.message
241
+ end
242
+
243
+ created_networks_file = env[:machine].data_dir + 'created_networks'
244
+
245
+ message = "Saving information about created network "
246
+ message << "#{@interface_network[:name]}, "
247
+ message << "UUID=#{@interface_network[:libvirt_network].uuid} "
248
+ message << "to file #{created_networks_file}."
249
+ @logger.info(message)
250
+
251
+ File.open(created_networks_file, 'a') do |file|
252
+ file.puts @interface_network[:libvirt_network].uuid
253
+ end
254
+ end
255
+
256
+ def autostart_network
257
+ begin
258
+ @interface_network[:libvirt_network].autostart = true
259
+ rescue => e
260
+ raise Errors::AutostartNetworkError,
261
+ :error_message => e.message
262
+ end
263
+ end
264
+
265
+ def activate_network
266
+ begin
267
+ @interface_network[:libvirt_network].create
268
+ rescue => e
269
+ raise Errors::ActivateNetworkError,
270
+ :error_message => e.message
271
+ end
272
+ end
273
+
274
+ end
275
+ end
276
+ end
277
+ end
@@ -0,0 +1,80 @@
1
+ require 'log4r'
2
+ require 'nokogiri'
3
+
4
+ module VagrantPlugins
5
+ module Libvirt
6
+ module Action
7
+ # Destroy all networks created for this specific domain. Skip
8
+ # removing if network has still active connections.
9
+ class DestroyNetworks
10
+
11
+ def initialize(app, env)
12
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_networks")
13
+ @app = app
14
+ end
15
+
16
+ def call(env)
17
+ # If there were some networks created for this machine, in machines
18
+ # data directory, created_networks file holds UUIDs of each network.
19
+ created_networks_file = env[:machine].data_dir + 'created_networks'
20
+
21
+ # If created_networks file doesn't exist, there are no networks we
22
+ # need to remove.
23
+ return @app.call(env) if not File.exist?(created_networks_file)
24
+
25
+ # Iterate over each created network UUID and try to remove it.
26
+ created_networks = []
27
+ file = File.open(created_networks_file, 'r')
28
+ file.readlines.each do |network_uuid|
29
+ begin
30
+ libvirt_network = env[:libvirt_compute].client.lookup_network_by_uuid(
31
+ network_uuid)
32
+ rescue
33
+ next
34
+ end
35
+
36
+ # Maybe network doesn't exist anymore.
37
+ next if not libvirt_network
38
+
39
+ # Skip removing if network has still active connections.
40
+ xml = Nokogiri::XML(libvirt_network.xml_desc)
41
+ connections = xml.xpath('/network/@connections').first
42
+ if connections != nil
43
+ created_networks << network_uuid
44
+ next
45
+ end
46
+
47
+ # Shutdown network first.
48
+ begin
49
+ libvirt_network.destroy
50
+ rescue => e
51
+ end
52
+
53
+ # Undefine network.
54
+ begin
55
+ libvirt_network.undefine
56
+ rescue => e
57
+ raise Error::DestroyNetworkError,
58
+ :network_name => libvirt_network.name,
59
+ :error_message => e.message
60
+ end
61
+ end
62
+ file.close
63
+
64
+ # Update status of created networks after removing some/all of them.
65
+ if created_networks.length > 0
66
+ File.open(created_networks_file, 'w') do |file|
67
+ created_networks.each do |network_uuid|
68
+ file.puts network_uuid
69
+ end
70
+ end
71
+ else
72
+ File.delete(created_networks_file)
73
+ end
74
+
75
+ @app.call(env)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -20,9 +20,11 @@ module VagrantPlugins
20
20
  b2.use HandleBoxImage
21
21
  b2.use CreateDomainVolume
22
22
  b2.use CreateDomain
23
- b2.use CreateNetworkInterfaces
24
23
 
25
24
  b2.use TimedProvision
25
+ b2.use CreateNetworks
26
+ b2.use CreateNetworkInterfaces
27
+ b2.use SetHostname
26
28
  b2.use StartDomain
27
29
  b2.use WaitTillUp
28
30
  b2.use SyncFolders
@@ -79,10 +81,8 @@ module VagrantPlugins
79
81
  b2.use Call, IsRunning do |env2, b3|
80
82
  next if !env2[:result]
81
83
 
82
- # VM is running, halt it.. Cleanup running instance data. Now
83
- # only IP address is stored.
84
+ # VM is running, halt it.
84
85
  b3.use HaltDomain
85
- b3.use CleanupDataDir
86
86
  end
87
87
  end
88
88
  end
@@ -101,9 +101,7 @@ module VagrantPlugins
101
101
 
102
102
  b2.use ConnectLibvirt
103
103
  b2.use DestroyDomain
104
-
105
- # Cleanup running instance data. Now only IP address is stored.
106
- b2.use CleanupDataDir
104
+ b2.use DestroyNetworks
107
105
  end
108
106
  end
109
107
  end
@@ -262,13 +260,14 @@ module VagrantPlugins
262
260
  autoload :SetNameOfDomain, action_root.join("set_name_of_domain")
263
261
  autoload :CreateDomainVolume, action_root.join("create_domain_volume")
264
262
  autoload :CreateDomain, action_root.join("create_domain")
263
+ autoload :CreateNetworks, action_root.join("create_networks")
265
264
  autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
266
265
  autoload :DestroyDomain, action_root.join("destroy_domain")
266
+ autoload :DestroyNetworks, action_root.join("destroy_networks")
267
267
  autoload :StartDomain, action_root.join("start_domain")
268
268
  autoload :HaltDomain, action_root.join("halt_domain")
269
269
  autoload :SuspendDomain, action_root.join("suspend_domain")
270
270
  autoload :ResumeDomain, action_root.join("resume_domain")
271
- autoload :CleanupDataDir, action_root.join("cleanup_data_dir")
272
271
  autoload :ReadState, action_root.join("read_state")
273
272
  autoload :ReadSSHInfo, action_root.join("read_ssh_info")
274
273
  autoload :TimedProvision, action_root.join("timed_provision")
@@ -25,6 +25,7 @@ module VagrantPlugins
25
25
  # Domain specific settings used while creating new domain.
26
26
  attr_accessor :memory
27
27
  attr_accessor :cpus
28
+ attr_accessor :nested
28
29
 
29
30
  def initialize
30
31
  @driver = UNSET_VALUE
@@ -37,6 +38,7 @@ module VagrantPlugins
37
38
  # Domain specific settings.
38
39
  @memory = UNSET_VALUE
39
40
  @cpus = UNSET_VALUE
41
+ @nested = UNSET_VALUE
40
42
  end
41
43
 
42
44
  def finalize!
@@ -50,6 +52,7 @@ module VagrantPlugins
50
52
  # Domain specific settings.
51
53
  @memory = 512 if @memory == UNSET_VALUE
52
54
  @cpus = 1 if @cpus == UNSET_VALUE
55
+ @nested = false if @nested == UNSET_VALUE
53
56
  end
54
57
 
55
58
  def validate(machine)
@@ -74,6 +74,30 @@ module VagrantPlugins
74
74
  error_key(:interface_slot_not_available)
75
75
  end
76
76
 
77
+ class NetworkNameAndAddressMismatch < VagrantLibvirtError
78
+ error_key(:network_name_and_address_mismatch)
79
+ end
80
+
81
+ class CreateNetworkError < VagrantLibvirtError
82
+ error_key(:create_network_error)
83
+ end
84
+
85
+ class DestroyNetworkError < VagrantLibvirtError
86
+ error_key(:destroy_network_error)
87
+ end
88
+
89
+ class NetworkNotAvailableError < VagrantLibvirtError
90
+ error_key(:network_not_available_error)
91
+ end
92
+
93
+ class AutostartNetworkError < VagrantLibvirtError
94
+ error_key(:autostart_network_error)
95
+ end
96
+
97
+ class ActivateNetworkError < VagrantLibvirtError
98
+ error_key(:activate_network_error)
99
+ end
100
+
77
101
  class RsyncError < VagrantLibvirtError
78
102
  error_key(:rsync_error)
79
103
  end
@@ -2,6 +2,14 @@
2
2
  <name><%= @name %></name>
3
3
  <memory><%= @memory_size %></memory>
4
4
  <vcpu><%= @cpus %></vcpu>
5
+
6
+ <% if @nested %>
7
+ <cpu match='exact'>
8
+ <model>core2duo</model>
9
+ <feature policy='require' name='vmx'/>
10
+ </cpu>
11
+ <% end %>
12
+
5
13
  <os>
6
14
  <type arch='x86_64'>hvm</type>
7
15
  <boot dev='hd'/>
@@ -0,0 +1,21 @@
1
+ <network ipv6='yes'>
2
+ <name><%= @network_name %></name>
3
+ <bridge name="<%= @network_bridge_name %>" />
4
+
5
+ <% if @network_forward_mode != false %>
6
+ <% if @network_nat_interface %>
7
+ <forward mode="<%= @network_forward_mode %>" dev="<%= @network_nat_interface %>" />
8
+ <% else %>
9
+ <forward mode="<%= @network_forward_mode %>" />
10
+ <% end %>
11
+ <% end %>
12
+
13
+ <ip address="<%= @network_address %>" netmask="<%= @network_netmask %>">
14
+ <% if @network_dhcp_enabled %>
15
+ <dhcp>
16
+ <range start="<%= @network_range_start %>" end="<%= @network_range_stop %>" />
17
+ </dhcp>
18
+ <% end %>
19
+ </ip>
20
+
21
+ </network>
@@ -0,0 +1,57 @@
1
+ require 'nokogiri'
2
+ require 'vagrant/util/network_ip'
3
+
4
+ module VagrantPlugins
5
+ module Libvirt
6
+ module Util
7
+ module LibvirtUtil
8
+ include Vagrant::Util::NetworkIP
9
+
10
+ # Return a list of all (active and inactive) libvirt networks as a list
11
+ # of hashes with their name, network address and status (active or not).
12
+ def libvirt_networks(libvirt_client)
13
+ libvirt_networks = []
14
+
15
+ active = libvirt_client.list_networks
16
+ inactive = libvirt_client.list_defined_networks
17
+
18
+ # Iterate over all (active and inactive) networks.
19
+ active.concat(inactive).each do |network_name|
20
+ libvirt_network = libvirt_client.lookup_network_by_name(
21
+ network_name)
22
+
23
+ # Parse ip address and netmask from the network xml description.
24
+ xml = Nokogiri::XML(libvirt_network.xml_desc)
25
+ ip = xml.xpath('/network/ip/@address').first
26
+ ip = ip.value if ip
27
+ netmask = xml.xpath('/network/ip/@netmask').first
28
+ netmask = netmask.value if netmask
29
+
30
+ # Calculate network address of network from ip address and
31
+ # netmask.
32
+ if ip and netmask
33
+ network_address = network_address(ip, netmask)
34
+ else
35
+ network_address = nil
36
+ end
37
+
38
+ libvirt_networks << {
39
+ :name => network_name,
40
+ :ip_address => ip,
41
+ :netmask => netmask,
42
+ :network_address => network_address,
43
+ :bridge_name => libvirt_network.bridge_name,
44
+ :created => true,
45
+ :active => libvirt_network.active?,
46
+ :autostart => libvirt_network.autostart?,
47
+ :libvirt_network => libvirt_network,
48
+ }
49
+ end
50
+
51
+ libvirt_networks
52
+ end
53
+
54
+ end
55
+ end
56
+ end
57
+ end
@@ -4,6 +4,7 @@ module VagrantPlugins
4
4
  autoload :ErbTemplate, 'vagrant-libvirt/util/erb_template'
5
5
  autoload :Collection, 'vagrant-libvirt/util/collection'
6
6
  autoload :Timer, 'vagrant-libvirt/util/timer'
7
+ autoload :LibvirtUtil, 'vagrant-libvirt/util/libvirt_util'
7
8
  end
8
9
  end
9
10
  end
@@ -1,5 +1,5 @@
1
1
  module VagrantPlugins
2
2
  module Libvirt
3
- VERSION = "0.0.4"
3
+ VERSION = "0.0.5"
4
4
  end
5
5
  end
data/locales/en.yml CHANGED
@@ -101,6 +101,20 @@ en:
101
101
  Error while attaching new device to domain. %{error_message}
102
102
  no_ip_address_error: |-
103
103
  No IP address found.
104
+ network_name_and_address_mismatch: |-
105
+ Address %{ip_address} does not match with network name %{network_name}.
106
+ Please fix your configuration and run vagrant again.
107
+ create_network_error: |-
108
+ Error occured while creating new network: %{error_message}.
109
+ network_not_available_error: |-
110
+ Network %{network_name} is not available. Specify available network
111
+ name, or an ip address if you want to create a new network.
112
+ activate_network_error: |-
113
+ Error while activating network: %{error_message}.
114
+ autostart_network_error: |-
115
+ Error while setting up autostart on network: %{error_message}.
116
+ destroy_network_error: |-
117
+ Error while removing network %{network_name}. %{error_message}.
104
118
 
105
119
  states:
106
120
  short_paused: |-
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: vagrant-libvirt
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.4
4
+ version: 0.0.5
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-05-05 00:00:00.000000000 Z
12
+ date: 2013-05-10 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: fog
@@ -77,12 +77,13 @@ files:
77
77
  - example_box/metadata.json
78
78
  - lib/vagrant-libvirt.rb
79
79
  - lib/vagrant-libvirt/action.rb
80
- - lib/vagrant-libvirt/action/cleanup_data_dir.rb
81
80
  - lib/vagrant-libvirt/action/connect_libvirt.rb
82
81
  - lib/vagrant-libvirt/action/create_domain.rb
83
82
  - lib/vagrant-libvirt/action/create_domain_volume.rb
84
83
  - lib/vagrant-libvirt/action/create_network_interfaces.rb
84
+ - lib/vagrant-libvirt/action/create_networks.rb
85
85
  - lib/vagrant-libvirt/action/destroy_domain.rb
86
+ - lib/vagrant-libvirt/action/destroy_networks.rb
86
87
  - lib/vagrant-libvirt/action/halt_domain.rb
87
88
  - lib/vagrant-libvirt/action/handle_box_image.rb
88
89
  - lib/vagrant-libvirt/action/handle_storage_pool.rb
@@ -109,10 +110,12 @@ files:
109
110
  - lib/vagrant-libvirt/templates/default_storage_pool.xml.erb
110
111
  - lib/vagrant-libvirt/templates/domain.xml.erb
111
112
  - lib/vagrant-libvirt/templates/interface.xml.erb
113
+ - lib/vagrant-libvirt/templates/private_network.xml.erb
112
114
  - lib/vagrant-libvirt/templates/volume_snapshot.xml.erb
113
115
  - lib/vagrant-libvirt/util.rb
114
116
  - lib/vagrant-libvirt/util/collection.rb
115
117
  - lib/vagrant-libvirt/util/erb_template.rb
118
+ - lib/vagrant-libvirt/util/libvirt_util.rb
116
119
  - lib/vagrant-libvirt/util/timer.rb
117
120
  - lib/vagrant-libvirt/version.rb
118
121
  - locales/en.yml
@@ -1,22 +0,0 @@
1
- require 'log4r'
2
-
3
- module VagrantPlugins
4
- module Libvirt
5
- module Action
6
- class CleanupDataDir
7
- def initialize(app, env)
8
- @logger = Log4r::Logger.new("vagrant_libvirt::action::cleanup_data_dir")
9
- @app = app
10
- end
11
-
12
- def call(env)
13
- # Remove file holding IP address
14
- ip_file_path = env[:machine].data_dir + 'ip'
15
- File.delete(ip_file_path) if File.exists?(ip_file_path)
16
-
17
- @app.call(env)
18
- end
19
- end
20
- end
21
- end
22
- end