Switch stackrc and undercloud.py to use Keystone v3
- Update undercloud stackrc to use Keystone v3 API. It updates OS_AUTH_URL to use a versionless endpoint, change OS_TENANT_NAME to OS_PROJECT_NAME, force OS_IDENTITY_API_VERSION to 3 and add OS_PROJECT_DOMAIN_NAME pointing to default domain. - In undercloud.py, rename tenant to project which is the right word in Keystone v3 vocabulary. - Support the _member_ role addition to the admin user using v3 Co-Authored-By: Juan Antonio Osorio Robles <jaosorior@redhat.com> Change-Id: I4fe6d9767a104af1cfef83b27869df7210e65b83 Partial-implement: blueprint keystone-v3
This commit is contained in:
parent
4077ec823c
commit
96f09de818
@ -7,17 +7,17 @@ export OS_PASSWORD
|
||||
OS_AUTH_TYPE=password
|
||||
export OS_AUTH_TYPE
|
||||
{{#service_certificate}}
|
||||
OS_AUTH_URL=https://{{public_host}}:13000/v2.0
|
||||
OS_AUTH_URL=https://{{public_host}}:13000/
|
||||
PYTHONWARNINGS="ignore:Certificate has no, ignore:A true SSLContext object is not available"
|
||||
export OS_AUTH_URL
|
||||
export PYTHONWARNINGS
|
||||
{{/service_certificate}}
|
||||
{{^service_certificate}}
|
||||
OS_AUTH_URL=http://{{local-ip}}:5000/v2.0
|
||||
OS_AUTH_URL=http://{{local-ip}}:5000/
|
||||
export OS_AUTH_URL
|
||||
{{/service_certificate}}
|
||||
OS_USERNAME=admin
|
||||
OS_TENANT_NAME=admin
|
||||
OS_PROJECT_NAME=admin
|
||||
COMPUTE_API_VERSION=1.1
|
||||
# 1.29 is the latest API version in Ironic Ocata supported by ironicclient
|
||||
IRONIC_API_VERSION=1.29
|
||||
@ -25,12 +25,18 @@ OS_BAREMETAL_API_VERSION=$IRONIC_API_VERSION
|
||||
OS_NO_CACHE=True
|
||||
OS_CLOUDNAME=undercloud
|
||||
export OS_USERNAME
|
||||
export OS_TENANT_NAME
|
||||
export OS_PROJECT_NAME
|
||||
export COMPUTE_API_VERSION
|
||||
export IRONIC_API_VERSION
|
||||
export OS_BAREMETAL_API_VERSION
|
||||
export OS_NO_CACHE
|
||||
export OS_CLOUDNAME
|
||||
OS_IDENTITY_API_VERSION='3'
|
||||
export OS_IDENTITY_API_VERSION
|
||||
OS_PROJECT_DOMAIN_NAME='Default'
|
||||
export OS_PROJECT_DOMAIN_NAME
|
||||
OS_USER_DOMAIN_NAME='Default'
|
||||
export OS_USER_DOMAIN_NAME
|
||||
|
||||
# Add OS_CLOUDNAME to PS1
|
||||
if [ -z "${CLOUDPROMPT_ENABLED:-}" ]; then
|
||||
|
@ -22,7 +22,6 @@ import tempfile
|
||||
import fixtures
|
||||
from keystoneauth1 import exceptions as ks_exceptions
|
||||
import mock
|
||||
from mistralclient.api import base as mistralclient_base
|
||||
from novaclient import exceptions
|
||||
from oslo_config import fixture as config_fixture
|
||||
from oslotest import base
|
||||
@ -135,10 +134,10 @@ class TestUndercloud(BaseTestCase):
|
||||
def test_extract_from_stackrc(self):
|
||||
with open(os.path.expanduser('~/stackrc'), 'w') as f:
|
||||
f.write('OS_USERNAME=aturing\n')
|
||||
f.write('OS_AUTH_URL=http://bletchley:5000/v2.0\n')
|
||||
f.write('OS_AUTH_URL=http://bletchley:5000/\n')
|
||||
self.assertEqual('aturing',
|
||||
undercloud._extract_from_stackrc('OS_USERNAME'))
|
||||
self.assertEqual('http://bletchley:5000/v2.0',
|
||||
self.assertEqual('http://bletchley:5000/',
|
||||
undercloud._extract_from_stackrc('OS_AUTH_URL'))
|
||||
|
||||
@mock.patch('instack_undercloud.undercloud._check_hostname')
|
||||
@ -662,7 +661,7 @@ class TestPostConfig(base.BaseTestCase):
|
||||
'http://192.168.24.1:8989/v2',
|
||||
}
|
||||
mock_get_auth_values.return_value = ('aturing', '3nigma', 'hut8',
|
||||
'http://bletchley:5000/v2.0')
|
||||
'http://bletchley:5000/')
|
||||
mock_instance_nova = mock.Mock()
|
||||
mock_nova_client.return_value = mock_instance_nova
|
||||
mock_instance_swift = mock.Mock()
|
||||
@ -672,7 +671,7 @@ class TestPostConfig(base.BaseTestCase):
|
||||
undercloud._post_config(instack_env)
|
||||
mock_nova_client.assert_called_with(
|
||||
2, 'aturing', '3nigma', project_name='hut8',
|
||||
auth_url='http://bletchley:5000/v2.0')
|
||||
auth_url='http://bletchley:5000/')
|
||||
self.assertTrue(mock_copy_stackrc.called)
|
||||
mock_configure_ssh_keys.assert_called_with(mock_instance_nova)
|
||||
calls = [mock.call(mock_instance_nova, 'baremetal'),
|
||||
@ -710,7 +709,7 @@ class TestPostConfig(base.BaseTestCase):
|
||||
def test_create_config_environment(self):
|
||||
mock_mistral = mock.Mock()
|
||||
mock_mistral.environments.get.side_effect = (
|
||||
mistralclient_base.APIException)
|
||||
ks_exceptions.NotFound)
|
||||
|
||||
env = {
|
||||
"UNDERCLOUD_CEILOMETER_SNMPD_PASSWORD": "snmpd-pass"
|
||||
@ -770,7 +769,7 @@ class TestPostConfig(base.BaseTestCase):
|
||||
|
||||
def _mock_ksclient_roles(self, mock_auth_values, mock_ksdiscover, roles):
|
||||
mock_auth_values.return_value = ('user', 'password',
|
||||
'tenant', 'http://test:123')
|
||||
'project', 'http://test:123')
|
||||
mock_discover = mock.Mock()
|
||||
mock_ksdiscover.return_value = mock_discover
|
||||
mock_client = mock.Mock()
|
||||
@ -784,12 +783,14 @@ class TestPostConfig(base.BaseTestCase):
|
||||
mock_client.roles = mock_roles
|
||||
mock_discover.create_client.return_value = mock_client
|
||||
|
||||
mock_tenant_list = [mock.Mock(), mock.Mock()]
|
||||
mock_tenant_list[0].name = 'admin'
|
||||
mock_tenant_list[0].id = 'admin-id'
|
||||
mock_tenant_list[1].name = 'service'
|
||||
mock_tenant_list[1].id = 'service-id'
|
||||
mock_client.tenants.list.return_value = mock_tenant_list
|
||||
mock_client.version = 'v3'
|
||||
|
||||
mock_project_list = [mock.Mock(), mock.Mock()]
|
||||
mock_project_list[0].name = 'admin'
|
||||
mock_project_list[0].id = 'admin-id'
|
||||
mock_project_list[1].name = 'service'
|
||||
mock_project_list[1].id = 'service-id'
|
||||
mock_client.projects.list.return_value = mock_project_list
|
||||
|
||||
mock_user_list = [mock.Mock(), mock.Mock()]
|
||||
mock_user_list[0].name = 'admin'
|
||||
@ -807,7 +808,7 @@ class TestPostConfig(base.BaseTestCase):
|
||||
mock_ksdiscover,
|
||||
['admin'])
|
||||
undercloud._member_role_exists()
|
||||
self.assertFalse(mock_client.tenants.list.called)
|
||||
self.assertFalse(mock_client.projects.list.called)
|
||||
|
||||
@mock.patch('keystoneclient.discover.Discover')
|
||||
@mock.patch('instack_undercloud.undercloud._get_auth_values')
|
||||
@ -821,8 +822,8 @@ class TestPostConfig(base.BaseTestCase):
|
||||
undercloud._member_role_exists()
|
||||
mock_user = mock_client.users.list.return_value[0]
|
||||
mock_role = mock_client.roles.list.return_value[1]
|
||||
mock_client.roles.add_user_role.assert_called_once_with(
|
||||
mock_user, mock_role, 'admin-id')
|
||||
mock_client.roles.grant.assert_called_once_with(
|
||||
mock_role, user=mock_user, project='admin-id')
|
||||
|
||||
@mock.patch('keystoneclient.discover.Discover')
|
||||
@mock.patch('instack_undercloud.undercloud._get_auth_values')
|
||||
@ -834,12 +835,12 @@ class TestPostConfig(base.BaseTestCase):
|
||||
mock_ksdiscover,
|
||||
['admin', '_member_'])
|
||||
fake_exception = ks_exceptions.http.Conflict('test')
|
||||
mock_client.roles.add_user_role.side_effect = fake_exception
|
||||
mock_client.roles.grant.side_effect = fake_exception
|
||||
undercloud._member_role_exists()
|
||||
mock_user = mock_client.users.list.return_value[0]
|
||||
mock_role = mock_client.roles.list.return_value[1]
|
||||
mock_client.roles.add_user_role.assert_called_once_with(
|
||||
mock_user, mock_role, 'admin-id')
|
||||
mock_client.roles.grant.assert_called_once_with(
|
||||
mock_role, user=mock_user, project='admin-id')
|
||||
|
||||
def _create_flavor_mocks(self):
|
||||
mock_nova = mock.Mock()
|
||||
|
@ -31,12 +31,11 @@ import time
|
||||
import uuid
|
||||
import yaml
|
||||
|
||||
from keystoneauth1 import exceptions as ks_exceptions
|
||||
from keystoneauth1 import session
|
||||
from keystoneclient import auth
|
||||
from keystoneauth1 import exceptions as ks_exceptions
|
||||
from keystoneclient import discover
|
||||
import keystoneauth1.identity.generic as ks_auth
|
||||
from mistralclient.api import client as mistralclient
|
||||
from mistralclient.api import base as mistralclient_base
|
||||
import novaclient as nc
|
||||
from novaclient import client as novaclient
|
||||
from novaclient import exceptions
|
||||
@ -941,20 +940,16 @@ def _member_role_exists():
|
||||
# This is a workaround for puppet removing the deprecated _member_
|
||||
# role on upgrade - if it exists we must restore role assignments
|
||||
# or trusts stored in the undercloud heat will break
|
||||
user, password, tenant, auth_url = _get_auth_values()
|
||||
# Note this is made somewhat verbose due to trying to handle
|
||||
# any format auth_url (versionless, v2,0/v3 suffix)
|
||||
auth_plugin_class = auth.get_plugin_class('password')
|
||||
user, password, project, auth_url = _get_auth_values()
|
||||
auth_kwargs = {
|
||||
'auth_url': auth_url,
|
||||
'username': user,
|
||||
'password': password,
|
||||
'project_name': tenant}
|
||||
if 'v2.0' not in auth_url:
|
||||
auth_kwargs.update({
|
||||
'project_domain_name': 'default',
|
||||
'user_domain_name': 'default'})
|
||||
auth_plugin = auth_plugin_class(**auth_kwargs)
|
||||
'project_name': project,
|
||||
'project_domain_name': 'Default',
|
||||
'user_domain_name': 'Default',
|
||||
}
|
||||
auth_plugin = ks_auth.Password(**auth_kwargs)
|
||||
sess = session.Session(auth=auth_plugin)
|
||||
disc = discover.Discover(session=sess)
|
||||
c = disc.create_client()
|
||||
@ -963,10 +958,24 @@ def _member_role_exists():
|
||||
except IndexError:
|
||||
# Do nothing if there is no _member_ role
|
||||
return
|
||||
admin_tenant = [t for t in c.tenants.list() if t.name == 'admin'][0]
|
||||
if c.version == 'v2.0':
|
||||
client_projects = c.tenants
|
||||
else:
|
||||
client_projects = c.projects
|
||||
admin_project = [t for t in client_projects.list() if t.name == 'admin'][0]
|
||||
admin_user = [u for u in c.users.list() if u.name == 'admin'][0]
|
||||
if c.version == 'v2.0':
|
||||
try:
|
||||
c.roles.add_user_role(admin_user, member_role, admin_tenant.id)
|
||||
c.roles.add_user_role(admin_user, member_role, admin_project.id)
|
||||
LOG.info('Added _member_ role to admin user')
|
||||
except ks_exceptions.http.Conflict:
|
||||
# They already had the role
|
||||
pass
|
||||
else:
|
||||
try:
|
||||
c.roles.grant(member_role,
|
||||
user=admin_user,
|
||||
project=admin_project.id)
|
||||
LOG.info('Added _member_ role to admin user')
|
||||
except ks_exceptions.http.Conflict:
|
||||
# They already had the role
|
||||
@ -1243,14 +1252,14 @@ def _ensure_user_identity(id_path):
|
||||
def _get_auth_values():
|
||||
"""Get auth values from stackrc
|
||||
|
||||
Returns the user, password, tenant and auth_url as read from stackrc,
|
||||
Returns the user, password, project and auth_url as read from stackrc,
|
||||
in that order as a tuple.
|
||||
"""
|
||||
user = _extract_from_stackrc('OS_USERNAME')
|
||||
password = _run_command(['sudo', 'hiera', 'admin_password']).rstrip()
|
||||
tenant = _extract_from_stackrc('OS_TENANT_NAME')
|
||||
project = _extract_from_stackrc('OS_PROJECT_NAME')
|
||||
auth_url = _extract_from_stackrc('OS_AUTH_URL')
|
||||
return user, password, tenant, auth_url
|
||||
return user, password, project, auth_url
|
||||
|
||||
|
||||
def _configure_ssh_keys(nova):
|
||||
@ -1325,7 +1334,7 @@ def _create_mistral_config_environment(instack_env, mistral):
|
||||
env_name = "tripleo.undercloud-config"
|
||||
try:
|
||||
mistral.environments.get(env_name)
|
||||
except mistralclient_base.APIException:
|
||||
except ks_exceptions.NotFound:
|
||||
mistral.environments.create(
|
||||
name=env_name,
|
||||
variables=json.dumps({
|
||||
@ -1411,13 +1420,23 @@ def _post_config_mistral(instack_env, mistral, swift):
|
||||
|
||||
def _post_config(instack_env):
|
||||
_copy_stackrc()
|
||||
user, password, tenant, auth_url = _get_auth_values()
|
||||
user, password, project, auth_url = _get_auth_values()
|
||||
auth_kwargs = {
|
||||
'auth_url': auth_url,
|
||||
'username': user,
|
||||
'password': password,
|
||||
'project_name': project,
|
||||
'project_domain_name': 'Default',
|
||||
'user_domain_name': 'Default',
|
||||
}
|
||||
auth_plugin = ks_auth.Password(**auth_kwargs)
|
||||
sess = session.Session(auth=auth_plugin)
|
||||
# TODO(andreykurilin): remove this check with support of novaclient 6.0.0
|
||||
if nc.__version__[0] == "6":
|
||||
nova = novaclient.Client(2, user, password, tenant, auth_url=auth_url)
|
||||
nova = novaclient.Client(2, user, password, project, auth_url=auth_url)
|
||||
else:
|
||||
nova = novaclient.Client(2, user, password, auth_url=auth_url,
|
||||
project_name=tenant)
|
||||
project_name=project)
|
||||
|
||||
_configure_ssh_keys(nova)
|
||||
_delete_default_flavors(nova)
|
||||
@ -1432,18 +1451,11 @@ def _post_config(instack_env):
|
||||
mistral_url = instack_env['UNDERCLOUD_ENDPOINT_MISTRAL_PUBLIC']
|
||||
mistral = mistralclient.client(
|
||||
mistral_url=mistral_url,
|
||||
username=user,
|
||||
api_key=password,
|
||||
project_name=tenant,
|
||||
auth_url=auth_url)
|
||||
session=sess)
|
||||
swift = swiftclient.Connection(
|
||||
authurl=auth_url,
|
||||
user=user,
|
||||
key=password,
|
||||
tenant_name=tenant,
|
||||
auth_version='2.0'
|
||||
session=sess
|
||||
)
|
||||
|
||||
_post_config_mistral(instack_env, mistral, swift)
|
||||
_member_role_exists()
|
||||
|
||||
|
6
releasenotes/notes/stackrc-v3-1e4513172af13806.yaml
Normal file
6
releasenotes/notes/stackrc-v3-1e4513172af13806.yaml
Normal file
@ -0,0 +1,6 @@
|
||||
---
|
||||
features:
|
||||
- Update undercloud stackrc to use Keystone v3 API.
|
||||
It updates OS_AUTH_URL to use a versionless endpoint, change OS_TENANT_NAME
|
||||
to OS_PROJECT_NAME, force OS_IDENTITY_API_VERSION to 3 and
|
||||
add OS_PROJECT_DOMAIN_NAME pointing to default domain.
|
Loading…
Reference in New Issue
Block a user