Remove the dependancy on Mistral

The keystone utils for getting project endpoints is the last reason that
we import directly from Mistral. This change copies over the relevant
code that we need.

Removing the dep also discovered two dependancies that we had but didn't
explicitly state previously.

Change-Id: If4cb4ac4ca75ea0f165196b4a0a08ea3d3a16e25
This commit is contained in:
Dougal Matthews 2017-06-23 10:40:26 +01:00
parent 76b3e025b7
commit 9bc1f31469
6 changed files with 148 additions and 16 deletions

View File

@ -14,9 +14,10 @@ oslo.utils>=3.20.0 # Apache-2.0
python-glanceclient>=2.7.0 # Apache-2.0
python-ironicclient>=1.11.0 # Apache-2.0
six>=1.9.0 # MIT
mistral!=2015.1.0,>=3.0.0 # Apache-2.0
mistral-lib>=0.2.0 # Apache-2.0
oslo.concurrency>=3.8.0 # Apache-2.0
python-ironic-inspector-client>=1.5.0 # Apache-2.0
python-mistralclient>=3.1.0 # Apache-2.0
Jinja2!=2.9.0,!=2.9.1,!=2.9.2,!=2.9.3,!=2.9.4,>=2.8 # BSD License (3 clause)
python-novaclient>=9.0.0 # Apache-2.0
passlib>=1.7.0 # BSD

View File

