Project

General

Profile

« Previous | Next » 

Revision 6798dc58

Added by Lukas Zapletal about 8 years ago

Fixes #13761 - libvirt provider with native bindings

View differences:

.gitignore
.bundle
pkg/
test/tmp/*
/tmp/
bundler.d/libvirt.rb
group :libvirt do
gem 'ruby-libvirt'
end
config/settings.d/dhcp.yml.example
# valid providers:
# - dhcp_isc (ISC dhcp server)
# - dhcp_native_ms (Microsoft native implementation)
# - dhcp_virsh (simple implementation for libvirt)
# - dhcp_libvirt
#:use_provider: dhcp_isc
#:server: 127.0.0.1
# subnets restricts the subnets queried to a subset, to reduce the query time.
config/settings.d/dhcp_libvirt.yml.example
---
# Libvirt DHCP provider configuration
# Libvirt network. Only one network is supported.
#:network: default
# Libvirt connection. Make sure proxy effective user have permission to connect.
#:url: qemu:///system
config/settings.d/dhcp_virsh.yml.example
---
#
# Configuration file for libvirtd-specific 'virsh' dhcp provider
#
# There's currently no configuration options for this provider.
# Virsh network name is a global parameter that can be set
# in the main settings.yml file in 'virsh_network' parameter.
#
config/settings.d/dns.yml.example
---
# Can be true, false, or http/https to enable just one of the protocols
:enabled: false
:enabled: false
# Valid providers:
# dns_nsupdate
# dns_nsupdate_gss (for GSS-TSIG support)
# dns_virsh
# dns_libvirt
# dns_dnscmd
#:use_provider: dns_nsupdate
# use this setting if you want to override default TTL setting (86400)
config/settings.d/dns_libvirt.yml.example
---
# Libvirt DNS provider configuration
# Libvirt network. Only one network is supported.
#:network: default
# Libvirt connection. Make sure proxy effective user have permission to connect.
#:url: qemu:///system
config/settings.d/dns_virsh.yml.example
---
#
# Configuration file for libvirtd-specific 'virsh' dns provider
#
# There's currently no configuration options for this provider.
# Virsh network name is a global parameter that can be set
# in the main settings.yml file in 'virsh_network' parameter.
#
config/settings.yml.example
# default values for https_port is 8443
#:https_port: 8443
# Shared options for virsh DNS/DHCP provider
:virsh_network: default
# Log configuration
# Uncomment and modify if you want to change the location of the log file or use STDOUT or SYSLOG values
#:log_file: /var/log/foreman-proxy/proxy.log
extra/migrate_settings.rb
persist_migrations_state(executed_migrations, result_dir_path)
end
def execute_migrations(migrations)
def execute_migrations(migrations, print_name_stdout = true)
migrations.each do |migration|
m = migration.new(working_dir_path)
puts m.migration_name.to_s
puts(m.migration_name.to_s) if print_name_stdout
m.create_migration_dirs
if migration == migrations.first
extra/migrations/20160411000000_migrate_libvirt_settings.rb
require 'fileutils'
require 'yaml'
class MigrateVirshToLibvirtConfig < ::Proxy::Migration
def migrate
input_yaml = YAML.load_file(path(src_dir, "settings.yml"))
copy_original_configuration_except("settings.yml", path("settings.d", "dhcp_virsh.yml"), path("settings.d", "dns_virsh.yml"))
write_yaml(path(dst_dir, "settings.d", "dhcp_libvirt.yml"), transform_dhcp_yaml(input_yaml))
write_yaml(path(dst_dir, "settings.d", "dns_libvirt.yml"), transform_dns_yaml(input_yaml))
write_yaml(path(dst_dir, "settings.yml"), transform_settings_yaml(input_yaml))
end
def transform_settings_yaml(yaml)
yaml.delete(:virsh_network)
yaml
end
def transform_dns_yaml(yaml)
network = yaml[:virsh_network] || 'default'
{ :network => network }
end
def transform_dhcp_yaml(yaml)
network = yaml[:virsh_network] || 'default'
{ :network => network }
end
def write_yaml(filepath, yaml)
File.open(filepath, 'w') do |f|
f.write(yaml.to_yaml)
end
end
end
lib/proxy/settings/global.rb
:daemon_pid => "/var/run/foreman-proxy/foreman-proxy.pid",
:forward_verify => true,
:bind_host => "*",
:virsh_network => 'default',
:log_buffer => 2000,
:log_buffer_errors => 1000,
:ssl_disabled_ciphers => []
lib/proxy/virsh.rb
module Proxy::Virsh
include Proxy::Log
include Proxy::Util
class Error < RuntimeError; end
attr_reader :network
def dump_xml
@xml_dump ||= virsh('net-dumpxml', network)
end
def virsh *params
unless sudo_cmd = which("sudo", "/usr/bin", "/usr/sbin")
raise Error, "virsh provider error: sudo binary was not found"
end
unless virsh_cmd = which("virsh", "/usr/bin", "/usr/sbin")
raise Error, "virsh provider error: virsh binary was not found"
end
logger.debug command = ([sudo_cmd, virsh_cmd] + params + ['2>&1']).join(' ')
stdout = `#{command}`
if $? == 0
return stdout
else
raise Error, "virsh provider error: virsh call failed (#{$?}) - #{stdout}"
end
end
end
lib/smart_proxy_main.rb
require 'dns_nsupdate/dns_nsupdate'
require 'dns_nsupdate/dns_nsupdate_gss'
require 'dns_dnscmd/dns_dnscmd'
require 'dns_virsh/dns_virsh'
require 'dns_libvirt/dns_libvirt'
require 'templates/templates'
require 'tftp/tftp'
require 'dhcp/dhcp'
require 'dhcp_isc/dhcp_isc'
require 'dhcp_native_ms/dhcp_native_ms'
require 'dhcp_virsh/dhcp_virsh'
require 'dhcp_libvirt/dhcp_libvirt'
require 'puppetca/puppetca'
require 'puppet_proxy/puppet'
require 'bmc/bmc'
modules/dhcp_libvirt/dependencies.rb
require 'dhcp_common/dependency_injection/dependencies'
class Proxy::DHCP::DependencyInjection::Dependencies
dependency :dhcp_provider, Proxy::DHCP::Libvirt::Provider
end
modules/dhcp_libvirt/dhcp_libvirt.rb
require 'dhcp_common/dhcp_common'
require 'dhcp_libvirt/dhcp_libvirt_plugin'
modules/dhcp_libvirt/dhcp_libvirt_main.rb
require 'dhcp_libvirt/libvirt_dhcp_network'
require 'rexml/document'
require 'ipaddr'
require 'dhcp_common/server'
module Proxy::DHCP::Libvirt
class Provider < ::Proxy::DHCP::Server
attr_reader :libvirt_network, :network
def initialize(options = {})
@network = options[:network] || Proxy::DHCP::Libvirt::Plugin.settings.network
@libvirt_network = options[:libvirt_network] || ::Proxy::DHCP::Libvirt::LibvirtDHCPNetwork.new(
options[:url] || Proxy::DHCP::Libvirt::Plugin.settings.url,
@network)
super(@network)
end
def initialize_for_testing(params)
@service = params[:service] || service
self
end
def load_subnets
super
service.add_subnets(*parse_config_for_subnets)
end
def parse_config_for_subnets
ret_val = []
doc = REXML::Document.new xml = libvirt_network.dump_xml
doc.elements.each("network/ip") do |e|
next if e.attributes["family"] == "ipv6"
gateway = e.attributes["address"]
if e.attributes["netmask"].nil? then
# converts a prefix/cidr notation to octets
netmask = IPAddr.new(gateway).mask(e.attributes["prefix"]).to_mask
else
netmask = e.attributes["netmask"]
end
a_network = IPAddr.new(gateway).mask(netmask).to_s
ret_val << Proxy::DHCP::Subnet.new(a_network, netmask)
end
raise Proxy::DHCP::Error("Only one subnet is supported") if ret_val.size > 1
ret_val
rescue Exception => e
logger.error msg = "Unable to parse subnets XML: #{e}"
logger.debug xml if defined?(xml)
raise Proxy::DHCP::Error, msg
end
def parse_config_for_dhcp_reservations(subnet)
to_ret = []
doc = REXML::Document.new xml = libvirt_network.dump_xml
REXML::XPath.each(doc, "//network/ip[not(@family) or @family='ipv4']/dhcp/host") do |e|
to_ret << Proxy::DHCP::Reservation.new(
:subnet => subnet,
:ip => e.attributes["ip"],
:mac => e.attributes["mac"],
:hostname => e.attributes["name"])
end
to_ret
rescue Exception => e
logger.error msg = "Unable to parse reservations XML: #{e}"
logger.debug xml if defined?(xml)
raise Proxy::DHCP::Error, msg
end
def load_subnet_data(subnet)
super(subnet)
reservations = parse_config_for_dhcp_reservations(subnet)
reservations.each { |record| service.add_host(record.subnet_address, record) }
leases = libvirt_network.dhcp_leases
leases.each do |element|
lease = Proxy::DHCP::Lease.new(
:subnet => subnet,
:ip => element['ipaddr'],
:mac => element['mac'],
:starts => Time.now.utc,
:ends => Time.at(element['expirytime'] || 0).utc,
:state => 'active'
)
service.add_lease(lease.subnet_address, lease)
end
end
def add_record(options={})
record = super(options)
libvirt_network.add_dhcp_record record
record
rescue ::Libvirt::Error => e
logger.error msg = "Error adding DHCP record: #{e}"
raise Proxy::DHCP::Error, msg
end
def del_record(_, record)
# libvirt only supports one subnet per network
libvirt_network.del_dhcp_record record
rescue ::Libvirt::Error => e
logger.error msg = "Error removing DHCP record: #{e}"
raise Proxy::DHCP::Error, msg
end
end
end
modules/dhcp_libvirt/dhcp_libvirt_plugin.rb
module ::Proxy::DHCP::Libvirt
class Plugin < ::Proxy::Provider
plugin :dhcp_libvirt, ::Proxy::VERSION
requires :dhcp, ::Proxy::VERSION
default_settings :url => "qemu:///system", :network => 'default'
after_activation do
require 'dhcp_libvirt/dhcp_libvirt_main'
require 'dhcp_libvirt/dependencies'
end
end
end
modules/dhcp_libvirt/libvirt_dhcp_network.rb
require 'libvirt'
require 'libvirt_common/libvirt_network'
module ::Proxy::DHCP::Libvirt
class LibvirtDHCPNetwork < Proxy::LibvirtNetwork
def dhcp_leases
find_network.dhcp_leases
rescue ArgumentError
# workaround for ruby-libvirt < 0.6.1 - DHCP leases API is broken there
# (http://libvirt.org/git/?p=ruby-libvirt.git;a=commit;h=c2d4192ebf28b8030b753b715a72f0cdf725d313)
[]
end
def add_dhcp_record(record)
nametag = "name=\"#{record.name}\"" if record.name
xml = "<host mac=\"#{record.mac}\" ip=\"#{record.ip}\" #{nametag}/>"
network_update ::Libvirt::Network::UPDATE_COMMAND_ADD_LAST, ::Libvirt::Network::NETWORK_SECTION_IP_DHCP_HOST, xml
end
def del_dhcp_record(record)
nametag = "name=\"#{record.name}\"" if record.name
xml = "<host mac=\"#{record.mac}\" ip=\"#{record.ip}\" #{nametag}/>"
network_update ::Libvirt::Network::UPDATE_COMMAND_DELETE, ::Libvirt::Network::NETWORK_SECTION_IP_DHCP_HOST, xml
end
end
end
modules/dhcp_virsh/dependencies.rb
require 'dhcp_common/dependency_injection/dependencies'
class Proxy::DHCP::DependencyInjection::Dependencies
dependency :dhcp_provider, Proxy::DHCP::Virsh::Provider
end
modules/dhcp_virsh/dhcp_virsh.rb
require 'dhcp_common/dhcp_common'
require 'dhcp_virsh/dhcp_virsh_plugin'
modules/dhcp_virsh/dhcp_virsh_main.rb
require 'proxy/virsh'
require 'rexml/document'
require 'ipaddr'
require 'dhcp_common/server'
module Proxy::DHCP::Virsh
class Provider < ::Proxy::DHCP::Server
include Proxy::Virsh
def initialize
super("127.0.0.1")
@network = Proxy::SETTINGS.virsh_network
end
def initialize_for_testing(params)
@name = params[:name] || @name
@service = params[:service] || service
@network = params[:network] || @network
self
end
# we support only one subnet
def load_subnets
super
service.add_subnets(*parse_config_for_subnets)
end
def parse_config_for_subnets
ret_val = []
begin
doc = REXML::Document.new xml = dump_xml
doc.elements.each("network/ip") do |e|
next if e.attributes["family"] == "ipv6"
gateway = e.attributes["address"]
if e.attributes["netmask"].nil? then
# converts a prefix/cidr notation to octets
netmask = IPAddr.new(gateway).mask(e.attributes["prefix"]).to_mask
else
netmask = e.attributes["netmask"]
end
network = IPAddr.new(gateway).mask(netmask).to_s
ret_val << Proxy::DHCP::Subnet.new(network, netmask)
end
rescue Exception => e
msg = "DHCP virsh provider error: unable to retrive virsh info: #{e}"
logger.error msg
logger.debug xml if defined?(xml)
raise Proxy::DHCP::Error, msg
end
ret_val
end
def parse_config_for_dhcp_records(subnet)
to_ret = []
begin
doc = REXML::Document.new xml = dump_xml
REXML::XPath.each(doc, "//network/ip[not(@family) or @family='ipv4']/dhcp/host") do |e|
to_ret << Proxy::DHCP::Reservation.new(:subnet => subnet, :ip => e.attributes["ip"],
:mac => e.attributes["mac"], :hostname => e.attributes["name"])
end
rescue Exception => e
msg = "DHCP virsh provider error: unable to retrive virsh info: #{e}"
logger.error msg
logger.debug xml if defined?(xml)
raise Proxy::DHCP::Error, msg
end
to_ret
end
def load_subnet_data subnet
super(subnet)
records = parse_config_for_dhcp_records(subnet)
records.each { |record| service.add_host(record.subnet_address, record) }
end
def add_record options={}
record = super(options)
virsh_update_dhcp 'add-last', record.mac, record.ip, record.name
record
end
def del_record subnet, record
virsh_update_dhcp 'delete', record.mac, record.ip, record[:hostname]
end
def virsh_update_dhcp command, mac, ip, name
mac = escape_for_shell(mac)
ip = escape_for_shell(ip)
net = escape_for_shell(network)
if name
name = escape_for_shell(name)
xml = "'<host mac=\"#{mac}\" name=\"#{name}\" ip=\"#{ip}\"/>'"
else
xml = "'<host mac=\"#{mac}\" ip=\"#{ip}\"/>'"
end
virsh "net-update", net, command, "ip-dhcp-host", "--xml", xml, "--live", "--config"
rescue Proxy::Virsh::Error => e
raise Proxy::DHCP::Error, "Failed to update DHCP: #{e}"
end
end
end
modules/dhcp_virsh/dhcp_virsh_plugin.rb
module ::Proxy::DHCP::Virsh
class Plugin < ::Proxy::Provider
plugin :dhcp_virsh, ::Proxy::VERSION
requires :dhcp, ::Proxy::VERSION
after_activation do
require 'dhcp_virsh/dhcp_virsh_main'
require 'dhcp_virsh/dependencies'
end
end
end
modules/dns_libvirt/dependencies.rb
require 'dns_common/dependency_injection/dependencies'
class Proxy::Dns::DependencyInjection::Dependencies
dependency :dns_provider, Proxy::Dns::Libvirt::Record
end
modules/dns_libvirt/dns_libvirt.rb
require 'dns_libvirt/dns_libvirt_plugin'
modules/dns_libvirt/dns_libvirt_main.rb
require 'dns_libvirt/libvirt_dns_network'
require 'rexml/document'
require 'dns_common/dns_common'
module Proxy::Dns::Libvirt
class Record < ::Proxy::Dns::Record
include Proxy::Log
include Proxy::Util
attr_reader :libvirt_network, :network
def initialize(options = {})
@network = options[:network] || Proxy::Dns::Libvirt::Plugin.settings.network
@libvirt_network = options[:libvirt_network] || ::Proxy::Dns::Libvirt::LibvirtDNSNetwork.new(
options[:url] || Proxy::Dns::Libvirt::Plugin.settings.url,
@network)
super(@network)
end
def create_a_record(fqdn, ip)
libvirt_network.add_dns_a_record fqdn, ip
rescue ::Libvirt::Error => e
logger.error msg = "Error adding DNS A record: #{e}"
raise Proxy::Dns::Error, msg
end
alias :create_aaaa_record :create_a_record
def create_ptr_record(fqdn, ip)
# libvirt does not support PTR
end
def remove_a_record(fqdn)
libvirt_network.del_dns_a_record fqdn, find_ip_for_host(fqdn)
rescue ::Libvirt::Error => e
logger.error msg = "Error adding DNS A record: #{e}"
raise Proxy::Dns::Error, msg
end
alias :remove_aaaa_record :remove_a_record
def remove_ptr_record(ip)
# libvirt does not support PTR
end
def find_ip_for_host host
begin
doc = REXML::Document.new xml = libvirt_network.dump_xml
doc.elements.each("network/dns/host/hostname") do |e|
if e.text == host
return e.parent.attributes["ip"]
end
end
rescue Exception => e
logger.error msg = "Unable to retrieve IP for #{host}: #{e}"
logger.debug xml if defined?(xml)
raise Proxy::Dns::Error, msg
end
raise Proxy::Dns::NotFound.new("Cannot find IP entry for #{host}")
end
end
end
modules/dns_libvirt/dns_libvirt_plugin.rb
module ::Proxy::Dns::Libvirt
class Plugin < ::Proxy::Provider
plugin :dns_libvirt, ::Proxy::VERSION
requires :dns, ::Proxy::VERSION
default_settings :url => "qemu:///system", :network => 'default'
after_activation do
require 'dns_libvirt/dns_libvirt_main'
require 'dns_libvirt/dependencies'
end
end
end
modules/dns_libvirt/libvirt_dns_network.rb
require 'libvirt'
require 'libvirt_common/libvirt_network'
module ::Proxy::Dns::Libvirt
class LibvirtDNSNetwork < Proxy::LibvirtNetwork
def add_dns_a_record(fqdn, ip)
xml = "<host ip=\"#{ip}\"><hostname>#{fqdn}</hostname></host>"
network_update ::Libvirt::Network::UPDATE_COMMAND_ADD_LAST, ::Libvirt::Network::NETWORK_SECTION_DNS_HOST, xml
end
def del_dns_a_record(fqdn, ip)
xml = "<host ip=\"#{ip}\"><hostname>#{fqdn}</hostname></host>"
network_update ::Libvirt::Network::UPDATE_COMMAND_DELETE, ::Libvirt::Network::NETWORK_SECTION_DNS_HOST, xml
end
end
end
modules/dns_virsh/dependencies.rb
require 'dns_common/dependency_injection/dependencies'
class Proxy::Dns::DependencyInjection::Dependencies
dependency :dns_provider, Proxy::Dns::Virsh::Record
end
modules/dns_virsh/dns_virsh.rb
require 'dns_virsh/dns_virsh_plugin'
modules/dns_virsh/dns_virsh_main.rb
require "proxy/virsh"
require 'rexml/document'
require 'dns_common/dns_common'
module Proxy::Dns::Virsh
class Record < ::Proxy::Dns::Record
include Proxy::Log
include Proxy::Util
include Proxy::Virsh
def initialize(a_network = nil)
@network = a_network || ::Proxy::SETTINGS.virsh_network
raise "DNS virsh provider needs 'virsh_network' option" unless @network
super(nil, nil)
end
def create_a_record(fqdn, ip)
result = virsh_update_dns 'add-last', fqdn, ip
if result =~ /^Updated/
return true
else
raise Proxy::Dns::Error.new("DNS update error: #{result}")
end
end
def create_ptr_record(fqdn, ip)
logger.warn "not creating PTR record for #{fqdn} (unsupported)"
end
def remove_a_record(fqdn)
result = virsh_update_dns 'delete', fqdn, find_ip_for_host(fqdn)
if result =~ /^Updated/
return true
else
raise Proxy::Dns::Error.new("DNS update error: #{result}")
end
end
def remove_ptr_record(ip)
logger.warn "not deleting PTR record for #{ip} (unsupported)"
end
def find_ip_for_host host
begin
doc = REXML::Document.new xml = dump_xml
doc.elements.each("network/dns/host/hostname") do |e|
if e.text == host
return e.parent.attributes["ip"]
end
end
rescue Exception => e
msg = "DNS virsh provider error: unable to retrieve virsh info: #{e}"
logger.error msg
logger.debug xml if defined?(xml)
raise Proxy::Dns::Error, msg
end
raise Proxy::Dns::NotFound.new("Cannot find DNS entry for #{host}")
end
def virsh_update_dns command, hostname, ip
hostname = escape_for_shell(hostname)
ip = escape_for_shell(ip)
net = escape_for_shell(network)
virsh "net-update", net, command, "dns-host",
"--xml", "'<host ip=\"#{ip}\"><hostname>#{hostname}</hostname></host>'",
"--live", "--config"
rescue Proxy::Virsh::Error => e
raise Proxy::Dns::Error, "Failed to update DNS: #{e}"
end
end
end
modules/dns_virsh/dns_virsh_plugin.rb
module ::Proxy::Dns::Virsh
class Plugin < ::Proxy::Provider
plugin :dns_virsh, ::Proxy::VERSION
requires :dns, ::Proxy::VERSION
after_activation do
require 'dns_virsh/dns_virsh_main'
require 'dns_virsh/dependencies'
end
end
end
modules/libvirt_common/libvirt_network.rb
require 'libvirt'
module Proxy
class LibvirtNetwork
include Proxy::Log
include Proxy::Util
def initialize(url = nil, network = nil)
@network = network
@url = url
end
def connection
@connection ||= ::Libvirt::open(@url)
@connection
end
def dump_xml
find_network.xml_desc
end
def find_network
connection.lookup_network_by_name(@network)
end
def network_update command, section, xml
flags = ::Libvirt::Network::NETWORK_UPDATE_AFFECT_LIVE | ::Libvirt::Network::NETWORK_UPDATE_AFFECT_CONFIG
logger.debug "Libvirt update: #{xml}"
find_network.update command, section, -1, xml, flags
rescue ::Libvirt::Error => e
logger.error "Error calling libvirt update: #{e}"
logger.debug xml
raise e
end
end
end
test/dhcp_libvirt/dhcp_libvirt_provider_interface_test.rb
require 'test_helper'
require 'dhcp_libvirt/dhcp_libvirt_main'
class LibvirtDhcpProviderInterfaceTest < Test::Unit::TestCase
def test_provider_interface
::Libvirt.stubs(:open).returns(true)
assert_dhcp_provider_interface(::Proxy::DHCP::Libvirt::Provider)
end
end
test/dhcp_libvirt/dhcp_libvirt_provider_test.rb
require 'test_helper'
require 'dhcp_libvirt/dhcp_libvirt'
require 'dhcp_libvirt/dhcp_libvirt_main'
require 'dhcp_common/dependency_injection/dependencies'
class DhcpLibvirtProviderTest < Test::Unit::TestCase
def setup
fixture = <<XMLFIXTURE
<network>
<name>default</name>
<uuid>25703051-f5d4-4a31-80b7-37bbbc4d19e1</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:ed:a7:f7'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac="00:16:3e:77:e2:ed" name="foo-1.example.com" ip="192.168.122.10" />
<host mac="00:16:3e:77:e2:ee" name="foo-2.example.com" ip="192.168.122.11" />
</dhcp>
</ip>
</network>
XMLFIXTURE
@json_leases = [{
"ipaddr" => "192.168.122.22",
"mac" => "52:54:00:13:05:12",
"expirytime" => 1_455_723_598
}]
@libvirt_network = mock()
@libvirt_network.stubs(:dump_xml).returns(fixture)
@libvirt_network.stubs(:dhcp_leases).returns(@json_leases)
@subnet = Proxy::DHCP::Subnet.new("192.168.122.0", "255.255.255.0")
@service = Proxy::DHCP::SubnetService.new
@subnet_store = @service.subnets = Proxy::MemoryStore.new
@subject = ::Proxy::DHCP::Libvirt::Provider.new(:network => 'default', :libvirt_network => @libvirt_network, :name => "127.0.0.1")
@subject.initialize_for_testing(:service => @service)
end
def test_default_settings
::Proxy::DHCP::Libvirt::Plugin.load_test_settings({})
assert_equal 'default', Proxy::DHCP::Libvirt::Provider.new(:libvirt_network => @libvirt_network).network
end
def test_libvirt_provider_initialization
::Proxy::DHCP::Libvirt::Plugin.load_test_settings(:network => 'some_network')
assert_equal 'some_network', Proxy::DHCP::Libvirt::Provider.new(:libvirt_network => @libvirt_network).network
end
def test_libvirt_network_class
assert_equal ::Proxy::DHCP::Libvirt::LibvirtDHCPNetwork, ::Proxy::DHCP::Libvirt::Provider.new.libvirt_network.class
end
def test_should_load_subnets
@subject.load_subnets
assert @service.find_subnet("192.168.122.0")
assert_equal 1, @service.all_subnets.size
end
def test_should_load_subnet_data
@subject.load_subnet_data(@subnet)
assert @service.find_host_by_ip("192.168.122.0", "192.168.122.10")
assert @service.find_host_by_ip("192.168.122.0", "192.168.122.11")
assert @service.find_lease_by_ip("192.168.122.0", "192.168.122.22")
assert @service.find_lease_by_mac("192.168.122.0", "52:54:00:13:05:12")
assert_equal 2, @service.all_hosts.size
end
def test_should_add_record
record_hash = { :name => "test.example.com", :ip => "192.168.122.95", :mac => "00:11:bb:cc:dd:ee", :network => "192.168.122.0/255.255.255.0", :subnet => @subnet }
record = Proxy::DHCP::Reservation.new(record_hash)
@service.add_subnet(@subnet)
@subject.libvirt_network.expects(:add_dhcp_record).with(record)
::Proxy::DHCP::Server.any_instance.expects(:add_record).returns(record)
@subject.add_record(hash_symbols_to_strings(record_hash))
end
def test_should_remove_record
record = Proxy::DHCP::Reservation.new(:name => "test.example.com", :ip => "192.168.122.10", :mac => "00:11:bb:cc:dd:ee", :subnet => @subnet)
@service.add_subnet(@subnet)
@service.add_host("192.168.122.0", record)
@subject.libvirt_network.expects(:del_dhcp_record).with(record)
@subject.del_record(@subnet, record)
end
end
test/dhcp_libvirt/libvirt_dhcp_network_test.rb
require 'test_helper'
require 'ostruct'
require 'dhcp_libvirt/libvirt_dhcp_network'
class LibvirtDHCPNetworkTest < Test::Unit::TestCase
def setup
@connection = mock()
@network = mock()
@connection.stubs(:lookup_network_by_name).returns(@network)
::Libvirt.stubs(:open).returns(@connection)
@subject = ::Proxy::DHCP::Libvirt::LibvirtDHCPNetwork.new
@subject.stubs(:find_network).returns(@network)
@network.stubs(:xml_desc).returns('')
@network.stubs(:dhcp_leases).returns([])
@flags = ::Libvirt::Network::NETWORK_UPDATE_AFFECT_LIVE | ::Libvirt::Network::NETWORK_UPDATE_AFFECT_CONFIG
end
def test_dump_xml
a_xml = '<xml></xml>'
@network.expects(:xml_desc).returns(a_xml)
assert_equal a_xml, @subject.dump_xml
end
def test_dhcp_leases
leases = [:a, :b]
@network.expects(:dhcp_leases).returns(leases)
assert_equal leases, @subject.dhcp_leases
end
def test_add_dhcp_record
record = OpenStruct.new(
"name" => "test.example.com",
"ip" => "192.168.122.10",
"mac" => "00:11:bb:cc:dd:ee")
xml = "<host mac=\"#{record.mac}\" ip=\"#{record.ip}\" name=\"#{record.name}\"/>"
@network.expects(:update).with(::Libvirt::Network::UPDATE_COMMAND_ADD_LAST, ::Libvirt::Network::NETWORK_SECTION_IP_DHCP_HOST, -1, xml, @flags).returns(true)
assert_equal true, @subject.add_dhcp_record(record)
end
def test_del_dhcp_record
record = OpenStruct.new(
"name" => "test.example.com",
"ip" => "192.168.122.10",
"mac" => "00:11:bb:cc:dd:ee")
#record = Proxy::DHCP::Reservation.new(:name => "test.example.com", :ip => "192.168.122.10", :mac => "00:11:bb:cc:dd:ee", :subnet => subnet)
xml = "<host mac=\"#{record.mac}\" ip=\"#{record.ip}\" name=\"#{record.name}\"/>"
@network.expects(:update).with(::Libvirt::Network::UPDATE_COMMAND_DELETE, ::Libvirt::Network::NETWORK_SECTION_IP_DHCP_HOST, -1, xml, @flags).returns(true)
assert_equal true, @subject.del_dhcp_record(record)
end
end
test/dhcp_virsh/dhcp_virsh_provider_interface_test.rb
require 'test_helper'
require 'dhcp_virsh/dhcp_virsh_main'
class VirshDhcpProviderInterfaceTest < Test::Unit::TestCase
def test_provider_interface
assert_dhcp_provider_interface(::Proxy::DHCP::Virsh::Provider)
end
end
test/dhcp_virsh/virsh_provider_test.rb
require 'test_helper'
require 'dhcp_virsh/dhcp_virsh'
require 'dhcp_virsh/dhcp_virsh_main'
require 'dhcp_common/dependency_injection/dependencies'
class VirshProviderTest < Test::Unit::TestCase
def setup
@service = Proxy::DHCP::SubnetService.new
@subnet_store = @service.subnets = Proxy::MemoryStore.new
@server = ::Proxy::DHCP::Virsh::Provider.new.initialize_for_testing(:virsh_network => 'default',
:name => "127.0.0.1", :service => @service)
@dump_xml = <<EODUMPXML
<network>
<name>default</name>
<uuid>25703051-f5d4-4a31-80b7-37bbbc4d19e1</uuid>
<forward mode='nat'>
<nat>
<port start='1024' end='65535'/>
</nat>
</forward>
<bridge name='virbr0' stp='on' delay='0'/>
<mac address='52:54:00:ed:a7:f7'/>
<ip address='192.168.122.1' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.2' end='192.168.122.254'/>
<host mac="00:16:3e:77:e2:ed" name="foo-1.example.com" ip="192.168.122.10" />
<host mac="00:16:3e:77:e2:ee" name="foo-2.example.com" ip="192.168.122.11" />
</dhcp>
</ip>
</network>
EODUMPXML
end
class DhcpVirshProviderForTesting < Proxy::DHCP::Virsh::Provider
attr_reader :network
end
def test_virsh_provider_initialization
Proxy::SETTINGS.stubs(:virsh_network).returns('another_one')
assert_equal 'another_one', DhcpVirshProviderForTesting.new.network
end
def test_should_load_subnets
@server.expects(:dump_xml).returns(@dump_xml)
@server.load_subnets
assert @service.find_subnet("192.168.122.0")
assert_equal 1, @service.all_subnets.size
end
def test_should_load_subnet_data
@server.expects(:dump_xml).returns(@dump_xml)
@server.load_subnet_data(Proxy::DHCP::Subnet.new("192.168.122.0", "255.255.255.0"))
assert @service.find_host_by_ip("192.168.122.0", "192.168.122.10")
assert @service.find_host_by_ip("192.168.122.0", "192.168.122.11")
assert_equal 2, @service.all_hosts.size
end
def test_should_add_record
to_add = { "hostname" => "test.example.com", "ip" => "192.168.122.10",
"mac" => "00:11:bb:cc:dd:ee", "network" => "192.168.122.0/255.255.255.0" }
@service.add_subnet(Proxy::DHCP::Subnet.new("192.168.122.0", "255.255.255.0"))
@server.expects(:virsh_update_dhcp).with('add-last', to_add['mac'], to_add['ip'], to_add['hostname'])
@server.add_record(to_add)
end
def test_should_remove_record
subnet = Proxy::DHCP::Subnet.new("192.168.122.0", "255.255.255.0")
@service.add_subnet(subnet)
to_delete = Proxy::DHCP::Reservation.new(:name => "test.example.com", :ip => "192.168.122.10",
:mac => "00:11:bb:cc:dd:ee", :subnet => subnet)
@service.add_host("192.168.122.0", to_delete)
@server.expects(:virsh_update_dhcp).with('delete', to_delete.mac, to_delete.ip, to_delete.hostname)
@server.del_record("192.168.122.0", to_delete)
end
end
test/dns_libvirt/dns_libvirt_provider_test.rb
require 'test_helper'
require 'dns_libvirt/dns_libvirt_plugin'
require 'dns_libvirt/dns_libvirt_main'
class DnsLibvirtProviderTest < Test::Unit::TestCase
def setup
fixture = <<XMLFIXTURE
<network>
<name>default</name>
<domain name='local.lan'/>
<dns>
<host ip='192.168.122.1'>
<hostname>some.example.com</hostname>
</host>
</dns>
<ip address='192.168.122.0' netmask='255.255.255.0'>
<dhcp>
<range start='192.168.122.1' end='192.168.122.250'/>
<host mac='52:54:00:e2:62:08' name='some.example.com' ip='192.168.122.1'/>
</dhcp>
</ip>
</network>
XMLFIXTURE
@libvirt_network = mock()
@libvirt_network.stubs(:dump_xml).returns(fixture)
@subject = Proxy::Dns::Libvirt::Record.new(
:libvirt_network => @libvirt_network
)
end
def test_default_settings
::Proxy::Dns::Libvirt::Plugin.load_test_settings({})
assert_equal 'default', Proxy::Dns::Libvirt::Plugin.settings.network
end
def test_provider_initialization
::Proxy::Dns::Libvirt::Plugin.load_test_settings(:network => 'some_network')
assert_equal "some_network", Proxy::Dns::Libvirt::Record.new(:libvirt_network => @libvirt_network).network
end
def test_libvirt_network_class
assert_equal ::Proxy::Dns::Libvirt::LibvirtDNSNetwork, ::Proxy::Dns::Libvirt::Record.new.libvirt_network.class
end
def test_add_a_record
fqdn = "abc.example.com"
ip = "192.168.122.2"
@subject.libvirt_network.expects(:add_dns_a_record).with(fqdn, ip)
@subject.create_a_record(fqdn, ip)
end
def test_del_a_record
fqdn = "abc.example.com"
ip = "192.168.122.2"
@subject.expects(:find_ip_for_host).with(fqdn).returns(ip)
@subject.libvirt_network.expects(:del_dns_a_record).with(fqdn, ip)
@subject.remove_a_record(fqdn)
end
def test_add_aaaa_record
fqdn = "abc6.example.com"
ip = "2001:db8:85a3:0:0:8a2e:370:7334"
@subject.libvirt_network.expects(:add_dns_a_record).with(fqdn, ip)
@subject.create_a_record(fqdn, ip)
end
def test_del_aaaa_record
fqdn = "abc6.example.com"
ip = "2001:db8:85a3:0:0:8a2e:370:7334"
@subject.expects(:find_ip_for_host).with(fqdn).returns(ip)
@subject.libvirt_network.expects(:del_dns_a_record).with(fqdn, ip)
@subject.remove_a_record(fqdn)
end
def test_del_a_record_failure
assert_raise Proxy::Dns::NotFound do
@subject.remove_a_record('does_not_exist')
end
end
end
test/dns_libvirt/libvirt_dns_network_test.rb
require 'test_helper'
require 'ostruct'
require 'dns_libvirt/libvirt_dns_network'
class LibvirtDNSNetworkTest < Test::Unit::TestCase
def setup
@connection = mock()
@network = mock()
@connection.stubs(:lookup_network_by_name).returns(@network)
::Libvirt.stubs(:open).returns(@connection)
@subject = ::Proxy::Dns::Libvirt::LibvirtDNSNetwork.new
@subject.stubs(:find_network).returns(@network)
@network.stubs(:xml_desc).returns('')
@network.stubs(:dhcp_leases).returns([])
@flags = ::Libvirt::Network::NETWORK_UPDATE_AFFECT_LIVE | ::Libvirt::Network::NETWORK_UPDATE_AFFECT_CONFIG
end
def test_add_dns_a_record
record = OpenStruct.new(
"fqdn" => "test.example.com",
"ip" => "192.168.122.10")
xml = "<host ip=\"#{record.ip}\"><hostname>#{record.fqdn}</hostname></host>"
@network.expects(:update).with(::Libvirt::Network::UPDATE_COMMAND_ADD_LAST, ::Libvirt::Network::NETWORK_SECTION_DNS_HOST, -1, xml, @flags).returns(true)
assert_equal true, @subject.add_dns_a_record(record.fqdn, record.ip)
end
def test_del_dns_a_record
record = OpenStruct.new(
"fqdn" => "test.example.com",
"ip" => "192.168.122.10")
xml = "<host ip=\"#{record.ip}\"><hostname>#{record.fqdn}</hostname></host>"
@network.expects(:update).with(::Libvirt::Network::UPDATE_COMMAND_DELETE, ::Libvirt::Network::NETWORK_SECTION_DNS_HOST, -1, xml, @flags).returns(true)
assert_equal true, @subject.del_dns_a_record(record.fqdn, record.ip)
end
end
test/dns_virsh/dns_virsh_test.rb
require 'test_helper'
require 'dns_virsh/dns_virsh_plugin'
require 'dns_virsh/dns_virsh_main'
class DnsVirshTest < Test::Unit::TestCase
def test_virsh_provider_initialization
::Proxy::SETTINGS.stubs(:virsh_network).returns('some_network')
server = Proxy::Dns::Virsh::Record.new
assert_equal "some_network", server.network
end
def test_virsh_entry_not_exists_returns_proxy_dns_notfound
Proxy::Dns::Virsh::Record.any_instance.stubs(:dump_xml).returns('<network><name>default</name></network>')
server = Proxy::Dns::Virsh::Record.new('default')
assert_raise Proxy::Dns::NotFound do
server.remove_a_record('not_existing.example.com')
end
end
def test_virsh_removes_existing_entry
xml_response = <<XMLRESPONSE
<network>
<name>default</name>
<domain name='local.lan'/>
<dns>
<host ip='127.13.0.2'>
<hostname>not_existing.example.com</hostname>
</host>
</dns>
<ip address='127.13.0.1' netmask='255.255.255.0'>
<dhcp>
<range start='127.13.0.2' end='127.13.0.100'/>
<host mac='52:54:00:e2:62:08' name='not_existing.example.com' ip='127.13.0.2'/>
</dhcp>
</ip>
</network>
XMLRESPONSE
Proxy::Dns::Virsh::Record.any_instance.stubs(:dump_xml).returns(xml_response)
server = Proxy::Dns::Virsh::Record.new("default")
server.expects(:escape_for_shell).at_least(2).returns(true)
server.expects(:virsh).returns('Updated')
assert server.remove_a_record('not_existing.example.com')
end
end
test/migrations/libvirt_migration_test.rb
require 'test_helper'
require File.join(File.dirname(__FILE__),'../../extra/migrate_settings')
::Proxy::Migration.inject_migrations_instance(::Proxy::Migrations.new("dummy"))
require File.join(File.dirname(__FILE__),'../../extra/migrations/20160411000000_migrate_libvirt_settings')
class ProxyLibvirtMigrationTest < Test::Unit::TestCase
def setup
@old_config = YAML.load_file(File.join(File.dirname(__FILE__),'./migration_settings.yml'))
@migration = MigrateVirshToLibvirtConfig.new("/tmp")
@dhcp_data = @migration.transform_dhcp_yaml(@old_config.dup)
@dns_data = @migration.transform_dns_yaml(@old_config.dup)
end
def test_transform_dhcp_yaml_empty
assert_equal 'default', @migration.transform_dhcp_yaml({})[:network]
end
def test_output_has_correct_dhcp_network
assert_equal 'mynetwork', @dhcp_data[:network]
end
def test_transform_dns_yaml_empty
assert_equal 'default', @migration.transform_dns_yaml({})[:network]
end
def test_output_has_correct_dns_network
assert_equal 'mynetwork', @dns_data[:network]
end
end
test/migrations/migration_dns_settings.yml
---
:enabled: true
# Valid providers:
# nsupdate
# nsupdate_gss (for GSS-TSIG support)
# virsh
# dnscmd
:dns_provider: nsupdate
:dns_key: /etc/bind/rndc.key
:dns_server: 127.0.0.1
test/migrations/migration_settings.yml
:bmc: true
:bmc_default_provider: ipmitool
:virsh_network: mynetwork
:chefproxy: true
:chef_authenticate_nodes: true
:chef_server_url: "https://foreman.example.com"
test/migrations/migration_test.rb
@migration.migrations.first.any_instance.expects(:create_migration_dirs)
migrator.expects(:copy_original_configuration).with(migration.src_dir)
migrator.execute_migrations([@migration.migrations.first])
migrator.execute_migrations([@migration.migrations.first], false)
assert_equal 1, migrator.executed_migrations.size
assert migrator.executed_migrations.first.instance_of?(@migration.migrations.first)
test/migrations/monolithic_config_file_migration_test.rb
:daemon_pid => "/var/run/foreman-proxy/foreman-proxy.pid",
:log_file => "/var/log/foreman-proxy/proxy.log",
:log_level => "DEBUG",
:https_port => 8443
:https_port => 8443,
:virsh_network => "mynetwork"
end
def test_output_has_correct_tftp_settings
test/test_helper.rb
require 'provider_interface_validation/dhcp_provider'
include DhcpProviderInterfaceValidation
def hash_symbols_to_strings(hash)
Hash[hash.collect{|k,v| [k.to_s,v]}]
end

Also available in: Unified diff