add ability for test pxe in private network

This commit is contained in:
dima 2016-12-06 19:45:27 +01:00
parent 981ca18b5e
commit b92b02b6d6
3 changed files with 94 additions and 31 deletions

View File

@ -83,14 +83,14 @@ kvm type virtual machines with `virsh` or `virt-manager`.
Next, you must have [Vagrant Next, you must have [Vagrant
installed](http://docs.vagrantup.com/v2/installation/index.html). 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 *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. 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: 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 ```shell
apt-get build-dep vagrant ruby-libvirt apt-get build-dep vagrant ruby-libvirt
apt-get install qemu libvirt-bin ebtables dnsmasq 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 [<bus>] Device [<device>]: ID [<vendor>:[<product>]`. The `version` can be extracted from the detailed output of the device using `lsusb -D /dev/usb/[<bus>]/[<device>]`. For example: You can extract that information from output of `lsusb` command. Every line contains the information in format `Bus [<bus>] Device [<device>]: ID [<vendor>:[<product>]`. The `version` can be extracted from the detailed output of the device using `lsusb -D /dev/usb/[<bus>]/[<device>]`. For example:
```shell ```shell
# get bcdDevice from # get bcdDevice from
$: lsusb $: lsusb
Bus 001 Device 009: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader Bus 001 Device 009: ID 08e6:3437 Gemalto (was Gemplus) GemPC Twin SmartCard Reader
@ -903,6 +903,25 @@ Vagrant.configure("2") do |config|
end 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 ## SSH Access To VM
vagrant-libvirt supports vagrant's [standard ssh vagrant-libvirt supports vagrant's [standard ssh

View File

@ -227,6 +227,7 @@ module VagrantPlugins
# Do we need to create new network? # Do we need to create new network?
unless @interface_network[:created] unless @interface_network[:created]
@interface_network[:name] = @options[:network_name] @interface_network[:name] = @options[:network_name]
@interface_network[:ip_address] ||= @options[:host_ip]
# Generate a unique name for network bridge. # Generate a unique name for network bridge.
@interface_network[:bridge_name] = generate_bridge_name @interface_network[:bridge_name] = generate_bridge_name

View File

@ -1,64 +1,107 @@
require "log4r" require 'log4r'
require 'nokogiri' require 'nokogiri'
module VagrantPlugins module VagrantPlugins
module ProviderLibvirt module ProviderLibvirt
module Action module Action
# boot order useful for pxe in discovery workflow
class SetBootOrder class SetBootOrder
def initialize(app, env) def initialize(app, env)
@app = app @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 config = env[:machine].provider_config
@boot_order = config.boot_order @boot_order = config.boot_order
end end
def call(env) def call(env)
# Get domain first # Get domain first
begin begin
domain = env[:machine].provider.driver.connection.client.lookup_domain_by_uuid( domain = env[:machine].provider
env[:machine].id.to_s) .driver
.connection
.client
.lookup_domain_by_uuid(
env[:machine].id.to_s
)
rescue => e rescue => e
raise Errors::NoDomainError, raise Errors::NoDomainError,
:error_message => e.message error_message: e.message
end 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 @boot_order.count >= 1
# If a domain is initially defined with no box or disk or with an explicit boot order, libvirt adds <boot dev="foo"> # If a domain is initially defined with no box or disk or
# This conflicts with an explicit boot_order configuration, so we need to remove it from the domain xml and feed it back. # with an explicit boot order, libvirt adds <boot dev="foo">
# 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. # 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 = Nokogiri::XML(domain.xml_desc)
xml.search("/domain/os/boot").each do |node| xml.search('/domain/os/boot').each(&:remove)
node.remove
end
# Parse the XML and find each defined drive and network interfacee # Parse the XML and find each defined drive and network interfacee
hd = xml.search("/domain/devices/disk[@device='disk']") hd = xml.search("/domain/devices/disk[@device='disk']")
cdrom = xml.search("/domain/devices/disk[@device='cdrom']") 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 # Generate an array per device group and a flattened
devices = {"hd" => hd, "cdrom" => cdrom, "network" => network} # array from all of those
final_boot_order = @boot_order.flat_map {|category| devices[category] } 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| final_boot_order.each_with_index do |node, index|
boot = "<boot order='#{index+1}'/>" boot = "<boot order='#{index + 1}'/>"
node.add_child(boot) node.add_child(boot)
if node.name == 'disk' logger_msg(node, index)
@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
end end
# Finally redefine the domain XML through libvirt to apply the boot ordering # Finally redefine the domain XML through libvirt
env[:machine].provider.driver.connection.client.define_domain_xml(xml.to_s) # to apply the boot ordering
env[:machine].provider
.driver
.connection
.client
.define_domain_xml(xml.to_s)
end end
@app.call(env) @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 end
end end