diff --git a/README.md b/README.md index 8dcbfbb69..c2070d85e 100644 --- a/README.md +++ b/README.md @@ -58,7 +58,7 @@ The following YAML configuration sets up two pools, `debian-7-i386` and `debian- provider: vsphere ``` -See the provided YAML configuration example, [vmpooler.yaml.example](vmpooler.yaml.example), for additional configuration options and parameters. +See the provided YAML configuration example, [vmpooler.yaml.example](vmpooler.yaml.example), for additional configuration options and parameters or for supporting multiple providers. ### Running via Docker diff --git a/lib/vmpooler/pool_manager.rb b/lib/vmpooler/pool_manager.rb index 35e796668..8901c05f1 100644 --- a/lib/vmpooler/pool_manager.rb +++ b/lib/vmpooler/pool_manager.rb @@ -694,14 +694,20 @@ def _check_pool(pool, provider) raise end - def create_provider_object(config, logger, metrics, provider_name, options) - case provider_name + # create a provider object based on the providers/*.rb class that implements providers/base.rb + # provider_class: needs to match a provider class in providers/*.rb ie Vmpooler::PoolManager::Provider::X + # provider_name: should be a unique provider name + # + # returns an object Vmpooler::PoolManager::Provider::* + # or raises an error if the class does not exist + def create_provider_object(config, logger, metrics, provider_class, provider_name, options) + case provider_class when 'vsphere' Vmpooler::PoolManager::Provider::VSphere.new(config, logger, metrics, provider_name, options) when 'dummy' Vmpooler::PoolManager::Provider::Dummy.new(config, logger, metrics, provider_name, options) else - raise("Provider '#{provider_name}' is unknown") + raise("Provider '#{provider_class}' is unknown for pool with provider '#{provider_name}'") end end @@ -731,8 +737,28 @@ def execute!(maxloop = 0, loop_delay = 1) # Create the providers $config[:pools].each do |pool| provider_name = pool['provider'] + # The provider_class parameter can be defined in the provider's data eg + #:providers: + # :vsphere: + # provider_class: 'vsphere' + # :another-vsphere: + # provider_class: 'vsphere' + # the above would create two providers/vsphere.rb class objects named 'vsphere' and 'another-vsphere' + # each pools would then define which provider definition to use: vsphere or another-vsphere + # + # if provider_class is not defined it will try to use the provider_name as the class, this is to be + # backwards compatible for example when there is only one provider listed + # :providers: + # :dummy: + # filename: 'db.txs' + # the above example would create an object based on the class providers/dummy.rb + if $config[:providers].nil? || $config[:providers][provider_name.to_sym].nil? || $config[:providers][provider_name.to_sym]['provider_class'].nil? + provider_class = provider_name + else + provider_class = $config[:providers][provider_name.to_sym]['provider_class'] + end begin - $providers[provider_name] = create_provider_object($config, $logger, $metrics, provider_name, {}) if $providers[provider_name].nil? + $providers[provider_name] = create_provider_object($config, $logger, $metrics, provider_class, provider_name, {}) if $providers[provider_name].nil? rescue => err $logger.log('s', "Error while creating provider for pool #{pool['name']}: #{err}") raise diff --git a/lib/vmpooler/providers/vsphere.rb b/lib/vmpooler/providers/vsphere.rb index ee6b3bcb0..d4ec7c1d3 100644 --- a/lib/vmpooler/providers/vsphere.rb +++ b/lib/vmpooler/providers/vsphere.rb @@ -35,6 +35,7 @@ def initialize(config, logger, metrics, name, options) end end + # name of the provider class def name 'vsphere' end diff --git a/spec/unit/pool_manager_spec.rb b/spec/unit/pool_manager_spec.rb index de7c11ea1..f642f6ae3 100644 --- a/spec/unit/pool_manager_spec.rb +++ b/spec/unit/pool_manager_spec.rb @@ -1754,7 +1754,7 @@ it 'should call create_provider_object idempotently' do # Even though there are two pools using the vsphere provider (the default), it should only # create the provider object once. - expect(subject).to receive(:create_provider_object).with(Object, Object, Object, 'vsphere', Object).and_return(vsphere_provider) + expect(subject).to receive(:create_provider_object).with(Object, Object, Object, 'vsphere', 'vsphere', Object).and_return(vsphere_provider) subject.execute!(1,0) end @@ -1774,6 +1774,50 @@ end end + context 'creating multiple vsphere Providers' do + let(:vsphere_provider) { double('vsphere_provider') } + let(:vsphere_provider2) { double('vsphere_provider') } + let(:provider1) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, 'vsphere', provider_options) } + let(:provider2) { Vmpooler::PoolManager::Provider::Base.new(config, logger, metrics, 'secondvsphere', provider_options) } + let(:config) { + YAML.load(<<-EOT +--- +:providers: + :vsphere: + server: 'blah1' + provider_class: 'vsphere' + :secondvsphere: + server: 'blah2' + provider_class: 'vsphere' +:pools: + - name: #{pool} + provider: 'vsphere' + - name: 'secondpool' + provider: 'secondvsphere' +EOT + )} + + it 'should call create_provider_object twice' do + # The two pools use a different provider name, but each provider_class is vsphere + expect(subject).to receive(:create_provider_object).with(Object, Object, Object, "vsphere", "vsphere", Object).and_return(vsphere_provider) + expect(subject).to receive(:create_provider_object).with(Object, Object, Object, "vsphere", "secondvsphere", Object).and_return(vsphere_provider2) + subject.execute!(1,0) + end + + it 'should have vsphere providers with different servers' do + allow(subject).to receive(:get_provider_for_pool).with(pool).and_return(provider1) + result = subject.get_provider_for_pool(pool) + allow(provider1).to receive(:provider_config).and_call_original + expect(result.provider_config['server']).to eq('blah1') + + allow(subject).to receive(:get_provider_for_pool).with('secondpool').and_return(provider2) + result = subject.get_provider_for_pool('secondpool') + allow(provider1).to receive(:provider_config).and_call_original + expect(result.provider_config['server']).to eq('blah2') + subject.execute!(1,0) + end + end + context 'modify configuration on startup' do context 'move vSphere configuration to providers location' do let(:config) { diff --git a/vmpooler.yaml.example b/vmpooler.yaml.example index 86fd85223..6b0d4c616 100644 --- a/vmpooler.yaml.example +++ b/vmpooler.yaml.example @@ -32,6 +32,11 @@ # - insecure # Whether to ignore any HTTPS negotiation errors (e.g. untrusted self-signed certificates) # (optional: default true) +# +# - provider_class +# For multiple providers, specify one of the supported backing services (vsphere or dummy) +# (optional: will default to it's parent :key: name eg. 'vsphere') +# # Example: :vsphere: @@ -39,6 +44,23 @@ username: 'vmpooler' password: 'swimsw1msw!m' +# If you want to support more than one provider with different parameters (server, username or passwords) you have to specify the +# backing service in the provider_class configuration parameter for example 'vsphere' or 'dummy'. Each pool can specify +# the provider to use. +# +# Multiple providers example: + + :vsphere-pdx: + server: 'vsphere.pdx.company.com' + username: 'vmpooler-pdx' + password: 'swimsw1msw!m' + provider_class: 'vsphere' + :vsphere-bfs: + server: 'vsphere.bfs.company.com' + username: 'vmpooler-bfs' + password: 'swimsw1msw!m' + provider_class: 'vsphere' + # :dummy: # # The dummy backing service is a simple text file service that can be used @@ -417,6 +439,8 @@ # The name of the VM provider which manage this pool. This should match # a name in the :providers: section above e.g. vsphere # (required; will default to vsphere for backwards compatibility) +# If you have more than one provider, this is where you would choose which +# one to use for this pool # # - clone_target # Per-pool option to override the global 'clone_target' cluster.