Added db API layer to add instance tag-list filtering support
Added database API layer which allow filtering intances using new filters 'not-tags' and 'not-tags-any': * not-tags means NOT(T1 AND T2) * not-tags-any means NOT(T1 OR T2) Implements: blueprint tag-instances Co-Authored-By: Pavel Kholkin <pkholkin@mirantis.com> Change-Id: I0f5f8898639ecfb7a736f4d6ffcb3ccd7f03a213
This commit is contained in:
@@ -2247,16 +2247,24 @@ def instance_get_all_by_filters_sort(context, filters, limit=None, marker=None,
|
||||
of these tags:
|
||||
|
||||
`tags` -- One or more strings that will be used to filter results
|
||||
in an AND expression.
|
||||
in an AND expression: T1 AND T2
|
||||
|
||||
`tags-any` -- One or more strings that will be used to filter results in
|
||||
an OR expression.
|
||||
an OR expression: T1 OR T2
|
||||
|
||||
`not-tags` -- One or more strings that will be used to filter results in
|
||||
an NOT AND expression: NOT (T1 AND T2)
|
||||
|
||||
`not-tags-any` -- One or more strings that will be used to filter results
|
||||
in an NOT OR expression: NOT (T1 OR T2)
|
||||
|
||||
Tags should be represented as list::
|
||||
|
||||
| filters = {
|
||||
| 'tags': [some-tag, some-another-tag],
|
||||
| 'tags-any: [some-any-tag, some-another-any-tag]
|
||||
| 'tags-any: [some-any-tag, some-another-any-tag],
|
||||
| 'not-tags: [some-not-tag, some-another-not-tag],
|
||||
| 'not-tags-any: [some-not-any-tag, some-another-not-any-tag]
|
||||
| }
|
||||
|
||||
"""
|
||||
@@ -2349,6 +2357,25 @@ def instance_get_all_by_filters_sort(context, filters, limit=None, marker=None,
|
||||
query_prefix = query_prefix.join(tag_alias, models.Instance.tags)
|
||||
query_prefix = query_prefix.filter(tag_alias.tag.in_(tags))
|
||||
|
||||
if 'not-tags' in filters:
|
||||
tags = filters.pop('not-tags')
|
||||
first_tag = tags.pop(0)
|
||||
subq = query_prefix.session.query(models.Tag.resource_id)
|
||||
subq = subq.join(models.Instance.tags)
|
||||
subq = subq.filter(models.Tag.tag == first_tag)
|
||||
|
||||
for tag in tags:
|
||||
tag_alias = aliased(models.Tag)
|
||||
subq = subq.join(tag_alias, models.Instance.tags)
|
||||
subq = subq.filter(tag_alias.tag == tag)
|
||||
|
||||
query_prefix = query_prefix.filter(~models.Instance.uuid.in_(subq))
|
||||
|
||||
if 'not-tags-any' in filters:
|
||||
tags = filters.pop('not-tags-any')
|
||||
query_prefix = query_prefix.filter(~models.Instance.tags.any(
|
||||
models.Tag.tag.in_(tags)))
|
||||
|
||||
if not context.is_admin:
|
||||
# If we're not admin context, add appropriate filter..
|
||||
if context.project_id:
|
||||
|
||||
@@ -9848,3 +9848,146 @@ class TestInstanceInfoCache(test.TestCase):
|
||||
info_cache = db.instance_info_cache_get(self.context, instance_uuid)
|
||||
self.assertEqual(network_info, info_cache.network_info)
|
||||
self.assertEqual(instance_uuid, info_cache.instance_uuid)
|
||||
|
||||
|
||||
class TestInstanceTagsFiltering(test.TestCase):
|
||||
sample_data = {
|
||||
'project_id': 'project1'
|
||||
}
|
||||
|
||||
def setUp(self):
|
||||
super(TestInstanceTagsFiltering, self).setUp()
|
||||
self.ctxt = context.RequestContext('user1', 'project1')
|
||||
|
||||
def _create_instances(self, count):
|
||||
return [db.instance_create(self.ctxt, self.sample_data)['uuid']
|
||||
for i in range(count)]
|
||||
|
||||
def _assertEqualInstanceUUIDs(self, expected_uuids, observed_instances):
|
||||
observed_uuids = [inst['uuid'] for inst in observed_instances]
|
||||
self.assertEqual(sorted(expected_uuids), sorted(observed_uuids))
|
||||
|
||||
def test_instance_get_all_by_filters_not_tags(self):
|
||||
uuids = self._create_instances(8)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[5], [u't1', u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[6], [u't3', u't4'])
|
||||
db.instance_tag_set(self.ctxt, uuids[7], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(
|
||||
self.ctxt, {'not-tags': [u't1', u't2']})
|
||||
|
||||
self._assertEqualInstanceUUIDs([uuids[0], uuids[1], uuids[3], uuids[4],
|
||||
uuids[6], uuids[7]], result)
|
||||
|
||||
def test_instance_get_all_by_filters_not_tags_any(self):
|
||||
uuids = self._create_instances(8)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[5], [u't1', u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[6], [u't3', u't4'])
|
||||
db.instance_tag_set(self.ctxt, uuids[7], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(
|
||||
self.ctxt, {'not-tags-any': [u't1', u't2']})
|
||||
self._assertEqualInstanceUUIDs([uuids[4], uuids[6], uuids[7]], result)
|
||||
|
||||
def test_instance_get_all_by_filters_not_tags_and_tags(self):
|
||||
uuids = self._create_instances(5)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1', u't2', u't4', u't5'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't1', u't2', u't4'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't1', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags': [u't1', u't2'],
|
||||
'not-tags': [u't4', u't5']})
|
||||
self._assertEqualInstanceUUIDs([uuids[1], uuids[2]], result)
|
||||
|
||||
def test_instance_get_all_by_filters_tags_contradictory(self):
|
||||
uuids = self._create_instances(4)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags': [u't1'],
|
||||
'not-tags': [u't1']})
|
||||
self.assertEqual([], result)
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags': [u't1'],
|
||||
'not-tags-any': [u't1']})
|
||||
self.assertEqual([], result)
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags-any': [u't1'],
|
||||
'not-tags-any': [u't1']})
|
||||
self.assertEqual([], result)
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags-any': [u't1'],
|
||||
'not-tags': [u't1']})
|
||||
self.assertEqual([], result)
|
||||
|
||||
def test_instance_get_all_by_filters_not_tags_and_tags_any(self):
|
||||
uuids = self._create_instances(6)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't1', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [u't1', u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[5], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags-any': [u't1', u't2'],
|
||||
'not-tags': [u't1', u't2']})
|
||||
self._assertEqualInstanceUUIDs([uuids[0], uuids[1], uuids[3]], result)
|
||||
|
||||
def test_instance_get_all_by_filters_not_tags_and_not_tags_any(self):
|
||||
uuids = self._create_instances(6)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't2', u't5'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't1', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [u't1', u't2', u't4', u't5'])
|
||||
db.instance_tag_set(self.ctxt, uuids[5], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'not-tags': [u't1', u't2'],
|
||||
'not-tags-any': [u't3', u't4']})
|
||||
self._assertEqualInstanceUUIDs([uuids[0], uuids[1], uuids[5]], result)
|
||||
|
||||
def test_instance_get_all_by_filters_all_tag_filters(self):
|
||||
uuids = self._create_instances(9)
|
||||
|
||||
db.instance_tag_set(self.ctxt, uuids[0], [u't1', u't3', u't7'])
|
||||
db.instance_tag_set(self.ctxt, uuids[1], [u't1', u't2'])
|
||||
db.instance_tag_set(self.ctxt, uuids[2], [u't1', u't2', u't7'])
|
||||
db.instance_tag_set(self.ctxt, uuids[3], [u't1', u't2', u't3', u't5'])
|
||||
db.instance_tag_set(self.ctxt, uuids[4], [u't1', u't2', u't3', u't7'])
|
||||
db.instance_tag_set(self.ctxt, uuids[5], [u't1', u't2', u't3'])
|
||||
db.instance_tag_set(self.ctxt, uuids[6], [u't1', u't2', u't3', u't4',
|
||||
u't5'])
|
||||
db.instance_tag_set(self.ctxt, uuids[7], [u't1', u't2', u't3', u't4',
|
||||
u't5', u't6'])
|
||||
db.instance_tag_set(self.ctxt, uuids[8], [])
|
||||
|
||||
result = db.instance_get_all_by_filters(self.ctxt,
|
||||
{'tags': [u't1', u't2'],
|
||||
'tags-any': [u't3', u't4'],
|
||||
'not-tags': [u't5', u't6'],
|
||||
'not-tags-any': [u't7', u't8']})
|
||||
self._assertEqualInstanceUUIDs([uuids[3], uuids[5], uuids[6]], result)
|
||||
|
||||
Reference in New Issue
Block a user