From b92b02b6d64547ad5c274040a892e05122199d36 Mon Sep 17 00:00:00 2001 From: dima Date: Tue, 6 Dec 2016 19:45:27 +0100 Subject: [PATCH] add ability for test pxe in private network --- README.md | 27 +++++- lib/vagrant-libvirt/action/create_networks.rb | 1 + lib/vagrant-libvirt/action/set_boot_order.rb | 97 +++++++++++++------ 3 files changed, 94 insertions(+), 31 deletions(-) diff --git a/README.md b/README.md index efd3c15..1c74962 100644 --- a/README.md +++ b/README.md @@ -83,14 +83,14 @@ kvm type virtual machines with `virsh` or `virt-manager`. Next, you must have [Vagrant installed](http://docs.vagrantup.com/v2/installation/index.html). -Vagrant-libvirt supports Vagrant 1.5, 1.6, 1.7 and 1.8. +Vagrant-libvirt supports Vagrant 1.5, 1.6, 1.7 and 1.8. *We only test with the upstream version!* If you decide to install your distros version and you run into problems, as a first step you should switch to upstream. -Now you need to make sure your have all the build dependencies installed for +Now you need to make sure your have all the build dependencies installed for vagrant-libvirt. This depends on your distro. An overview: -* Ubuntu 12.04/14.04/16.04, Debian: +* Ubuntu 12.04/14.04/16.04, Debian: ```shell apt-get build-dep vagrant ruby-libvirt apt-get install qemu libvirt-bin ebtables dnsmasq @@ -780,7 +780,7 @@ You can define filter for redirected devices. These filters can be positiv or ne You can extract that information from output of `lsusb` command. Every line contains the information in format `Bus [] Device []: ID [:[]`. The `version` can be extracted from the detailed output of the device using `lsusb -D /dev/usb/[]/[]`. For example: ```shell -# get bcdDevice from +# get bcdDevice from $: lsusb Bus 001 Device 009: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader @@ -903,6 +903,25 @@ Vagrant.configure("2") do |config| end ``` +Example for vm with 2 networks and only 1 is bootable and has dhcp server in this subnet, for example foreman with dhcp server +Name of network "foreman_managed" is key for define boot order +```ruby + config.vm.define :pxeclient do |pxeclient| + pxeclient.vm.network :private_network,ip: '10.0.0.5', + libvirt__network_name: "foreman_managed", + libvirt__dhcp_enabled: false, + libvirt__host_ip: '10.0.0.1' + + pxeclient.vm.provider :libvirt do |domain| + domain.memory = 1000 + boot_network = {'network' => 'foreman_managed'} + domain.storage :file, :size => '100G', :type => 'qcow2' + domain.boot boot_network + domain.boot 'hd' + end + end +``` + ## SSH Access To VM vagrant-libvirt supports vagrant's [standard ssh diff --git a/lib/vagrant-libvirt/action/create_networks.rb b/lib/vagrant-libvirt/action/create_networks.rb index ab21011..03d2b46 100644 --- a/lib/vagrant-libvirt/action/create_networks.rb +++ b/lib/vagrant-libvirt/action/create_networks.rb @@ -227,6 +227,7 @@ module VagrantPlugins # Do we need to create new network? unless @interface_network[:created] @interface_network[:name] = @options[:network_name] + @interface_network[:ip_address] ||= @options[:host_ip] # Generate a unique name for network bridge. @interface_network[:bridge_name] = generate_bridge_name diff --git a/lib/vagrant-libvirt/action/set_boot_order.rb b/lib/vagrant-libvirt/action/set_boot_order.rb index e3c74f1..49893ea 100644 --- a/lib/vagrant-libvirt/action/set_boot_order.rb +++ b/lib/vagrant-libvirt/action/set_boot_order.rb @@ -1,64 +1,107 @@ -require "log4r" +require 'log4r' require 'nokogiri' module VagrantPlugins module ProviderLibvirt module Action + # boot order useful for pxe in discovery workflow class SetBootOrder def initialize(app, env) @app = app - @logger = Log4r::Logger.new("vagrant_libvirt::action::set_boot_order") + @logger = Log4r::Logger.new('vagrant_libvirt::action::set_boot_order') config = env[:machine].provider_config @boot_order = config.boot_order end def call(env) - # Get domain first + # Get domain first begin - domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid( - env[:machine].id.to_s) + domain = env[:machine].provider + .driver + .connection + .client + .lookup_domain_by_uuid( + env[:machine].id.to_s + ) rescue => e raise Errors::NoDomainError, - :error_message => e.message + error_message: e.message end - # Only execute specific boot ordering if this is defined in the Vagrant file + # Only execute specific boot ordering if this is defined + # in the Vagrant file if @boot_order.count >= 1 - - # If a domain is initially defined with no box or disk or with an explicit boot order, libvirt adds - # This conflicts with an explicit boot_order configuration, so we need to remove it from the domain xml and feed it back. - # Also see https://bugzilla.redhat.com/show_bug.cgi?id=1248514 as to why we have to do this after all devices have been defined. + + # If a domain is initially defined with no box or disk or + # with an explicit boot order, libvirt adds + # This conflicts with an explicit boot_order configuration, + # so we need to remove it from the domain xml and feed it back. + # Also see https://bugzilla.redhat.com/show_bug.cgi?id=1248514 + # as to why we have to do this after all devices have been defined. xml = Nokogiri::XML(domain.xml_desc) - xml.search("/domain/os/boot").each do |node| - node.remove - end + xml.search('/domain/os/boot').each(&:remove) # Parse the XML and find each defined drive and network interfacee hd = xml.search("/domain/devices/disk[@device='disk']") cdrom = xml.search("/domain/devices/disk[@device='cdrom']") - network = xml.search("/domain/devices/interface[@type='network' or @type='udp']") + # implemented only for 1 network + nets = @boot_order.flat_map do |x| + x.class == Hash ? x : nil + end.compact + raise 'Defined only for 1 network for boot' if nets.size > 1 + network = search_network(nets, xml) - # Generate an array per device group and a flattened array from all of those - devices = {"hd" => hd, "cdrom" => cdrom, "network" => network} - final_boot_order = @boot_order.flat_map {|category| devices[category] } + # Generate an array per device group and a flattened + # array from all of those + devices = { 'hd' => hd, + 'cdrom' => cdrom, + 'network' => network } - # Loop over the entire defined boot order array and create boot order entries in the domain XML + final_boot_order = final_boot_order(@boot_order, devices) + # Loop over the entire defined boot order array and + # create boot order entries in the domain XML final_boot_order.each_with_index do |node, index| - boot = "" + boot = "" node.add_child(boot) - if node.name == 'disk' - @logger.debug "Setting #{node['device']} to boot index #{index+1}" - elsif node.name == 'interface' - @logger.debug "Setting #{node.name} to boot index #{index+1}" - end + logger_msg(node, index) end - # Finally redefine the domain XML through libvirt to apply the boot ordering - env[:machine].provider.driver.connection.client.define_domain_xml(xml.to_s) + # Finally redefine the domain XML through libvirt + # to apply the boot ordering + env[:machine].provider + .driver + .connection + .client + .define_domain_xml(xml.to_s) end @app.call(env) + end + def final_boot_order(boot_order, devices) + boot_order.flat_map do |category| + devices[category.class == Hash ? category.keys.first : category] + end + end + + def search_network(nets, xml) + str = '/domain/devices/interface' + str += "[(@type='network' or @type='udp')" + unless nets.empty? + str += " and source[@network='#{nets.first['network']}']" + end + str += ']' + @logger.debug(str) + xml.search(str) + end + + def logger_msg(node, index) + name = if node.name == 'disk' + node['device'] + elsif node.name == 'interface' + node.name + end + @logger.debug "Setting #{name} to boot index #{index + 1}" end end end