Add ability to hide stacks based on tag
Add a "hidden_stack_tags" option that contains a list of tag names. Stacks that have one or more of these tags will be hidden. Add an option to show hidden stacks in the stack listing. blueprint stack-tags Change-Id: I45a5ac6d73a9a61629a56f88270e3a97fafb378a
This commit is contained in:
parent
9817ed77ef
commit
d4361580ba
@ -194,6 +194,11 @@ engine_opts = [
|
||||
'HEAT_SIGNAL will allow calls to the Heat API '
|
||||
'resource-signal using the provided keystone '
|
||||
'credentials')),
|
||||
cfg.ListOpt('hidden_stack_tags',
|
||||
default=[],
|
||||
help=_('Stacks containing these tag names will be hidden. '
|
||||
'Multiple tags should be given in a comma-delimited '
|
||||
'list (eg. hidden_stack_tags=hide_me,me_too).')),
|
||||
cfg.StrOpt('onready',
|
||||
help=_('Deprecated.')),
|
||||
cfg.BoolOpt('stack_scheduler_hints',
|
||||
|
@ -142,10 +142,10 @@ def stack_get_by_name(context, stack_name):
|
||||
|
||||
def stack_get_all(context, limit=None, sort_keys=None, marker=None,
|
||||
sort_dir=None, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
return IMPL.stack_get_all(context, limit, sort_keys,
|
||||
marker, sort_dir, filters, tenant_safe,
|
||||
show_deleted, show_nested)
|
||||
show_deleted, show_nested, show_hidden)
|
||||
|
||||
|
||||
def stack_get_all_by_owner_id(context, owner_id):
|
||||
@ -153,11 +153,12 @@ def stack_get_all_by_owner_id(context, owner_id):
|
||||
|
||||
|
||||
def stack_count_all(context, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
return IMPL.stack_count_all(context, filters=filters,
|
||||
tenant_safe=tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested)
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden)
|
||||
|
||||
|
||||
def stack_create(context, values):
|
||||
|
@ -34,6 +34,7 @@ from heat.db.sqlalchemy import models
|
||||
from heat.rpc import api as rpc_api
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_opt('hidden_stack_tags', 'heat.common.config')
|
||||
CONF.import_opt('max_events_per_stack', 'heat.common.config')
|
||||
CONF.import_group('profiler', 'heat.common.config')
|
||||
|
||||
@ -398,7 +399,7 @@ def _paginate_query(context, query, model, limit=None, sort_keys=None,
|
||||
|
||||
|
||||
def _query_stack_get_all(context, tenant_safe=True, show_deleted=False,
|
||||
show_nested=False):
|
||||
show_nested=False, show_hidden=False):
|
||||
if show_nested:
|
||||
query = soft_delete_aware_query(
|
||||
context, models.Stack, show_deleted=show_deleted
|
||||
@ -410,15 +411,22 @@ def _query_stack_get_all(context, tenant_safe=True, show_deleted=False,
|
||||
|
||||
if tenant_safe:
|
||||
query = query.filter_by(tenant=context.tenant_id)
|
||||
|
||||
if not show_hidden:
|
||||
query = query.filter(
|
||||
~models.Stack.tags.any(
|
||||
models.StackTag.tag.in_(cfg.CONF.hidden_stack_tags)))
|
||||
|
||||
return query
|
||||
|
||||
|
||||
def stack_get_all(context, limit=None, sort_keys=None, marker=None,
|
||||
sort_dir=None, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
query = _query_stack_get_all(context, tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested)
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden)
|
||||
return _filter_and_page_query(context, query, limit, sort_keys,
|
||||
marker, sort_dir, filters).all()
|
||||
|
||||
@ -440,10 +448,11 @@ def _filter_and_page_query(context, query, limit=None, sort_keys=None,
|
||||
|
||||
|
||||
def stack_count_all(context, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
query = _query_stack_get_all(context, tenant_safe=tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested)
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden)
|
||||
query = db_filters.exact_filter(query, models.Stack, filters)
|
||||
return query.count()
|
||||
|
||||
|
@ -315,7 +315,8 @@ class EngineService(service.Service):
|
||||
admin_context = context.get_admin_context()
|
||||
stacks = stack_object.Stack.get_all(
|
||||
admin_context,
|
||||
tenant_safe=False)
|
||||
tenant_safe=False,
|
||||
show_hidden=True)
|
||||
for s in stacks:
|
||||
self.stack_watch.start_watch_task(s.id, admin_context)
|
||||
|
||||
@ -466,7 +467,7 @@ class EngineService(service.Service):
|
||||
@context.request_context
|
||||
def list_stacks(self, cnxt, limit=None, marker=None, sort_keys=None,
|
||||
sort_dir=None, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
"""
|
||||
The list_stacks method returns attributes of all stacks. It supports
|
||||
pagination (``limit`` and ``marker``), sorting (``sort_keys`` and
|
||||
@ -481,17 +482,19 @@ class EngineService(service.Service):
|
||||
:param tenant_safe: if true, scope the request by the current tenant
|
||||
:param show_deleted: if true, show soft-deleted stacks
|
||||
:param show_nested: if true, show nested stacks
|
||||
:param show_hidden: if true, show hidden stacks
|
||||
:returns: a list of formatted stacks
|
||||
"""
|
||||
stacks = parser.Stack.load_all(cnxt, limit, marker, sort_keys,
|
||||
sort_dir, filters, tenant_safe,
|
||||
show_deleted, resolve_data=False,
|
||||
show_nested=show_nested)
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden)
|
||||
return [api.format_stack(stack) for stack in stacks]
|
||||
|
||||
@context.request_context
|
||||
def count_stacks(self, cnxt, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
"""
|
||||
Return the number of stacks that match the given filters
|
||||
:param cnxt: RPC context.
|
||||
@ -499,6 +502,7 @@ class EngineService(service.Service):
|
||||
:param tenant_safe: if true, scope the request by the current tenant
|
||||
:param show_deleted: if true, count will include the deleted stacks
|
||||
:param show_nested: if true, count will include nested stacks
|
||||
:param show_hidden: if true, show hidden stacks
|
||||
:returns: a integer representing the number of matched stacks
|
||||
"""
|
||||
return stack_object.Stack.count_all(
|
||||
@ -506,7 +510,8 @@ class EngineService(service.Service):
|
||||
filters=filters,
|
||||
tenant_safe=tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested)
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden)
|
||||
|
||||
def _validate_deferred_auth_context(self, cnxt, stack):
|
||||
if cfg.CONF.deferred_auth_method != 'password':
|
||||
|
@ -354,7 +354,7 @@ class Stack(collections.Mapping):
|
||||
def load_all(cls, context, limit=None, marker=None, sort_keys=None,
|
||||
sort_dir=None, filters=None, tenant_safe=True,
|
||||
show_deleted=False, resolve_data=True,
|
||||
show_nested=False):
|
||||
show_nested=False, show_hidden=False):
|
||||
stacks = stack_object.Stack.get_all(
|
||||
context,
|
||||
limit,
|
||||
@ -364,7 +364,8 @@ class Stack(collections.Mapping):
|
||||
filters,
|
||||
tenant_safe,
|
||||
show_deleted,
|
||||
show_nested) or []
|
||||
show_nested,
|
||||
show_hidden) or []
|
||||
for stack in stacks:
|
||||
yield cls._from_db(context, stack, resolve_data=resolve_data)
|
||||
|
||||
|
@ -91,7 +91,7 @@ class EngineClient(object):
|
||||
|
||||
def list_stacks(self, ctxt, limit=None, marker=None, sort_keys=None,
|
||||
sort_dir=None, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
"""
|
||||
The list_stacks method returns attributes of all stacks. It supports
|
||||
pagination (``limit`` and ``marker``), sorting (``sort_keys`` and
|
||||
@ -106,6 +106,7 @@ class EngineClient(object):
|
||||
:param tenant_safe: if true, scope the request by the current tenant
|
||||
:param show_deleted: if true, show soft-deleted stacks
|
||||
:param show_nested: if true, show nested stacks
|
||||
:param show_hidden: if true, show hidden stacks
|
||||
:returns: a list of stacks
|
||||
"""
|
||||
return self.call(ctxt,
|
||||
@ -114,10 +115,11 @@ class EngineClient(object):
|
||||
sort_dir=sort_dir, filters=filters,
|
||||
tenant_safe=tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested))
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden))
|
||||
|
||||
def count_stacks(self, ctxt, filters=None, tenant_safe=True,
|
||||
show_deleted=False, show_nested=False):
|
||||
show_deleted=False, show_nested=False, show_hidden=False):
|
||||
"""
|
||||
Return the number of stacks that match the given filters
|
||||
:param ctxt: RPC context.
|
||||
@ -125,13 +127,15 @@ class EngineClient(object):
|
||||
:param tenant_safe: if true, scope the request by the current tenant
|
||||
:param show_deleted: if true, count will include the deleted stacks
|
||||
:param show_nested: if true, count will include nested stacks
|
||||
:param show_hidden: if true, show hidden stacks
|
||||
:returns: a integer representing the number of matched stacks
|
||||
"""
|
||||
return self.call(ctxt, self.make_msg('count_stacks',
|
||||
filters=filters,
|
||||
tenant_safe=tenant_safe,
|
||||
show_deleted=show_deleted,
|
||||
show_nested=show_nested))
|
||||
show_nested=show_nested,
|
||||
show_hidden=show_hidden))
|
||||
|
||||
def show_stack(self, ctxt, stack_identity):
|
||||
"""
|
||||
|
@ -17,6 +17,7 @@ import uuid
|
||||
|
||||
import mock
|
||||
import mox
|
||||
from oslo_config import cfg
|
||||
from oslo_utils import timeutils
|
||||
import six
|
||||
|
||||
@ -552,6 +553,26 @@ class SqlAlchemyTest(common.HeatTestCase):
|
||||
db_api.stack_get_all(self.ctx, sort_keys=sort_keys)
|
||||
self.assertEqual(['id'], sort_keys)
|
||||
|
||||
def test_stack_get_all_hidden_tags(self):
|
||||
cfg.CONF.set_override('hidden_stack_tags', ['hidden'])
|
||||
|
||||
stacks = [self._setup_test_stack('stack', x)[1] for x in UUIDs]
|
||||
stacks[0].tags = ['hidden']
|
||||
stacks[0].store()
|
||||
stacks[1].tags = ['random']
|
||||
stacks[1].store()
|
||||
|
||||
st_db = db_api.stack_get_all(self.ctx, show_hidden=True)
|
||||
self.assertEqual(3, len(st_db))
|
||||
|
||||
st_db_visible = db_api.stack_get_all(self.ctx, show_hidden=False)
|
||||
self.assertEqual(2, len(st_db_visible))
|
||||
|
||||
# Make sure the hidden stack isn't in the stacks returned by
|
||||
# stack_get_all_visible()
|
||||
for stack in st_db_visible:
|
||||
self.assertNotEqual(stacks[0].id, stack.id)
|
||||
|
||||
def test_stack_count_all(self):
|
||||
stacks = [self._setup_test_stack('stack', x)[1] for x in UUIDs]
|
||||
|
||||
@ -572,6 +593,21 @@ class SqlAlchemyTest(common.HeatTestCase):
|
||||
st_db = db_api.stack_count_all(self.ctx, show_deleted=True)
|
||||
self.assertEqual(3, st_db)
|
||||
|
||||
def test_count_all_hidden_tags(self):
|
||||
cfg.CONF.set_override('hidden_stack_tags', ['hidden'])
|
||||
|
||||
stacks = [self._setup_test_stack('stack', x)[1] for x in UUIDs]
|
||||
stacks[0].tags = ['hidden']
|
||||
stacks[0].store()
|
||||
stacks[1].tags = ['random']
|
||||
stacks[1].store()
|
||||
|
||||
st_db = db_api.stack_count_all(self.ctx, show_hidden=True)
|
||||
self.assertEqual(3, st_db)
|
||||
|
||||
st_db_visible = db_api.stack_count_all(self.ctx, show_hidden=False)
|
||||
self.assertEqual(2, st_db_visible)
|
||||
|
||||
def test_stack_count_all_with_filters(self):
|
||||
self._setup_test_stack('foo', UUID1)
|
||||
self._setup_test_stack('bar', UUID2)
|
||||
|
@ -161,7 +161,8 @@ class CfnStackControllerTest(common.HeatTestCase):
|
||||
self.assertEqual(expected, result)
|
||||
default_args = {'limit': None, 'sort_keys': None, 'marker': None,
|
||||
'sort_dir': None, 'filters': None, 'tenant_safe': True,
|
||||
'show_deleted': False, 'show_nested': False}
|
||||
'show_deleted': False, 'show_nested': False,
|
||||
'show_hidden': False}
|
||||
mock_call.assert_called_once_with(
|
||||
dummy_req.context, ('list_stacks', default_args))
|
||||
|
||||
|
@ -385,7 +385,8 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
self.assertEqual(expected, result)
|
||||
default_args = {'limit': None, 'sort_keys': None, 'marker': None,
|
||||
'sort_dir': None, 'filters': None, 'tenant_safe': True,
|
||||
'show_deleted': False, 'show_nested': False}
|
||||
'show_deleted': False, 'show_nested': False,
|
||||
'show_hidden': False}
|
||||
mock_call.assert_called_once_with(
|
||||
req.context, ('list_stacks', default_args))
|
||||
|
||||
@ -406,7 +407,7 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
|
||||
rpc_call_args, _ = mock_call.call_args
|
||||
engine_args = rpc_call_args[1][1]
|
||||
self.assertEqual(8, len(engine_args))
|
||||
self.assertEqual(9, len(engine_args))
|
||||
self.assertIn('limit', engine_args)
|
||||
self.assertIn('sort_keys', engine_args)
|
||||
self.assertIn('marker', engine_args)
|
||||
@ -664,7 +665,8 @@ class StackControllerTest(ControllerTest, common.HeatTestCase):
|
||||
self.assertEqual(expected, result)
|
||||
default_args = {'limit': None, 'sort_keys': None, 'marker': None,
|
||||
'sort_dir': None, 'filters': None, 'tenant_safe': True,
|
||||
'show_deleted': False, 'show_nested': False}
|
||||
'show_deleted': False, 'show_nested': False,
|
||||
'show_hidden': False}
|
||||
mock_call.assert_called_once_with(
|
||||
req.context, ('list_stacks', default_args))
|
||||
|
||||
|
@ -1750,7 +1750,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
self.eng.thread_group_mgr = None
|
||||
self.eng.create_periodic_tasks()
|
||||
|
||||
mock_get_all.assert_called_once_with(mock.ANY, tenant_safe=False)
|
||||
mock_get_all.assert_called_once_with(mock.ANY, tenant_safe=False,
|
||||
show_hidden=True)
|
||||
calls = start_watch_task.call_args_list
|
||||
self.assertEqual(2, start_watch_task.call_count)
|
||||
self.assertIn(mock.call(1, mock.ANY), calls)
|
||||
@ -2057,6 +2058,7 @@ class StackServiceTest(common.HeatTestCase):
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
@ -2072,6 +2074,7 @@ class StackServiceTest(common.HeatTestCase):
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
@ -2086,6 +2089,7 @@ class StackServiceTest(common.HeatTestCase):
|
||||
True,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
@ -2100,6 +2104,7 @@ class StackServiceTest(common.HeatTestCase):
|
||||
False,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
@ -2114,6 +2119,7 @@ class StackServiceTest(common.HeatTestCase):
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
True,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
@ -2128,6 +2134,22 @@ class StackServiceTest(common.HeatTestCase):
|
||||
mock.ANY,
|
||||
True,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'get_all')
|
||||
def test_stack_list_show_hidden(self, mock_stack_get_all):
|
||||
self.eng.list_stacks(self.ctx, show_hidden=True)
|
||||
mock_stack_get_all.assert_called_once_with(mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
mock.ANY,
|
||||
True,
|
||||
)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
@ -2137,7 +2159,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
filters={'foo': 'bar'},
|
||||
tenant_safe=mock.ANY,
|
||||
show_deleted=False,
|
||||
show_nested=False)
|
||||
show_nested=False,
|
||||
show_hidden=False)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
def test_count_stacks_tenant_safe_default_true(self, mock_stack_count_all):
|
||||
@ -2146,7 +2169,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
filters=mock.ANY,
|
||||
tenant_safe=True,
|
||||
show_deleted=False,
|
||||
show_nested=False)
|
||||
show_nested=False,
|
||||
show_hidden=False)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
def test_count_stacks_passes_tenant_safe_info(self, mock_stack_count_all):
|
||||
@ -2155,7 +2179,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
filters=mock.ANY,
|
||||
tenant_safe=False,
|
||||
show_deleted=False,
|
||||
show_nested=False)
|
||||
show_nested=False,
|
||||
show_hidden=False)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
def test_count_stacks_show_nested(self, mock_stack_count_all):
|
||||
@ -2164,7 +2189,8 @@ class StackServiceTest(common.HeatTestCase):
|
||||
filters=mock.ANY,
|
||||
tenant_safe=True,
|
||||
show_deleted=False,
|
||||
show_nested=True)
|
||||
show_nested=True,
|
||||
show_hidden=False)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
def test_count_stack_show_deleted(self, mock_stack_count_all):
|
||||
@ -2173,7 +2199,18 @@ class StackServiceTest(common.HeatTestCase):
|
||||
filters=mock.ANY,
|
||||
tenant_safe=True,
|
||||
show_deleted=True,
|
||||
show_nested=False)
|
||||
show_nested=False,
|
||||
show_hidden=False)
|
||||
|
||||
@mock.patch.object(stack_object.Stack, 'count_all')
|
||||
def test_count_stack_show_hidden(self, mock_stack_count_all):
|
||||
self.eng.count_stacks(self.ctx, show_hidden=True)
|
||||
mock_stack_count_all.assert_called_once_with(mock.ANY,
|
||||
filters=mock.ANY,
|
||||
tenant_safe=True,
|
||||
show_deleted=False,
|
||||
show_nested=False,
|
||||
show_hidden=True)
|
||||
|
||||
@stack_context('service_abandon_stack')
|
||||
def test_abandon_stack(self):
|
||||
|
@ -115,6 +115,7 @@ class EngineRpcAPITestCase(common.HeatTestCase):
|
||||
'tenant_safe': mock.ANY,
|
||||
'show_deleted': mock.ANY,
|
||||
'show_nested': mock.ANY,
|
||||
'show_hidden': mock.ANY,
|
||||
}
|
||||
self._test_engine_api('list_stacks', 'call', **default_args)
|
||||
|
||||
@ -124,6 +125,7 @@ class EngineRpcAPITestCase(common.HeatTestCase):
|
||||
'tenant_safe': mock.ANY,
|
||||
'show_deleted': mock.ANY,
|
||||
'show_nested': mock.ANY,
|
||||
'show_hidden': mock.ANY,
|
||||
}
|
||||
self._test_engine_api('count_stacks', 'call', **default_args)
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user