Add support to create a Keystone domain for Heat

Heat in Kilo will require configuration that specifies a domain
that is set up in Keystone in its configuration. To facilitate that
undeprecate initialize_for_heat(), wean it off its use of the admin
token, and add a CLI utility to make calling it from -incubator easy.

Change-Id: Ifae32d806575fd48cc7d261deedd94efb5511d0d
This commit is contained in:
Steve Kowalik 2014-11-17 16:40:55 +10:30
parent f811472c5f
commit 601d7bdc16
5 changed files with 124 additions and 34 deletions

View File

@ -0,0 +1,53 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# 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.
import argparse
import logging
import textwrap
import os_cloud_config.cmd.utils._clients as clients
from os_cloud_config.cmd.utils import environment
from os_cloud_config.keystone import initialize_for_heat
def parse_args():
description = textwrap.dedent("""
Create a domain for Heat to use, as well as a user to administer it.
This will create a heat domain in Keystone, as well as an admin user that
has rights to administer the domain.
""")
parser = argparse.ArgumentParser(
formatter_class=argparse.RawDescriptionHelpFormatter,
description=description)
parser.add_argument('-d', '--domain-admin-password',
dest='domain_admin_password',
help="domain admin user's password to be set",
required=True)
environment._add_logging_arguments(parser)
return parser.parse_args()
def main():
args = parse_args()
environment._configure_logging(args)
try:
environment._ensure()
keystone_client = clients.get_keystone_v3_client()
initialize_for_heat(keystone_client, args.domain_admin_password)
except Exception:
logging.exception("Unexpected error during command execution")
return 1
return 0

View File

@ -0,0 +1,35 @@
# Copyright (c) 2014 Hewlett-Packard Development Company, L.P.
#
# 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.
import sys
import mock
from os_cloud_config.cmd import init_keystone_heat_domain
from os_cloud_config.tests import base
class InitKeystoneHeatDomainTest(base.TestCase):
@mock.patch('os_cloud_config.cmd.init_keystone_heat_domain.environment')
@mock.patch('os_cloud_config.cmd.init_keystone_heat_domain'
'.initialize_for_heat')
@mock.patch('os_cloud_config.cmd.utils._clients.get_keystone_v3_client',
return_value='keystone_v3_client_mock')
@mock.patch.object(sys, 'argv', ['init-keystone', '-d', 'password'])
def test_script(self, environment_mock, initialize_mock, client_mock):
init_keystone_heat_domain.main()
initialize_mock.assert_called_once_with('keystone_v3_client_mock',
'password')

View File

@ -161,35 +161,34 @@ def initialize_for_swift(host, admin_token, ssl=None, public=None):
keystone.roles.create('ResellerAdmin') keystone.roles.create('ResellerAdmin')
def initialize_for_heat(host, admin_token, domain_admin_password, def initialize_for_heat(keystone, domain_admin_password):
ssl=None, public=None):
"""Create Heat domain and an admin user for it. """Create Heat domain and an admin user for it.
:param host: ip/hostname of node where Keystone is running :param keystone: A keystone v3 client
:param admin_token: admin token to use with Keystone's admin endpoint
:param domain_admin_password: heat domain admin's password to be set :param domain_admin_password: heat domain admin's password to be set
:param ssl: ip/hostname to use as the ssl endpoint, if required
:param public: ip/hostname to use as the public endpoint, if the default
is not suitable
""" """
LOG.warn('This function is deprecated.') try:
heat_domain = keystone.domains.find(name='heat')
keystone = _create_admin_client_v2(host, admin_token, ssl, public) LOG.debug('Domain heat already exists.')
admin_role = keystone.roles.find(name='admin') except exceptions.NotFound:
LOG.debug('Creating heat domain.')
LOG.debug('Creating heat domain.') heat_domain = keystone.domains.create(
heat_domain = keystone.domains.create( 'heat',
'heat', description='Owns users and tenants created by heat'
description='Owns users and tenants created by heat' )
) try:
LOG.debug('Creating heat_domain_admin user.') heat_admin = keystone.users.find(name='heat_domain_admin')
heat_admin = keystone.users.create( LOG.debug('Heat domain admin already exists.')
'heat_domain_admin', except exceptions.NotFound:
description='Manages users and tenants created by heat', LOG.debug('Creating heat_domain_admin user.')
domain=heat_domain, heat_admin = keystone.users.create(
password=domain_admin_password, 'heat_domain_admin',
) description='Manages users and tenants created by heat',
domain=heat_domain,
password=domain_admin_password,
)
LOG.debug('Granting admin role to heat_domain_admin user on heat domain.') LOG.debug('Granting admin role to heat_domain_admin user on heat domain.')
admin_role = keystone.roles.find(name='admin')
keystone.roles.grant(admin_role, user=heat_admin, domain=heat_domain) keystone.roles.grant(admin_role, user=heat_admin, domain=heat_domain)

View File

@ -77,22 +77,24 @@ class KeystoneTest(base.TestCase):
[mock.call('swiftoperator'), mock.call('ResellerAdmin')]) [mock.call('swiftoperator'), mock.call('ResellerAdmin')])
def test_initialize_for_heat(self): def test_initialize_for_heat(self):
self._patch_client() client = mock.MagicMock()
client.domains.find.side_effect = exceptions.NotFound
client.users.find.side_effect = exceptions.NotFound
keystone.initialize_for_heat('192.0.0.3', 'mytoken', 'heatadminpasswd') keystone.initialize_for_heat(client, 'heatadminpasswd')
self.client.domains.create.assert_called_once_with( client.domains.create.assert_called_once_with(
'heat', description='Owns users and tenants created by heat') 'heat', description='Owns users and tenants created by heat')
self.client.users.create.assert_called_once_with( client.users.create.assert_called_once_with(
'heat_domain_admin', 'heat_domain_admin',
description='Manages users and tenants created by heat', description='Manages users and tenants created by heat',
domain=self.client.domains.create.return_value, domain=client.domains.create.return_value,
password='heatadminpasswd') password='heatadminpasswd')
self.client.roles.find.assert_called_once_with(name='admin') client.roles.find.assert_called_once_with(name='admin')
self.client.roles.grant.assert_called_once_with( client.roles.grant.assert_called_once_with(
self.client.roles.find.return_value, client.roles.find.return_value,
user=self.client.users.create.return_value, user=client.users.create.return_value,
domain=self.client.domains.create.return_value) domain=client.domains.create.return_value)
@mock.patch('subprocess.check_call') @mock.patch('subprocess.check_call')
def test_idempotent_initialize(self, check_call_mock): def test_idempotent_initialize(self, check_call_mock):

View File

@ -27,6 +27,7 @@ packages =
console_scripts = console_scripts =
generate-keystone-pki = os_cloud_config.cmd.generate_keystone_pki:main generate-keystone-pki = os_cloud_config.cmd.generate_keystone_pki:main
init-keystone = os_cloud_config.cmd.init_keystone:main init-keystone = os_cloud_config.cmd.init_keystone:main
init-keystone-heat-domain = os_cloud_config.cmd.init_keystone_heat_domain:main
register-nodes = os_cloud_config.cmd.register_nodes:main register-nodes = os_cloud_config.cmd.register_nodes:main
setup-endpoints = os_cloud_config.cmd.setup_endpoints:main setup-endpoints = os_cloud_config.cmd.setup_endpoints:main
setup-flavors = os_cloud_config.cmd.setup_flavors:main setup-flavors = os_cloud_config.cmd.setup_flavors:main