Plugin types are not exposed to the client.

Glance Search clients currently don't have
anyway to know what "types" are available.

Search clients need to know the "type" to
include in the search request for the
particular resource.

Change-Id: I8baed7c5183752b1f52f8050ce321aacec701f1c
Closes-Bug: #1439061
This commit is contained in:
Lakshmi N Sampath 2015-04-01 08:38:15 -07:00
parent 503a7bb4dd
commit d6d5c501c1
7 changed files with 138 additions and 12 deletions

View File

@ -3,5 +3,6 @@
"default": "", "default": "",
"catalog_index": "role:admin", "catalog_index": "role:admin",
"catalog_search": "" "catalog_search": "",
"catalog_plugins": ""
} }

View File

@ -690,6 +690,10 @@ class CatalogSearchRepoProxy(object):
self.policy.enforce(self.context, 'catalog_search', {}) self.policy.enforce(self.context, 'catalog_search', {})
return self.search_repo.search(*args, **kwargs) return self.search_repo.search(*args, **kwargs)
def plugins_info(self, *args, **kwargs):
self.policy.enforce(self.context, 'catalog_plugins', {})
return self.search_repo.plugins_info(*args, **kwargs)
def index(self, *args, **kwargs): def index(self, *args, **kwargs):
self.policy.enforce(self.context, 'catalog_index', {}) self.policy.enforce(self.context, 'catalog_index', {})
return self.search_repo.index(*args, **kwargs) return self.search_repo.index(*args, **kwargs)

View File

@ -32,6 +32,7 @@ import functools
import os import os
import platform import platform
import re import re
import stevedore
import subprocess import subprocess
import sys import sys
import uuid import uuid
@ -729,3 +730,10 @@ def stash_conf_values():
conf['cert_file'] = CONF.cert_file conf['cert_file'] = CONF.cert_file
return conf return conf
def get_search_plugins():
namespace = 'glance.search.index_backend'
ext_manager = stevedore.extension.ExtensionManager(
namespace, invoke_on_load=True)
return ext_manager.extensions

View File

