[DRE-commits] [vagrant-libvirt] 34/104: * Private network support. * Creating private networks if ip address is set and network is not available. * Guest network interfaces configuration.
Antonio Terceiro
terceiro at moszumanska.debian.org
Sun Apr 24 13:55:42 UTC 2016
This is an automated email from the git hooks/post-receive script.
terceiro pushed a commit to annotated tag 0.0.11
in repository vagrant-libvirt.
commit 813a7c811e981a0b9e90ffcb1b989274bc7917d9
Author: pradels <les.pradels at gmail.com>
Date: Thu May 9 20:32:06 2013 +0200
* Private network support.
* Creating private networks if ip address is set and network
is not available.
* Guest network interfaces configuration.
---
lib/vagrant-libvirt/action.rb | 6 +-
.../action/create_network_interfaces.rb | 117 +++++++--
lib/vagrant-libvirt/action/create_networks.rb | 269 +++++++++++++++++++++
lib/vagrant-libvirt/action/destroy_networks.rb | 80 ++++++
lib/vagrant-libvirt/errors.rb | 24 ++
.../templates/private_network.xml.erb | 21 ++
lib/vagrant-libvirt/util.rb | 1 +
lib/vagrant-libvirt/util/libvirt_util.rb | 57 +++++
locales/en.yml | 14 ++
9 files changed, 566 insertions(+), 23 deletions(-)
diff --git a/lib/vagrant-libvirt/action.rb b/lib/vagrant-libvirt/action.rb
index 5b0893f..259a28f 100644
--- a/lib/vagrant-libvirt/action.rb
+++ b/lib/vagrant-libvirt/action.rb
@@ -20,9 +20,10 @@ module VagrantPlugins
b2.use HandleBoxImage
b2.use CreateDomainVolume
b2.use CreateDomain
- b2.use CreateNetworkInterfaces
b2.use TimedProvision
+ b2.use CreateNetworks
+ b2.use CreateNetworkInterfaces
b2.use StartDomain
b2.use WaitTillUp
b2.use SyncFolders
@@ -101,6 +102,7 @@ module VagrantPlugins
b2.use ConnectLibvirt
b2.use DestroyDomain
+ b2.use DestroyNetworks
# Cleanup running instance data. Now only IP address is stored.
b2.use CleanupDataDir
@@ -262,8 +264,10 @@ module VagrantPlugins
autoload :SetNameOfDomain, action_root.join("set_name_of_domain")
autoload :CreateDomainVolume, action_root.join("create_domain_volume")
autoload :CreateDomain, action_root.join("create_domain")
+ autoload :CreateNetworks, action_root.join("create_networks")
autoload :CreateNetworkInterfaces, action_root.join("create_network_interfaces")
autoload :DestroyDomain, action_root.join("destroy_domain")
+ autoload :DestroyNetworks, action_root.join("destroy_networks")
autoload :StartDomain, action_root.join("start_domain")
autoload :HaltDomain, action_root.join("halt_domain")
autoload :SuspendDomain, action_root.join("suspend_domain")
diff --git a/lib/vagrant-libvirt/action/create_network_interfaces.rb b/lib/vagrant-libvirt/action/create_network_interfaces.rb
index d8b7f24..5eebb75 100644
--- a/lib/vagrant-libvirt/action/create_network_interfaces.rb
+++ b/lib/vagrant-libvirt/action/create_network_interfaces.rb
@@ -1,12 +1,18 @@
require 'log4r'
+require 'vagrant/util/network_ip'
+require 'vagrant/util/scoped_hash_override'
module VagrantPlugins
module Libvirt
module Action
# Create network interfaces for domain, before domain is running.
+ # Networks for connecting those interfaces should be already prepared.
class CreateNetworkInterfaces
include VagrantPlugins::Libvirt::Util::ErbTemplate
+ include VagrantPlugins::Libvirt::Util::LibvirtUtil
+ include Vagrant::Util::NetworkIP
+ include Vagrant::Util::ScopedHashOverride
def initialize(app, env)
@logger = Log4r::Logger.new("vagrant_libvirt::action::create_network_interfaces")
@@ -23,41 +29,59 @@ module VagrantPlugins
:error_message => e.message
end
- # Setup list of interfaces before creating them
+ # Setup list of interfaces before creating them.
adapters = []
# Assign main interface for provisioning to first slot.
# Use network 'default' as network for ssh connecting and
- # machine provisioning. This should be maybe configurable in
- # Vagrantfile in future.
- adapters[0] = 'default'
-
+ # machine provisioning.
+ #
+ # TODO Network name with DHCP for first interface should be
+ # configurable.
+ adapters[0] = {
+ :network_name => 'default'
+ }
+
+ # Assign interfaces to slots.
env[:machine].config.vm.networks.each do |type, options|
- # Other types than bridged are not supported for now.
- next if type != :bridged
-
- network_name = 'default'
- network_name = options[:bridge] if options[:bridge]
-
+ # Only private network is supported now. Port forwarding and public
+ # network are not supported via libvirt API, so they are not
+ # implemented in this provider.
+ next if type != :private_network
+
+ # Get options for this interface. Options can be specified in
+ # Vagrantfile in short format (:ip => ...), or provider format
+ # (:libvirt__network_name => ...).
+ options = scoped_hash_override(options, :libvirt)
+ options = { :netmask => '255.255.255.0' }.merge(options)
+
+ # TODO fill first ifaces with adapter option specified.
if options[:adapter]
if adapters[options[:adapter]]
raise Errors::InterfaceSlotNotAvailable
end
- adapters[options[:adapter].to_i] = network_name
+ free_slot = options[:adapter].to_i
else
- empty_slot = find_empty(adapters, start=1)
- raise Errors::InterfaceSlotNotAvailable if empty_slot == nil
+ free_slot = find_empty(adapters, start=1)
+ raise Errors::InterfaceSlotNotAvailable if free_slot == nil
+ end
- adapters[empty_slot] = network_name
- end
+ # We have slot for interface, fill it with interface configuration.
+ adapters[free_slot] = options
+ adapters[free_slot][:network_name] = interface_network(
+ env[:libvirt_compute].client, adapters[free_slot])
end
- # Create each interface as new domain device
- adapters.each_with_index do |network_name, slot_number|
+ # Create each interface as new domain device.
+ adapters.each_with_index do |iface_configuration, slot_number|
@iface_number = slot_number
- @network_name = network_name
- @logger.info("Creating network interface eth#{@iface_number}")
+ @network_name = iface_configuration[:network_name]
+
+ message = "Creating network interface eth#{@iface_number}"
+ message << " connected to network #{@network_name}."
+ @logger.info(message)
+
begin
domain.attach_device(to_xml('interface'))
rescue => e
@@ -66,7 +90,40 @@ module VagrantPlugins
end
end
+ # Continue the middleware chain.
@app.call(env)
+
+ # Configure interfaces that user requested. Machine should be up and
+ # running now.
+ networks_to_configure = []
+
+ adapters.each_with_index do |options, slot_number|
+ # Skip configuring first interface. It's used for provisioning and
+ # it has to be available during provisioning - ifdown command is
+ # not acceptable here.
+ next if slot_number == 0
+
+ network = {
+ :interface => slot_number,
+ #:mac => ...,
+ }
+
+ if options[:ip]
+ network = {
+ :type => :static,
+ :ip => options[:ip],
+ :netmask => options[:netmask],
+ }.merge(network)
+ else
+ network[:type] = :dhcp
+ end
+
+ networks_to_configure << network
+ end
+
+ env[:ui].info I18n.t("vagrant.actions.vm.network.configuring")
+ env[:machine].guest.capability(
+ :configure_networks, networks_to_configure)
end
private
@@ -77,9 +134,25 @@ module VagrantPlugins
end
return nil
end
- end
+ # Return network name according to interface options.
+ def interface_network(libvirt_client, options)
+ return options[:network_name] if options[:network_name]
+
+ # Get list of all (active and inactive) libvirt networks.
+ available_networks = libvirt_networks(libvirt_client)
+
+ if options[:ip]
+ address = network_address(options[:ip], options[:netmask])
+ available_networks.each do |network|
+ return network[:name] if address == network[:network_address]
+ end
+ end
+
+ # TODO Network default can be missing or named different.
+ return 'default'
+ end
+ end
end
end
end
-
diff --git a/lib/vagrant-libvirt/action/create_networks.rb b/lib/vagrant-libvirt/action/create_networks.rb
new file mode 100644
index 0000000..64b82ce
--- /dev/null
+++ b/lib/vagrant-libvirt/action/create_networks.rb
@@ -0,0 +1,269 @@
+require 'log4r'
+require 'vagrant/util/network_ip'
+require 'vagrant/util/scoped_hash_override'
+require 'ipaddr'
+
+module VagrantPlugins
+ module Libvirt
+ module Action
+ # Prepare all networks needed for domain connections.
+ class CreateNetworks
+ include Vagrant::Util::NetworkIP
+ include Vagrant::Util::ScopedHashOverride
+ include VagrantPlugins::Libvirt::Util::ErbTemplate
+ include VagrantPlugins::Libvirt::Util::LibvirtUtil
+
+ def initialize(app, env)
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::create_networks")
+ @app = app
+
+ @available_networks = []
+ @options = {}
+ @libvirt_client = env[:libvirt_compute].client
+ end
+
+ def call(env)
+
+ # Iterate over networks requested from config. If some network is not
+ # available, create it if possible. Otherwise raise an error.
+ env[:machine].config.vm.networks.each do |type, options|
+
+ # Get a list of all (active and inactive) libvirt networks. This
+ # list is used throughout this class and should be easier to
+ # process than libvirt API calls.
+ @available_networks = libvirt_networks(env[:libvirt_compute].client)
+
+ # Now, we support private networks only. There are two other types
+ # public network and port forwarding, but there are problems with
+ # creating them via libvirt API, so this provider doesn't implement
+ # them.
+ next if type != :private_network
+
+ # Get options for this interface network. Options can be specified
+ # in Vagrantfile in short format (:ip => ...), or provider format
+ # (:libvirt__network_name => ...).
+ @options = scoped_hash_override(options, :libvirt)
+ @options = {
+ :netmask => '255.255.255.0',
+ }.merge(@options)
+
+ # Prepare a hash describing network for this specific interface.
+ @interface_network = {
+ :name => nil,
+ :ip_address => nil,
+ :netmask => @options[:netmask],
+ :network_address => nil,
+ :bridge_name => nil,
+ :created => false,
+ :active => false,
+ :autostart => false,
+ :libvirt_network => nil,
+ }
+
+ if @options[:ip]
+ handle_ip_option(env)
+ elsif @options[:network_name]
+ handle_network_name_option
+ end
+
+ autostart_network if not @interface_network[:autostart]
+ activate_network if not @interface_network[:active]
+ end
+
+ @app.call(env)
+ end
+
+ private
+
+ # Return hash of network for specified name, or nil if not found.
+ def lookup_network_by_name(network_name)
+ @available_networks.each do |network|
+ return network if network[:name] == network_name
+ end
+ nil
+ end
+
+ # Return hash of network for specified bridge, or nil if not found.
+ def lookup_bridge_by_name(bridge_name)
+ @available_networks.each do |network|
+ return network if network[:bridge_name] == bridge_name
+ end
+ nil
+ end
+
+ # Handle only situations, when ip is specified. Variables @options and
+ # @available_networks should be filled before calling this function.
+ def handle_ip_option(env)
+ return if not @options[:ip]
+
+ net_address = network_address(@options[:ip], @options[:netmask])
+ @interface_network[:network_address] = net_address
+
+ # Set IP address of network (actually bridge). It will be used as
+ # gateway address for machines connected to this network.
+ net = IPAddr.new(net_address)
+ @interface_network[:ip_address] = net.to_range.begin.succ
+
+ # Is there an available network matching to configured ip
+ # address?
+ @available_networks.each do |available_network|
+ if available_network[:network_address] == \
+ @interface_network[:network_address]
+ @interface_network = available_network
+ break
+ end
+ end
+
+ if @options[:network_name]
+ if @interface_network[:created]
+ # Just check for mismatch error here - if name and ip from
+ # config match together.
+ if @options[:network_name] != @interface_network[:name]
+ raise Errors::NetworkNameAndAddressMismatch,
+ :ip_address => @options[:ip],
+ :network_name => @options[:network_name]
+ end
+ else
+ # Network is not created, but name is set. We need to check,
+ # whether network name from config doesn't already exist.
+ if lookup_network_by_name @options[:network_name]
+ raise Errors::NetworkNameAndAddressMismatch,
+ :ip_address => @options[:ip],
+ :network_name => @options[:network_name]
+ end
+
+ # Network with 'name' doesn't exist. Set it as name for new
+ # network.
+ @interface_network[:name] = @options[:network_name]
+ end
+ end
+
+ # Do we need to create new network?
+ if not @interface_network[:created]
+
+ # TODO stop after some loops. Don't create infinite loops.
+
+ # Is name for new network set? If not, generate a unique one.
+ count = 0
+ while @interface_network[:name] == nil do
+
+ # Generate a network name.
+ network_name = env[:root_path].basename.to_s.dup
+ network_name << count.to_s
+ count += 1
+
+ # Check if network name is unique.
+ next if lookup_network_by_name(network_name)
+
+ @interface_network[:name] = network_name
+ end
+
+ # Generate a unique name for network bridge.
+ count = 0
+ while @interface_network[:bridge_name] == nil do
+ bridge_name = 'virbr'
+ bridge_name << count.to_s
+ count += 1
+
+ next if lookup_bridge_by_name(bridge_name)
+
+ @interface_network[:bridge_name] = bridge_name
+ end
+
+ # Create a private network.
+ create_private_network(env)
+ end
+ end
+
+ # Handle network_name option, if ip was not specified. Variables
+ # @options and @available_networks should be filled before calling this
+ # function.
+ def handle_network_name_option
+ return if @options[:ip] or not @options[:network_name]
+
+ @interface_network = lookup_network_by_name(@options[:network_name])
+ if not @interface_network
+ raise Errors::NetworkNotAvailableError,
+ :network_name => @options[:network_name]
+ end
+ end
+
+ def create_private_network(env)
+ @network_name = @interface_network[:name]
+ @network_bridge_name = @interface_network[:bridge_name]
+ @network_address = @interface_network[:ip_address]
+ @network_netmask = @interface_network[:netmask]
+
+ if @options[:isolated]
+ @network_forward_mode = false
+ else
+ @network_forward_mode = 'nat'
+
+ if @options[:nat_interface]
+ @network_nat_interface = @options[:nat_interface]
+ end
+ end
+
+ if @options[:dhcp_enabled]
+ # Find out DHCP addresses pool range.
+ network_address = "#{@interface_network[:network_address]}/"
+ network_address << "#{@interface_network[:netmask]}"
+ net = IPAddr.new(network_address)
+
+ # First is address of network, second is gateway. Start the range two
+ # addresses after network address.
+ start_address = net.to_range.begin.succ.succ
+
+ # Stop address must not be broadcast.
+ stop_address = net.to_range.end & IPAddr.new('255.255.255.254')
+
+ @network_dhcp_enabled = true
+ @network_range_start = start_address
+ @network_range_stop = stop_address
+ else
+ @network_dhcp_enabled = false
+ end
+
+ begin
+ @interface_network[:libvirt_network] = \
+ @libvirt_client.define_network_xml(to_xml('private_network'))
+ rescue => e
+ raise Errors::CreateNetworkError,
+ :error_message => e.message
+ end
+
+ created_networks_file = env[:machine].data_dir + 'created_networks'
+
+ message = "Saving information about created network "
+ message << "#{@interface_network[:name]}, "
+ message << "UUID=#{@interface_network[:libvirt_network].uuid} "
+ message << "to file #{created_networks_file}."
+ @logger.info(message)
+
+ File.open(created_networks_file, 'a') do |file|
+ file.puts @interface_network[:libvirt_network].uuid
+ end
+ end
+
+ def autostart_network
+ begin
+ @interface_network[:libvirt_network].autostart = true
+ rescue => e
+ raise Errors::AutostartNetworkError,
+ :error_message => e.message
+ end
+ end
+
+ def activate_network
+ begin
+ @interface_network[:libvirt_network].create
+ rescue => e
+ raise Errors::ActivateNetworkError,
+ :error_message => e.message
+ end
+ end
+
+ end
+ end
+ end
+end
diff --git a/lib/vagrant-libvirt/action/destroy_networks.rb b/lib/vagrant-libvirt/action/destroy_networks.rb
new file mode 100644
index 0000000..853be33
--- /dev/null
+++ b/lib/vagrant-libvirt/action/destroy_networks.rb
@@ -0,0 +1,80 @@
+require 'log4r'
+require 'nokogiri'
+
+module VagrantPlugins
+ module Libvirt
+ module Action
+ # Destroy all networks created for this specific domain. Skip
+ # removing if network has still active connections.
+ class DestroyNetworks
+
+ def initialize(app, env)
+ @logger = Log4r::Logger.new("vagrant_libvirt::action::destroy_networks")
+ @app = app
+ end
+
+ def call(env)
+ # If there were some networks created for this machine, in machines
+ # data directory, created_networks file holds UUIDs of each network.
+ created_networks_file = env[:machine].data_dir + 'created_networks'
+
+ # If created_networks file doesn't exist, there are no networks we
+ # need to remove.
+ return @app.call(env) if not File.exist?(created_networks_file)
+
+ # Iterate over each created network UUID and try to remove it.
+ created_networks = []
+ file = File.open(created_networks_file, 'r')
+ file.readlines.each do |network_uuid|
+ begin
+ libvirt_network = env[:libvirt_compute].client.lookup_network_by_uuid(
+ network_uuid)
+ rescue
+ next
+ end
+
+ # Maybe network doesn't exist anymore.
+ next if not libvirt_network
+
+ # Skip removing if network has still active connections.
+ xml = Nokogiri::XML(libvirt_network.xml_desc)
+ connections = xml.xpath('/network/@connections').first
+ if connections != nil
+ created_networks << network_uuid
+ next
+ end
+
+ # Shutdown network first.
+ begin
+ libvirt_network.destroy
+ rescue => e
+ end
+
+ # Undefine network.
+ begin
+ libvirt_network.undefine
+ rescue => e
+ raise Error::DestroyNetworkError,
+ :network_name => libvirt_network.name,
+ :error_message => e.message
+ end
+ end
+ file.close
+
+ # Update status of created networks after removing some/all of them.
+ if created_networks.length > 0
+ File.open(created_networks_file, 'w') do |file|
+ created_networks.each do |network_uuid|
+ file.puts network_uuid
+ end
+ end
+ else
+ File.delete(created_networks_file)
+ end
+
+ @app.call(env)
+ end
+ end
+ end
+ end
+end
diff --git a/lib/vagrant-libvirt/errors.rb b/lib/vagrant-libvirt/errors.rb
index 288874d..1aafa85 100644
--- a/lib/vagrant-libvirt/errors.rb
+++ b/lib/vagrant-libvirt/errors.rb
@@ -74,6 +74,30 @@ module VagrantPlugins
error_key(:interface_slot_not_available)
end
+ class NetworkNameAndAddressMismatch < VagrantLibvirtError
+ error_key(:network_name_and_address_mismatch)
+ end
+
+ class CreateNetworkError < VagrantLibvirtError
+ error_key(:create_network_error)
+ end
+
+ class DestroyNetworkError < VagrantLibvirtError
+ error_key(:destroy_network_error)
+ end
+
+ class NetworkNotAvailableError < VagrantLibvirtError
+ error_key(:network_not_available_error)
+ end
+
+ class AutostartNetworkError < VagrantLibvirtError
+ error_key(:autostart_network_error)
+ end
+
+ class ActivateNetworkError < VagrantLibvirtError
+ error_key(:activate_network_error)
+ end
+
class RsyncError < VagrantLibvirtError
error_key(:rsync_error)
end
diff --git a/lib/vagrant-libvirt/templates/private_network.xml.erb b/lib/vagrant-libvirt/templates/private_network.xml.erb
new file mode 100644
index 0000000..8b96df7
--- /dev/null
+++ b/lib/vagrant-libvirt/templates/private_network.xml.erb
@@ -0,0 +1,21 @@
+<network ipv6='yes'>
+ <name><%= @network_name %></name>
+ <bridge name="<%= @network_bridge_name %>" />
+
+ <% if @network_forward_mode != false %>
+ <% if @network_nat_interface %>
+ <forward mode="<%= @network_forward_mode %>" dev="<%= @network_nat_interface %>" />
+ <% else %>
+ <forward mode="<%= @network_forward_mode %>" />
+ <% end %>
+ <% end %>
+
+ <ip address="<%= @network_address %>" netmask="<%= @network_netmask %>">
+ <% if @network_dhcp_enabled %>
+ <dhcp>
+ <range start="<%= @network_range_start %>" end="<%= @network_range_stop %>" />
+ </dhcp>
+ <% end %>
+ </ip>
+
+</network>
diff --git a/lib/vagrant-libvirt/util.rb b/lib/vagrant-libvirt/util.rb
index 870fea4..3f4cc47 100644
--- a/lib/vagrant-libvirt/util.rb
+++ b/lib/vagrant-libvirt/util.rb
@@ -4,6 +4,7 @@ module VagrantPlugins
autoload :ErbTemplate, 'vagrant-libvirt/util/erb_template'
autoload :Collection, 'vagrant-libvirt/util/collection'
autoload :Timer, 'vagrant-libvirt/util/timer'
+ autoload :LibvirtUtil, 'vagrant-libvirt/util/libvirt_util'
end
end
end
diff --git a/lib/vagrant-libvirt/util/libvirt_util.rb b/lib/vagrant-libvirt/util/libvirt_util.rb
new file mode 100644
index 0000000..907b4e3
--- /dev/null
+++ b/lib/vagrant-libvirt/util/libvirt_util.rb
@@ -0,0 +1,57 @@
+require 'nokogiri'
+require 'vagrant/util/network_ip'
+
+module VagrantPlugins
+ module Libvirt
+ module Util
+ module LibvirtUtil
+ include Vagrant::Util::NetworkIP
+
+ # Return a list of all (active and inactive) libvirt networks as a list
+ # of hashes with their name, network address and status (active or not).
+ def libvirt_networks(libvirt_client)
+ libvirt_networks = []
+
+ active = libvirt_client.list_networks
+ inactive = libvirt_client.list_defined_networks
+
+ # Iterate over all (active and inactive) networks.
+ active.concat(inactive).each do |network_name|
+ libvirt_network = libvirt_client.lookup_network_by_name(
+ network_name)
+
+ # Parse ip address and netmask from the network xml description.
+ xml = Nokogiri::XML(libvirt_network.xml_desc)
+ ip = xml.xpath('/network/ip/@address').first
+ ip = ip.value if ip
+ netmask = xml.xpath('/network/ip/@netmask').first
+ netmask = netmask.value if netmask
+
+ # Calculate network address of network from ip address and
+ # netmask.
+ if ip and netmask
+ network_address = network_address(ip, netmask)
+ else
+ network_address = nil
+ end
+
+ libvirt_networks << {
+ :name => network_name,
+ :ip_address => ip,
+ :netmask => netmask,
+ :network_address => network_address,
+ :bridge_name => libvirt_network.bridge_name,
+ :created => true,
+ :active => libvirt_network.active?,
+ :autostart => libvirt_network.autostart?,
+ :libvirt_network => libvirt_network,
+ }
+ end
+
+ libvirt_networks
+ end
+
+ end
+ end
+ end
+end
diff --git a/locales/en.yml b/locales/en.yml
index dba56ad..e19438c 100644
--- a/locales/en.yml
+++ b/locales/en.yml
@@ -101,6 +101,20 @@ en:
Error while attaching new device to domain. %{error_message}
no_ip_address_error: |-
No IP address found.
+ network_name_and_address_mismatch: |-
+ Address %{ip_address} does not match with network name %{network_name}.
+ Please fix your configuration and run vagrant again.
+ create_network_error: |-
+ Error occured while creating new network: %{error_message}.
+ network_not_available_error: |-
+ Network %{network_name} is not available. Specify available network
+ name, or an ip address if you want to create a new network.
+ activate_network_error: |-
+ Error while activating network: %{error_message}.
+ autostart_network_error: |-
+ Error while setting up autostart on network: %{error_message}.
+ destroy_network_error: |-
+ Error while removing network %{network_name}. %{error_message}.
states:
short_paused: |-
--
Alioth's /usr/local/bin/git-commit-notice on /srv/git.debian.org/git/pkg-ruby-extras/vagrant-libvirt.git
More information about the Pkg-ruby-extras-commits
mailing list