Merge pull request #1343 from abbbi/1341_poc

Retrieve IP Address of management interface using qemu-guest-agent commands #1341
This commit is contained in:
Michael Ablassmeier 2021-09-24 11:12:17 +02:00 committed by GitHub
commit e9213b6caa
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
7 changed files with 104 additions and 2 deletions

View File

@ -933,6 +933,23 @@ DHCP server. dnsmasq writes lease information in the `/var/lib/libvirt/dnsmasq`
directory. Vagrant-libvirt looks for the MAC address in this file and extracts
the corresponding IP address.
It is also possible to use the Qemu Agent to extract the management interface
configuration from the booted virtual machine. This is helpful in libvirt
environments where no local dnsmasq is used for automatic address assigment,
but external dhcp services via bridged libvirt networks.
Prerequisite is to enable the qemu agent channel via ([Libvirt communication
channels](#libvirt-communication-channels)) and the virtual machine image must
have the agent pre-installed before deploy. The agent will start automatically
if it detects an attached channel during boot.
* `qemu_use_agent` - false by default, if set to true, attempt to extract configured
ip address via qemu agent.
To use the management network interface with an external dhcp service you need
to setup a bridged host network manually and define it via
`management_network_name` in your Vagrantfile.
## Additional Disks
You can create and attach additional disks to a VM via `libvirt.storage :file`.

View File

@ -162,7 +162,7 @@ module VagrantPlugins
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]
if @options[:network_name] != @interface_network[:name] and @qemu_use_agent = false
raise Errors::NetworkNameAndAddressMismatch,
ip_address: @options[:ip],
network_name: @options[:network_name]

View File

@ -193,6 +193,9 @@ module VagrantPlugins
# Use QEMU session instead of system
attr_accessor :qemu_use_session
# Use QEMU Agent to get ip address
attr_accessor :qemu_use_agent
def initialize
@uri = UNSET_VALUE
@driver = UNSET_VALUE
@ -332,6 +335,9 @@ module VagrantPlugins
@qemu_env = UNSET_VALUE
@qemu_use_session = UNSET_VALUE
# Use Qemu agent to get ip address
@qemu_use_agent = UNSET_VALUE
end
def boot(device)
@ -936,6 +942,8 @@ module VagrantPlugins
# Additional QEMU commandline environment variables
@qemu_env = {} if @qemu_env == UNSET_VALUE
@qemu_use_agent = true if @qemu_use_agent != UNSET_VALUE
end
def validate(machine)
@ -949,6 +957,17 @@ module VagrantPlugins
end
end
if @qemu_use_agent == true
# if qemu agent is used to optain domain ip configuration, at least
# one qemu channel has to be configured. As there are various options,
# error out and leave configuration to the user
unless machine.provider_config.channels.any? { |channel| channel[:target_name].start_with?("org.qemu.guest_agent") }
errors << "qemu agent option enabled, but no qemu agent channel configured: please add at least one qemu agent channel to vagrant config"
end
end
machine.provider_config.disks.each do |disk|
if disk[:path] && (disk[:path][0] == '/')
errors << "absolute volume paths like '#{disk[:path]}' not yet supported"

View File

@ -3,6 +3,7 @@
require 'fog/libvirt'
require 'libvirt'
require 'log4r'
require 'json'
module VagrantPlugins
module ProviderLibvirt
@ -58,7 +59,7 @@ module VagrantPlugins
config = @machine.provider_config
@@system_connection = Libvirt::open_read_only(config.system_uri)
@@system_connection = Libvirt::open(config.system_uri)
@@system_connection
end
@ -99,6 +100,12 @@ module VagrantPlugins
return get_ipaddress_from_system domain.mac
end
# attempt to get ip address from qemu agent
if @machine.provider_config.qemu_use_agent == true
@logger.info('Get IP via qemu agent')
return get_ipaddress_from_qemu_agent(domain, machine.id)
end
# Get IP address from dhcp leases table
begin
ip_address = get_ipaddress_from_domain(domain)
@ -146,6 +153,40 @@ module VagrantPlugins
ip_address
end
def get_ipaddress_from_qemu_agent(domain, machine_id)
ip_address = nil
addresses = nil
dom = system_connection.lookup_domain_by_uuid(machine_id)
begin
response = dom.qemu_agent_command('{"execute":"guest-network-get-interfaces"}', timeout=10)
@logger.debug("Got Response from qemu agent")
@logger.debug(response)
addresses = JSON.parse(response)
rescue => e
@logger.debug("Unable to receive IP via qemu agent: [%s]" % e.message)
end
unless addresses.nil?
addresses["return"].each{ |interface|
if domain.mac == interface["hardware-address"]
@logger.debug("Found mathing interface: [%s]" % interface["name"])
if interface.has_key?("ip-addresses")
interface["ip-addresses"].each{ |ip|
# returning ipv6 addresses might break windows guests because
# winrm cant handle connection, winrm fails with "invalid uri"
if ip["ip-address-type"] == "ipv4"
ip_address = ip["ip-address"]
@logger.debug("Return IP: [%s]" % ip_address)
break
end
}
end
end
}
end
ip_address
end
def get_ipaddress_from_domain(domain)
ip_address = nil
domain.wait_for(2) do

View File

@ -20,6 +20,7 @@ module VagrantPlugins
def configured_networks(env, logger)
qemu_use_session = env[:machine].provider_config.qemu_use_session
qemu_use_agent = env[:machine].provider_config.qemu_use_agent
management_network_device = env[:machine].provider_config.management_network_device
management_network_name = env[:machine].provider_config.management_network_name
management_network_address = env[:machine].provider_config.management_network_address

13
tests/qemu_agent/Vagrantfile vendored Normal file
View File

@ -0,0 +1,13 @@
# -*- mode: ruby -*-
# vi: set ft=ruby :
#
# frozen_string_literal: true
Vagrant.configure("2") do |config|
config.vm.box = "generic/debian10"
config.vm.synced_folder ".", "/vagrant", disabled: true
config.vm.provider :libvirt do |libvirt|
libvirt.channel :type => 'unix', :target_name => 'org.qemu.guest_agent.0', :target_type => 'virtio'
libvirt.qemu_use_agent = true
end
end

View File

@ -119,6 +119,17 @@ cleanup() {
cleanup
}
@test "bring up and use qemu agent for connectivity" {
export VAGRANT_CWD=tests/qemu_agent
cleanup
run ${VAGRANT_CMD} up ${VAGRANT_OPT}
echo "${output}"
echo "status = ${status}"
[ "$status" -eq 0 ]
echo "${output}"
cleanup
}
@test "ip is reachable with private network" {
export VAGRANT_CWD=tests/private_network
cleanup