@ -17,6 +17,8 @@ import elasticsearch
from elasticsearch import helpers from elasticsearch import helpers
from oslo_config import cfg from oslo_config import cfg
from glance.common import utils
search_opts = [ search_opts = [
cfg.ListOpt('hosts', default=['127.0.0.1:9200'], cfg.ListOpt('hosts', default=['127.0.0.1:9200'],
@ -40,6 +42,8 @@ class CatalogSearchRepo(object):
def __init__(self, context, es_api): def __init__(self, context, es_api):
self.context = context self.context = context
self.es_api = es_api self.es_api = es_api
self.plugins = utils.get_search_plugins() or []
self.plugins_info_dict = self._get_plugin_info()
def search(self, index, doc_type, query, fields, offset, limit, def search(self, index, doc_type, query, fields, offset, limit,
ignore_unavailable=True): ignore_unavailable=True):
@ -58,3 +62,16 @@ class CatalogSearchRepo(object):
index=default_index, index=default_index,
doc_type=default_type, doc_type=default_type,
actions=actions) actions=actions)
def plugins_info(self):
return self.plugins_info_dict
def _get_plugin_info(self):
plugin_info = dict()
plugin_info['plugins'] = []
for plugin in self.plugins:
info = dict()
info['type'] = plugin.obj.get_document_type()
info['index'] = plugin.obj.get_index_name()
plugin_info['plugins'].append(info)
return plugin_info

View File

@ -41,6 +41,17 @@ class API(wsgi.Router):
conditions={'method': ['PUT', 'DELETE', conditions={'method': ['PUT', 'DELETE',
'PATCH', 'HEAD']}) 'PATCH', 'HEAD']})
mapper.connect('/search/plugins',
controller=search_catalog_resource,
action='plugins_info',
conditions={'method': ['GET']})
mapper.connect('/search/plugins',
controller=reject_method_resource,
action='reject',
allowed_methods='GET',
conditions={'method': ['POST', 'PUT', 'DELETE',
'PATCH', 'HEAD']})
mapper.connect('/index', mapper.connect('/index',
controller=search_catalog_resource, controller=search_catalog_resource,
action='index', action='index',

View File

@ -18,7 +18,6 @@ import json
from oslo.config import cfg from oslo.config import cfg
from oslo_log import log as logging from oslo_log import log as logging
import six import six
import stevedore
import webob.exc import webob.exc
from glance.api import policy from glance.api import policy
@ -76,6 +75,18 @@ class SearchController(object):
LOG.error(utils.exception_to_str(e)) LOG.error(utils.exception_to_str(e))
raise webob.exc.HTTPInternalServerError() raise webob.exc.HTTPInternalServerError()
def plugins_info(self, req):
try:
search_repo = self.gateway.get_catalog_search_repo(req.context)
return search_repo.plugins_info()
except exception.Forbidden as e:
raise webob.exc.HTTPForbidden(explanation=e.msg)
except exception.NotFound as e:
raise webob.exc.HTTPNotFound(explanation=e.msg)
except Exception as e:
LOG.error(utils.exception_to_str(e))
raise webob.exc.HTTPInternalServerError()
def index(self, req, actions, default_index=None, default_type=None): def index(self, req, actions, default_index=None, default_type=None):
try: try:
search_repo = self.gateway.get_catalog_search_repo(req.context) search_repo = self.gateway.get_catalog_search_repo(req.context)
@ -351,22 +362,20 @@ class ResponseSerializer(wsgi.JSONResponseSerializer):
response.unicode_body = six.text_type(body) response.unicode_body = six.text_type(body)
response.content_type = 'application/json' response.content_type = 'application/json'
def plugins_info(self, response, query_result):
body = json.dumps(query_result, ensure_ascii=False)
response.unicode_body = six.text_type(body)
response.content_type = 'application/json'
def index(self, response, query_result): def index(self, response, query_result):
body = json.dumps(query_result, ensure_ascii=False) body = json.dumps(query_result, ensure_ascii=False)
response.unicode_body = six.text_type(body) response.unicode_body = six.text_type(body)
response.content_type = 'application/json' response.content_type = 'application/json'
def get_plugins():
namespace = 'glance.search.index_backend'
ext_manager = stevedore.extension.ExtensionManager(
namespace, invoke_on_load=True)
return ext_manager.extensions
def create_resource(): def create_resource():
"""Search resource factory method""" """Search resource factory method"""
plugins = get_plugins() plugins = utils.get_search_plugins()
deserializer = RequestDeserializer(plugins) deserializer = RequestDeserializer(plugins)
serializer = ResponseSerializer() serializer = ResponseSerializer()
controller = SearchController(plugins) controller = SearchController(plugins)

View File

@ -18,6 +18,7 @@ from oslo.serialization import jsonutils
import webob.exc import webob.exc
from glance.common import exception from glance.common import exception
from glance.common import utils
import glance.gateway import glance.gateway
import glance.search import glance.search
from glance.search.api.v0_1 import search as search from glance.search.api.v0_1 import search as search
@ -234,12 +235,52 @@ class TestSearchController(base.IsolatedUnitTest):
webob.exc.HTTPInternalServerError, self.search_controller.index, webob.exc.HTTPInternalServerError, self.search_controller.index,
request, actions) request, actions)
def test_plugins_info(self):
request = unit_test_utils.get_fake_request()
self.search_controller.plugins_info = mock.Mock(return_value="{}")
self.search_controller.plugins_info(request)
self.search_controller.plugins_info.assert_called_once_with(request)
def test_plugins_info_repo(self):
request = unit_test_utils.get_fake_request()
repo = glance.search.CatalogSearchRepo
repo.plugins_info = mock.Mock(return_value="{}")
self.search_controller.plugins_info(request)
repo.plugins_info.assert_called_once_with()
def test_plugins_info_forbidden(self):
request = unit_test_utils.get_fake_request()
repo = glance.search.CatalogSearchRepo
repo.plugins_info = mock.Mock(side_effect=exception.Forbidden)
self.assertRaises(
webob.exc.HTTPForbidden, self.search_controller.plugins_info,
request)
def test_plugins_info_not_found(self):
request = unit_test_utils.get_fake_request()
repo = glance.search.CatalogSearchRepo
repo.plugins_info = mock.Mock(side_effect=exception.NotFound)
self.assertRaises(webob.exc.HTTPNotFound,
self.search_controller.plugins_info, request)
def test_plugins_info_internal_server_error(self):
request = unit_test_utils.get_fake_request()
repo = glance.search.CatalogSearchRepo
repo.plugins_info = mock.Mock(side_effect=Exception)
self.assertRaises(webob.exc.HTTPInternalServerError,
self.search_controller.plugins_info, request)
class TestSearchDeserializer(test_utils.BaseTestCase): class TestSearchDeserializer(test_utils.BaseTestCase):
def setUp(self): def setUp(self):
super(TestSearchDeserializer, self).setUp() super(TestSearchDeserializer, self).setUp()
self.deserializer = search.RequestDeserializer(search.get_plugins()) self.deserializer = search.RequestDeserializer(
utils.get_search_plugins()
)
def test_single_index(self): def test_single_index(self):
request = unit_test_utils.get_fake_request() request = unit_test_utils.get_fake_request()
@ -411,7 +452,9 @@ class TestIndexDeserializer(test_utils.BaseTestCase):
def setUp(self): def setUp(self):
super(TestIndexDeserializer, self).setUp() super(TestIndexDeserializer, self).setUp()
self.deserializer = search.RequestDeserializer(search.get_plugins()) self.deserializer = search.RequestDeserializer(
utils.get_search_plugins()
)
def test_empty_request(self): def test_empty_request(self):
request = unit_test_utils.get_fake_request() request = unit_test_utils.get_fake_request()
@ -874,6 +917,39 @@ class TestResponseSerializer(test_utils.BaseTestCase):
super(TestResponseSerializer, self).setUp() super(TestResponseSerializer, self).setUp()
self.serializer = search.ResponseSerializer() self.serializer = search.ResponseSerializer()
def test_plugins_info(self):
expected = {
"plugins": [
{
"index": "glance",
"type": "image"
},
{
"index": "glance",
"type": "metadef"
}
]
}
request = webob.Request.blank('/v0.1/search')
response = webob.Response(request=request)
result = {
"plugins": [
{
"index": "glance",
"type": "image"
},
{
"index": "glance",
"type": "metadef"
}
]
}
self.serializer.search(response, result)
actual = jsonutils.loads(response.body)
self.assertEqual(expected, actual)
self.assertEqual('application/json', response.content_type)
def test_search(self): def test_search(self):
expected = [{ expected = [{
'id': '1', 'id': '1',