Add type/provider to manage share types

This change introduces a native manila_type resource to manage share
types in Manila. Legacy defined resource types are deprecated in favor
of the new resource.

Change-Id: I29fa23baca0f8ceec955d0dcb7aa7e3fd36fce55
This commit is contained in:
Takashi Kajinami 2022-02-18 23:31:25 +09:00
parent d90216b44e
commit 74298c4edd
10 changed files with 527 additions and 4 deletions

View File

@ -0,0 +1,100 @@
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'
require 'puppet/provider/openstack/credentials'
class Puppet::Provider::Manila < Puppet::Provider::Openstack
extend Puppet::Provider::Openstack::Auth
def self.conf_filename
'/etc/manila/manila.conf'
end
def self.manila_conf
return @manila_conf if @manila_conf
@manila_conf = Puppet::Util::IniConfig::File.new
@manila_conf.read(conf_filename)
@manila_conf
end
def self.request(service, action, properties=nil)
begin
super
rescue Puppet::Error::OpenstackAuthInputError, Puppet::Error::OpenstackUnauthorizedError => error
manila_request(service, action, error, properties)
end
end
def self.manila_request(service, action, error, properties=nil)
properties ||= []
@credentials.username = manila_credentials['username']
@credentials.password = manila_credentials['password']
@credentials.project_name = manila_credentials['project_name']
@credentials.auth_url = auth_endpoint
@credentials.user_domain_name = manila_credentials['user_domain_name']
@credentials.project_domain_name = manila_credentials['project_domain_name']
if manila_credentials['region_name']
@credentials.region_name = manila_credentials['region_name']
end
raise error unless @credentials.set?
Puppet::Provider::Openstack.request(service, action, properties, @credentials)
end
def self.manila_credentials
@manila_credentials ||= get_manila_credentials
end
def manila_credentials
self.class.manila_credentials
end
def self.get_manila_credentials
auth_keys = ['auth_url', 'project_name', 'username',
'password']
conf = manila_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']['project_domain_name']
creds['project_domain_name'] = conf['keystone_authtoken']['project_domain_name']
else
creds['project_domain_name'] = 'Default'
end
if conf['keystone_authtoken']['user_domain_name']
creds['user_domain_name'] = conf['keystone_authtoken']['user_domain_name']
else
creds['user_domain_name'] = 'Default'
end
if conf['keystone_authtoken']['region_name']
creds['region_name'] = conf['keystone_authtoken']['region_name']
end
return creds
else
raise(Puppet::Error, "File: #{conf_filename} does not contain all " +
"required sections. Manila types will not work if manila is not " +
"correctly configured.")
end
end
def self.get_auth_endpoint
q = manila_credentials
"#{q['auth_url']}"
end
def self.auth_endpoint
@auth_endpoint ||= get_auth_endpoint
end
def self.reset
@manila_conf = nil
@manila_credentials = nil
@auth_endpoint = nil
end
end

View File

