Merge "Add new search APIs using query pipeline and aggregate search"

This commit is contained in:
Zuul 2020-05-16 01:48:56 +00:00 committed by Gerrit Code Review
commit c004c601db
2 changed files with 169 additions and 19 deletions

View File

@ -1896,6 +1896,25 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
self.nsxlib._build_query,
tags=user_tags)
def test_nsx_search_all(self):
"""Test search all base method."""
mock_search = mock.Mock()
mock_search.side_effect = [
{"cursor": "2",
"result_count": 3,
"results": [{"id": "s1"}, {"id": "s2"}]},
{"cursor": "3",
"result_count": 3,
"results": [{"id": "s3"}]}]
args = "foo"
kwargs = {"a1": "v1", "a2": "v2"}
results = self.nsxlib._search_all(mock_search, *args, **kwargs)
mock_search.assert_has_calls([
mock.call(*args, cursor=0, **kwargs),
mock.call(*args, cursor=2, **kwargs)])
self.assertEqual(3, len(results))
self.assertEqual([{"id": "s1"}, {"id": "s2"}, {"id": "s3"}], results)
def test_nsx_search_all_by_tags(self):
"""Test search all of resources with the specified tag."""
with mock.patch.object(self.nsxlib.client, 'url_get') as search:
@ -1915,6 +1934,57 @@ class TestNsxSearch(nsxlib_testcase.NsxClientTestCase):
mock.call((self.search_path + '&cursor=2') % query)])
self.assertEqual(3, len(results))
@mock.patch("vmware_nsxlib.v3.lib.NsxLibBase._search_all")
def test_nsx_search_all_by_attribute_values(self, mock_search_all):
"""Test search all resources with the specified attribute values."""
resource_type = "dummy_type"
name, values = "dummy_attr", ["id1", "id2", "id3"]
self.nsxlib.search_all_resource_by_attribute_values(
resource_type, name, values)
mock_search_all.assert_called_once_with(
self.nsxlib.search_resource_by_attribute_values, resource_type,
name, values)
@mock.patch("vmware_nsxlib.v3.lib.NsxLibBase._search_all")
def test_nsx_search_all_by_filters(self, mock_search_all):
"""Test search all resources with the specified filters."""
resource_type = "dummy_type"
filters = [{"field_names": "foo", "value": "bar"}]
extra_attrs = {"related": [{"resource_type": "FirewallRule",
"join_condition": "section_id:id"}]}
self.nsxlib.search_all_resource_by_filters(
resource_type, filters, **extra_attrs)
mock_search_all.assert_called_once_with(
self.nsxlib.search_resource_by_filters, resource_type, filters,
**extra_attrs)
def test_nsx_search_resource_by_attribute_values(self):
"""Test search resources with the specified attribute values."""
with mock.patch.object(self.nsxlib.client, "url_post") as search:
resource_type = "dummy_type"
attr_name, attr_values = "dummy_name", ["value1", "value2"]
attr_query = " OR ".join(attr_values)
query = "resource_type:%s AND %s:(%s)" % (
resource_type, attr_name, attr_query)
body = {"query_pipeline": [{"query": query}]}
self.nsxlib.search_resource_by_attribute_values(
resource_type, attr_name, attr_values)
search.assert_called_with("search/querypipeline", body)
def test_nsx_search_resource_by_filters(self):
"""Test search resources with the specified filters."""
with mock.patch.object(self.nsxlib.client, "url_post") as search:
parent_type, child_type = "parent_type", "child_type"
filters = [{"field_names": "foo", "value": "bar"}]
related = [{"resource_type": child_type,
"join_condition": "section_id:id"}]
body = {"primary": {"resource_type": parent_type,
"filters": filters},
"related": related}
self.nsxlib.search_resource_by_filters(
parent_type, filters, related=related)
search.assert_called_with("search/aggregate", body)
def test_get_id_by_resource_and_tag(self):
id = 'test'
scope = 'user'

View File

