From 5e62f69b5e0026c1615b11d18e5fa6d3d221dccb Mon Sep 17 00:00:00 2001 From: Takashi Kajinami Date: Thu, 28 Oct 2021 22:50:37 +0900 Subject: [PATCH] Use system scope credentials in providers This change enforces usage of system scope credentials to manage flavors and aggregates, following the new policy rules for SRBAC support in nova. Depends-on: https://review.opendev.org/815311 Change-Id: Ic87422ae98943054ee58343157301d8fc780211f --- lib/puppet/provider/nova.rb | 110 ++++----- .../provider/nova_aggregate/openstack.rb | 25 +- lib/puppet/provider/nova_flavor/openstack.rb | 23 +- lib/puppet/provider/nova_service/openstack.rb | 4 +- manifests/keystone/authtoken.pp | 29 +++ spec/classes/nova_keystone_auth_spec.rb | 3 +- .../provider/nova_aggregate/openstack_spec.rb | 215 +++++++++--------- .../provider/nova_flavor/openstack_spec.rb | 30 ++- .../provider/nova_service/openstack_spec.rb | 2 +- spec/unit/provider/nova_spec.rb | 52 ----- 10 files changed, 221 insertions(+), 272 deletions(-) diff --git a/lib/puppet/provider/nova.rb b/lib/puppet/provider/nova.rb index 76a6cfa7d..ce7b22ac4 100644 --- a/lib/puppet/provider/nova.rb +++ b/lib/puppet/provider/nova.rb @@ -1,9 +1,3 @@ -# Run test ie with: rspec spec/unit/provider/nova_spec.rb - -# Add openstacklib code to $LOAD_PATH so that we can load this during -# standalone compiles without error. -File.expand_path('../../../../openstacklib/lib', File.dirname(__FILE__)).tap { |dir| $LOAD_PATH.unshift(dir) unless $LOAD_PATH.include?(dir) } - require 'puppet/util/inifile' require 'puppet/provider/openstack' require 'puppet/provider/openstack/auth' @@ -13,27 +7,44 @@ class Puppet::Provider::Nova < Puppet::Provider::Openstack extend Puppet::Provider::Openstack::Auth - def self.request(service, action, properties=nil) - begin - super - rescue Puppet::Error::OpenstackAuthInputError => error - nova_request(service, action, error, properties) - end + CLOUDS_FILENAME = '/etc/nova/clouds.yaml' + + def self.project_request(service, action, properties=nil, options={}) + self.request(service, action, properties, options, 'project') end - def self.nova_request(service, action, error, properties=nil) + def self.system_request(service, action, properties=nil, options={}) + self.request(service, action, properties, options, 'system') + end + + def self.request(service, action, properties=nil, options={}, scope='project') + super + rescue Puppet::Error::OpenstackAuthInputError => error + nova_request(service, action, error, properties, options, scope) + end + + def self.nova_request(service, action, error, properties=nil, options={}, scope='project') properties ||= [] - @credentials.username = nova_credentials['username'] - @credentials.password = nova_credentials['password'] - @credentials.project_name = nova_credentials['project_name'] - @credentials.auth_url = auth_endpoint - @credentials.user_domain_name = nova_credentials['user_domain_name'] - @credentials.project_domain_name = nova_credentials['project_domain_name'] - if nova_credentials['region_name'] - @credentials.region_name = nova_credentials['region_name'] + + unless @system_credential + @system_credential = Puppet::Provider::Openstack::CredentialsV3.new + @system_credential.cloud = 'system' + @system_credential.client_config_file = clouds_filename end - raise error unless @credentials.set? - Puppet::Provider::Openstack.request(service, action, properties, @credentials) + + unless @project_credential + @project_credential = Puppet::Provider::Openstack::CredentialsV3.new + @project_credential.cloud = 'project' + @project_credential.client_config_file = clouds_filename + end + + if scope == 'system' + cred = @system_credential + else + cred = @project_credential + end + + Puppet::Provider::Openstack.request(service, action, properties, cred, options) end def self.nova_manage_request(*args) @@ -70,58 +81,13 @@ class Puppet::Provider::Nova < Puppet::Provider::Openstack @nova_conf end - def self.nova_credentials - @nova_credentials ||= get_nova_credentials - end - - def nova_credentials - self.class.nova_credentials - end - - def self.get_nova_credentials - #needed keys for authentication - auth_keys = ['auth_url', 'project_name', 'username', 'password'] - conf = nova_conf - if conf and conf['keystone_authtoken'] and - auth_keys.all?{|k| !conf['keystone_authtoken'][k].nil?} - creds = Hash[ auth_keys.map \ - { |k| [k, conf['keystone_authtoken'][k].strip] } ] - if !conf['keystone_authtoken']['region_name'].nil? - creds['region_name'] = conf['keystone_authtoken']['region_name'].strip - end - - if !conf['keystone_authtoken']['project_domain_name'].nil? - creds['project_domain_name'] = conf['keystone_authtoken']['project_domain_name'].strip - else - creds['project_domain_name'] = 'Default' - end - - if !conf['keystone_authtoken']['user_domain_name'].nil? - creds['user_domain_name'] = conf['keystone_authtoken']['user_domain_name'].strip - else - creds['user_domain_name'] = 'Default' - end - - return creds - else - raise(Puppet::Error, "File: #{conf_filename} does not contain all " + - "required sections. Nova types will not work if nova is not " + - "correctly configured.") - end - end - - def self.get_auth_endpoint - q = nova_credentials - "#{q['auth_url']}" - end - - def self.auth_endpoint - @auth_endpoint ||= get_auth_endpoint + def self.clouds_filename + CLOUDS_FILENAME end def self.reset - @nova_conf = nil - @nova_credentials = nil + @project_credential = nil + @system_credential = nil end def self.str2hash(s) diff --git a/lib/puppet/provider/nova_aggregate/openstack.rb b/lib/puppet/provider/nova_aggregate/openstack.rb index baa744e1b..68d474e0c 100644 --- a/lib/puppet/provider/nova_aggregate/openstack.rb +++ b/lib/puppet/provider/nova_aggregate/openstack.rb @@ -13,8 +13,8 @@ Puppet::Type.type(:nova_aggregate).provide( mk_resource_methods def self.instances - request('aggregate', 'list').collect do |el| - attrs = request('aggregate', 'show', el[:name]) + system_request('aggregate', 'list').collect do |el| + attrs = system_request('aggregate', 'show', el[:name]) properties = parsestring(attrs[:properties]) rescue nil new( :ensure => :present, @@ -43,7 +43,7 @@ Puppet::Type.type(:nova_aggregate).provide( def self.get_known_hosts # get list of hosts known to be active from openstack - return request('compute service', 'list', ['--service', 'nova-compute']).map{|el| el[:host]} + return system_request('compute service', 'list', ['--service', 'nova-compute']).map{|el| el[:host]} end def exists? @@ -53,9 +53,9 @@ Puppet::Type.type(:nova_aggregate).provide( def destroy @property_hash[:hosts].each do |h| properties = [@property_hash[:name], h] - self.class.request('aggregate', 'remove host', properties) + self.class.system_request('aggregate', 'remove host', properties) end - self.class.request('aggregate', 'delete', @property_hash[:name]) + self.class.system_request('aggregate', 'delete', @property_hash[:name]) end def create @@ -68,7 +68,7 @@ Puppet::Type.type(:nova_aggregate).provide( properties << "--property" << "#{key}=#{value}" end end - @property_hash = self.class.request('aggregate', 'create', properties) + @property_hash = self.class.system_request('aggregate', 'create', properties) if not @resource[:hosts].nil? and not @resource[:hosts].empty? # filter host list by known hosts if filter_hosts is set if @resource[:filter_hosts] == :true @@ -76,13 +76,14 @@ Puppet::Type.type(:nova_aggregate).provide( end @resource[:hosts].each do |host| properties = [@property_hash[:name], host] - self.class.request('aggregate', 'add host', properties) + self.class.system_request('aggregate', 'add host', properties) end end + @property_hash[:ensure] = :present end def availability_zone=(value) - self.class.request('aggregate', 'set', [ @resource[:name], '--zone', @resource[:availability_zone] ]) + self.class.system_request('aggregate', 'set', [ @resource[:name], '--zone', @resource[:availability_zone] ]) end def metadata=(value) @@ -92,13 +93,13 @@ Puppet::Type.type(:nova_aggregate).provide( (@property_hash[:metadata].keys - @resource[:metadata].keys).each do |key| properties << "--property" << "#{key}" end - self.class.request('aggregate', 'unset', properties) + self.class.system_request('aggregate', 'unset', properties) end properties = [@resource[:name] ] @resource[:metadata].each do |key, value| properties << "--property" << "#{key}=#{value}" end - self.class.request('aggregate', 'set', properties) + self.class.system_request('aggregate', 'set', properties) end def hosts=(value) @@ -109,11 +110,11 @@ Puppet::Type.type(:nova_aggregate).provide( if not @property_hash[:hosts].nil? # remove hosts that are not present in update (@property_hash[:hosts] - value).each do |host| - self.class.request('aggregate', 'remove host', [@property_hash[:id], host]) + self.class.system_request('aggregate', 'remove host', [@property_hash[:id], host]) end # add hosts that are not already present (value - @property_hash[:hosts]).each do |host| - self.class.request('aggregate', 'add host', [@property_hash[:id], host]) + self.class.system_request('aggregate', 'add host', [@property_hash[:id], host]) end end end diff --git a/lib/puppet/provider/nova_flavor/openstack.rb b/lib/puppet/provider/nova_flavor/openstack.rb index ebd630243..6ce0ee3bc 100644 --- a/lib/puppet/provider/nova_flavor/openstack.rb +++ b/lib/puppet/provider/nova_flavor/openstack.rb @@ -26,16 +26,16 @@ Puppet::Type.type(:nova_flavor).provide( (opts << '--vcpus' << @resource[:vcpus]) if @resource[:vcpus] (opts << '--swap' << @resource[:swap]) if @resource[:swap] (opts << '--rxtx-factor' << @resource[:rxtx_factor]) if @resource[:rxtx_factor] - @property_hash = self.class.request('flavor', 'create', opts) + @property_hash = self.class.system_request('flavor', 'create', opts) if @resource[:properties] prop_opts = [@resource[:name]] prop_opts << props_to_s(@resource[:properties]) - self.class.request('flavor', 'set', prop_opts) + self.class.system_request('flavor', 'set', prop_opts) end - if @resource[:project] + if @resource[:project] and @resource[:project] != '' proj_opts = [@resource[:name]] proj_opts << '--project' << @resource[:project] - self.class.request('flavor', 'set', proj_opts) + self.class.system_request('flavor', 'set', proj_opts) end @property_hash[:ensure] = :present end @@ -45,7 +45,7 @@ Puppet::Type.type(:nova_flavor).provide( end def destroy - self.class.request('flavor', 'delete', @property_hash[:id]) + self.class.system_request('flavor', 'delete', @property_hash[:id]) end mk_resource_methods @@ -87,8 +87,8 @@ Puppet::Type.type(:nova_flavor).provide( end def self.instances - request('flavor', 'list', ['--long', '--all']).collect do |attrs| - project = request('flavor', 'show', [attrs[:id], '-c', 'access_project_ids']) + system_request('flavor', 'list', ['--long', '--all']).collect do |attrs| + project = system_request('flavor', 'show', [attrs[:id], '-c', 'access_project_ids']) access_project_ids = project[:access_project_ids] # Client can return None and this should be considered as '' @@ -133,17 +133,17 @@ Puppet::Type.type(:nova_flavor).provide( opts = [@resource[:name]] opts << props_to_s(@property_flush[:properties]) - self.class.request('flavor', 'set', opts) + self.class.system_request('flavor', 'set', opts) @property_flush.clear end unless @project_flush.empty? opts = [@resource[:name]] - unless @project_flush[:project] + unless @project_flush[:project] == '' opts << '--project' << @project_flush[:project] - self.class.request('flavor', 'set', opts) + self.class.system_request('flavor', 'set', opts) else opts << '--project' << @property_hash[:project] - self.class.request('flavor', 'unset', opts) + self.class.system_request('flavor', 'unset', opts) end @project_flush.clear end @@ -154,4 +154,3 @@ Puppet::Type.type(:nova_flavor).provide( props.flat_map{ |k, v| ['--property', "#{k}=#{v}"] } end end - diff --git a/lib/puppet/provider/nova_service/openstack.rb b/lib/puppet/provider/nova_service/openstack.rb index b0eee89eb..98976eb2f 100644 --- a/lib/puppet/provider/nova_service/openstack.rb +++ b/lib/puppet/provider/nova_service/openstack.rb @@ -14,7 +14,7 @@ Puppet::Type.type(:nova_service).provide( def self.instances hosts = {} - request('compute service', 'list').collect do |host_svc| + system_request('compute service', 'list').collect do |host_svc| hname = host_svc[:host] if hosts[hname].nil? hosts[hname] = Hash.new {|h,k| h[k]=[]} @@ -53,7 +53,7 @@ Puppet::Type.type(:nova_service).provide( svcname_id_map.each do |service_name, id| if (@resource[:service_name].empty? || (@resource[:service_name].include? service_name)) - self.class.request('compute service', 'delete', id) + self.class.system_request('compute service', 'delete', id) end end @property_hash[:ensure] = :absent diff --git a/manifests/keystone/authtoken.pp b/manifests/keystone/authtoken.pp index 24585b2ca..23d5eeb87 100644 --- a/manifests/keystone/authtoken.pp +++ b/manifests/keystone/authtoken.pp @@ -284,4 +284,33 @@ class nova::keystone::authtoken( service_type => $service_type, interface => $interface; } + + + $system_scope_real = is_service_default($system_scope) ? { + true => pick($::nova::keystone::auth::system_scope, 'all'), + default => $system_scope, + } + + $region_name_real = is_service_default($region_name) ? { + true => undef, + default => $region_name, + } + + $interface_real = is_service_default($interface) ? { + true => undef, + default => $interface, + } + + openstacklib::clouds { '/etc/nova/clouds.yaml': + username => $username, + password => $password, + auth_url => $auth_url, + project_name => $project_name, + system_scope => $system_scope_real, + region_name => $region_name_real, + interface => $interface_real, + } + Anchor['nova::config::begin'] + -> Openstacklib::Clouds['/etc/nova/clouds.yaml'] + -> Anchor['nova::config::end'] } diff --git a/spec/classes/nova_keystone_auth_spec.rb b/spec/classes/nova_keystone_auth_spec.rb index 7c195193b..e82c81723 100644 --- a/spec/classes/nova_keystone_auth_spec.rb +++ b/spec/classes/nova_keystone_auth_spec.rb @@ -50,7 +50,8 @@ describe 'nova::keystone::auth' do :system_roles => ['admin', 'member', 'reader'], :public_url => 'https://10.10.10.10:80', :internal_url => 'http://10.10.10.11:81', - :admin_url => 'http://10.10.10.12:81' } + :admin_url => 'http://10.10.10.12:81', + } end it { is_expected.to contain_keystone__resource__service_identity('nova').with( diff --git a/spec/unit/provider/nova_aggregate/openstack_spec.rb b/spec/unit/provider/nova_aggregate/openstack_spec.rb index fc02197c7..d1afa8b24 100644 --- a/spec/unit/provider/nova_aggregate/openstack_spec.rb +++ b/spec/unit/provider/nova_aggregate/openstack_spec.rb @@ -2,14 +2,12 @@ require 'puppet' require 'spec_helper' require 'puppet/provider/nova_aggregate/openstack' -provider_class = Puppet::Type.type(:nova_aggregate).provider(:openstack) +describe Puppet::Type.type(:nova_aggregate).provider(:openstack) do -describe provider_class do - - shared_examples 'authenticated with environment variables' do + let(:set_env) do ENV['OS_USERNAME'] = 'test' ENV['OS_PASSWORD'] = 'abc123' - ENV['OS_PROJECT_NAME'] = 'test' + ENV['OS_SYSTEM_SCOPE'] = 'all' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' end @@ -30,96 +28,93 @@ describe provider_class do end let(:provider) do - provider_class.new(resource) + described_class.new(resource) end - it_behaves_like 'authenticated with environment variables' do - describe '#instances' do - it 'finds existing aggregates' do - provider_class.expects(:openstack) - .with('aggregate', 'list', '--quiet', '--format', 'csv', []) - .returns('"ID","Name","Availability Zone" + before(:each) do + set_env + end + + describe '#instances' do + it 'finds existing aggregates' do + described_class.expects(:openstack) + .with('aggregate', 'list', '--quiet', '--format', 'csv', []) + .returns('"ID","Name","Availability Zone" just,"simple","just" ') - provider_class.expects(:openstack) - .with('aggregate', 'show', '--format', 'shell', 'simple') - .returns('"id="just" + described_class.expects(:openstack) + .with('aggregate', 'show', '--format', 'shell', 'simple') + .returns('"id="just" name="simple" availability_zone=just" properties="key1=\'tomato\', key2=\'mushroom\'" hosts="[]" ') - instances = provider_class.instances - expect(instances.count).to eq(1) - expect(instances[0].name).to eq('simple') - expect(instances[0].metadata).to eq({"key1"=>"tomato", "key2"=>"mushroom"}) - end + instances = described_class.instances + expect(instances.count).to eq(1) + expect(instances[0].name).to eq('simple') + expect(instances[0].metadata).to eq({"key1"=>"tomato", "key2"=>"mushroom"}) end + end - describe '#create' do - it 'creates aggregate' do - provider.class.stubs(:openstack) - .with('aggregate', 'list', '--quiet', '--format', 'csv', '--long') - .returns('"ID","Name","Availability Zone","Properties" -') - provider.class.stubs(:openstack) - .with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', '--property', 'nice=cookie' ]) - .returns('name="just" + describe '#create' do + it 'creates aggregate' do + described_class.expects(:openstack) + .with('aggregate', 'create', '--format', 'shell', + ['just', '--zone', 'simple', '--property', 'nice=cookie' ]) + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[]" ') - provider.class.stubs(:openstack) - .with('aggregate', 'add host', ['just', 'example']) - .returns('name="just" + described_class.expects(:openstack) + .with('aggregate', 'add host', ['just', 'example']) + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[u\'example\']" ') - provider.exists? - provider.create - expect(provider.exists?).to be_falsey - end + provider.create + expect(provider.exists?).to be_truthy + end + end + + describe '#destroy' do + it 'removes aggregate with hosts' do + described_class.expects(:openstack) + .with('aggregate', 'remove host', ['just', 'example']) + described_class.expects(:openstack) + .with('aggregate', 'delete', 'just') + provider.instance_variable_set(:@property_hash, aggregate_attrs) + provider.destroy + expect(provider.exists?).to be_falsey + end + end + + describe '#pythondict2hash' do + it 'should return a hash with key-value when provided with a unicode python dict' do + s = "{u'key': 'value', u'key2': 'value2'}" + expect(described_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"}) end - describe '#destroy' do - it 'removes aggregate with hosts' do - provider_class.expects(:openstack) - .with('aggregate', 'remove host', ['just', 'example']) - provider_class.expects(:openstack) - .with('aggregate', 'delete', 'just') - provider.instance_variable_set(:@property_hash, aggregate_attrs) - provider.destroy - expect(provider.exists?).to be_falsey - end + it 'should return a hash with key-value when provided with a python dict' do + s = "{'key': 'value', 'key2': 'value2'}" + expect(described_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"}) + end + end + + describe '#parsestring' do + it 'should call string2hash when provided with a string' do + s = "key='value', key2='value2'" + expect(described_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"}) end - describe '#pythondict2hash' do - it 'should return a hash with key-value when provided with a unicode python dict' do - s = "{u'key': 'value', u'key2': 'value2'}" - expect(provider_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"}) - end - - it 'should return a hash with key-value when provided with a python dict' do - s = "{'key': 'value', 'key2': 'value2'}" - expect(provider_class.pythondict2hash(s)).to eq({"key"=>"value", "key2"=>"value2"}) - end + it 'should call pythondict2hash when provided with a hash' do + s = "{u'key': 'value', u'key2': 'value2'}" + expect(described_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"}) end - - describe '#parsestring' do - it 'should call string2hash when provided with a string' do - s = "key='value', key2='value2'" - expect(provider_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"}) - end - - it 'should call pythondict2hash when provided with a hash' do - s = "{u'key': 'value', u'key2': 'value2'}" - expect(provider_class.parsestring(s)).to eq({"key"=>"value", "key2"=>"value2"}) - end - end - end end @@ -143,83 +138,83 @@ hosts="[u\'example\']" end let(:provider) do - provider_class.new(resource) + described_class.new(resource) end - it_behaves_like 'authenticated with environment variables' do + before(:each) do + set_env + end - # create an aggregate and actually aggregate hosts to it - describe 'create aggregate and add/remove hosts with filter_hosts toggled' do + # create an aggregate and actually aggregate hosts to it + describe 'create aggregate and add/remove hosts with filter_hosts toggled' do - it 'creates aggregate with filter_hosts toggled' do + it 'creates aggregate with filter_hosts toggled' do - provider.class.stubs(:get_known_hosts) - .returns(['known', 'known_too']) + provider.class.stubs(:get_known_hosts) + .returns(['known', 'known_too']) - # these expectations are the actual tests that check the provider's behaviour - # and make sure only known hosts ('known' is the only known host) will be - # aggregated. + # these expectations are the actual tests that check the provider's behaviour + # and make sure only known hosts ('known' is the only known host) will be + # aggregated. - provider_class.expects(:openstack) - .with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', "--property", "nice=cookie"]) - .once - .returns('name="just" + described_class.expects(:openstack) + .with('aggregate', 'create', '--format', 'shell', ['just', '--zone', 'simple', "--property", "nice=cookie"]) + .once + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[]" ') - provider_class.expects(:openstack) - .with('aggregate', 'add host', ['just', 'known']) - .once - .returns('name="just" + described_class.expects(:openstack) + .with('aggregate', 'add host', ['just', 'known']) + .once + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[u\'known\']" ') - provider_class.expects(:openstack) - .with('aggregate', 'add host', ['just', 'known_too']) - .once - .returns('name="just" + described_class.expects(:openstack) + .with('aggregate', 'add host', ['just', 'known_too']) + .once + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[u\'known\', u\'known_too\']" ') - provider_class.expects(:openstack) - .with('aggregate', 'remove host', ['just', 'known']) - .once - .returns('name="just" + described_class.expects(:openstack) + .with('aggregate', 'remove host', ['just', 'known']) + .once + .returns('name="just" id="just" availability_zone="simple" properties="{u\'nice\': u\'cookie\'}" hosts="[u\'known_too\']" ') - # this creates a provider with the attributes defined above as 'aggregate_attrs' - # and tries to add some hosts and then to remove some hosts. - # the hosts will be filtered against known active hosts and the expectations - # described above are the actual tests that check the provider's behaviour + # this creates a provider with the attributes defined above as 'aggregate_attrs' + # and tries to add some hosts and then to remove some hosts. + # the hosts will be filtered against known active hosts and the expectations + # described above are the actual tests that check the provider's behaviour - provider.create - property_hash = provider.instance_variable_get(:@property_hash) - property_hash[:hosts] = ['known'] - provider.instance_variable_set(:@property_hash, property_hash) + provider.create + property_hash = provider.instance_variable_get(:@property_hash) + property_hash[:hosts] = ['known'] + provider.instance_variable_set(:@property_hash, property_hash) - provider.hosts = ['known', 'known_too', 'unknown'] - property_hash = provider.instance_variable_get(:@property_hash) - property_hash[:hosts] = ['known', 'known_too'] - provider.instance_variable_set(:@property_hash, property_hash) + provider.hosts = ['known', 'known_too', 'unknown'] + property_hash = provider.instance_variable_get(:@property_hash) + property_hash[:hosts] = ['known', 'known_too'] + provider.instance_variable_set(:@property_hash, property_hash) - provider.hosts = ['known_too'] + provider.hosts = ['known_too'] - end end - end end diff --git a/spec/unit/provider/nova_flavor/openstack_spec.rb b/spec/unit/provider/nova_flavor/openstack_spec.rb index 5bd995740..a474620d6 100644 --- a/spec/unit/provider/nova_flavor/openstack_spec.rb +++ b/spec/unit/provider/nova_flavor/openstack_spec.rb @@ -2,9 +2,14 @@ require 'puppet' require 'spec_helper' require 'puppet/provider/nova_flavor/openstack' -provider_class = Puppet::Type.type(:nova_flavor).provider(:openstack) +describe Puppet::Type.type(:nova_flavor).provider(:openstack) do -describe provider_class do + let(:set_env) do + ENV['OS_USERNAME'] = 'test' + ENV['OS_PASSWORD'] = 'abc123' + ENV['OS_SYSTEM_SCOPE'] = 'all' + ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' + end describe 'managing flavors' do let(:flavor_attrs) do @@ -23,17 +28,20 @@ describe provider_class do end let(:provider) do - provider_class.new(resource) + described_class.new(resource) + end + + before(:each) do + set_env end describe '#create' do it 'creates flavor' do - provider.class.stubs(:openstack) - .with('flavor', 'list', ['--long', '--all']) - .returns('"ID", "Name", "RAM", "Disk", "Ephemeral", "VCPUs", "Is Public", "Swap", "RXTX Factor", "Properties"') - provider.class.stubs(:openstack) - .with('flavor', 'create', 'shell', ['example', '--public', '--id', '1', '--ram', '512', '--disk', '1', '--vcpus', '1']) - .returns('os-flv-disabled:disabled="False" + described_class.expects(:openstack) + .with('flavor', 'create', '--format', 'shell', + ['example', '--public', '--id', '1', '--ram', '512', + '--disk', '1', '--vcpus', '1']) + .returns('os-flv-disabled:disabled="False" os-flv-ext-data:ephemeral="0" disk="1" id="1" @@ -43,12 +51,14 @@ ram="512" rxtx_factor="1.0" swap="" vcpus="1"') + provider.create + expect(provider.exists?).to be_truthy end end describe '#destroy' do it 'removes flavor' do - provider_class.expects(:openstack) + described_class.expects(:openstack) .with('flavor', 'delete', '1') provider.instance_variable_set(:@property_hash, flavor_attrs) provider.destroy diff --git a/spec/unit/provider/nova_service/openstack_spec.rb b/spec/unit/provider/nova_service/openstack_spec.rb index 13311d110..dc194d915 100644 --- a/spec/unit/provider/nova_service/openstack_spec.rb +++ b/spec/unit/provider/nova_service/openstack_spec.rb @@ -9,7 +9,7 @@ describe provider_class do shared_examples 'authenticated with environment variables' do ENV['OS_USERNAME'] = 'test' ENV['OS_PASSWORD'] = 'abc123' - ENV['OS_PROJECT_NAME'] = 'test' + ENV['OS_SYSTEM_SCOPE'] = 'all' ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000/v3' end diff --git a/spec/unit/provider/nova_spec.rb b/spec/unit/provider/nova_spec.rb index 04dee15fd..f3b66f813 100644 --- a/spec/unit/provider/nova_spec.rb +++ b/spec/unit/provider/nova_spec.rb @@ -9,62 +9,10 @@ describe Puppet::Provider::Nova do described_class end - let :credential_hash do - { - 'auth_url' => 'https://192.168.56.210:5000/v3/', - 'project_name' => 'admin_tenant', - 'username' => 'admin', - 'password' => 'password', - 'region_name' => 'Region1', - } - end - - let :auth_endpoint do - 'https://192.168.56.210:5000/v3/' - end - - let :credential_error do - /Nova types will not work/ - end - after :each do klass.reset end - describe 'when determining credentials' do - - it 'should fail if config is empty' do - conf = {} - klass.expects(:nova_conf).returns(conf) - expect do - klass.nova_credentials - end.to raise_error(Puppet::Error, credential_error) - end - - it 'should fail if config does not have keystone_authtoken section.' do - conf = {'foo' => 'bar'} - klass.expects(:nova_conf).returns(conf) - expect do - klass.nova_credentials - end.to raise_error(Puppet::Error, credential_error) - end - - it 'should fail if config does not contain all auth params' do - conf = {'keystone_authtoken' => {'invalid_value' => 'foo'}} - klass.expects(:nova_conf).returns(conf) - expect do - klass.nova_credentials - end.to raise_error(Puppet::Error, credential_error) - end - - it 'should use specified uri in the auth endpoint' do - conf = {'keystone_authtoken' => credential_hash} - klass.expects(:nova_conf).returns(conf) - expect(klass.get_auth_endpoint).to eq(auth_endpoint) - end - - end - describe 'when parse a string line' do it 'should return the same string' do res = klass.str2hash("zone1")