340 lines
14 KiB
Ruby
340 lines
14 KiB
Ruby
# encoding: UTF-8
|
|
#
|
|
# Cookbook Name:: openstack-identity
|
|
# Provider:: register
|
|
#
|
|
# Copyright 2012, Rackspace US, Inc.
|
|
# Copyright 2012-2013, AT&T Services, Inc.
|
|
# Copyright 2013, Opscode, Inc.
|
|
# Copyright 2013, Craig Tracey <craigtracey@gmail.com>
|
|
#
|
|
# Licensed under the Apache License, Version 2.0 (the "License");
|
|
# you may not use this file except in compliance with the License.
|
|
# You may obtain a copy of the License at
|
|
#
|
|
# http://www.apache.org/licenses/LICENSE-2.0
|
|
#
|
|
# Unless required by applicable law or agreed to in writing, software
|
|
# distributed under the License is distributed on an "AS IS" BASIS,
|
|
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
|
# See the License for the specific language governing permissions and
|
|
# limitations under the License.
|
|
#
|
|
|
|
require 'chef/mixin/shell_out'
|
|
include Chef::Mixin::ShellOut
|
|
include ::Openstack
|
|
|
|
action :create_service do
|
|
new_resource.updated_by_last_action(false)
|
|
if node['openstack']['identity']['catalog']['backend'] == 'templated'
|
|
Chef::Log.info('Skipping service creation - templated catalog backend in use.')
|
|
else
|
|
begin
|
|
service_uuid = identity_uuid new_resource, 'service', 'type', new_resource.service_type
|
|
need_updated = false
|
|
if service_uuid
|
|
Chef::Log.info("Service Type '#{new_resource.service_type}' already exists..")
|
|
Chef::Log.info("Service UUID: #{service_uuid}")
|
|
need_updated = service_need_updated? new_resource
|
|
if need_updated
|
|
Chef::Log.info("Service Type '#{new_resource.service_type}' needs to be updated, delete it first.")
|
|
identity_command(new_resource, 'service-delete',
|
|
'' => service_uuid)
|
|
end
|
|
end
|
|
unless service_uuid && !need_updated
|
|
identity_command(new_resource, 'service-create',
|
|
'type' => new_resource.service_type,
|
|
'name' => new_resource.service_name,
|
|
'description' => new_resource.service_description)
|
|
Chef::Log.info("Created service '#{new_resource.service_name}'")
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to create service '#{new_resource.service_name}' Error:" + e.message
|
|
end
|
|
end
|
|
end
|
|
|
|
action :create_endpoint do
|
|
new_resource.updated_by_last_action(false)
|
|
if node['openstack']['identity']['catalog']['backend'] == 'templated'
|
|
Chef::Log.info('Skipping endpoint creation - templated catalog backend in use.')
|
|
else
|
|
begin
|
|
service_uuid = identity_uuid new_resource, 'service', 'type', new_resource.service_type
|
|
fail "Unable to find service type '#{new_resource.service_type}'" unless service_uuid
|
|
|
|
endpoint_uuid = identity_uuid new_resource, 'endpoint', 'service_id', service_uuid
|
|
need_updated = false
|
|
if endpoint_uuid
|
|
Chef::Log.info("Endpoint already exists for Service Type '#{new_resource.service_type}'.")
|
|
need_updated = endpoint_need_updated? new_resource, 'service_id', service_uuid
|
|
if need_updated
|
|
Chef::Log.info("Endpoint for Service Type '#{new_resource.service_type}' needs to be updated, delete it first.")
|
|
identity_command(new_resource, 'endpoint-delete',
|
|
'' => endpoint_uuid)
|
|
end
|
|
end
|
|
unless endpoint_uuid && !need_updated
|
|
identity_command(new_resource, 'endpoint-create',
|
|
'region' => new_resource.endpoint_region,
|
|
'service_id' => service_uuid,
|
|
'publicurl' => new_resource.endpoint_publicurl,
|
|
'internalurl' => new_resource.endpoint_internalurl,
|
|
'adminurl' => new_resource.endpoint_adminurl)
|
|
Chef::Log.info("Created endpoint for service type '#{new_resource.service_type}'")
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to create endpoint for service type '#{new_resource.service_type}' Error: " + e.message
|
|
end
|
|
end
|
|
end
|
|
|
|
action :create_tenant do
|
|
begin
|
|
new_resource.updated_by_last_action(false)
|
|
tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name
|
|
|
|
if tenant_uuid
|
|
Chef::Log.info("Tenant '#{new_resource.tenant_name}' already exists.. Not creating.")
|
|
Chef::Log.info("Tenant UUID: #{tenant_uuid}") if tenant_uuid
|
|
else
|
|
identity_command(new_resource, 'tenant-create',
|
|
'name' => new_resource.tenant_name,
|
|
'description' => new_resource.tenant_description,
|
|
'enabled' => new_resource.tenant_enabled)
|
|
Chef::Log.info("Created tenant '#{new_resource.tenant_name}'")
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to create tenant '#{new_resource.tenant_name}' Error: " + e.message
|
|
end
|
|
end
|
|
|
|
action :create_role do
|
|
begin
|
|
new_resource.updated_by_last_action(false)
|
|
role_uuid = identity_uuid new_resource, 'role', 'name', new_resource.role_name
|
|
|
|
if role_uuid
|
|
Chef::Log.info("Role '#{new_resource.role_name}' already exists.. Not creating.")
|
|
Chef::Log.info("Role UUID: #{role_uuid}")
|
|
else
|
|
identity_command(new_resource, 'role-create',
|
|
'name' => new_resource.role_name)
|
|
Chef::Log.info("Created Role '#{new_resource.role_name}'")
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to create role '#{new_resource.role_name}' Error: " + e.message
|
|
end
|
|
end
|
|
|
|
action :create_user do
|
|
begin
|
|
new_resource.updated_by_last_action(false)
|
|
|
|
output = identity_command(new_resource, 'user-list')
|
|
users = prettytable_to_array output
|
|
user_found = false
|
|
users.each do |user|
|
|
user_found = true if user['name'] == new_resource.user_name
|
|
end
|
|
|
|
if user_found
|
|
Chef::Log.info("User '#{new_resource.user_name}' already exists")
|
|
begin
|
|
# Check if password is already updated by getting a token
|
|
identity_command(new_resource, 'token-get', {}, 'user')
|
|
rescue StandardError => e
|
|
Chef::Log.debug('Get token error:' + e.message)
|
|
Chef::Log.info("Sync password for user '#{new_resource.user_name}'")
|
|
identity_command(new_resource, 'user-password-update',
|
|
'pass' => new_resource.user_pass,
|
|
'' => new_resource.user_name)
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
next
|
|
end
|
|
|
|
identity_command(new_resource, 'user-create',
|
|
'name' => new_resource.user_name,
|
|
'tenant' => new_resource.tenant_name,
|
|
'pass' => new_resource.user_pass,
|
|
'enabled' => new_resource.user_enabled)
|
|
Chef::Log.info("Created user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}'")
|
|
new_resource.updated_by_last_action(true)
|
|
rescue StandardError => e
|
|
raise "Unable to create user '#{new_resource.user_name}' for tenant '#{new_resource.tenant_name}' Error: " + e.message
|
|
end
|
|
end
|
|
|
|
action :grant_role do
|
|
begin
|
|
new_resource.updated_by_last_action(false)
|
|
|
|
role_uuid = identity_uuid new_resource, 'role', 'name', new_resource.role_name
|
|
fail "Unable to find role '#{new_resource.role_name}'" unless role_uuid
|
|
|
|
assigned_role_uuid = identity_uuid(new_resource, 'user-role', 'name',
|
|
new_resource.role_name,
|
|
'tenant' => new_resource.tenant_name,
|
|
'user' => new_resource.user_name)
|
|
if role_uuid == assigned_role_uuid
|
|
Chef::Log.info("Role '#{new_resource.role_name}' already granted to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'")
|
|
else
|
|
identity_command(new_resource, 'user-role-add',
|
|
'tenant' => new_resource.tenant_name,
|
|
'role-id' => role_uuid,
|
|
'user' => new_resource.user_name)
|
|
Chef::Log.info("Granted Role '#{new_resource.role_name}' to User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'")
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to grant role '#{new_resource.role_name}' to user '#{new_resource.user_name}' Error: " + e.message
|
|
end
|
|
end
|
|
|
|
action :create_ec2_credentials do
|
|
begin
|
|
new_resource.updated_by_last_action(false)
|
|
tenant_uuid = identity_uuid new_resource, 'tenant', 'name', new_resource.tenant_name
|
|
fail "Unable to find tenant '#{new_resource.tenant_name}'" unless tenant_uuid
|
|
|
|
user_uuid = identity_uuid(new_resource, 'user', 'name',
|
|
new_resource.user_name,
|
|
'tenant-id' => tenant_uuid)
|
|
fail "Unable to find user '#{new_resource.user_name}' with tenant '#{new_resource.tenant_name}'" unless user_uuid
|
|
|
|
# this is not really a uuid, but this will work nonetheless
|
|
access = identity_uuid new_resource, 'ec2-credentials', 'tenant', new_resource.tenant_name, { 'user-id' => user_uuid }, 'access'
|
|
if access
|
|
Chef::Log.info("EC2 credentials already exist for '#{new_resource.user_name}' in tenant '#{new_resource.tenant_name}'")
|
|
else
|
|
output = identity_command(new_resource, 'ec2-credentials-create',
|
|
{ 'user-id' => user_uuid,
|
|
'tenant-id' => tenant_uuid },
|
|
'admin')
|
|
Chef::Log.info("Created EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}'")
|
|
data = prettytable_to_array(output)
|
|
|
|
if data.length != 1
|
|
fail "Got bad data when creating ec2 credentials for #{new_resource.user_name} Data: #{data}"
|
|
else
|
|
# Update node attributes
|
|
node.set['credentials']['EC2'][new_resource.user_name]['access'] = data[0]['access']
|
|
node.set['credentials']['EC2'][new_resource.user_name]['secret'] = data[0]['secret']
|
|
node.save unless Chef::Config[:solo]
|
|
new_resource.updated_by_last_action(true)
|
|
end
|
|
end
|
|
rescue StandardError => e
|
|
raise "Unable to create EC2 Credentials for User '#{new_resource.user_name}' in Tenant '#{new_resource.tenant_name}' Error: " + e.message
|
|
end
|
|
end
|
|
|
|
private
|
|
|
|
def generate_boot_creds(resource)
|
|
{
|
|
'OS_SERVICE_ENDPOINT' => resource.auth_uri,
|
|
'OS_SERVICE_TOKEN' => resource.bootstrap_token
|
|
}
|
|
end
|
|
|
|
def generate_admin_creds(resource)
|
|
identity_endpoint = resource.identity_endpoint
|
|
identity_endpoint = admin_endpoint('identity').to_s unless identity_endpoint
|
|
{
|
|
'OS_USERNAME' => resource.admin_user,
|
|
'OS_PASSWORD' => resource.admin_pass,
|
|
'OS_TENANT_NAME' => resource.admin_tenant_name,
|
|
'OS_AUTH_URL' => identity_endpoint
|
|
}
|
|
end
|
|
|
|
def generate_user_creds(resource)
|
|
identity_endpoint = resource.identity_endpoint
|
|
identity_endpoint = public_endpoint('identity').to_s unless identity_endpoint
|
|
{
|
|
'OS_USERNAME' => resource.user_name,
|
|
'OS_PASSWORD' => resource.user_pass,
|
|
'OS_TENANT_NAME' => resource.tenant_name,
|
|
'OS_AUTH_URL' => identity_endpoint
|
|
}
|
|
end
|
|
|
|
def get_env(resource, env = 'boot')
|
|
case env
|
|
when 'boot'
|
|
generate_boot_creds(resource)
|
|
when 'user'
|
|
generate_user_creds(resource)
|
|
when 'admin'
|
|
generate_admin_creds(resource)
|
|
end
|
|
end
|
|
|
|
def identity_command(resource, cmd, args = {}, env = 'boot')
|
|
keystonecmd = build_keystone_cmd(cmd, args)
|
|
cmd_env = get_env(resource, env)
|
|
Chef::Log.debug("Running identity command: #{keystonecmd} env: " + cmd_env.to_s)
|
|
rc = shell_out(keystonecmd, env: cmd_env)
|
|
fail "#{rc.stderr} (#{rc.exitstatus})" if rc.exitstatus != 0
|
|
rc.stdout
|
|
end
|
|
|
|
def build_keystone_cmd(cmd, args)
|
|
keystonecmd = ['keystone'] << '--insecure' << cmd
|
|
args.each do |key, val|
|
|
keystonecmd << "--#{key}" unless key.empty?
|
|
keystonecmd << val.to_s
|
|
end
|
|
keystonecmd
|
|
end
|
|
|
|
def identity_uuid(resource, type, key, value, args = {}, uuid_field = 'id')
|
|
rc = nil
|
|
begin
|
|
output = identity_command resource, "#{type}-list", args
|
|
output = prettytable_to_array(output)
|
|
rc = (type == 'endpoint') ? (search_uuid(output, uuid_field, key => value, 'region' => resource.endpoint_region)) : (search_uuid(output, uuid_field, key => value))
|
|
rescue RuntimeError => e
|
|
raise "Could not lookup uuid for #{type}:#{key}=>#{value}. Error was #{e.message}"
|
|
end
|
|
rc
|
|
end
|
|
|
|
def search_uuid(output, uuid_field, required_hash = {})
|
|
rc = nil
|
|
output.each do |obj|
|
|
rc = obj[uuid_field] if obj.key?(uuid_field) && required_hash.values - obj.values_at(*required_hash.keys) == []
|
|
end
|
|
rc
|
|
end
|
|
|
|
def service_need_updated?(resource, args = {}, uuid_field = 'id')
|
|
begin
|
|
output = identity_command resource, 'service-list', args
|
|
output = prettytable_to_array(output)
|
|
return search_uuid(output, uuid_field, 'name' => resource.service_name).nil?
|
|
rescue RuntimeError => e
|
|
raise "Could not check service attributes for service: type => #{resource.service_type}, name => #{resource.service_name}. Error was #{e.message}"
|
|
end
|
|
false
|
|
end
|
|
|
|
def endpoint_need_updated?(resource, key, value, args = {}, uuid_field = 'id')
|
|
begin
|
|
output = identity_command resource, 'endpoint-list', args
|
|
output = prettytable_to_array(output)
|
|
return search_uuid(output, uuid_field, key => value, 'region' => resource.endpoint_region, 'publicurl' => resource.endpoint_publicurl, 'internalurl' => resource.endpoint_internalurl, 'adminurl' => resource.endpoint_adminurl).nil?
|
|
rescue RuntimeError => e
|
|
raise "Could not check endpoint attributes for endpoint:#{key}=>#{value}. Error was #{e.message}"
|
|
end
|
|
false
|
|
end
|