class VagrantPlugins::ProviderLibvirt::Action::CreateNetworks

Prepare all networks needed for domain connections.

Public Class Methods

new(app, env) click to toggle source
# File lib/vagrant-libvirt/action/create_networks.rb, line 19
def initialize(app, env)
  mess = 'vagrant_libvirt::action::create_networks'
  @logger = Log4r::Logger.new(mess)
  @app = app

  @available_networks = []
  @options = {}
  @libvirt_client = env[:libvirt_compute].client
end

Public Instance Methods

call(env) click to toggle source
# File lib/vagrant-libvirt/action/create_networks.rb, line 29
def call(env)
  # only one vm at a time should try to set up networks
  # otherwise they'll have inconsitent views of current state
  # and conduct redundant operations that cause errors
  @@lock.synchronize do
    # Iterate over networks If some network is not
    # available, create it if possible. Otherwise raise an error.
    configured_networks(env, @logger).each do |options|
      # Only need to create private networks
      next if options[:iface_type] != :private_network
      @logger.debug "Searching for network with options #{options}"

      # should fix other methods so this doesn't have to be instance var
      @options = 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)

      # 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)
      # in vagrant 1.2.3 and later it is not possible to take this branch
      # because cannot have name without ip
      # https://github.com/mitchellh/vagrant/commit/cf2f6da4dbcb4f57c9cdb3b94dcd0bba62c5f5fd
      elsif @options[:network_name]
        handle_network_name_option
      end

      autostart_network if !@interface_network[:autostart]
      activate_network if !@interface_network[:active]
    end
  end

  @app.call(env)
end

Private Instance Methods

activate_network() click to toggle source
# File lib/vagrant-libvirt/action/create_networks.rb, line 286
def activate_network
  begin
    @interface_network[:libvirt_network].create
  rescue => e
    raise Errors::ActivateNetworkError, error_message: e.message
  end
end
autostart_network() click to toggle source
# File lib/vagrant-libvirt/action/create_networks.rb, line 278
def autostart_network
  begin
    @interface_network[:libvirt_network].autostart = true
  rescue => e
    raise Errors::AutostartNetworkError, error_message: e.message
  end
end
create_private_network(env) click to toggle source
# File lib/vagrant-libvirt/action/create_networks.rb, line 223
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]

  @network_forward_mode = @options[:forward_mode]
  if @options[:forward_device]
    @network_forward_device = @options[:forward_device]
  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 (by default).
    # So start the range two addresses after network address by default.
    # TODO: Detect if this IP is not set on the interface.
    start_address = @options[:dhcp_start] || net.to_range.begin.succ

    # Default to last possible address. (Stop address must not be broadcast address.)
    stop_address = @options[:dhcp_stop] || (net.to_range.end & IPAddr.new('255.255.255.254'))

    @network_dhcp_enabled = true
    @network_dhcp_bootp_file = @options[:dhcp_bootp_file]
    @network_dhcp_bootp_server = @options[:dhcp_bootp_server]
    @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'))
    @logger.debug "created 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
handle_ip_option(env) click to toggle source

Handle only situations, when ip is specified. Variables @options and @available_networks should be filled before calling this function.

# File lib/vagrant-libvirt/action/create_networks.rb, line 116
def handle_ip_option(env)
  return if !@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)
  # Default to first address (after network name)
  @interface_network[:ip_address] = @options[:host_ip].nil? ? net.to_range.begin.succ : IPAddr.new(@options[:host_ip])

  # 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
      @logger.debug "found existing network by ip, values are"
      @logger.debug @interface_network
      break
    end
  end

  if @interface_network[:created]
    verify_dhcp
  end

  if @options[:network_name]
    @logger.debug "Checking that network name does not clash with ip"
    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 !@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?
      @logger.debug "generating name for network"

      # 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?
      @logger.debug "generating name for bridge"
      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() click to toggle source

Handle network_name option, if ip was not specified. Variables @options and @available_networks should be filled before calling this function.

# File lib/vagrant-libvirt/action/create_networks.rb, line 211
def handle_network_name_option
  return if @options[:ip] || !@options[:network_name]

  @interface_network = lookup_network_by_name(@options[:network_name])
  if !@interface_network
    raise Errors::NetworkNotAvailableError,
          network_name: @options[:network_name]
  else
    verify_dhcp
  end
end
lookup_bridge_by_name(bridge_name) click to toggle source

Return hash of network for specified bridge, or nil if not found.

# File lib/vagrant-libvirt/action/create_networks.rb, line 92
def lookup_bridge_by_name(bridge_name)
  @logger.debug "looking up bridge named #{bridge_name}"
  @available_networks.each do |network|
    return network if network[:bridge_name] == bridge_name
  end
  nil
end
lookup_network_by_name(network_name) click to toggle source

Return hash of network for specified name, or nil if not found.

# File lib/vagrant-libvirt/action/create_networks.rb, line 83
def lookup_network_by_name(network_name)
  @logger.debug "looking up network named #{network_name}"
  @available_networks.each do |network|
    return network if network[:name] == network_name
  end
  nil
end
verify_dhcp() click to toggle source

Throw an error if dhcp setting for an existing network does not match what was configured in the vagrantfile since we always enable dhcp for the management network this ensures we wont start a vm vagrant cant reach Allow the situation where DHCP is not requested (:libvirt__dhcp_enabled == false) but where it is enabled on the virtual network

# File lib/vagrant-libvirt/action/create_networks.rb, line 106
def verify_dhcp
  if @interface_network[:dhcp_enabled] == true && @options[:dhcp_enabled] == false
    raise Errors::DHCPMismatch,
          network_name: @interface_network[:name],
          requested: @options[:dhcp_enabled] ? 'enabled' : 'disabled'
  end
end