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')
def initialize_for_heat(host, admin_token, domain_admin_password,
ssl=None, public=None):
def initialize_for_heat(keystone, domain_admin_password):
"""Create Heat domain and an admin user for it.
:param host: ip/hostname of node where Keystone is running
:param admin_token: admin token to use with Keystone's admin endpoint
:param keystone: A keystone v3 client
: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.')
keystone = _create_admin_client_v2(host, admin_token, ssl, public)
admin_role = keystone.roles.find(name='admin')
LOG.debug('Creating heat domain.')
heat_domain = keystone.domains.create(
'heat',
description='Owns users and tenants created by heat'
)
LOG.debug('Creating heat_domain_admin user.')
heat_admin = keystone.users.create(
'heat_domain_admin',
description='Manages users and tenants created by heat',
domain=heat_domain,
password=domain_admin_password,
)
try:
heat_domain = keystone.domains.find(name='heat')
LOG.debug('Domain heat already exists.')
except exceptions.NotFound:
LOG.debug('Creating heat domain.')
heat_domain = keystone.domains.create(
'heat',
description='Owns users and tenants created by heat'
)
try:
heat_admin = keystone.users.find(name='heat_domain_admin')
LOG.debug('Heat domain admin already exists.')
except exceptions.NotFound:
LOG.debug('Creating heat_domain_admin user.')
heat_admin = keystone.users.create(
'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.')
admin_role = keystone.roles.find(name='admin')
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')])
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')
self.client.users.create.assert_called_once_with(
client.users.create.assert_called_once_with(
'heat_domain_admin',
description='Manages users and tenants created by heat',
domain=self.client.domains.create.return_value,
domain=client.domains.create.return_value,
password='heatadminpasswd')
self.client.roles.find.assert_called_once_with(name='admin')
self.client.roles.grant.assert_called_once_with(
self.client.roles.find.return_value,
user=self.client.users.create.return_value,
domain=self.client.domains.create.return_value)
client.roles.find.assert_called_once_with(name='admin')
client.roles.grant.assert_called_once_with(
client.roles.find.return_value,
user=client.users.create.return_value,
domain=client.domains.create.return_value)
@mock.patch('subprocess.check_call')
def test_idempotent_initialize(self, check_call_mock):

View File

@ -27,6 +27,7 @@ packages =
console_scripts =
generate-keystone-pki = os_cloud_config.cmd.generate_keystone_pki: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
setup-endpoints = os_cloud_config.cmd.setup_endpoints:main
setup-flavors = os_cloud_config.cmd.setup_flavors:main