@ -19,13 +19,14 @@ from glanceclient.v2 import client as glanceclient
from heatclient.v1 import client as heatclient
import ironic_inspector_client
from ironicclient.v1 import client as ironicclient
from mistral.utils.openstack import keystone as keystone_utils
from mistral_lib import actions
from mistralclient.api import client as mistral_client
from novaclient.client import Client as nova_client
from swiftclient import client as swift_client
from swiftclient import exceptions as swiftexceptions
from tripleo_common import constants
from tripleo_common.utils import keystone as keystone_utils
class TripleOAction(actions.Action):
@ -34,7 +35,7 @@ class TripleOAction(actions.Action):
super(TripleOAction, self).__init__()
def get_object_client(self, context):
obj_ep = keystone_utils.get_endpoint_for_project('swift')
obj_ep = keystone_utils.get_endpoint_for_project(context, 'swift')
kwargs = {
'preauthurl': obj_ep.url % {'tenant_id': context.project_id},
@ -47,7 +48,8 @@ class TripleOAction(actions.Action):
return swift_client.Connection(**kwargs)
def get_baremetal_client(self, context):
ironic_endpoint = keystone_utils.get_endpoint_for_project('ironic')
ironic_endpoint = keystone_utils.get_endpoint_for_project(
context, 'ironic')
# FIXME(lucasagomes): Use ironicclient.get_client() instead
# of ironicclient.Client(). Client() might cause errors since
@ -69,7 +71,7 @@ class TripleOAction(actions.Action):
def get_baremetal_introspection_client(self, context):
bmi_endpoint = keystone_utils.get_endpoint_for_project(
'ironic-inspector')
context, 'ironic-inspector')
return ironic_inspector_client.ClientV1(
api_version='1.2',
@ -79,7 +81,8 @@ class TripleOAction(actions.Action):
)
def get_image_client(self, context):
glance_endpoint = keystone_utils.get_endpoint_for_project('glance')
glance_endpoint = keystone_utils.get_endpoint_for_project(
context, 'glance')
return glanceclient.Client(
glance_endpoint.url,
token=context.auth_token,
@ -87,7 +90,8 @@ class TripleOAction(actions.Action):
)
def get_orchestration_client(self, context):
heat_endpoint = keystone_utils.get_endpoint_for_project('heat')
heat_endpoint = keystone_utils.get_endpoint_for_project(
context, 'heat')
endpoint_url = keystone_utils.format_url(
heat_endpoint.url,
@ -102,7 +106,8 @@ class TripleOAction(actions.Action):
)
def get_workflow_client(self, context):
mistral_endpoint = keystone_utils.get_endpoint_for_project('mistral')
mistral_endpoint = keystone_utils.get_endpoint_for_project(
context, 'mistral')
mc = mistral_client.client(auth_token=context.auth_token,
mistral_url=mistral_endpoint.url)
@ -110,8 +115,10 @@ class TripleOAction(actions.Action):
return mc
def get_compute_client(self, context):
keystone_endpoint = keystone_utils.get_endpoint_for_project('keystone')
nova_endpoint = keystone_utils.get_endpoint_for_project('nova')
keystone_endpoint = keystone_utils.get_endpoint_for_project(
context, 'keystone')
nova_endpoint = keystone_utils.get_endpoint_for_project(
context, 'nova')
client = nova_client(
2,

View File

@ -118,3 +118,7 @@ class NotFound(Exception):
class RoleMetadataError(Exception):
"""Role metadata is invalid"""
class UnauthorizedException(Exception):
"""Authorization failed"""

View File

@ -17,10 +17,10 @@ import zlib
import mock
from ironicclient.v1 import client as ironicclient
from mistral.utils.openstack import keystone as keystone_utils
from tripleo_common.actions import base
from tripleo_common.tests import base as tests_base
from tripleo_common.utils import keystone as keystone_utils
from swiftclient.exceptions import ClientException
@ -41,7 +41,7 @@ class TestActionsBase(tests_base.TestCase):
mock_client.assert_called_once_with(
'http://ironic/v1', max_retries=12, os_ironic_api_version='1.15',
region_name='ironic-region', retry_interval=5, token=mock.ANY)
mock_endpoint.assert_called_once_with('ironic')
mock_endpoint.assert_called_once_with(mock_cxt, 'ironic')
mock_cxt.assert_not_called()
def test_cache_key(self, mock_endpoint):

View File

@ -857,8 +857,7 @@ class GetProfileOfFlavorActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_compute_client')
@mock.patch('mistral.context.ctx')
def test_profile_found(self, mock_ctx, mock_get_compute_client,
def test_profile_found(self, mock_get_compute_client,
mock_get_profile_of_flavor):
mock_ctx = mock.MagicMock()
mock_get_profile_of_flavor.return_value = 'compute'
@ -870,8 +869,7 @@ class GetProfileOfFlavorActionTest(base.TestCase):
@mock.patch('tripleo_common.utils.parameters.get_profile_of_flavor')
@mock.patch('tripleo_common.actions.base.TripleOAction.'
'get_compute_client')
@mock.patch('mistral.context.ctx')
def test_profile_not_found(self, mock_ctx, mock_get_compute_client,
def test_profile_not_found(self, mock_get_compute_client,
mock_get_profile_of_flavor):
mock_ctx = mock.MagicMock()
profile = (exception.DeriveParamsError, )

View File

@ -0,0 +1,122 @@
# Copyright (c) 2013 Mirantis Inc.
# Copyright (c) 2017 Red Hat, 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.
from keystoneclient import service_catalog as ks_service_catalog
from keystoneclient.v3 import client as ks_client
from keystoneclient.v3 import endpoints as ks_endpoints
import six
from tripleo_common import exception
def client(ctx):
auth_url = ctx.auth_uri
cl = ks_client.Client(
user_id=ctx.user_id,
token=ctx.auth_token,
tenant_id=ctx.project_id,
auth_url=auth_url
)
cl.management_url = auth_url
return cl
def get_endpoint_for_project(ctx, service_name=None, service_type=None,
region_name=None):
if service_name is None and service_type is None:
raise ValueError(
"Either 'service_name' or 'service_type' must be provided."
)
service_catalog = obtain_service_catalog(ctx)
# When region_name is not passed, first get from context as region_name
# could be passed to rest api in http header ('X-Region-Name'). Otherwise,
# just get region from mistral configuration.
region = (region_name or ctx.region_name)
service_endpoints = service_catalog.get_endpoints(
service_name=service_name,
service_type=service_type,
region_name=region
)
endpoint = None
os_actions_endpoint_type = 'public'
for endpoints in six.itervalues(service_endpoints):
for ep in endpoints:
# is V3 interface?
if 'interface' in ep:
interface_type = ep['interface']
if os_actions_endpoint_type in interface_type:
endpoint = ks_endpoints.Endpoint(
None,
ep,
loaded=True
)
break
# is V2 interface?
if 'publicURL' in ep:
endpoint_data = {
'url': ep['publicURL'],
'region': ep['region']
}
endpoint = ks_endpoints.Endpoint(
None,
endpoint_data,
loaded=True
)
break
if not endpoint:
raise RuntimeError(
"No endpoints found [service_name=%s, service_type=%s,"
" region_name=%s]"
% (service_name, service_type, region)
)
else:
return endpoint
def obtain_service_catalog(ctx):
token = ctx.auth_token
response = ctx.service_catalog
# Target service catalog may not be passed via API.
if not response and ctx.is_target:
response = client().tokens.get_token_data(
token,
include_catalog=True
)['token']
if not response:
raise exception.UnauthorizedException()
service_catalog = ks_service_catalog.ServiceCatalog.factory(response)
return service_catalog
def format_url(url_template, values):
# Since we can't use keystone module, we can do similar thing:
# see https://github.com/openstack/keystone/blob/master/keystone/
# catalog/core.py#L42-L60
return url_template.replace('$(', '%(') % values