@ -203,14 +203,83 @@ class NsxLibBase(object):
return do_search(url)
def search_all_by_tags(self, tags, resource_type=None, **extra_attrs):
"""Return all the results searched based on tags."""
def search_resource_by_attribute_values(self, resource_type, name, values,
cursor=None, page_size=None):
"""Search resources of a given type matching values of an attribute.
:param resource_type: String parameter specifying the desired
resource_type.
:param name: Attribute name to match.
:param values: List of attribute values to search for.
:param cursor: Opaque cursor to be used for getting next page of
records (supplied by current result page).
:param page_size: Maximum number of results to return in this page.
:returns: a list of resources of the requested type matching
specified attribute values.
"""
attribute_query = " OR ".join(values)
query = 'resource_type:%s' % resource_type + (
" AND %s:(%s)" % (name, attribute_query)
if attribute_query else "")
body = {"query_pipeline": [{"query": query}]}
args = []
if cursor:
args.append("cursor=%d" % cursor)
if page_size:
args.append("page_size=%d" % page_size)
url = "search/querypipeline" + ("?%s" % "&".join(args) if args else "")
# Retry the search in case of error
@utils.retry_upon_exception(exceptions.NsxSearchError,
max_attempts=self.client.max_attempts)
def do_search(url):
return self.client.url_post(url, body)
return do_search(url)
def search_resource_by_filters(self, resource_type, filters,
cursor=None, page_size=None, **extra_attrs):
"""Search resources of a given type matching specific filters.
:param resource_type: String parameter specifying the desired
resource_type.
:param filters: List of dictionaries containing filters. Each
filter dictionary is of the form:
{'field_names': <filter_key>, 'value': <filter_value>}
:param cursor: Opaque cursor to be used for getting next page of
records (supplied by current result page).
:param page_size: Maximum number of results to return in this page.
:param extra_attrs: Support querying by user specified attributes.
:returns: a list of resources of the requested type matching
specified filters.
"""
body = {"primary": {"resource_type": resource_type,
"filters": filters}}
related = extra_attrs.get("related")
if related:
body["related"] = related
args = []
if cursor:
args.append("cursor=%d" % cursor)
if page_size:
args.append("page_size=%d" % page_size)
url = "search/aggregate" + ("?%s" % "&".join(args) if args else "")
# Retry the search in case of error
@utils.retry_upon_exception(exceptions.NsxSearchError,
max_attempts=self.client.max_attempts)
def do_search(url):
return self.client.url_post(url, body)
return do_search(url)
def _search_all(self, search_func, *args, **kwargs):
results = []
cursor = 0
while True:
response = self.search_by_tags(
resource_type=resource_type, tags=tags, cursor=cursor,
**extra_attrs)
response = search_func(*args, cursor=cursor, **kwargs)
if not response['results']:
return results
results.extend(response['results'])
@ -219,21 +288,32 @@ class NsxLibBase(object):
if cursor >= result_count:
return results
def search_all_by_tags(self, tags, resource_type=None, **extra_attrs):
"""Return all the results searched based on tags."""
return self._search_all(self.search_by_tags,
resource_type=resource_type, tags=tags,
**extra_attrs)
def search_all_resource_by_attributes(self, resource_type, **attributes):
"""Return all the results searched based on attributes."""
results = []
cursor = 0
while True:
response = self.search_resource_by_attributes(
resource_type=resource_type,
cursor=cursor, **attributes)
if not response['results']:
return results
results.extend(response['results'])
cursor = int(response['cursor'])
result_count = int(response['result_count'])
if cursor >= result_count:
return results
"""Return all resources of a given type matching specific attributes.
"""
return self._search_all(self.search_resource_by_attributes,
resource_type=resource_type, **attributes)
def search_all_resource_by_attribute_values(self, resource_type, name,
values):
"""Return all resources of a given type matching an attribute value.
"""
return self._search_all(self.search_resource_by_attribute_values,
resource_type, name, values)
def search_all_resource_by_filters(self, resource_type, filters,
**extra_attrs):
"""Return all resources of a given type matching specific filters."""
return self._search_all(self.search_resource_by_filters, resource_type,
filters, **extra_attrs)
def get_id_by_resource_and_tag(self, resource_type, scope, tag,
alert_not_found=False,