Merge "Add keystone endpoint resource methods"
This commit is contained in:
commit
c4c196132b
|
@ -2840,3 +2840,115 @@ class OperatorCloud(OpenStackCloud):
|
|||
"Failed to delete service {id}".format(id=service['id']),
|
||||
exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
|
||||
def create_endpoint(self, service_name_or_id, public_url,
|
||||
internal_url=None, admin_url=None, region=None):
|
||||
"""Create a Keystone endpoint.
|
||||
|
||||
:param service_name_or_id: Service name or id for this endpoint.
|
||||
:param public_url: Endpoint public URL.
|
||||
:param internal_url: Endpoint internal URL.
|
||||
:param admin_url: Endpoint admin URL.
|
||||
:param region: Endpoint region.
|
||||
|
||||
:returns: a dict containing the endpoint description.
|
||||
|
||||
:raise OpenStackCloudException: if the service cannot be found or if
|
||||
something goes wrong during the openstack API call.
|
||||
"""
|
||||
# ToDo: support v3 api (dguerri)
|
||||
service = self.get_service(name_or_id=service_name_or_id)
|
||||
if service is None:
|
||||
raise OpenStackCloudException("service {service} not found".format(
|
||||
service=service_name_or_id))
|
||||
try:
|
||||
endpoint = self.manager.submitTask(_tasks.EndpointCreate(
|
||||
service_id=service['id'],
|
||||
region=region,
|
||||
publicurl=public_url,
|
||||
internalurl=internal_url,
|
||||
adminurl=admin_url
|
||||
))
|
||||
except Exception as e:
|
||||
self.log.debug(
|
||||
"Failed to create endpoint for service {service}".format(
|
||||
service=service['name'], exc_info=True))
|
||||
raise OpenStackCloudException(str(e))
|
||||
return meta.obj_to_dict(endpoint)
|
||||
|
||||
def list_endpoints(self):
|
||||
"""List Keystone endpoints.
|
||||
|
||||
:returns: a list of dict containing the endpoint description.
|
||||
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
# ToDo: support v3 api (dguerri)
|
||||
try:
|
||||
endpoints = self.manager.submitTask(_tasks.EndpointList())
|
||||
except Exception as e:
|
||||
self.log.debug("Failed to list endpoints", exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
return meta.obj_list_to_dict(endpoints)
|
||||
|
||||
def search_endpoints(self, id=None, filters=None):
|
||||
"""List Keystone endpoints.
|
||||
|
||||
:param id: endpoint id.
|
||||
:param filters: a dict containing additional filters to use. e.g.
|
||||
{'region': 'region-a.geo-1'}
|
||||
|
||||
:returns: a list of dict containing the endpoint description. Each dict
|
||||
contains the following attributes::
|
||||
- id: <endpoint id>
|
||||
- region: <endpoint region>
|
||||
- public_url: <endpoint public url>
|
||||
- internal_url: <endpoint internal url> (optional)
|
||||
- admin_url: <endpoint admin url> (optional)
|
||||
|
||||
:raises: ``OpenStackCloudException``: if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
endpoints = self.list_endpoints()
|
||||
return _utils._filter_list(endpoints, id, filters)
|
||||
|
||||
def get_endpoint(self, id, filters=None):
|
||||
"""Get exactly one Keystone endpoint.
|
||||
|
||||
:param id: endpoint id.
|
||||
:param filters: a dict containing additional filters to use. e.g.
|
||||
{'region': 'region-a.geo-1'}
|
||||
|
||||
:returns: a dict containing the endpoint description. i.e. a dict
|
||||
containing the following attributes::
|
||||
- id: <endpoint id>
|
||||
- region: <endpoint region>
|
||||
- public_url: <endpoint public url>
|
||||
- internal_url: <endpoint internal url> (optional)
|
||||
- admin_url: <endpoint admin url> (optional)
|
||||
"""
|
||||
return _utils._get_entity(self.search_endpoints, id, filters)
|
||||
|
||||
def delete_endpoint(self, id):
|
||||
"""Delete a Keystone endpoint.
|
||||
|
||||
:param id: Id of the endpoint to delete.
|
||||
|
||||
:returns: None
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API call.
|
||||
"""
|
||||
# ToDo: support v3 api (dguerri)
|
||||
endpoint = self.get_endpoint(id=id)
|
||||
if endpoint is None:
|
||||
return
|
||||
|
||||
try:
|
||||
self.manager.submitTask(_tasks.EndpointDelete(id=id))
|
||||
except Exception as e:
|
||||
self.log.debug(
|
||||
"Failed to delete endpoint {id}".format(id=id),
|
||||
exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
|
|
|
@ -341,3 +341,18 @@ class ServiceList(task_manager.Task):
|
|||
class ServiceDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.services.delete(**self.args)
|
||||
|
||||
|
||||
class EndpointCreate(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.endpoints.create(**self.args)
|
||||
|
||||
|
||||
class EndpointList(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.endpoints.list()
|
||||
|
||||
|
||||
class EndpointDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.endpoints.delete(**self.args)
|
||||
|
|
|
@ -20,6 +20,17 @@ Fakes used for testing
|
|||
"""
|
||||
|
||||
|
||||
class FakeEndpoint(object):
|
||||
def __init__(self, id, service_id, region, publicurl, internalurl=None,
|
||||
adminurl=None):
|
||||
self.id = id
|
||||
self.service_id = service_id
|
||||
self.region = region
|
||||
self.publicurl = publicurl
|
||||
self.internalurl = internalurl
|
||||
self.adminurl = adminurl
|
||||
|
||||
|
||||
class FakeFlavor(object):
|
||||
def __init__(self, id, name):
|
||||
self.id = id
|
||||
|
|
|
@ -0,0 +1,152 @@
|
|||
# Copyright (c) 2015 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.
|
||||
|
||||
"""
|
||||
test_endpoint
|
||||
----------------------------------
|
||||
|
||||
Functional tests for `shade` endpoint resource.
|
||||
"""
|
||||
|
||||
import string
|
||||
import random
|
||||
|
||||
from shade import operator_cloud
|
||||
from shade.exc import OpenStackCloudException
|
||||
from shade.tests import base
|
||||
|
||||
|
||||
class TestEndpoints(base.TestCase):
|
||||
|
||||
endpoint_attributes = ['id', 'region', 'publicurl', 'internalurl',
|
||||
'service_id', 'adminurl']
|
||||
|
||||
def setUp(self):
|
||||
super(TestEndpoints, self).setUp()
|
||||
# Shell should have OS-* envvars from openrc, typically loaded by job
|
||||
self.operator_cloud = operator_cloud()
|
||||
|
||||
# Generate a random name for services and regions in this test
|
||||
self.new_item_name = 'test_' + ''.join(
|
||||
random.choice(string.ascii_lowercase) for _ in range(5))
|
||||
|
||||
self.addCleanup(self._cleanup_services)
|
||||
self.addCleanup(self._cleanup_endpoints)
|
||||
|
||||
def _cleanup_endpoints(self):
|
||||
exception_list = list()
|
||||
for e in self.operator_cloud.list_endpoints():
|
||||
if e.get('region') is not None and \
|
||||
e['region'].startswith(self.new_item_name):
|
||||
try:
|
||||
self.operator_cloud.delete_endpoint(id=e['id'])
|
||||
except Exception as e:
|
||||
# We were unable to delete a service, let's try with next
|
||||
exception_list.append(e)
|
||||
continue
|
||||
if exception_list:
|
||||
# Raise an error: we must make users aware that something went
|
||||
# wrong
|
||||
raise OpenStackCloudException('\n'.join(exception_list))
|
||||
|
||||
def _cleanup_services(self):
|
||||
exception_list = list()
|
||||
for s in self.operator_cloud.list_services():
|
||||
if s['name'].startswith(self.new_item_name):
|
||||
try:
|
||||
self.operator_cloud.delete_service(name_or_id=s['id'])
|
||||
except Exception as e:
|
||||
# We were unable to delete a service, let's try with next
|
||||
exception_list.append(e)
|
||||
continue
|
||||
if exception_list:
|
||||
# Raise an error: we must make users aware that something went
|
||||
# wrong
|
||||
raise OpenStackCloudException('\n'.join(exception_list))
|
||||
|
||||
def test_create_endpoint(self):
|
||||
service_name = self.new_item_name + '_create'
|
||||
|
||||
service = self.operator_cloud.create_service(
|
||||
name=service_name, service_type='test_type',
|
||||
description='this is a test description')
|
||||
|
||||
endpoint = self.operator_cloud.create_endpoint(
|
||||
service_name_or_id=service['id'],
|
||||
public_url='http://public.test/',
|
||||
internal_url='http://internal.test/',
|
||||
admin_url='http://admin.url/',
|
||||
region=service_name)
|
||||
|
||||
self.assertIsNotNone(endpoint.get('id'))
|
||||
|
||||
# Test None parameters
|
||||
endpoint = self.operator_cloud.create_endpoint(
|
||||
service_name_or_id=service['id'],
|
||||
public_url='http://public.test/',
|
||||
region=service_name)
|
||||
|
||||
self.assertIsNotNone(endpoint.get('id'))
|
||||
|
||||
def test_list_endpoints(self):
|
||||
service_name = self.new_item_name + '_list'
|
||||
|
||||
service = self.operator_cloud.create_service(
|
||||
name=service_name, service_type='test_type',
|
||||
description='this is a test description')
|
||||
|
||||
endpoint = self.operator_cloud.create_endpoint(
|
||||
service_name_or_id=service['id'],
|
||||
public_url='http://public.test/',
|
||||
internal_url='http://internal.test/',
|
||||
region=service_name)
|
||||
|
||||
observed_endpoints = self.operator_cloud.list_endpoints()
|
||||
found = False
|
||||
for e in observed_endpoints:
|
||||
# Test all attributes are returned
|
||||
if e['id'] == endpoint['id']:
|
||||
found = True
|
||||
self.assertEqual(service['id'], e['service_id'])
|
||||
self.assertEqual('http://public.test/', e['publicurl'])
|
||||
self.assertEqual('http://internal.test/', e['internalurl'])
|
||||
self.assertEqual(service_name, e['region'])
|
||||
|
||||
self.assertTrue(found, msg='new endpoint not found in endpoints list!')
|
||||
|
||||
def test_delete_endpoint(self):
|
||||
service_name = self.new_item_name + '_delete'
|
||||
|
||||
service = self.operator_cloud.create_service(
|
||||
name=service_name, service_type='test_type',
|
||||
description='this is a test description')
|
||||
|
||||
endpoint = self.operator_cloud.create_endpoint(
|
||||
service_name_or_id=service['id'],
|
||||
public_url='http://public.test/',
|
||||
internal_url='http://internal.test/',
|
||||
region=service_name)
|
||||
|
||||
self.operator_cloud.delete_endpoint(endpoint['id'])
|
||||
|
||||
observed_endpoints = self.operator_cloud.list_endpoints()
|
||||
found = False
|
||||
for e in observed_endpoints:
|
||||
if e['id'] == endpoint['id']:
|
||||
found = True
|
||||
break
|
||||
self.failUnlessEqual(
|
||||
False, found, message='new endpoint was not deleted!')
|
|
@ -0,0 +1,132 @@
|
|||
# Copyright (c) 2015 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.
|
||||
|
||||
"""
|
||||
test_cloud_endpoints
|
||||
----------------------------------
|
||||
|
||||
Tests Keystone endpoints commands.
|
||||
"""
|
||||
|
||||
from mock import patch
|
||||
from shade import OperatorCloud
|
||||
from shade.tests.fakes import FakeEndpoint
|
||||
from shade.tests.unit import base
|
||||
|
||||
|
||||
# ToDo: support v3 api (dguerri)
|
||||
class TestCloudEndpoints(base.TestCase):
|
||||
mock_endpoints = [
|
||||
{'id': 'id1', 'service_id': 'sid1', 'region': 'region1',
|
||||
'publicurl': 'purl1', 'internalurl': None, 'adminurl': None},
|
||||
{'id': 'id2', 'service_id': 'sid2', 'region': 'region1',
|
||||
'publicurl': 'purl2', 'internalurl': None, 'adminurl': None},
|
||||
{'id': 'id3', 'service_id': 'sid3', 'region': 'region2',
|
||||
'publicurl': 'purl3', 'internalurl': 'iurl3', 'adminurl': 'aurl3'}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(TestCloudEndpoints, self).setUp()
|
||||
self.client = OperatorCloud("op_cloud", {})
|
||||
self.mock_ks_endpoints = \
|
||||
[FakeEndpoint(**kwa) for kwa in self.mock_endpoints]
|
||||
|
||||
@patch.object(OperatorCloud, 'list_services')
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_create_endpoint(self, mock_keystone_client, mock_list_services):
|
||||
mock_list_services.return_value = [
|
||||
{
|
||||
'id': 'service_id1',
|
||||
'name': 'service1',
|
||||
'type': 'type1',
|
||||
'description': 'desc1'
|
||||
}
|
||||
]
|
||||
mock_keystone_client.endpoints.create.return_value = \
|
||||
self.mock_ks_endpoints[0]
|
||||
|
||||
endpoint = self.client.create_endpoint(
|
||||
service_name_or_id='service1',
|
||||
region='mock_region',
|
||||
public_url='mock_public_url',
|
||||
internal_url='mock_internal_url',
|
||||
admin_url='mock_admin_url'
|
||||
)
|
||||
|
||||
mock_keystone_client.endpoints.create.assert_called_with(
|
||||
service_id='service_id1',
|
||||
region='mock_region',
|
||||
publicurl='mock_public_url',
|
||||
internalurl='mock_internal_url',
|
||||
adminurl='mock_admin_url'
|
||||
)
|
||||
|
||||
# test keys and values are correct
|
||||
for k, v in self.mock_endpoints[0].items():
|
||||
self.assertEquals(v, endpoint.get(k))
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_list_endpoints(self, mock_keystone_client):
|
||||
mock_keystone_client.endpoints.list.return_value = \
|
||||
self.mock_ks_endpoints
|
||||
|
||||
endpoints = self.client.list_endpoints()
|
||||
mock_keystone_client.endpoints.list.assert_called_with()
|
||||
|
||||
# test we are getting exactly len(self.mock_endpoints) elements
|
||||
self.assertEqual(len(self.mock_endpoints), len(endpoints))
|
||||
|
||||
# test keys and values are correct
|
||||
for mock_endpoint in self.mock_endpoints:
|
||||
found = False
|
||||
for e in endpoints:
|
||||
if e['id'] == mock_endpoint['id']:
|
||||
found = True
|
||||
for k, v in mock_endpoint.items():
|
||||
self.assertEquals(v, e.get(k))
|
||||
break
|
||||
self.assertTrue(
|
||||
found, msg="endpoint {id} not found!".format(
|
||||
id=mock_endpoint['id']))
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_search_endpoints(self, mock_keystone_client):
|
||||
mock_keystone_client.endpoints.list.return_value = \
|
||||
self.mock_ks_endpoints
|
||||
|
||||
# Search by id
|
||||
endpoints = self.client.search_endpoints(id='id3')
|
||||
# # test we are getting exactly 1 element
|
||||
self.assertEqual(1, len(endpoints))
|
||||
for k, v in self.mock_endpoints[2].items():
|
||||
self.assertEquals(v, endpoints[0].get(k))
|
||||
|
||||
# Not found
|
||||
endpoints = self.client.search_endpoints(id='blah!')
|
||||
self.assertEqual(0, len(endpoints))
|
||||
|
||||
# Multiple matches
|
||||
endpoints = self.client.search_endpoints(
|
||||
filters={'region': 'region1'})
|
||||
# # test we are getting exactly 2 elements
|
||||
self.assertEqual(2, len(endpoints))
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_delete_endpoint(self, mock_keystone_client):
|
||||
mock_keystone_client.endpoints.list.return_value = \
|
||||
self.mock_ks_endpoints
|
||||
|
||||
# Delete by id
|
||||
self.client.delete_endpoint(id='id2')
|
||||
mock_keystone_client.endpoints.delete.assert_called_with(id='id2')
|
Loading…
Reference in New Issue