@ -0,0 +1,154 @@
require File.join(File.dirname(__FILE__), '..','..','..', 'puppet/provider/manila')
Puppet::Type.type(:manila_type).provide(
:openstack,
:parent => Puppet::Provider::Manila
) do
desc 'Provider for manila types.'
@credentials = Puppet::Provider::Openstack::CredentialsV3.new
mk_resource_methods
def initialize(value={})
super(value)
@property_flush = {}
end
def self.do_not_manage
@do_not_manage
end
def self.do_not_manage=(value)
@do_not_manage = value
end
def create
if self.class.do_not_manage
fail("Not managing Manila_type[#{@resource[:name]}] due to earlier Manila API failures.")
end
opts = [@resource[:name]]
opts << @resource[:driver_handles_share_servers].to_s.capitalize
opts << '--public' << @resource[:is_public].to_s.capitalize
opts << '--snapshot-support' << @resource[:snapshot_support].to_s.capitalize
opts << '--create-share-from-snapshot-support' << @resource[:create_share_from_snapshot_support].to_s.capitalize
opts << '--revert-to-snapshot-support' << @resource[:revert_to_snapshot_support].to_s.capitalize
opts << '--mount-snapshot-support' << @resource[:mount_snapshot_support].to_s.capitalize
self.class.request('share type', 'create', opts)
[
:name,
:is_public,
:driver_handles_share_servers,
:snapshot_support,
:create_share_from_snapshot_support,
:revert_to_snapshot_support,
:mount_snapshot_support
].each do |attr|
@property_hash[attr] = @resource[attr]
end
@property_hash[:ensure] = :present
end
def destroy
if self.class.do_not_manage
fail("Not managing Manila_type[#{@resource[:name]}] due to earlier Manila API failures.")
end
self.class.request('share type', 'delete', name)
@property_hash.clear
@property_hash[:ensure] = :absent
end
def exists?
@property_hash[:ensure] == :present
end
def self.parse_specs(specs)
JSON.parse(specs.gsub(/'/, '"'))
end
def self.instances
self.do_not_manage = true
list = request('share type', 'list').collect do |type|
required_extra_specs = self.parse_specs(type[:required_extra_specs])
optional_extra_specs = self.parse_specs(type[:optional_extra_specs])
new({
:name => type[:name],
:ensure => :present,
:id => type[:id],
:is_public => (type[:visibility] == 'public'),
:driver_handles_share_servers => (required_extra_specs['driver_handles_share_servers'] == 'True'),
:snapshot_support => (optional_extra_specs['snapshot_support'] == 'True'),
:create_share_from_snapshot_support => (optional_extra_specs['create_share_from_snapshot_support'] == 'True'),
:revert_to_snapshot_support => (optional_extra_specs['revert_to_snapshot_support'] == 'True'),
:mount_snapshot_support => (optional_extra_specs['mount_snapshot_support'] == 'True'),
})
end
self.do_not_manage = false
list
end
def self.prefetch(resources)
types = instances
resources.keys.each do |name|
if provider = types.find{ |type| type.name == name }
resources[name].provider = provider
end
end
end
def flush
if !@property_flush.empty?
opts = [@resource[:name]]
if @property_flush.has_key?(:is_public)
opts << '--public' << @property_flush[:is_public].to_s.capitalize
end
if @property_flush.has_key?(:snapshot_support)
opts << '--snapshot-support' << @property_flush[:snapshot_support].to_s.capitalize
end
if @property_flush.has_key?(:create_share_from_snapshot_support)
opts << '--create-share-from-snapshot-support' << @property_flush[:create_share_from_snapshot_support].to_s.capitalize
end
if @property_flush.has_key?(:revert_to_snapshot_support)
opts << '--revert-to-snapshot-support' << @property_flush[:revert_to_snapshot_support].to_s.capitalize
end
if @property_flush.has_key?(:mount_snapshot_support)
opts << '--mount-snapshot-support' << @property_flush[:mount_snapshot_support].to_s.capitalize
end
self.class.request('share type', 'set', opts)
@property_flush.clear
end
end
[
:is_public,
:snapshot_support,
:create_share_from_snapshot_support,
:revert_to_snapshot_support,
:mount_snapshot_support
].each do |attr|
define_method(attr.to_s + "=") do |value|
if self.class.do_not_manage
fail("Not managing Manila_type[#{@resource[:name]}] due to earlier Manila API failures.")
end
@property_flush[attr] = value
end
end
[
:driver_handles_share_servers,
].each do |attr|
define_method(attr.to_s + "=") do |value|
fail("Property #{attr.to_s} does not support being updated")
end
end
end

View File

@ -0,0 +1,50 @@
Puppet::Type.newtype(:manila_type) do
desc 'Type for managing manila types.'
ensurable
newparam(:name, :namevar => true) do
newvalues(/\S+/)
end
newparam(:is_public, :boolean => true) do
desc 'Whether the type is public or not. Default to `true`'
newvalues(:true, :false)
defaultto true
end
newparam(:driver_handles_share_servers, :boolean => true) do
desc 'Whether the driver handles share servers. Default to `false`'
newvalues(:true, :false)
defaultto false
end
newparam(:snapshot_support, :boolean => true) do
desc 'Filter backends by their capability to create share snapshots'
newvalues(:true, :false)
defaultto false
end
newparam(:create_share_from_snapshot_support, :boolean => true) do
desc 'Filter backends by their capability to create shares from snapshots.'
newvalues(:true, :false)
defaultto false
end
newparam(:revert_to_snapshot_support, :boolean => true) do
desc 'Filter backends by their capability to revert shares to snapshots.'
newvalues(:true, :false)
defaultto false
end
newparam(:mount_snapshot_support, :boolean => true) do
desc 'Filter backends by their capability to mount share snapshots.'
newvalues(:true, :false)
defaultto false
end
autorequire(:anchor) do
['manila::service::end']
end
end

View File

@ -1,5 +1,6 @@
# ==Define: manila::type
#
# DEPRECATED!!
# Creates manila type and assigns backends.
#
# === Parameters
@ -48,8 +49,7 @@ define manila::type (
include manila::deps
include manila::client
# TODO: (xarses) This should be moved to a ruby provider so that among other
# reasons, the credential discovery magic can occur like in neutron.
warning('The manila::type resource type is deprecated. Use the manila_type resource instead')
$manila_env = [
"OS_TENANT_NAME=${os_tenant_name}",

View File

@ -41,8 +41,7 @@ define manila::type_set (
include manila::deps
include manila::client
# TODO: (xarses) This should be moved to a ruby provider so that among other
# reasons, the credential discovery magic can occur like in neutron.
warning('The manila::type_set resource type is deprecated. Use the manila_type resource instead')
$manila_env = [
"OS_TENANT_NAME=${os_tenant_name}",

View File

@ -0,0 +1,10 @@
---
features:
- |
The new ``manila_type`` resource has been added. This can be used to manage
share types in Manila.
deprecations:
- |
The ``manila::type`` resource type and the ``manila::type_set`` resource
type has been deprecated in favor of the new ``manila_type`` resource.

View File

@ -63,6 +63,9 @@ describe 'basic manila' do
class { 'manila::cron::db_purge': }
# missing: backends, share, service_instance
manila_type { 'sharetype':
}
EOS

View File

@ -0,0 +1,48 @@
require 'puppet'
require 'spec_helper'
require 'puppet/provider/manila'
require 'tempfile'
klass = Puppet::Provider::Manila
describe Puppet::Provider::Manila do
after :each do
klass.reset
end
describe 'when retrieving the auth credentials' do
it 'should fail if no auth params are passed and the manila config file does not have the expected contents' do
mock = {}
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
mock.expects(:read).with('/etc/manila/manila.conf')
expect do
klass.manila_credentials
end.to raise_error(Puppet::Error, /Manila types will not work/)
end
it 'should read conf file with all sections' do
creds_hash = {
'auth_url' => 'https://192.168.56.210:5000/v3/',
'project_name' => 'admin_tenant',
'username' => 'admin',
'password' => 'password',
'project_domain_name' => 'Default',
'user_domain_name' => 'Default',
}
mock = {
'keystone_authtoken' => {
'auth_url' => 'https://192.168.56.210:5000/v3/',
'project_name' => 'admin_tenant',
'username' => 'admin',
'password' => 'password',
}
}
Puppet::Util::IniConfig::File.expects(:new).returns(mock)
mock.expects(:read).with('/etc/manila/manila.conf')
expect(klass.manila_credentials).to eq(creds_hash)
end
end
end

View File

@ -0,0 +1,135 @@
require 'puppet'
require 'puppet/provider/manila_type/openstack'
provider_class = Puppet::Type.type(:manila_type).provider(:openstack)
describe provider_class do
let(:set_creds_env) do
ENV['OS_USERNAME'] = 'test'
ENV['OS_PASSWORD'] = 'abc123'
ENV['OS_PROJECT_NAME'] = 'test'
ENV['OS_AUTH_URL'] = 'http://127.0.0.1:5000'
end
let(:type_attributes) do
{
:name => 'test_type',
:ensure => :present,
:is_public => true,
:driver_handles_share_servers => false,
:snapshot_support => false,
:create_share_from_snapshot_support => false,
:revert_to_snapshot_support => false,
:mount_snapshot_support => false,
}
end
let(:resource) do
Puppet::Type::Manila_type.new(type_attributes)
end
let(:provider) do
provider_class.new(resource)
end
before(:each) { set_creds_env }
after(:each) do
Puppet::Type.type(:manila_type).provider(:openstack).reset
provider_class.reset
end
describe 'managing type' do
describe '#create' do
context 'with defaults' do
it 'creates a type' do
provider_class.expects(:openstack)
.with('share type', 'create', '--format', 'shell',
['test_type', 'False', '--public', 'True',
'--snapshot-support', 'False',
'--create-share-from-snapshot-support', 'False',
'--revert-to-snapshot-support', 'False',
'--mount-snapshot-support', 'False'])
.returns('id="90e19aff-1b35-4d60-9ee3-383c530275ab"
name="test_type"
visibility="public"
required_extra_specs="{\'driver_handles_share_servers\': \'False\'}"
optional_extra_specs="{\'snapshot_support\': \'False\', \'create_share_from_snapshot_support\': \'False\', \'revert_to_snapshot_support\': \'False\', \'mount_snapshot_support\': \'False\'}"
descrpiton="None"
')
provider.create
expect(provider.exists?).to be_truthy
end
end
end
describe '#instances' do
it 'finds types' do
provider_class.expects(:openstack)
.with('share type', 'list', '--quiet', '--format', 'csv', [])
.returns('"ID","Name","Visibility","Is Default","Required Extra Specs","Optional Extra Specs","Description"
"90e19aff-1b35-4d60-9ee3-383c530275ab","type0","public","False","{\'driver_handles_share_servers\': \'True\'}","{\'snapshot_support\': \'True\'}",""
"90e19aff-1b35-4d60-9ee3-383c530275ab","type1","private","False","{\'driver_handles_share_servers\': \'False\'}","{}",""
')
instances = provider_class.instances
expect(instances.count).to eq(2)
expect(instances[0].name).to eq('type0')
expect(instances[0].is_public).to be true
expect(instances[0].driver_handles_share_servers).to be true
expect(instances[0].snapshot_support).to be true
expect(instances[0].create_share_from_snapshot_support).to be false
expect(instances[0].revert_to_snapshot_support).to be false
expect(instances[0].mount_snapshot_support).to be false
expect(instances[1].name).to eq('type1')
expect(instances[1].is_public).to be false
expect(instances[1].driver_handles_share_servers).to be false
expect(instances[1].snapshot_support).to be false
expect(instances[1].create_share_from_snapshot_support).to be false
expect(instances[1].revert_to_snapshot_support).to be false
expect(instances[1].mount_snapshot_support).to be false
end
end
describe '#flush' do
context '.is_public' do
it 'updates type' do
provider_class.expects(:openstack)
.with('share type', 'set', ['test_type', '--public', 'False'])
provider.is_public = false
provider.flush
provider_class.expects(:openstack)
.with('share type', 'set', ['test_type', '--public', 'True'])
provider.is_public = true
provider.flush
end
end
context '.snapshot_support' do
it 'updates type' do
provider_class.expects(:openstack)
.with('share type', 'set', ['test_type', '--snapshot-support', 'False'])
provider.snapshot_support = false
provider.flush
provider_class.expects(:openstack)
.with('share type', 'set', ['test_type', '--snapshot-support', 'True'])
provider.snapshot_support = true
provider.flush
end
end
end
describe '#destroy' do
it 'destroys a type' do
provider_class.expects(:openstack)
.with('share type', 'delete', 'test_type')
provider.destroy
expect(provider.exists?).to be_falsey
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'puppet'
require 'puppet/type/manila_type'
describe Puppet::Type.type(:manila_type) do
before :each do
Puppet::Type.rmtype(:manila_type)
end
it 'should autorequire manila-api service' do
catalog = Puppet::Resource::Catalog.new
anchor = Puppet::Type.type(:anchor).new(:name => 'manila::service::end')
correct_input = {
:name => 'test_type',
}
manila_type = Puppet::Type.type(:manila_type).new(correct_input)
catalog.add_resource anchor, manila_type
dependency = manila_type.autorequire
expect(dependency.size).to eq(1)
expect(dependency[0].target).to eq(manila_type)
expect(dependency[0].source).to eq(anchor)
end
end