tricircle/tricircle/tests/unit/common/test_client.py

373 lines
14 KiB
Python

# Copyright 2015 Huawei Technologies Co., Ltd.
# All Rights Reserved
#
# 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.
import unittest
import uuid
import mock
from mock import patch
from oslo_config import cfg
import keystoneclient.v3.client as k_client
from tricircle.common import client
from tricircle.common import context
from tricircle.common import exceptions
from tricircle.common import resource_handle
from tricircle.db import api
from tricircle.db import core
FAKE_AZ = 'fake_az'
FAKE_RESOURCE = 'fake_res'
FAKE_SITE_ID = 'fake_pod_id'
FAKE_SITE_NAME = 'fake_region_name'
FAKE_SERVICE_ID = 'fake_service_id'
FAKE_SERVICE_NAME = 'fake_service_name'
FAKE_TYPE = 'fake_type'
FAKE_URL = 'http://127.0.0.1:12345'
FAKE_URL_INVALID = 'http://127.0.0.1:23456'
FAKE_RESOURCES = [{'name': 'res1'}, {'name': 'res2'}]
class _List(object):
def __init__(self, eles):
self.eles = eles
def list(self):
return self.eles
class _Dict(object):
def __init__(self, ele):
self.ele = ele
def to_dict(self):
return self.ele
class FakeKeystoneClient(object):
def __init__(self, **kwargs):
_services = kwargs['services']
_endpoints = kwargs['endpoints']
self.services = _List([_Dict(_service) for _service in _services])
self.endpoints = _List([_Dict(_endpoint) for _endpoint in _endpoints])
class FakeException(Exception):
pass
class FakeClient(object):
def __init__(self, url):
self.endpoint = url
def list_fake_res(self, search_opts):
# make sure endpoint is correctly set
if self.endpoint != FAKE_URL:
raise FakeException()
if not search_opts:
return [res for res in FAKE_RESOURCES]
else:
return [res for res in FAKE_RESOURCES if (
res['name'] == search_opts['name'])]
def create_fake_res(self, name):
if self.endpoint != FAKE_URL:
raise FakeException()
FAKE_RESOURCES.append({'name': name})
return {'name': name}
def delete_fake_res(self, name):
if self.endpoint != FAKE_URL:
raise FakeException()
try:
FAKE_RESOURCES.remove({'name': name})
except ValueError:
pass
def action_fake_res(self, name, rename):
if self.endpoint != FAKE_URL:
raise FakeException()
for res in FAKE_RESOURCES:
if res['name'] == name:
res['name'] = rename
break
class FakeResHandle(resource_handle.ResourceHandle):
def _get_client(self, cxt):
return FakeClient(self.endpoint_url)
def handle_list(self, cxt, resource, filters):
try:
cli = self._get_client(cxt)
return cli.list_fake_res(
resource_handle._transform_filters(filters))
except FakeException:
raise exceptions.EndpointNotAvailable(FAKE_TYPE, cli.endpoint)
def handle_create(self, cxt, resource, name):
try:
cli = self._get_client(cxt)
return cli.create_fake_res(name)
except FakeException:
raise exceptions.EndpointNotAvailable(FAKE_TYPE, cli.endpoint)
def handle_delete(self, cxt, resource, name):
try:
cli = self._get_client(cxt)
cli.delete_fake_res(name)
except FakeException:
raise exceptions.EndpointNotAvailable(FAKE_TYPE, cli.endpoint)
def handle_action(self, cxt, resource, action, name, rename):
try:
cli = self._get_client(cxt)
cli.action_fake_res(name, rename)
except FakeException:
raise exceptions.EndpointNotAvailable(FAKE_TYPE, cli.endpoint)
class ClientTest(unittest.TestCase):
def setUp(self):
core.initialize()
core.ModelBase.metadata.create_all(core.get_engine())
# enforce foreign key constraint for sqlite
core.get_engine().execute('pragma foreign_keys=on')
self.context = context.Context()
pod_dict = {
'pod_id': FAKE_SITE_ID,
'region_name': FAKE_SITE_NAME,
'az_name': FAKE_AZ
}
config_dict = {
'service_id': FAKE_SERVICE_ID,
'pod_id': FAKE_SITE_ID,
'service_type': FAKE_TYPE,
'service_url': FAKE_URL
}
api.create_pod(self.context, pod_dict)
api.create_cached_endpoints(self.context, config_dict)
global FAKE_RESOURCES
FAKE_RESOURCES = [{'name': 'res1'}, {'name': 'res2'}]
cfg.CONF.set_override(name='top_region_name', override=FAKE_SITE_NAME,
group='client')
self.client = client.Client()
self.client.resource_service_map[FAKE_RESOURCE] = FAKE_TYPE
self.client.operation_resources_map['list'].add(FAKE_RESOURCE)
self.client.operation_resources_map['create'].add(FAKE_RESOURCE)
self.client.operation_resources_map['delete'].add(FAKE_RESOURCE)
self.client.operation_resources_map['action'].add(FAKE_RESOURCE)
self.client.service_handle_map[FAKE_TYPE] = FakeResHandle(None)
def test_list(self):
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [])
self.assertEqual(resources, [{'name': 'res1'}, {'name': 'res2'}])
def test_list_with_filters(self):
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [{'key': 'name',
'comparator': 'eq',
'value': 'res2'}])
self.assertEqual(resources, [{'name': 'res2'}])
def test_create(self):
resource = self.client.create_resources(FAKE_RESOURCE, self.context,
'res3')
self.assertEqual(resource, {'name': 'res3'})
resources = self.client.list_resources(FAKE_RESOURCE, self.context)
self.assertEqual(resources, [{'name': 'res1'}, {'name': 'res2'},
{'name': 'res3'}])
def test_delete(self):
self.client.delete_resources(FAKE_RESOURCE, self.context, 'res1')
resources = self.client.list_resources(FAKE_RESOURCE, self.context)
self.assertEqual(resources, [{'name': 'res2'}])
def test_action(self):
self.client.action_resources(FAKE_RESOURCE, self.context,
'rename', 'res1', 'res3')
resources = self.client.list_resources(FAKE_RESOURCE, self.context)
self.assertEqual(resources, [{'name': 'res3'}, {'name': 'res2'}])
def test_list_create_endpoint_not_found(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=False,
group='client')
# delete the configuration so endpoint cannot be found
api.delete_cached_endpoints(self.context, FAKE_SERVICE_ID)
resources = self.client.list_resources(FAKE_RESOURCE, self.context)
# list_resources returns [] by default
self.assertEqual(resources, [])
resource = self.client.create_resources(FAKE_RESOURCE, self.context,
'res3')
# create_resources returns None by default
self.assertIsNone(resource)
def test_resource_not_supported(self):
# no such resource
self.assertRaises(exceptions.ResourceNotSupported,
self.client.list_resources,
'no_such_resource', self.context, [])
# remove "create" entry for FAKE_RESOURCE
self.client.operation_resources_map['create'].remove(FAKE_RESOURCE)
# operation not supported
self.assertRaises(exceptions.ResourceNotSupported,
self.client.create_resources,
FAKE_RESOURCE, self.context, [])
def test_list_endpoint_not_found(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=False,
group='client')
# delete the configuration so endpoint cannot be found
api.delete_cached_endpoints(self.context, FAKE_SERVICE_ID)
# list returns empty list when endpoint not found
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [])
self.assertEqual(resources, [])
def test_list_endpoint_not_found_retry(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=True,
group='client')
# delete the configuration so endpoint cannot be found
api.delete_cached_endpoints(self.context, FAKE_SERVICE_ID)
self.client._get_admin_token = mock.Mock()
self.client._get_admin_project_id = mock.Mock()
self.client._get_endpoint_from_keystone = mock.Mock()
self.client._get_endpoint_from_keystone.return_value = {}
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [])
# retry but endpoint still not found
self.assertEqual(resources, [])
self.client._get_endpoint_from_keystone.return_value = {
FAKE_SITE_NAME: {FAKE_TYPE: FAKE_URL}
}
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [])
self.assertEqual(resources, [{'name': 'res1'}, {'name': 'res2'}])
def test_list_endpoint_not_valid(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=False,
group='client')
update_dict = {'service_url': FAKE_URL_INVALID}
# update url to an invalid one
api.update_cached_endpoints(self.context,
FAKE_SERVICE_ID,
update_dict)
# auto refresh set to False, directly raise exception
self.assertRaises(exceptions.EndpointNotAvailable,
self.client.list_resources,
FAKE_RESOURCE, self.context, [])
def test_list_endpoint_not_valid_retry(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=True,
group='client')
update_dict = {'service_url': FAKE_URL_INVALID}
# update url to an invalid one
api.update_cached_endpoints(self.context,
FAKE_SERVICE_ID,
update_dict)
self.client._get_admin_token = mock.Mock()
self.client._get_admin_project_id = mock.Mock()
self.client._get_endpoint_from_keystone = mock.Mock()
self.client._get_endpoint_from_keystone.return_value = {}
# retry but still endpoint not updated
self.assertRaises(exceptions.EndpointNotAvailable,
self.client.list_resources,
FAKE_RESOURCE, self.context, [])
self.client._get_endpoint_from_keystone.return_value = {
FAKE_SITE_NAME: {FAKE_TYPE: FAKE_URL}
}
resources = self.client.list_resources(
FAKE_RESOURCE, self.context, [])
self.assertEqual(resources, [{'name': 'res1'}, {'name': 'res2'}])
@patch.object(k_client, 'Client')
def test_get_endpoint_from_keystone(self, mock_client):
services = [{'id': FAKE_SERVICE_ID,
'name': FAKE_SERVICE_NAME},
{'id': 'another_fake_service_id',
'name': 'another_fake_service_name'}]
endpoints = [{'interface': 'public',
'region': FAKE_SITE_NAME,
'service_id': FAKE_SERVICE_ID,
'url': FAKE_URL},
{'interface': 'admin',
'region': FAKE_SITE_NAME,
'service_id': FAKE_SERVICE_ID,
'url': FAKE_URL_INVALID}]
mock_client.return_value = FakeKeystoneClient(services=services,
endpoints=endpoints)
endpoint_map = self.client._get_endpoint_from_keystone(self.context)
# only public endpoint is saved
self.assertEqual(endpoint_map,
{FAKE_SITE_NAME: {FAKE_SERVICE_NAME: FAKE_URL}})
@patch.object(uuid, 'uuid4')
@patch.object(api, 'create_cached_endpoints')
@patch.object(api, 'update_cached_endpoints')
def test_update_endpoint_from_keystone(self, update_mock, create_mock,
uuid_mock):
self.client._get_admin_token = mock.Mock()
self.client._get_endpoint_from_keystone = mock.Mock()
self.client._get_endpoint_from_keystone.return_value = {
FAKE_SITE_NAME: {FAKE_TYPE: FAKE_URL,
'another_fake_type': 'http://127.0.0.1:34567'},
'not_registered_pod': {FAKE_TYPE: FAKE_URL}
}
uuid_mock.return_value = 'another_fake_service_id'
self.client.update_endpoint_from_keystone(self.context)
update_dict = {'service_url': FAKE_URL}
create_dict = {'service_id': 'another_fake_service_id',
'pod_id': FAKE_SITE_ID,
'service_type': 'another_fake_type',
'service_url': 'http://127.0.0.1:34567'}
# not registered pod is skipped
update_mock.assert_called_once_with(
self.context, FAKE_SERVICE_ID, update_dict)
create_mock.assert_called_once_with(self.context, create_dict)
def test_get_endpoint(self):
cfg.CONF.set_override(name='auto_refresh_endpoint', override=False,
group='client')
url = self.client.get_endpoint(self.context, FAKE_SITE_ID, FAKE_TYPE)
self.assertEqual(url, FAKE_URL)
@patch.object(FakeClient, 'list_fake_res')
def test_resource_handle_endpoint_unavailable(self, mock_list):
handle = FakeResHandle(None)
handle.endpoint_url = FAKE_URL
mock_list.side_effect = FakeException
self.assertRaises(exceptions.EndpointNotAvailable,
handle.handle_list, self.context, FAKE_RESOURCE, [])
# endpoint_url will not be set to None
self.assertIsNotNone(handle.endpoint_url)
def tearDown(self):
core.ModelBase.metadata.drop_all(core.get_engine())