mirror of
https://github.com/puppetlabs/vmpooler.git
synced 2026-01-26 01:58:41 -05:00
Stub out dns provider usage
This commit is contained in:
parent
16d23a0226
commit
ac96550f57
5 changed files with 300 additions and 6 deletions
67
lib/vmpooler/dns.rb
Normal file
67
lib/vmpooler/dns.rb
Normal file
|
|
@ -0,0 +1,67 @@
|
|||
require 'pathname'
|
||||
|
||||
module Vmpooler
|
||||
class Dns
|
||||
|
||||
# Load one or more VMPooler DNS plugin gems by name
|
||||
#
|
||||
# @param names [Array<String>] The list of gem names to load
|
||||
def self.load_by_name(names)
|
||||
names = Array(names)
|
||||
instance = new
|
||||
names.map { |name| instance.load_from_gems(name) }.flatten
|
||||
end
|
||||
|
||||
# Returns the plugin class for the specified dns config by name
|
||||
#
|
||||
# @param config [Object] The entire VMPooler config object
|
||||
# @param name [Symbol] The name of the dns config key to get the dns class
|
||||
# @return [String] The plugin class for the specifid dns config
|
||||
def self.get_dns_plugin_class_by_name(config, name)
|
||||
dns_configs = config[:dns_configs].keys
|
||||
plugin_class = ''
|
||||
|
||||
dns_configs.map do |dns_config_name|
|
||||
if dns_config_name.to_s == name
|
||||
plugin_class = config[:dns_configs][dns_config_name]['dns_class']
|
||||
end
|
||||
end
|
||||
|
||||
plugin_class
|
||||
end
|
||||
|
||||
# Returns a list of DNS plugin classes specified in the vmpooler configuration
|
||||
#
|
||||
# @param config [Object] The entire VMPooler config object
|
||||
# @return [Array<String] A list of DNS plugin classes
|
||||
def self.get_dns_plugin_config_classes(config)
|
||||
if config[:dns_configs]
|
||||
dns_configs = config[:dns_configs].keys
|
||||
dns_plugins = dns_configs.map do |dns_config_name|
|
||||
if config[:dns_configs][dns_config_name] && config[:dns_configs][dns_config_name]['dns_class']
|
||||
config[:dns_configs][dns_config_name]['dns_class'].to_s
|
||||
else
|
||||
dns_config_name.to_s
|
||||
end
|
||||
end.compact.uniq
|
||||
|
||||
# dynamic-dns is not actually a class, it's just used as a value to denote
|
||||
# that dynamic dns is used so no loading or record management is needed
|
||||
dns_plugins.delete('dynamic-dns')
|
||||
end
|
||||
|
||||
dns_plugins
|
||||
end
|
||||
|
||||
# Load a single DNS plugin gem by name
|
||||
#
|
||||
# @param name [String] The name of the DNS plugin gem to load
|
||||
# @return [String] The full require path to the specified gem
|
||||
def load_from_gems(name = nil)
|
||||
require_path = 'vmpooler/dns/' + name.gsub('-', '/')
|
||||
require require_path
|
||||
$logger.log('d', "[*] [dns_manager] Loading DNS plugins from dns_configs: #{name}")
|
||||
require_path
|
||||
end
|
||||
end
|
||||
end
|
||||
69
lib/vmpooler/dns/base.rb
Normal file
69
lib/vmpooler/dns/base.rb
Normal file
|
|
@ -0,0 +1,69 @@
|
|||
module Vmpooler
|
||||
class PoolManager
|
||||
class Dns
|
||||
class Base
|
||||
# These defs must be overidden in child classes
|
||||
|
||||
# Helper Methods
|
||||
# Global Logger object
|
||||
attr_reader :logger
|
||||
# Global Metrics object
|
||||
attr_reader :metrics
|
||||
# Provider options passed in during initialization
|
||||
attr_reader :dns_options
|
||||
|
||||
def initialize(config, logger, metrics, redis_connection_pool, name, options)
|
||||
@config = config
|
||||
@logger = logger
|
||||
@metrics = metrics
|
||||
@redis = redis_connection_pool
|
||||
@dns_plugin_name = name
|
||||
|
||||
@dns_options = options
|
||||
|
||||
logger.log('s', "[!] Creating dns plugin '#{name}'")
|
||||
# Your code goes here...
|
||||
end
|
||||
|
||||
def pool_config(pool_name)
|
||||
# Get the configuration of a specific pool
|
||||
@config[:pools].each do |pool|
|
||||
return pool if pool['name'] == pool_name
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
def global_config
|
||||
# This entire VM Pooler config
|
||||
@config
|
||||
end
|
||||
|
||||
def name
|
||||
@dns_plugin_name
|
||||
end
|
||||
|
||||
def get_ip(vm_name)
|
||||
@redis.with_metrics do |redis|
|
||||
ip = redis.hget("vmpooler__vm__#{vm_name}", 'ip')
|
||||
return ip
|
||||
end
|
||||
end
|
||||
|
||||
# returns
|
||||
# Array[String] : Array of pool names this provider services
|
||||
def provided_pools
|
||||
list = []
|
||||
@config[:pools].each do |pool|
|
||||
list << pool['name'] if pool['dns_config'] == name
|
||||
end
|
||||
list
|
||||
end
|
||||
|
||||
def create_or_replace_record(hostname)
|
||||
raise("#{self.class.name} does not implement create_or_replace_record")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -1,5 +1,6 @@
|
|||
# frozen_string_literal: true
|
||||
|
||||
require 'vmpooler/dns'
|
||||
require 'vmpooler/providers'
|
||||
require 'vmpooler/util/parsing'
|
||||
require 'spicy-proton'
|
||||
|
|
@ -26,6 +27,9 @@ module Vmpooler
|
|||
# VM Provider objects
|
||||
$providers = Concurrent::Hash.new
|
||||
|
||||
# VM DNS objects
|
||||
$dns_plugins = Concurrent::Hash.new
|
||||
|
||||
# Our thread-tracker object
|
||||
$threads = Concurrent::Hash.new
|
||||
|
||||
|
|
@ -39,6 +43,9 @@ module Vmpooler
|
|||
|
||||
# load specified providers from config file
|
||||
load_used_providers
|
||||
|
||||
# load specified dns plugins from config file
|
||||
load_used_dns_plugins
|
||||
end
|
||||
|
||||
def config
|
||||
|
|
@ -323,10 +330,10 @@ module Vmpooler
|
|||
end
|
||||
|
||||
# Clone a VM
|
||||
def clone_vm(pool_name, provider, request_id = nil, pool_alias = nil)
|
||||
def clone_vm(pool_name, provider, dns_plugin, request_id = nil, pool_alias = nil)
|
||||
Thread.new do
|
||||
begin
|
||||
_clone_vm(pool_name, provider, request_id, pool_alias)
|
||||
_clone_vm(pool_name, provider, dns_plugin, request_id, pool_alias)
|
||||
rescue StandardError => e
|
||||
if request_id
|
||||
$logger.log('s', "[!] [#{pool_name}] failed while cloning VM for request #{request_id} with an error: #{e}")
|
||||
|
|
@ -414,7 +421,7 @@ module Vmpooler
|
|||
[dns_ip, false]
|
||||
end
|
||||
|
||||
def _clone_vm(pool_name, provider, request_id = nil, pool_alias = nil)
|
||||
def _clone_vm(pool_name, provider, dns_plugin, request_id = nil, pool_alias = nil)
|
||||
new_vmname = find_unique_hostname(pool_name)
|
||||
pool_domain = Parsing.get_domain_for_pool(config, pool_name)
|
||||
mutex = vm_mutex(new_vmname)
|
||||
|
|
@ -447,6 +454,8 @@ module Vmpooler
|
|||
$logger.log('s', "[+] [#{pool_name}] '#{new_vmname}' cloned in #{finish} seconds")
|
||||
|
||||
$metrics.timing("clone.#{pool_name}", finish)
|
||||
|
||||
dns_plugin.create_or_replace_record(new_vmname)
|
||||
rescue StandardError
|
||||
@redis.with_metrics do |redis|
|
||||
redis.pipelined do |pipeline|
|
||||
|
|
@ -632,6 +641,12 @@ module Vmpooler
|
|||
result
|
||||
end
|
||||
|
||||
# load only dns plugins used in config file
|
||||
def load_used_dns_plugins
|
||||
dns_plugins = Vmpooler::Dns.get_dns_plugin_config_classes(config)
|
||||
Vmpooler::Dns.load_by_name(dns_plugins)
|
||||
end
|
||||
|
||||
# load only providers used in config file
|
||||
def load_used_providers
|
||||
Vmpooler::Providers.load_by_name(used_providers)
|
||||
|
|
@ -679,6 +694,15 @@ module Vmpooler
|
|||
$providers[provider_name]
|
||||
end
|
||||
|
||||
def get_dns_plugin_class_for_pool(pool_name)
|
||||
pool = $config[:pools].find { |p| p['name'] == pool_name }
|
||||
return nil unless pool
|
||||
|
||||
plugin_name = pool.fetch('dns_plugin')
|
||||
plugin_class = Vmpooler::Dns.get_dns_plugin_class_by_name(config, plugin_name)
|
||||
$dns_plugins[plugin_class]
|
||||
end
|
||||
|
||||
def check_disk_queue(maxloop = 0, loop_delay = 5)
|
||||
$logger.log('d', '[*] [disk_manager] starting worker thread')
|
||||
|
||||
|
|
@ -1290,6 +1314,8 @@ module Vmpooler
|
|||
$metrics.gauge("ready.#{pool_name}", ready)
|
||||
$metrics.gauge("running.#{pool_name}", running)
|
||||
|
||||
dns_plugin = get_dns_plugin_class_for_pool(pool_name)
|
||||
|
||||
unless pool_size == 0
|
||||
if redis.get("vmpooler__empty__#{pool_name}")
|
||||
redis.del("vmpooler__empty__#{pool_name}") unless ready == 0
|
||||
|
|
@ -1304,7 +1330,7 @@ module Vmpooler
|
|||
begin
|
||||
redis.incr('vmpooler__tasks__clone')
|
||||
pool_check_response[:cloned_vms] += 1
|
||||
clone_vm(pool_name, provider)
|
||||
clone_vm(pool_name, provider, dns_plugin)
|
||||
rescue StandardError => e
|
||||
$logger.log('s', "[!] [#{pool_name}] clone failed during check_pool with an error: #{e}")
|
||||
redis.decr('vmpooler__tasks__clone')
|
||||
|
|
@ -1390,6 +1416,16 @@ module Vmpooler
|
|||
raise("Provider '#{provider_class}' is unknown for pool with provider name '#{provider_name}'") if provider_klass.nil?
|
||||
end
|
||||
|
||||
def create_dns_object(config, logger, metrics, redis_connection_pool, dns_class, dns_name, options)
|
||||
dns_klass = Vmpooler::PoolManager::Dns
|
||||
dns_klass.constants.each do |classname|
|
||||
next unless classname.to_s.casecmp(dns_class) == 0
|
||||
|
||||
return dns_klass.const_get(classname).new(config, logger, metrics, redis_connection_pool, dns_name, options)
|
||||
end
|
||||
raise("DNS '#{dns_class}' is unknown for pool with dns name '#{dns_name}'") if dns_klass.nil?
|
||||
end
|
||||
|
||||
def check_ondemand_requests(maxloop = 0,
|
||||
loop_delay_min = CHECK_LOOP_DELAY_MIN_DEFAULT,
|
||||
loop_delay_max = CHECK_LOOP_DELAY_MAX_DEFAULT,
|
||||
|
|
@ -1473,20 +1509,21 @@ module Vmpooler
|
|||
pool_alias, pool, count, request_id = request.split(':')
|
||||
count = count.to_i
|
||||
provider = get_provider_for_pool(pool)
|
||||
dns_plugin = get_dns_plugin_class_for_pool(pool)
|
||||
slots = ondemand_clone_limit - clone_count
|
||||
break if slots == 0
|
||||
|
||||
if slots >= count
|
||||
count.times do
|
||||
redis.incr('vmpooler__tasks__ondemandclone')
|
||||
clone_vm(pool, provider, request_id, pool_alias)
|
||||
clone_vm(pool, provider, dns_plugin, request_id, pool_alias)
|
||||
end
|
||||
redis.zrem(queue_key, request)
|
||||
else
|
||||
remaining_count = count - slots
|
||||
slots.times do
|
||||
redis.incr('vmpooler__tasks__ondemandclone')
|
||||
clone_vm(pool, provider, request_id, pool_alias)
|
||||
clone_vm(pool, provider, dns_plugin, request_id, pool_alias)
|
||||
end
|
||||
redis.pipelined do |pipeline|
|
||||
pipeline.zrem(queue_key, request)
|
||||
|
|
@ -1601,6 +1638,7 @@ module Vmpooler
|
|||
# Create the providers
|
||||
$config[:pools].each do |pool|
|
||||
provider_name = pool['provider']
|
||||
dns_plugin_name = pool['dns_plugin']
|
||||
# The provider_class parameter can be defined in the provider's data eg
|
||||
# :providers:
|
||||
# :vsphere:
|
||||
|
|
@ -1621,12 +1659,22 @@ module Vmpooler
|
|||
else
|
||||
provider_class = $config[:providers][provider_name.to_sym]['provider_class']
|
||||
end
|
||||
|
||||
begin
|
||||
$providers[provider_name] = create_provider_object($config, $logger, $metrics, @redis, provider_class, provider_name, {}) if $providers[provider_name].nil?
|
||||
rescue StandardError => e
|
||||
$logger.log('s', "Error while creating provider for pool #{pool['name']}: #{e}")
|
||||
raise
|
||||
end
|
||||
|
||||
dns_plugin_class = $config[:dns_configs][dns_plugin_name.to_sym]['dns_class']
|
||||
|
||||
begin
|
||||
$dns_plugins[dns_plugin_class] = create_dns_object($config, $logger, $metrics, @redis, dns_plugin_class, dns_plugin_name, {}) if $dns_plugins[dns_plugin_class].nil?
|
||||
rescue StandardError => e
|
||||
$logger.log('s', "Error while creating dns plugin for pool #{pool['name']}: #{e}")
|
||||
raise
|
||||
end
|
||||
end
|
||||
|
||||
purge_unused_vms_and_resources
|
||||
|
|
|
|||
|
|
@ -34,6 +34,10 @@ module Vmpooler
|
|||
|
||||
# Helper Methods
|
||||
|
||||
def get_dns_plugin_for_pool(pool_name)
|
||||
|
||||
end
|
||||
|
||||
# inputs
|
||||
# [String] pool_name : Name of the pool to get the configuration
|
||||
# returns
|
||||
|
|
|
|||
|
|
@ -75,6 +75,27 @@ EOT
|
|||
end
|
||||
end
|
||||
|
||||
describe '#load_used_dns_plugins' do
|
||||
let(:config) { YAML.load(<<-EOT
|
||||
---
|
||||
:config:
|
||||
:dns_configs:
|
||||
:base:
|
||||
:pools:
|
||||
- name: '#{pool}'
|
||||
size: 1
|
||||
provider: 'spoof'
|
||||
EOT
|
||||
)
|
||||
}
|
||||
it do
|
||||
files = ['vmpooler/dns/base']
|
||||
expect(subject.load_used_dns_plugins).to eq(files)
|
||||
end
|
||||
|
||||
|
||||
end
|
||||
|
||||
describe '#used_providers' do
|
||||
context 'with no named providers' do
|
||||
let(:config) { YAML.load(<<-EOT
|
||||
|
|
@ -1724,6 +1745,51 @@ EOT
|
|||
end
|
||||
end
|
||||
|
||||
describe '#get_dns_plugin_class_name_for_pool' do
|
||||
let(:config) { YAML.load(<<-EOT
|
||||
---
|
||||
:dns_configs:
|
||||
:mock:
|
||||
dns_class: base
|
||||
:pools:
|
||||
- name: #{pool}
|
||||
dns_plugin: 'mock'
|
||||
EOT
|
||||
|
||||
)}
|
||||
before(:each) do
|
||||
allow(Vmpooler::Dns).to receive(:load_used_dns_plugins).and_return('vmpooler/dns/mock')
|
||||
end
|
||||
|
||||
it 'calls Vmpooler::Dns.get_dns_plugin_class_by_name' do
|
||||
expect(Vmpooler::Dns).to receive(:get_dns_plugin_class_by_name).with(config, 'mock')
|
||||
|
||||
subject.get_dns_plugin_class_name_for_pool(pool)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#get_dns_plugin_domain_for_pool' do
|
||||
let(:config) { YAML.load(<<-EOT
|
||||
---
|
||||
:dns_configs:
|
||||
:mock:
|
||||
dns_class: base
|
||||
:pools:
|
||||
- name: #{pool}
|
||||
dns_plugin: 'mock'
|
||||
EOT
|
||||
)}
|
||||
before(:each) do
|
||||
allow(Vmpooler::Dns).to receive(:load_used_dns_plugins).and_return('vmpooler/dns/mock')
|
||||
end
|
||||
|
||||
it 'calls Vmpooler::Dns.get_dns_plugin_domain_for_pool' do
|
||||
expect(Vmpooler::Dns).to receive(:get_dns_plugin_domain_by_name).with(config, 'mock')
|
||||
|
||||
subject.get_dns_plugin_domain_for_pool(pool)
|
||||
end
|
||||
end
|
||||
|
||||
describe '#check_disk_queue' do
|
||||
let(:threads) {[]}
|
||||
|
||||
|
|
@ -2817,6 +2883,46 @@ EOT
|
|||
subject.execute!(1,0)
|
||||
end
|
||||
|
||||
context 'creating Dns plugins' do
|
||||
let(:mock_dns_plugin) { double('mock_dns_plugin') }
|
||||
let(:config) {
|
||||
YAML.load(<<-EOT
|
||||
---
|
||||
:dns_configs:
|
||||
:mock:
|
||||
dns_class: base
|
||||
:pools:
|
||||
- name: #{pool}
|
||||
dns_plugin: 'mock'
|
||||
- name: 'dummy'
|
||||
dns_plugin: 'mock'
|
||||
- name: 'dummy2'
|
||||
dns_plugin: 'mock'
|
||||
EOT
|
||||
)}
|
||||
|
||||
it 'should call create_dns_object idempotently' do
|
||||
# Even though there are two pools using the mock dns plugin, it should only
|
||||
# create the dns object once.
|
||||
expect(subject).to receive(:create_dns_object).and_return(mock_dns_plugin)
|
||||
|
||||
subject.execute!(1,0)
|
||||
end
|
||||
|
||||
it 'should raise an error if the dns plugin cannot be created' do
|
||||
expect(subject).to receive(:create_dns_object).and_raise(RuntimeError, "MockError")
|
||||
|
||||
expect{ subject.execute!(1,0) }.to raise_error(/MockError/)
|
||||
end
|
||||
|
||||
it 'should log a message if the dns plugin cannot be created' do
|
||||
expect(subject).to receive(:create_dns_object).and_raise(RuntimeError, "MockError")
|
||||
expect(logger).to receive(:log).with('s',"Error while creating dns plugin for pool #{pool}: MockError")
|
||||
|
||||
expect{ subject.execute!(1,0) }.to raise_error(/MockError/)
|
||||
end
|
||||
end
|
||||
|
||||
context 'creating Providers' do
|
||||
let(:vsphere_provider) { double('vsphere_provider') }
|
||||
let(:config) {
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue