Add Keystone service resource methods
This change adds keystone service resource methods to OperatorClouds. Only Keystone v2 API is currently supported Change-Id: I5d0b44664a6839502d86fed8d68717b086c34a81
This commit is contained in:
parent
3fef40f07e
commit
da6929777d
@ -2724,3 +2724,103 @@ class OperatorCloud(OpenStackCloud):
|
||||
self.log.debug(
|
||||
"Failed to delete instance_info", exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
|
||||
def create_service(self, name, service_type, description=None):
|
||||
"""Create a service.
|
||||
|
||||
:param name: Service name.
|
||||
:param service_type: Service type.
|
||||
:param description: Service description (optional).
|
||||
|
||||
:returns: a dict containing the services description, i.e. the
|
||||
following attributes::
|
||||
- id: <service id>
|
||||
- name: <service name>
|
||||
- service_type: <service type>
|
||||
- description: <service description>
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during the
|
||||
openstack API call.
|
||||
|
||||
"""
|
||||
try:
|
||||
service = self.manager.submitTask(_tasks.ServiceCreate(
|
||||
name=name, service_type=service_type,
|
||||
description=description))
|
||||
except Exception as e:
|
||||
self.log.debug(
|
||||
"Failed to create service {name}".format(name=name),
|
||||
exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
return meta.obj_to_dict(service)
|
||||
|
||||
def list_services(self):
|
||||
"""List all Keystone services.
|
||||
|
||||
:returns: a list of dict containing the services description.
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during the
|
||||
openstack API call.
|
||||
"""
|
||||
try:
|
||||
services = self.manager.submitTask(_tasks.ServiceList())
|
||||
except Exception as e:
|
||||
self.log.debug("Failed to list services", exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
return meta.obj_list_to_dict(services)
|
||||
|
||||
def search_services(self, name_or_id=None, filters=None):
|
||||
"""Search Keystone services.
|
||||
|
||||
:param name_or_id: Name or id of the desired service.
|
||||
:param filters: a dict containing additional filters to use. e.g.
|
||||
{'service_type': 'network'}.
|
||||
|
||||
:returns: a list of dict containing the services description.
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during the
|
||||
openstack API call.
|
||||
"""
|
||||
services = self.list_services()
|
||||
return _utils._filter_list(services, name_or_id, filters)
|
||||
|
||||
def get_service(self, name_or_id, filters=None):
|
||||
"""Get exactly one Keystone service.
|
||||
|
||||
:param name_or_id: Name or id of the desired service.
|
||||
:param filters: a dict containing additional filters to use. e.g.
|
||||
{'service_type': 'network'}
|
||||
|
||||
:returns: a dict containing the services description, i.e. the
|
||||
following attributes::
|
||||
- id: <service id>
|
||||
- name: <service name>
|
||||
- service_type: <service type>
|
||||
- description: <service description>
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during the
|
||||
openstack API call or if multiple matches are found.
|
||||
"""
|
||||
return _utils._get_entity(self.search_services, name_or_id, filters)
|
||||
|
||||
def delete_service(self, name_or_id):
|
||||
"""Delete a Keystone service.
|
||||
|
||||
:param name_or_id: Service name or id.
|
||||
|
||||
:returns: None
|
||||
|
||||
:raises: ``OpenStackCloudException`` if something goes wrong during
|
||||
the openstack API call
|
||||
"""
|
||||
service = self.get_service(name_or_id=name_or_id)
|
||||
if service is None:
|
||||
return
|
||||
|
||||
try:
|
||||
self.manager.submitTask(_tasks.ServiceDelete(id=service['id']))
|
||||
except Exception as e:
|
||||
self.log.debug(
|
||||
"Failed to delete service {id}".format(id=service['id']),
|
||||
exc_info=True)
|
||||
raise OpenStackCloudException(str(e))
|
||||
|
@ -316,3 +316,18 @@ class MachineSetPower(task_manager.Task):
|
||||
class MachineSetProvision(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.ironic_client.node.set_provision_state(**self.args)
|
||||
|
||||
|
||||
class ServiceCreate(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.services.create(**self.args)
|
||||
|
||||
|
||||
class ServiceList(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.services.list()
|
||||
|
||||
|
||||
class ServiceDelete(task_manager.Task):
|
||||
def main(self, client):
|
||||
return client.keystone_client.services.delete(**self.args)
|
||||
|
@ -51,6 +51,14 @@ class FakeServer(object):
|
||||
self.status = status
|
||||
|
||||
|
||||
class FakeService(object):
|
||||
def __init__(self, id, name, type, description=''):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.type = type
|
||||
self.description = description
|
||||
|
||||
|
||||
class FakeUser(object):
|
||||
def __init__(self, id, email, name):
|
||||
self.id = id
|
||||
|
108
shade/tests/functional/test_services.py
Normal file
108
shade/tests/functional/test_services.py
Normal file
@ -0,0 +1,108 @@
|
||||
# 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_services
|
||||
----------------------------------
|
||||
|
||||
Functional tests for `shade` service resource.
|
||||
"""
|
||||
|
||||
import string
|
||||
import random
|
||||
|
||||
from shade import operator_cloud
|
||||
from shade.exc import OpenStackCloudException
|
||||
from shade.tests import base
|
||||
|
||||
|
||||
class TestServices(base.TestCase):
|
||||
|
||||
service_attributes = ['id', 'name', 'type', 'description']
|
||||
|
||||
def setUp(self):
|
||||
super(TestServices, self).setUp()
|
||||
# Shell should have OS-* envvars from openrc, typically loaded by job
|
||||
self.operator_cloud = operator_cloud()
|
||||
|
||||
# Generate a random name for services in this test
|
||||
self.new_service_name = 'test_' + ''.join(
|
||||
random.choice(string.ascii_lowercase) for _ in range(5))
|
||||
|
||||
self.addCleanup(self._cleanup_services)
|
||||
|
||||
def _cleanup_services(self):
|
||||
exception_list = list()
|
||||
for s in self.operator_cloud.list_services():
|
||||
if s['name'].startswith(self.new_service_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_service(self):
|
||||
service = self.operator_cloud.create_service(
|
||||
name=self.new_service_name + '_create', service_type='test_type',
|
||||
description='this is a test description')
|
||||
self.assertIsNotNone(service.get('id'))
|
||||
|
||||
def test_list_services(self):
|
||||
service = self.operator_cloud.create_service(
|
||||
name=self.new_service_name + '_list', service_type='test_type')
|
||||
observed_services = self.operator_cloud.list_services()
|
||||
self.assertIsInstance(observed_services, list)
|
||||
found = False
|
||||
for s in observed_services:
|
||||
# Test all attributes are returned
|
||||
if s['id'] == service['id']:
|
||||
self.assertEqual(self.new_service_name + '_list',
|
||||
s.get('name'))
|
||||
self.assertEqual('test_type', s.get('type'))
|
||||
found = True
|
||||
self.assertTrue(found, msg='new service not found in service list!')
|
||||
|
||||
def test_delete_service_by_name(self):
|
||||
# Test delete by name
|
||||
service = self.operator_cloud.create_service(
|
||||
name=self.new_service_name + '_delete_by_name',
|
||||
service_type='test_type')
|
||||
self.operator_cloud.delete_service(name_or_id=service['name'])
|
||||
observed_services = self.operator_cloud.list_services()
|
||||
found = False
|
||||
for s in observed_services:
|
||||
if s['id'] == service['id']:
|
||||
found = True
|
||||
break
|
||||
self.failUnlessEqual(False, found, message='service was not deleted!')
|
||||
|
||||
def test_delete_service_by_id(self):
|
||||
# Test delete by id
|
||||
service = self.operator_cloud.create_service(
|
||||
name=self.new_service_name + '_delete_by_id',
|
||||
service_type='test_type')
|
||||
self.operator_cloud.delete_service(name_or_id=service['id'])
|
||||
observed_services = self.operator_cloud.list_services()
|
||||
found = False
|
||||
for s in observed_services:
|
||||
if s['id'] == service['id']:
|
||||
found = True
|
||||
self.failUnlessEqual(False, found, message='service was not deleted!')
|
132
shade/tests/unit/test_services.py
Normal file
132
shade/tests/unit/test_services.py
Normal file
@ -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_services
|
||||
----------------------------------
|
||||
|
||||
Tests Keystone services commands.
|
||||
"""
|
||||
|
||||
from mock import patch
|
||||
from shade import OpenStackCloudException
|
||||
from shade import OperatorCloud
|
||||
from shade.tests.fakes import FakeService
|
||||
from shade.tests.unit import base
|
||||
|
||||
|
||||
class CloudServices(base.TestCase):
|
||||
mock_services = [
|
||||
{'id': 'id1', 'name': 'service1', 'type': 'type1',
|
||||
'description': 'desc1'},
|
||||
{'id': 'id2', 'name': 'service2', 'type': 'type2',
|
||||
'description': 'desc2'},
|
||||
{'id': 'id3', 'name': 'service3', 'type': 'type2',
|
||||
'description': 'desc3'},
|
||||
{'id': 'id4', 'name': 'service4', 'type': 'type3',
|
||||
'description': 'desc4'}
|
||||
]
|
||||
|
||||
def setUp(self):
|
||||
super(CloudServices, self).setUp()
|
||||
self.client = OperatorCloud("op_cloud", {})
|
||||
self.mock_ks_services = [FakeService(**kwa) for kwa in
|
||||
self.mock_services]
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_create_service(self, mock_keystone_client):
|
||||
kwargs = {
|
||||
'name': 'a service',
|
||||
'service_type': 'network',
|
||||
'description': 'This is a test service'
|
||||
}
|
||||
|
||||
self.client.create_service(**kwargs)
|
||||
mock_keystone_client.services.create.assert_called_with(**kwargs)
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_list_services(self, mock_keystone_client):
|
||||
mock_keystone_client.services.list.return_value = \
|
||||
self.mock_ks_services
|
||||
|
||||
services = self.client.list_services()
|
||||
mock_keystone_client.services.list.assert_called_with()
|
||||
|
||||
self.assertItemsEqual(self.mock_services, services)
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_get_service(self, mock_keystone_client):
|
||||
mock_keystone_client.services.list.return_value = \
|
||||
self.mock_ks_services
|
||||
|
||||
# Search by id
|
||||
service = self.client.get_service(name_or_id='id4')
|
||||
# test we are getting exactly 1 element
|
||||
self.assertEqual(service, self.mock_services[3])
|
||||
|
||||
# Search by name
|
||||
service = self.client.get_service(name_or_id='service2')
|
||||
# test we are getting exactly 1 element
|
||||
self.assertEqual(service, self.mock_services[1])
|
||||
|
||||
# Not found
|
||||
service = self.client.get_service(name_or_id='blah!')
|
||||
self.assertIs(None, service)
|
||||
|
||||
# Multiple matches
|
||||
# test we are getting an Exception
|
||||
self.assertRaises(OpenStackCloudException, self.client.get_service,
|
||||
name_or_id=None, filters={'type': 'type2'})
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_search_services(self, mock_keystone_client):
|
||||
mock_keystone_client.services.list.return_value = \
|
||||
self.mock_ks_services
|
||||
|
||||
# Search by id
|
||||
services = self.client.search_services(name_or_id='id4')
|
||||
# test we are getting exactly 1 element
|
||||
self.assertEqual(1, len(services))
|
||||
self.assertEqual(services, [self.mock_services[3]])
|
||||
|
||||
# Search by name
|
||||
services = self.client.search_services(name_or_id='service2')
|
||||
# test we are getting exactly 1 element
|
||||
self.assertEqual(1, len(services))
|
||||
self.assertEqual(services, [self.mock_services[1]])
|
||||
|
||||
# Not found
|
||||
services = self.client.search_services(name_or_id='blah!')
|
||||
self.assertEqual(0, len(services))
|
||||
|
||||
# Multiple matches
|
||||
services = self.client.search_services(
|
||||
filters={'type': 'type2'})
|
||||
# test we are getting exactly 2 elements
|
||||
self.assertEqual(2, len(services))
|
||||
self.assertEqual(services, [self.mock_services[1],
|
||||
self.mock_services[2]])
|
||||
|
||||
@patch.object(OperatorCloud, 'keystone_client')
|
||||
def test_delete_service(self, mock_keystone_client):
|
||||
mock_keystone_client.services.list.return_value = \
|
||||
self.mock_ks_services
|
||||
|
||||
# Delete by name
|
||||
self.client.delete_service(name_or_id='service3')
|
||||
mock_keystone_client.services.delete.assert_called_with(id='id3')
|
||||
|
||||
# Delete by id
|
||||
self.client.delete_service('id1')
|
||||
mock_keystone_client.services.delete.assert_called_with(id='id1')
|
Loading…
Reference in New Issue
Block a user