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 +9 -0
- data/README.md +55 -12
- data/lib/vagrant-libvirt/action/create_domain.rb +1 -0
- data/lib/vagrant-libvirt/action/create_network_interfaces.rb +95 -22
- data/lib/vagrant-libvirt/action/create_networks.rb +277 -0
- data/lib/vagrant-libvirt/action/destroy_networks.rb +80 -0
- data/lib/vagrant-libvirt/action.rb +7 -8
- data/lib/vagrant-libvirt/config.rb +3 -0
- data/lib/vagrant-libvirt/errors.rb +24 -0
- data/lib/vagrant-libvirt/templates/domain.xml.erb +8 -0
- data/lib/vagrant-libvirt/templates/private_network.xml.erb +21 -0
- data/lib/vagrant-libvirt/util/libvirt_util.rb +57 -0
- data/lib/vagrant-libvirt/util.rb +1 -0
- data/lib/vagrant-libvirt/version.rb +1 -1
- data/locales/en.yml +14 -0
- metadata +6 -3
- data/lib/vagrant-libvirt/action/cleanup_data_dir.rb +0 -22
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.
|
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.
|
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
|
-
|
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 = "
|
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`
|
144
|
-
|
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
|
|
@@ -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.
|
32
|
-
#
|
33
|
-
|
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
|
-
#
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
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
|
-
|
64
|
+
free_slot = options[:adapter].to_i
|
48
65
|
else
|
49
|
-
|
50
|
-
raise Errors::InterfaceSlotNotAvailable if
|
66
|
+
free_slot = find_empty(adapters, start=1)
|
67
|
+
raise Errors::InterfaceSlotNotAvailable if free_slot == nil
|
68
|
+
end
|
51
69
|
|
52
|
-
|
53
|
-
|
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 |
|
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
|
-
|
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
|
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
|
data/lib/vagrant-libvirt/util.rb
CHANGED
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
|
+
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-
|
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
|