return reservations
+def _filter_domain_id_from_parents(domain_id, tree):
+ """Removes the domain_id from the tree if present"""
+ new_tree = None
+ if tree:
+ parent, children = next(iter(tree.items()))
+ # Don't add the domain id to the parents hierarchy
+ if parent != domain_id:
+ new_tree = {parent: _filter_domain_id_from_parents(domain_id,
+ children)}
+
+ return new_tree
+
+
def get_project_hierarchy(context, project_id, subtree_as_ids=False,
parents_as_ids=False):
"""A Helper method to get the project hierarchy.
Along with hierarchical multitenancy in keystone API v3, projects can be
hierarchically organized. Therefore, we need to know the project
hierarchy, if any, in order to do nested quota operations properly.
+ If the domain is being used as the top most parent, it is filtered out from
+ the parent tree and parent_id.
"""
try:
keystone = _keystone_client(context)
project = keystone.projects.get(project_id,
subtree_as_ids=subtree_as_ids,
parents_as_ids=parents_as_ids)
- generic_project.parent_id = project.parent_id
+
+ generic_project.parent_id = None
+ if project.parent_id != project.domain_id:
+ generic_project.parent_id = project.parent_id
+
generic_project.subtree = (
project.subtree if subtree_as_ids else None)
- generic_project.parents = (
- project.parents if parents_as_ids else None)
+
+ generic_project.parents = None
+ if parents_as_ids:
+ generic_project.parents = _filter_domain_id_from_parents(
+ project.domain_id, project.parents)
except exceptions.NotFound:
msg = (_("Tenant ID: %s does not exist.") % project_id)
raise webob.exc.HTTPNotFound(explanation=msg)
self.id = id
self.parent_id = parent_id
self.subtree = None
+ self.parents = None
+ self.domain_id = 'default'
def setUp(self):
super(QuotaUtilsTest, self).setUp()
self.context.project_id, parents_as_ids=False, subtree_as_ids=True)
self.assertEqual(expected_project.__dict__, project.__dict__)
+ def _setup_mock_ksclient(self, mock_client, version='v3',
+ subtree=None, parents=None):
+ keystoneclient = mock_client.return_value
+ keystoneclient.version = version
+ proj = self.FakeProject(self.context.project_id)
+ proj.subtree = subtree
+ if parents:
+ proj.parents = parents
+ proj.parent_id = next(iter(parents.keys()))
+ keystoneclient.projects.get.return_value = proj
+
+ @mock.patch('keystoneclient.client.Client')
+ def test__filter_domain_id_from_parents_domain_as_parent(
+ self, mock_client):
+ # Test with a top level project (domain is direct parent)
+ self._setup_mock_ksclient(mock_client, parents={'default': None})
+ project = quota_utils.get_project_hierarchy(
+ self.context, self.context.project_id, parents_as_ids=True)
+ self.assertIsNone(project.parent_id)
+ self.assertIsNone(project.parents)
+
+ @mock.patch('keystoneclient.client.Client')
+ def test__filter_domain_id_from_parents_domain_as_grandparent(
+ self, mock_client):
+ # Test with a child project (domain is more than a parent)
+ self._setup_mock_ksclient(mock_client,
+ parents={'bar': {'default': None}})
+ project = quota_utils.get_project_hierarchy(
+ self.context, self.context.project_id, parents_as_ids=True)
+ self.assertEqual('bar', project.parent_id)
+ self.assertEqual({'bar': None}, project.parents)
+
+ @mock.patch('keystoneclient.client.Client')
+ def test__filter_domain_id_from_parents_no_domain_in_parents(
+ self, mock_client):
+ # Test that if top most parent is not a domain (to simulate an older
+ # keystone version) nothing gets removed from the tree
+ parents = {'bar': {'foo': None}}
+ self._setup_mock_ksclient(mock_client, parents=parents)
+ project = quota_utils.get_project_hierarchy(
+ self.context, self.context.project_id, parents_as_ids=True)
+ self.assertEqual('bar', project.parent_id)
+ self.assertEqual(parents, project.parents)
+
+ @mock.patch('keystoneclient.client.Client')
+ def test__filter_domain_id_from_parents_no_parents(
+ self, mock_client):
+ # Test that if top no parents are present (to simulate an older
+ # keystone version) things don't blow up
+ self._setup_mock_ksclient(mock_client)
+ project = quota_utils.get_project_hierarchy(
+ self.context, self.context.project_id, parents_as_ids=True)
+ self.assertIsNone(project.parent_id)
+ self.assertIsNone(project.parents)
+
@mock.patch('cinder.quota_utils._keystone_client')
def test_validate_nested_projects_with_keystone_v2(self, _keystone_client):
_keystone_client.side_effect = exceptions.VersionNotAvailable