diff --git a/metadata.rb b/metadata.rb index bcd2287..f86ff73 100644 --- a/metadata.rb +++ b/metadata.rb @@ -4,10 +4,11 @@ maintainer_email "matt@opscode.com" license "Apache 2.0" description "The OpenStack Identity service Keystone." long_description IO.read(File.join(File.dirname(__FILE__), 'README.md')) -version "2012.2.0" +version "2012.2.1" recipe "keystone::db", "Configures database for use with keystone" recipe "keystone::server", "Installs and Configures Keystone Service" +recipe "keystone::registration", "Adds user, tenant, role and endpoint records to Keystone" %w{ ubuntu fedora redhat centos }.each do |os| supports os diff --git a/providers/credentials.rb b/providers/credentials.rb index 8f36a84..b4093c1 100644 --- a/providers/credentials.rb +++ b/providers/credentials.rb @@ -183,42 +183,9 @@ def _http_get resource, path end -# Returns a token for use by a Keystone Admin user when -# issuing requests to the Keystone Admin API -def _get_admin_token auth_admin_uri, admin_tenant_name, admin_user, admin_password - # Construct a HTTP object from the supplied URI pointing to the - # Keystone Admin API endpoint. - uri = ::URI.parse(auth_admin_uri) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true if uri.scheme == 'https' - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - path = _path uri, "tokens" - - payload = Hash.new - payload['auth'] = Hash.new - payload['auth']['passwordCredentials'] = Hash.new - payload['auth']['passwordCredentials']['username'] = admin_user - payload['auth']['passwordCredentials']['password'] = admin_password - payload['auth']['tenantName'] = admin_tenant_name - - req = Net::HTTP::Post.new(path) - req.add_field 'Content-type', 'application/json' - req.add_field 'user-agent', 'Chef keystone_register admin_token' - req.body = JSON.generate(payload) - resp = http.request req - if resp.is_a?(Net::HTTPOK) - data = JSON.parse resp.body - token = data['access']['token']['id'] - else - Chef::Log.error("Unable to get admin token.") - Chef::Log.error("Response Code: #{resp.code}") - Chef::Log.error("Response Message: #{resp.message}") - end -end - # Constructs the request object with all the requisite headers added def _build_request resource, request - admin_token = _get_admin_token resource.auth_uri, resource.admin_tenant_name, resource.admin_user, resource.admin_password + admin_token = resource.bootstrap_token request.add_field 'X-Auth-Token', admin_token request.add_field 'Content-type', 'application/json' request.add_field 'user-agent', 'Chef keystone_credentials' diff --git a/providers/register.rb b/providers/register.rb index 62b62af..23396b0 100644 --- a/providers/register.rb +++ b/providers/register.rb @@ -423,7 +423,7 @@ end # Short-cut for returning an Net::HTTP::Post to a path on the admin API endpoint. -# Headers and admin token validation are already performed. All +# Headers and bootstrap token validation are already performed. All # the caller needs to do is call http.request, supplying the returned object def _http_post resource, path uri = ::URI.parse(resource.auth_uri) @@ -434,7 +434,7 @@ end # Short-cut for returning an Net::HTTP::Put to a path on the admin API endpoint. -# Headers and admin token validation are already performed. All +# Headers and bootstrap token validation are already performed. All # the caller needs to do is call http.request, supplying the returned object def _http_put resource, path uri = ::URI.parse(resource.auth_uri) @@ -454,44 +454,9 @@ def _http_get resource, path _build_request resource, request end - -# Returns a token for use by a Keystone Admin user when -# issuing requests to the Keystone Admin API -def _get_admin_token auth_admin_uri, admin_tenant_name, admin_user, admin_password - # Construct a HTTP object from the supplied URI pointing to the - # Keystone Admin API endpoint. - uri = ::URI.parse(auth_admin_uri) - http = Net::HTTP.new(uri.host, uri.port) - http.use_ssl = true if uri.scheme == 'https' - http.verify_mode = OpenSSL::SSL::VERIFY_NONE - path = _path uri, "tokens" - - payload = Hash.new - payload['auth'] = Hash.new - payload['auth']['passwordCredentials'] = Hash.new - payload['auth']['passwordCredentials']['username'] = admin_user - payload['auth']['passwordCredentials']['password'] = admin_password - payload['auth']['tenantName'] = admin_tenant_name - - req = Net::HTTP::Post.new(path) - req.add_field 'Content-type', 'application/json' - req.add_field 'user-agent', 'Chef keystone_register admin_token' - req.body = JSON.generate(payload) - resp = http.request req - if resp.is_a?(Net::HTTPOK) - data = JSON.parse resp.body - token = data['access']['token']['id'] - else - Chef::Log.error("Unable to get admin token.") - Chef::Log.error("Response Code: #{resp.code}") - Chef::Log.error("Response Message: #{resp.message}") - Chef::Log.error("Response Body: #{resp.body}") - end -end - # Constructs the request object with all the requisite headers added def _build_request resource, request - admin_token = _get_admin_token resource.auth_uri, resource.admin_tenant_name, resource.admin_user, resource.admin_password + admin_token = resource.bootstrap_token request.add_field 'X-Auth-Token', admin_token request.add_field 'Content-type', 'application/json' request.add_field 'user-agent', 'Chef keystone_register' diff --git a/recipes/registration.rb b/recipes/registration.rb new file mode 100644 index 0000000..2ae1625 --- /dev/null +++ b/recipes/registration.rb @@ -0,0 +1,166 @@ +# +# Cookbook Name:: keystone +# Recipe:: setup +# +# Copyright 2012, Rackspace US, Inc. +# Copyright 2012, Opscode, Inc. +# +# 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 "uri" + +class ::Chef::Recipe + include ::Openstack +end + +identity_admin_endpoint = endpoint "identity-admin" +identity_endpoint = endpoint "identity-api" + +admin_tenant_name = node["keystone"]["admin_tenant_name"] +admin_user = node["keystone"]["admin_user"] +admin_pass = user_password node["keystone"]["admin_user"] +auth_uri = ::URI.decode identity_admin_endpoint.to_s + +bootstrap_token = secret "secrets", "keystone_bootstrap_token" + +# We need to bootstrap the keystone admin user so that calls +# to keystone_register will succeed, since those provider calls +# use the admin tenant/user/pass to get an admin token. +bash "bootstrap-keystone-admin" do + # A shortcut bootstrap command was added to python-keystoneclient + # in early Grizzly timeframe... but we need to do all the commands + # here manually since the python-keystoneclient package included + # in CloudArchive (for now) doesn't have it... + insecure = node["openstack"]["auth"]["validate_certs"] ? "" : " --insecure" + base_ks_cmd = "keystone#{insecure} --endpoint=#{auth_uri} --token=#{bootstrap_token}" + code <<-EOF +set -x +function get_id () { + echo `"$@" | grep ' id ' | awk '{print $4}'` +} +#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} +if [[ $? -eq 1 ]]; then + ADMIN_TENANT=$(get_id #{base_ks_cmd} tenant-create --name=#{admin_tenant_name}) +else + ADMIN_TENANT=$(#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} | awk '{print $2}') +fi +#{base_ks_cmd} role-list | grep admin +if [[ $? -eq 1 ]]; then + ADMIN_ROLE=$(get_id #{base_ks_cmd} role-create --name=admin) +else + ADMIN_ROLE=$(#{base_ks_cmd} role-list | grep admin | awk '{print $2}') +fi +#{base_ks_cmd} user-list | grep #{admin_user} +if [[ $? -eq 1 ]]; then + ADMIN_USER=$(get_id #{base_ks_cmd} user-create --name=#{admin_user} --pass="#{admin_pass}" --email=#{admin_user}@example.com) +else + ADMIN_USER=$(#{base_ks_cmd} user-list | grep #{admin_user} | awk '{print $2}') +fi +#{base_ks_cmd} user-role-list --user-id=$ADMIN_USER --tenant-id=$ADMIN_TENANT | grep admin +if [[ $? -eq 1 ]]; then + #{base_ks_cmd} user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $ADMIN_TENANT +fi +exit 0 +EOF +end + +node["keystone"]["tenants"].each do |tenant_name| + ## Add openstack tenant ## + keystone_register "Register '#{tenant_name}' Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + tenant_name tenant_name + tenant_description "#{tenant_name} Tenant" + tenant_enabled "true" # Not required as this is the default + + action :create_tenant + end +end + +node["keystone"]["roles"].each do |role_key| + keystone_register "Register '#{role_key.to_s}' Role" do + auth_uri auth_uri + bootstrap_token bootstrap_token + role_name role_key + + action :create_role + end +end + +node["keystone"]["users"].each do |username, user_info| + keystone_register "Register '#{username}' User" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name username + user_pass user_info["password"] + tenant_name user_info["default_tenant"] + user_enabled "true" # Not required as this is the default + + action :create_user + end + + user_info["roles"].each do |rolename, tenant_list| + tenant_list.each do |tenantname| + keystone_register "Grant '#{rolename}' Role to '#{username}' User in '#{tenantname}' Tenant" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name username + role_name rolename + tenant_name tenantname + + action :grant_role + end + end + end +end + +keystone_register "Register Identity Service" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_name "keystone" + service_type "identity" + service_description "Keystone Identity Service" + + action :create_service +end + +node.set["keystone"]["adminURL"] = identity_admin_endpoint.to_s +node.set["keystone"]["internalURL"] = identity_endpoint.to_s +node.set["keystone"]["publicURL"] = identity_endpoint.to_s + +Chef::Log.info "Keystone AdminURL: #{identity_admin_endpoint.to_s}" +Chef::Log.info "Keystone InternalURL: #{identity_endpoint.to_s}" +Chef::Log.info "Keystone PublicURL: #{identity_endpoint.to_s}" + +keystone_register "Register Identity Endpoint" do + auth_uri auth_uri + bootstrap_token bootstrap_token + service_type "identity" + endpoint_region node["keystone"]["region"] + endpoint_adminurl node["keystone"]["adminURL"] + endpoint_internalurl node["keystone"]["adminURL"] + endpoint_publicurl node["keystone"]["publicURL"] + + action :create_endpoint +end + +node["keystone"]["users"].each do |username, user_info| + keystone_credentials "Create EC2 credentials for '#{username}' user" do + auth_uri auth_uri + bootstrap_token bootstrap_token + user_name username + tenant_name user_info["default_tenant"] + end +end + diff --git a/recipes/server.rb b/recipes/server.rb index 1b7bb2b..6520483 100644 --- a/recipes/server.rb +++ b/recipes/server.rb @@ -95,11 +95,6 @@ ec2_endpoint = endpoint "compute-ec2-api" image_endpoint = endpoint "image-api" volume_endpoint = endpoint "volume-api" -admin_tenant_name = node["keystone"]["admin_tenant_name"] -admin_user = node["keystone"]["admin_user"] -admin_pass = user_password node["keystone"]["admin_user"] -auth_uri = ::URI.decode identity_admin_endpoint.to_s - db_user = node["keystone"]["db"]["username"] db_pass = db_password "keystone" sql_connection = db_uri("identity", db_user, db_pass) @@ -156,155 +151,3 @@ end # sync db after keystone.conf is generated execute "keystone-manage db_sync" # idempotent - -# We need to bootstrap the keystone admin user so that calls -# to keystone_register will succeed, since those provider calls -# use the admin tenant/user/pass to get an admin token. -bash "bootstrap-keystone-admin" do - # A shortcut bootstrap command was added to python-keystoneclient - # in early Grizzly timeframe... but we need to do all the commands - # here manually since the python-keystoneclient package included - # in CloudArchive (for now) doesn't have it... - #command "keystone bootstrap --os-token=#{bootstrap_token} --user-name=#{admin_user} --tenant-name=#{admin_tenant_name} --pass=#{admin_pass}" - insecure = node["openstack"]["auth"]["validate_certs"] ? "" : " --insecure" - base_ks_cmd = "keystone#{insecure} --endpoint=#{auth_uri} --token=#{bootstrap_token}" - code <<-EOF -set -x -function get_id () { - echo `"$@" | grep ' id ' | awk '{print $4}'` -} -#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} -if [[ $? -eq 1 ]]; then - ADMIN_TENANT=$(get_id #{base_ks_cmd} tenant-create --name=#{admin_tenant_name}) -else - ADMIN_TENANT=$(#{base_ks_cmd} tenant-list | grep #{admin_tenant_name} | awk '{print $2}') -fi -#{base_ks_cmd} role-list | grep admin -if [[ $? -eq 1 ]]; then - ADMIN_ROLE=$(get_id #{base_ks_cmd} role-create --name=admin) -else - ADMIN_ROLE=$(#{base_ks_cmd} role-list | grep admin | awk '{print $2}') -fi -#{base_ks_cmd} user-list | grep #{admin_user} -if [[ $? -eq 1 ]]; then - ADMIN_USER=$(get_id #{base_ks_cmd} user-create --name=#{admin_user} --pass="#{admin_pass}" --email=#{admin_user}@example.com) -else - ADMIN_USER=$(#{base_ks_cmd} user-list | grep #{admin_user} | awk '{print $2}') -fi -#{base_ks_cmd} user-role-list --user-id=$ADMIN_USER --tenant-id=$ADMIN_TENANT | grep admin -if [[ $? -eq 1 ]]; then - #{base_ks_cmd} user-role-add --user-id $ADMIN_USER --role-id $ADMIN_ROLE --tenant-id $ADMIN_TENANT -fi -exit 0 -EOF -end - -#TODO(shep): this should probably be derived from keystone.users hash keys -node["keystone"]["tenants"].each do |tenant_name| - ## Add openstack tenant ## - keystone_register "Register '#{tenant_name}' Tenant" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - tenant_name tenant_name - tenant_description "#{tenant_name} Tenant" - tenant_enabled "true" # Not required as this is the default - - action :create_tenant - end -end - -## Add Roles ## - -node["keystone"]["roles"].each do |role_key| - keystone_register "Register '#{role_key.to_s}' Role" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - role_name role_key - - action :create_role - end -end - -node["keystone"]["users"].each do |username, user_info| - keystone_register "Register '#{username}' User" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - user_name username - user_pass user_info["password"] - tenant_name user_info["default_tenant"] - user_enabled "true" # Not required as this is the default - - action :create_user - end - - user_info["roles"].each do |rolename, tenant_list| - tenant_list.each do |tenantname| - keystone_register "Grant '#{rolename}' Role to '#{username}' User in '#{tenantname}' Tenant" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - user_name username - role_name rolename - tenant_name tenantname - - action :grant_role - end - end - end -end - -## Add Services ## - -keystone_register "Register Identity Service" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - service_name "keystone" - service_type "identity" - service_description "Keystone Identity Service" - - action :create_service -end - -## Add Endpoints ## - -node.set["keystone"]["adminURL"] = identity_admin_endpoint.to_s -node.set["keystone"]["internalURL"] = identity_endpoint.to_s -node.set["keystone"]["publicURL"] = identity_endpoint.to_s - -Chef::Log.info "Keystone AdminURL: #{identity_admin_endpoint.to_s}" -Chef::Log.info "Keystone InternalURL: #{identity_admin_endpoint.to_s}" -Chef::Log.info "Keystone PublicURL: #{identity_endpoint.to_s}" - -keystone_register "Register Identity Endpoint" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - service_type "identity" - endpoint_region node["keystone"]["region"] - endpoint_adminurl node["keystone"]["adminURL"] - endpoint_internalurl node["keystone"]["adminURL"] - endpoint_publicurl node["keystone"]["publicURL"] - - action :create_endpoint -end - -node["keystone"]["users"].each do |username, user_info| - keystone_credentials "Create EC2 credentials for '#{username}' user" do - auth_uri auth_uri - admin_user admin_user - admin_tenant_name admin_tenant_name - admin_password admin_pass - user_name username - tenant_name user_info["default_tenant"] - end -end diff --git a/resources/credentials.rb b/resources/credentials.rb index 6219f4e..ba4171f 100644 --- a/resources/credentials.rb +++ b/resources/credentials.rb @@ -27,9 +27,7 @@ def initialize(*args) end attribute :auth_uri, :kind_of => String -attribute :admin_user, :kind_of => String -attribute :admin_password, :kind_of => String -attribute :admin_tenant_name, :kind_of => String +attribute :bootstrap_token, :kind_of => String attribute :tenant_name, :kind_of => String attribute :user_name, :kind_of => String diff --git a/resources/register.rb b/resources/register.rb index 1c2d94f..4c42ca6 100644 --- a/resources/register.rb +++ b/resources/register.rb @@ -27,9 +27,7 @@ def initialize(*args) end attribute :auth_uri, :kind_of => String -attribute :admin_user, :kind_of => String -attribute :admin_password, :kind_of => String -attribute :admin_tenant_name, :kind_of => String +attribute :bootstrap_token, :kind_of => String # Used by both :create_service and :create_endpoint attribute :service_type, :kind_of => String, :equal_to => [ "image", "identity", "compute", "storage", "ec2", "volume", "object-store" ]