Merge "Use client credentials to retrieve service list"
This commit is contained in:
commit
cba49f38fe
@ -109,21 +109,24 @@ class KeystoneAction(base.OpenStackAction):
|
|||||||
|
|
||||||
LOG.debug("Keystone action security context: %s" % ctx)
|
LOG.debug("Keystone action security context: %s" % ctx)
|
||||||
|
|
||||||
|
# TODO(akovi) cacert is deprecated in favor of session
|
||||||
|
# TODO(akovi) this piece of code should be refactored
|
||||||
|
# TODO(akovi) to follow the new guide lines
|
||||||
kwargs = {
|
kwargs = {
|
||||||
'token': ctx.auth_token,
|
'token': ctx.auth_token,
|
||||||
'auth_url': CONF.keystone_authtoken.auth_uri,
|
'auth_url': ctx.auth_uri,
|
||||||
'project_id': ctx.project_id,
|
'project_id': ctx.project_id,
|
||||||
'cacert': CONF.keystone_authtoken.cafile,
|
'cacert': ctx.auth_cacert,
|
||||||
}
|
}
|
||||||
|
|
||||||
# In case of trust-scoped token explicitly pass endpoint parameter.
|
# In case of trust-scoped token explicitly pass endpoint parameter.
|
||||||
if (ctx.is_trust_scoped
|
if (ctx.is_trust_scoped
|
||||||
or keystone_utils.is_token_trust_scoped(ctx.auth_token)):
|
or keystone_utils.is_token_trust_scoped(ctx.auth_token)):
|
||||||
kwargs['endpoint'] = CONF.keystone_authtoken.auth_uri
|
kwargs['endpoint'] = ctx.auth_uri
|
||||||
|
|
||||||
client = self._client_class(**kwargs)
|
client = self._client_class(**kwargs)
|
||||||
|
|
||||||
client.management_url = CONF.keystone_authtoken.auth_uri
|
client.management_url = ctx.auth_uri
|
||||||
|
|
||||||
return client
|
return client
|
||||||
|
|
||||||
@ -214,7 +217,7 @@ class NeutronAction(base.OpenStackAction):
|
|||||||
endpoint_url=neutron_endpoint.url,
|
endpoint_url=neutron_endpoint.url,
|
||||||
region_name=neutron_endpoint.region,
|
region_name=neutron_endpoint.region,
|
||||||
token=ctx.auth_token,
|
token=ctx.auth_token,
|
||||||
auth_url=CONF.keystone_authtoken.auth_uri
|
auth_url=ctx.auth_uri
|
||||||
)
|
)
|
||||||
|
|
||||||
|
|
||||||
@ -595,7 +598,7 @@ class DesignateAction(base.OpenStackAction):
|
|||||||
client = self._client_class(
|
client = self._client_class(
|
||||||
endpoint=designate_url,
|
endpoint=designate_url,
|
||||||
tenant_id=ctx.project_id,
|
tenant_id=ctx.project_id,
|
||||||
auth_url=CONF.keystone_authtoken.auth_uri,
|
auth_url=ctx.auth_uri,
|
||||||
region_name=designate_endpoint.region,
|
region_name=designate_endpoint.region,
|
||||||
service_type='dns'
|
service_type='dns'
|
||||||
)
|
)
|
||||||
|
@ -68,6 +68,8 @@ class BaseContext(object):
|
|||||||
class MistralContext(BaseContext):
|
class MistralContext(BaseContext):
|
||||||
# Use set([...]) since set literals are not supported in Python 2.6.
|
# Use set([...]) since set literals are not supported in Python 2.6.
|
||||||
_elements = set([
|
_elements = set([
|
||||||
|
"auth_uri",
|
||||||
|
"auth_cacert",
|
||||||
"user_id",
|
"user_id",
|
||||||
"project_id",
|
"project_id",
|
||||||
"auth_token",
|
"auth_token",
|
||||||
@ -120,6 +122,8 @@ def spawn(thread_description, func, *args, **kwargs):
|
|||||||
|
|
||||||
def context_from_headers(headers):
|
def context_from_headers(headers):
|
||||||
return MistralContext(
|
return MistralContext(
|
||||||
|
auth_uri=CONF.keystone_authtoken.auth_uri,
|
||||||
|
auth_cacert=CONF.keystone_authtoken.cafile,
|
||||||
user_id=headers.get('X-User-Id'),
|
user_id=headers.get('X-User-Id'),
|
||||||
project_id=headers.get('X-Project-Id'),
|
project_id=headers.get('X-Project-Id'),
|
||||||
auth_token=headers.get('X-Auth-Token'),
|
auth_token=headers.get('X-Auth-Token'),
|
||||||
|
@ -15,6 +15,57 @@
|
|||||||
from mistral.tests.unit import base
|
from mistral.tests.unit import base
|
||||||
from mistral.utils.openstack import keystone
|
from mistral.utils.openstack import keystone
|
||||||
|
|
||||||
|
SERVICES_CATALOG = [
|
||||||
|
{
|
||||||
|
"type": "compute",
|
||||||
|
"name": "nova",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "private",
|
||||||
|
"url": "https://example.com/nova/private",
|
||||||
|
"region": "RegionOne"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"url": "https://example.com/nova/public",
|
||||||
|
"region": "RegionOne"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "compute",
|
||||||
|
"name": "nova2",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"url": "https://example.com/nova2/public/r1",
|
||||||
|
"region": "RegionOne"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"url": "https://example.com/nova2/public/r2",
|
||||||
|
"region": "RegionTwo"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"type": "orchestration",
|
||||||
|
"name": "heat",
|
||||||
|
"endpoints": [
|
||||||
|
{
|
||||||
|
"interface": "private",
|
||||||
|
"url": "https://example.com/heat/private",
|
||||||
|
"region": "RegionOne"
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"interface": "public",
|
||||||
|
"url": "https://example.com/heat/public",
|
||||||
|
"region": "RegionOne"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
|
||||||
|
|
||||||
class KeystoneUtilsTest(base.BaseTest):
|
class KeystoneUtilsTest(base.BaseTest):
|
||||||
def setUp(self):
|
def setUp(self):
|
||||||
@ -41,3 +92,24 @@ class KeystoneUtilsTest(base.BaseTest):
|
|||||||
expected,
|
expected,
|
||||||
keystone.format_url(url_template, self.values)
|
keystone.format_url(url_template, self.values)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def test_service_endpoints_select(self):
|
||||||
|
def find(name, typ=None, catalog=SERVICES_CATALOG):
|
||||||
|
return keystone.select_service_endpoints(name, typ, catalog)
|
||||||
|
|
||||||
|
endpoints = find('nova', 'compute')
|
||||||
|
self.assertEqual('https://example.com/nova/public', endpoints[0].url,
|
||||||
|
message='public interface must be selected')
|
||||||
|
|
||||||
|
endpoints = find('nova2')
|
||||||
|
self.assertEqual(2, len(endpoints),
|
||||||
|
message='public endpoints must be selected '
|
||||||
|
'in each region')
|
||||||
|
|
||||||
|
endpoints = find('heat')
|
||||||
|
self.assertEqual('https://example.com/heat/public', endpoints[0].url,
|
||||||
|
message='selection should work without type set')
|
||||||
|
|
||||||
|
endpoints = find('nova', None, [])
|
||||||
|
self.assertEqual([], endpoints,
|
||||||
|
message='empty catalog should be accepted')
|
||||||
|
@ -14,6 +14,7 @@
|
|||||||
# limitations under the License.
|
# limitations under the License.
|
||||||
|
|
||||||
from keystoneclient.v3 import client as ks_client
|
from keystoneclient.v3 import client as ks_client
|
||||||
|
from keystoneclient.v3.endpoints import Endpoint
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
|
|
||||||
from mistral import context
|
from mistral import context
|
||||||
@ -23,7 +24,7 @@ CONF = cfg.CONF
|
|||||||
|
|
||||||
def client():
|
def client():
|
||||||
ctx = context.ctx()
|
ctx = context.ctx()
|
||||||
auth_url = CONF.keystone_authtoken.auth_uri
|
auth_url = ctx.auth_uri
|
||||||
|
|
||||||
cl = ks_client.Client(
|
cl = ks_client.Client(
|
||||||
username=ctx.user_name,
|
username=ctx.user_name,
|
||||||
@ -62,37 +63,49 @@ def client_for_trusts(trust_id):
|
|||||||
|
|
||||||
|
|
||||||
def get_endpoint_for_project(service_name=None, service_type=None):
|
def get_endpoint_for_project(service_name=None, service_type=None):
|
||||||
admin_project_name = CONF.keystone_authtoken.admin_tenant_name
|
if service_name is None and service_type is None:
|
||||||
keystone_client = _admin_client(project_name=admin_project_name)
|
|
||||||
service_list = keystone_client.services.list()
|
|
||||||
|
|
||||||
if service_name:
|
|
||||||
service_ids = [s.id for s in service_list if s.name == service_name]
|
|
||||||
elif service_type:
|
|
||||||
service_ids = [s.id for s in service_list if s.type == service_type]
|
|
||||||
else:
|
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"Either 'service_name' or 'service_type' must be provided."
|
"Either 'service_name' or 'service_type' must be provided."
|
||||||
)
|
)
|
||||||
|
|
||||||
if not service_ids:
|
ctx = context.ctx()
|
||||||
raise Exception("Either service '%s' or service type "
|
|
||||||
"'%s' doesn't exist!" % (service_name, service_type))
|
|
||||||
|
|
||||||
endpoints = keystone_client.endpoints.list(
|
token = ctx.auth_token
|
||||||
service=service_ids[0],
|
response = client().tokens.get_token_data(token, include_catalog=True)
|
||||||
interface='public'
|
|
||||||
)
|
endpoints = select_service_endpoints(
|
||||||
|
service_name,
|
||||||
|
service_type,
|
||||||
|
response["token"]["catalog"])
|
||||||
|
|
||||||
if not endpoints:
|
if not endpoints:
|
||||||
raise Exception(
|
raise Exception(
|
||||||
"No endpoints found [service_name=%s, service_type=%s]"
|
"No endpoints found [service_name=%s, service_type=%s]"
|
||||||
% (service_name, service_type)
|
% (service_name, service_type)
|
||||||
)
|
)
|
||||||
|
else:
|
||||||
|
# TODO(rakhmerov): We may have more than one endpoint because
|
||||||
|
# TODO(rakhmerov): of regions and ideally we need a config option
|
||||||
|
# TODO(rakhmerov): for region
|
||||||
|
return endpoints[0]
|
||||||
|
|
||||||
# TODO(rakhmerov): We may have more than one endpoint because of regions
|
|
||||||
# TODO(rakhmerov): and ideally we need a config option for region
|
def select_service_endpoints(service_name, service_type, services):
|
||||||
return endpoints[0]
|
endpoints = []
|
||||||
|
|
||||||
|
for catalog in services:
|
||||||
|
|
||||||
|
if service_name and catalog["name"] != service_name:
|
||||||
|
continue
|
||||||
|
|
||||||
|
if service_type and catalog["type"] != service_type:
|
||||||
|
continue
|
||||||
|
|
||||||
|
for endpoint in catalog["endpoints"]:
|
||||||
|
if endpoint["interface"] == 'public':
|
||||||
|
endpoints.append(Endpoint(None, endpoint, loaded=True))
|
||||||
|
|
||||||
|
return endpoints
|
||||||
|
|
||||||
|
|
||||||
def get_keystone_endpoint_v2():
|
def get_keystone_endpoint_v2():
|
||||||
|
Loading…
Reference in New Issue
Block a user