]> git.donarmstrong.com Git - dsa-puppet.git/blobdiff - 3rdparty/modules/keystone/lib/puppet/provider/keystone_user/openstack.rb
try with modules from master
[dsa-puppet.git] / 3rdparty / modules / keystone / lib / puppet / provider / keystone_user / openstack.rb
index 6c8d04aa292f7d9bee447cba712986590921abd2..98a34cd3cd05a8d5ab4a5f506ebe2de4348a3c6b 100644 (file)
@@ -1,6 +1,5 @@
-require 'net/http'
-require 'json'
 require 'puppet/provider/keystone'
+
 Puppet::Type.type(:keystone_user).provide(
   :openstack,
   :parent => Puppet::Provider::Keystone
@@ -8,137 +7,120 @@ Puppet::Type.type(:keystone_user).provide(
 
   desc "Provider to manage keystone users."
 
+  @credentials = Puppet::Provider::Openstack::CredentialsV2_0.new
+
   def initialize(value={})
     super(value)
     @property_flush = {}
   end
 
   def create
-    properties = []
+    properties = [resource[:name]]
     if resource[:enabled] == :true
       properties << '--enable'
     elsif resource[:enabled] == :false
       properties << '--disable'
     end
     if resource[:password]
-      properties << '--password'
-      properties << resource[:password]
+      properties << '--password' << resource[:password]
     end
     if resource[:tenant]
-      properties << '--project'
-      properties << resource[:tenant]
+      properties << '--project' << resource[:tenant]
     end
     if resource[:email]
-      properties << '--email'
-      properties << resource[:email]
+      properties << '--email' << resource[:email]
     end
-    @instance = request('user', 'create', resource[:name], resource[:auth], properties)
+    self.class.request('user', 'create', properties)
+    @property_hash[:ensure] = :present
   end
 
-  def exists?
-    ! instance(resource[:name]).empty?
+  def destroy
+    self.class.request('user', 'delete', @property_hash[:id])
+    @property_hash.clear
   end
 
-  def destroy
-    request('user', 'delete', resource[:name], resource[:auth])
+  def flush
+    options = []
+    if @property_flush && !@property_flush.empty?
+      options << '--enable'  if @property_flush[:enabled] == :true
+      options << '--disable' if @property_flush[:enabled] == :false
+      # There is a --description flag for the set command, but it does not work if the value is empty
+      options << '--password' << resource[:password] if @property_flush[:password]
+      options << '--email'    << resource[:email]    if @property_flush[:email]
+      # project handled in tenant= separately
+      unless options.empty?
+        options << @property_hash[:id]
+        self.class.request('user', 'set', options)
+      end
+      @property_flush.clear
+    end
   end
 
+  def exists?
+    @property_hash[:ensure] == :present
+  end
+
+  # Types properties
+  def enabled
+    bool_to_sym(@property_hash[:enabled])
+  end
 
   def enabled=(value)
     @property_flush[:enabled] = value
   end
 
-  def enabled
-    bool_to_sym(instance(resource[:name])[:enabled])
+  def email
+    @property_hash[:email]
   end
 
+  def email=(value)
+    @property_flush[:email] = value
+  end
 
-  def password=(value)
-    @property_flush[:password] = value
+  def id
+    @property_hash[:id]
   end
 
   def password
-    # if we don't know a password we can't test it
-    return nil if resource[:password] == nil
-    # if the user is disabled then the password can't be changed
-    return resource[:password] if resource[:enabled] == :false
-    # if replacing password is disabled, then don't change it
-    return resource[:password] if resource[:replace_password] == :false
-    # we can't get the value of the password but we can test to see if the one we know
-    # about works, if it doesn't then return nil, causing it to be reset
-    endpoint = nil
-    if password_credentials_set?(resource[:auth]) || service_credentials_set?(resource[:auth])
-      endpoint = (resource[:auth])['auth_url']
-    elsif openrc_set?(resource[:auth])
-      endpoint = get_credentials_from_openrc(resource[:auth])['auth_url']
-    elsif env_vars_set?
-      endpoint = ENV['OS_AUTH_URL']
-    else
-      # try to get endpoint from keystone.conf
-      endpoint = get_admin_endpoint
-    end
-    if endpoint == nil
-      raise(Puppet::Error::OpenstackAuthInputError, 'Could not find auth url to check user password.')
+    res = nil
+    return res if resource[:password] == nil
+    if resource[:enabled] == :false || resource[:replace_password] == :false
+      # Unchanged password
+      res = resource[:password]
     else
-      auth_params = {
-        'username'    => resource[:name],
-        'password'    => resource[:password],
-        'tenant_name' => resource[:tenant],
-        'auth_url'    => endpoint,
-      }
-      # LP#1408754
-      # Ideally this would be checked with the `openstack token issue` command,
-      # but that command is not available with version 0.3.0 of openstackclient
-      # which is what ships on Ubuntu during Juno.
-      # Instead we'll check whether the user can authenticate with curl.
-      creds_hash = {
-        :auth => {
-          :passwordCredentials => {
-            :username => auth_params['username'],
-            :password => auth_params['password'],
-          }
-        }
-      }
-      url = URI.parse(endpoint)
-      # There is issue with ipv6 where address has to be in brackets, this causes the
-      # underlying ruby TCPSocket to fail. Net::HTTP.new will fail without brackets on
-      # joining the ipv6 address with :port or passing brackets to TCPSocket. It was
-      # found that if we use Net::HTTP.start with url.hostname the incriminated code
-      # won't be hit.
-      use_ssl = url.scheme == "https" ? true : false
-      http = Net::HTTP.start(url.hostname, url.port, {:use_ssl => use_ssl})
-      request = Net::HTTP::Post.new('/v2.0/tokens')
-      request.body = creds_hash.to_json
-      request.content_type = 'application/json'
-      response = http.request(request)
-      if response.code.to_i == 401 || response.code.to_i == 403 # 401 => unauthorized, 403 => userDisabled
-        return nil
-      elsif ! (response.code == 200 || response.code == 203)
-        return resource[:password]
+      # Password validation
+      credentials = Puppet::Provider::Openstack::CredentialsV2_0.new
+      credentials.auth_url     = self.class.get_endpoint
+      credentials.password     = resource[:password]
+      credentials.project_name = resource[:tenant]
+      credentials.username     = resource[:name]
+      begin
+        token = Puppet::Provider::Openstack.request('token', 'issue', ['--format', 'value'], credentials)
+      rescue Puppet::Error::OpenstackUnauthorizedError
+        # password is invalid
       else
-        raise(Puppet::Error, "Received bad response while trying to authenticate user: #{response.body}")
+        res = resource[:password] unless token.empty?
       end
     end
+    return res
   end
 
-  def tenant=(value)
-    begin
-      request('user', 'set', resource[:name], resource[:auth], '--project', value)
-    rescue Puppet::ExecutionFailure => e
-      if e.message =~ /You are not authorized to perform the requested action: LDAP user update/
-        # read-only LDAP identity backend - just fall through
-      else
-        raise e
-      end
-      # note: read-write ldap will silently fail, not raise an exception
-    end
-    set_project(value)
+  def password=(value)
+    @property_flush[:password] = value
+  end
+
+  def replace_password
+    @property_hash[:replace_password]
+  end
+
+  def replace_password=(value)
+    @property_flush[:replace_password] = value
   end
 
   def tenant
     return resource[:tenant] if sym_to_bool(resource[:ignore_default_tenant])
     # use the one returned from instances
-    tenant_name = instance(resource[:name])[:project]
+    tenant_name = @property_hash[:project]
     if tenant_name.nil? or tenant_name.empty?
       # if none (i.e. ldap backend) use the given one
       tenant_name = resource[:tenant]
@@ -151,7 +133,7 @@ Puppet::Type.type(:keystone_user).provide(
     # If the user list command doesn't report the project, it might still be there
     # We don't need to know exactly what it is, we just need to know whether it's
     # the one we're trying to set.
-    roles = request('user role', 'list', resource[:name], resource[:auth], ['--project', tenant_name])
+    roles = self.class.request('user role', 'list', [resource[:name], '--project', tenant_name])
     if roles.empty?
       return nil
     else
@@ -159,64 +141,48 @@ Puppet::Type.type(:keystone_user).provide(
     end
   end
 
-  def replace_password
-    instance(resource[:name])[:replace_password]
-  end
-
-  def replace_password=(value)
-    @property_flush[:replace_password] = value
-  end
-
-  def email=(value)
-    @property_flush[:email] = value
-  end
-
-  def email
-    instance(resource[:name])[:email]
-  end
-
-  def id
-    instance(resource[:name])[:id]
+  def tenant=(value)
+    self.class.request('user', 'set', [resource[:name], '--project', value])
+    rescue Puppet::ExecutionFailure => e
+      if e.message =~ /You are not authorized to perform the requested action: LDAP user update/
+        # read-only LDAP identity backend - just fall through
+      else
+        raise e
+      end
+      # note: read-write ldap will silently fail, not raise an exception
+    else
+    @property_hash[:tenant] = self.class.set_project(value, resource[:name])
   end
 
   def self.instances
-    list = request('user', 'list', nil, nil, '--long')
+    list = request('user', 'list', '--long')
     list.collect do |user|
       new(
         :name        => user[:name],
         :ensure      => :present,
         :enabled     => user[:enabled].downcase.chomp == 'true' ? true : false,
         :password    => user[:password],
-        :tenant      => user[:project],
+        :project     => user[:project],
         :email       => user[:email],
         :id          => user[:id]
       )
     end
   end
 
-  def instances
-    instances = request('user', 'list', nil, resource[:auth], '--long')
-    instances.collect do |user|
-      {
-        :name        => user[:name],
-        :enabled     => user[:enabled].downcase.chomp == 'true' ? true : false,
-        :password    => user[:password],
-        :project     => user[:project],
-        :email       => user[:email],
-        :id          => user[:id]
-      }
+  def self.prefetch(resources)
+    users = instances
+    resources.keys.each do |name|
+       if provider = users.find{ |user| user.name == name }
+        resources[name].provider = provider
+      end
     end
   end
 
-  def instance(name)
-    @instance ||= instances.select { |instance| instance[:name] == name }.first || {}
-  end
-
-  def set_project(newproject)
+  def self.set_project(newproject, name)
     # some backends do not store the project/tenant in the user object, so we have to
     # to modify the project/tenant instead
     # First, see if the project actually needs to change
-    roles = request('user role', 'list', resource[:name], resource[:auth], ['--project', newproject])
+    roles = request('user role', 'list', [name, '--project', newproject])
     unless roles.empty?
       return # if already set, just skip
     end
@@ -228,26 +194,11 @@ Puppet::Type.type(:keystone_user).provide(
     # ok for a user to have the _member_ role and another role.
     default_role = "_member_"
     begin
-      request('role', 'show', default_role, resource[:auth])
+      request('role', 'show', [default_role])
     rescue
       debug("Keystone role #{default_role} does not exist - creating")
-      request('role', 'create', default_role, resource[:auth])
-    end
-    request('role', 'add', default_role, resource[:auth],
-            '--project', newproject, '--user', resource[:name])
-  end
-
-  def flush
-    options = []
-    if @property_flush
-      (options << '--enable') if @property_flush[:enabled] == :true
-      (options << '--disable') if @property_flush[:enabled] == :false
-      # There is a --description flag for the set command, but it does not work if the value is empty
-      (options << '--password' << resource[:password]) if @property_flush[:password]
-      (options << '--email'    << resource[:email])    if @property_flush[:email]
-      # project handled in tenant= separately
-      request('user', 'set', resource[:name], resource[:auth], options) unless options.empty?
+      request('role', 'create', [default_role])
     end
+    request('role', 'add', [default_role, '--project', newproject, '--user', name])
   end
-
 end