Modify/Move project validation methods to api_utils
We currently only validate the existence of a project on the default types code so that we cannot set it for non existing projects, but there are other places where we would benefit from having this validation, such as volume type access, setting quotas. To achieve this we are moving methods from quota_utils and default_types to api_utils. This patch also modifies the method get_project_hierarchy to get_project and removes related hierarchy tests since with the removal of nested quota driver we don't require the hierarchy properties anymore. Related-Bug: #1638804 Change-Id: I20f8e500f583eb85f24a4c36f344c11c7face06c
This commit is contained in:
parent
393c2e4ad9
commit
dcf66a96a4
@ -10,13 +10,23 @@
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
from keystoneauth1 import exceptions as ks_exc
|
||||
from keystoneauth1 import identity
|
||||
from keystoneauth1 import loading as ka_loading
|
||||
from keystoneclient import client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
from oslo_utils import strutils
|
||||
import webob
|
||||
from webob import exc
|
||||
|
||||
from cinder import exception
|
||||
from cinder.i18n import _
|
||||
|
||||
CONF = cfg.CONF
|
||||
CONF.import_group('keystone_authtoken',
|
||||
'keystonemiddleware.auth_token.__init__')
|
||||
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
@ -142,3 +152,84 @@ def walk_class_hierarchy(clazz, encountered=None):
|
||||
for subsubclass in walk_class_hierarchy(subclass, encountered):
|
||||
yield subsubclass
|
||||
yield subclass
|
||||
|
||||
|
||||
def _keystone_client(context, version=(3, 0)):
|
||||
"""Creates and returns an instance of a generic keystone client.
|
||||
|
||||
:param context: The request context
|
||||
:param version: version of Keystone to request
|
||||
:return: keystoneclient.client.Client object
|
||||
"""
|
||||
if context.system_scope is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
system_scope=context.system_scope
|
||||
)
|
||||
elif context.domain_id is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
domain_id=context.domain_id
|
||||
)
|
||||
elif context.project_id is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
project_id=context.project_id
|
||||
)
|
||||
else:
|
||||
# We're dealing with an unscoped token from keystone that doesn't
|
||||
# carry any authoritative power outside of the user simplify proving
|
||||
# they know their own password. This token isn't associated with any
|
||||
# authorization target (e.g., system, domain, or project).
|
||||
auth_plugin = context.get_auth_plugin()
|
||||
|
||||
client_session = ka_loading.session.Session().load_from_options(
|
||||
auth=auth_plugin,
|
||||
insecure=CONF.keystone_authtoken.insecure,
|
||||
cacert=CONF.keystone_authtoken.cafile,
|
||||
key=CONF.keystone_authtoken.keyfile,
|
||||
cert=CONF.keystone_authtoken.certfile,
|
||||
split_loggers=CONF.service_user.split_loggers)
|
||||
return client.Client(auth_url=CONF.keystone_authtoken.auth_url,
|
||||
session=client_session, version=version)
|
||||
|
||||
|
||||
class GenericProjectInfo(object):
|
||||
"""Abstraction layer for Keystone V2 and V3 project objects"""
|
||||
def __init__(self, project_id, project_keystone_api_version,
|
||||
domain_id=None, name=None, description=None):
|
||||
self.id = project_id
|
||||
self.keystone_api_version = project_keystone_api_version
|
||||
self.domain_id = domain_id
|
||||
self.name = name
|
||||
self.description = description
|
||||
|
||||
|
||||
def get_project(context, project_id):
|
||||
"""Method to verify project exists in keystone"""
|
||||
keystone = _keystone_client(context)
|
||||
generic_project = GenericProjectInfo(project_id, keystone.version)
|
||||
project = keystone.projects.get(project_id)
|
||||
generic_project.domain_id = project.domain_id
|
||||
generic_project.name = project.name
|
||||
generic_project.description = project.description
|
||||
return generic_project
|
||||
|
||||
|
||||
def validate_project_and_authorize(context, project_id, policy_check=None,
|
||||
validate_only=False):
|
||||
try:
|
||||
target_project = get_project(context, project_id)
|
||||
if not validate_only:
|
||||
target_project = {'project_id': target_project.id}
|
||||
context.authorize(policy_check, target=target_project)
|
||||
except ks_exc.http.NotFound:
|
||||
explanation = _("Project with id %s not found." % project_id)
|
||||
raise exc.HTTPNotFound(explanation=explanation)
|
||||
except exception.NotAuthorized:
|
||||
explanation = _("You are not authorized to perform this "
|
||||
"operation.")
|
||||
raise exc.HTTPForbidden(explanation=explanation)
|
||||
|
@ -16,9 +16,9 @@
|
||||
"""The resource filters api."""
|
||||
from http import HTTPStatus
|
||||
|
||||
from keystoneauth1 import exceptions as ks_exc
|
||||
from webob import exc
|
||||
|
||||
from cinder.api import api_utils as utils
|
||||
from cinder.api import microversions as mv
|
||||
from cinder.api.openstack import wsgi
|
||||
from cinder.api.schemas import default_types
|
||||
@ -29,7 +29,6 @@ from cinder import exception
|
||||
from cinder.i18n import _
|
||||
from cinder import objects
|
||||
from cinder.policies import default_types as policy
|
||||
from cinder import quota_utils
|
||||
|
||||
|
||||
class DefaultTypesController(wsgi.Controller):
|
||||
@ -37,22 +36,6 @@ class DefaultTypesController(wsgi.Controller):
|
||||
|
||||
_view_builder_class = default_types_view.ViewBuilder
|
||||
|
||||
def _validate_project_and_authorize(self, context, project_id,
|
||||
policy_check):
|
||||
try:
|
||||
target_project = quota_utils.get_project_hierarchy(context,
|
||||
project_id)
|
||||
target_project = {'project_id': target_project.id,
|
||||
'domain_id': target_project.domain_id}
|
||||
context.authorize(policy_check, target=target_project)
|
||||
except ks_exc.http.NotFound:
|
||||
explanation = _("Project with id %s not found." % project_id)
|
||||
raise exc.HTTPNotFound(explanation=explanation)
|
||||
except exception.NotAuthorized:
|
||||
explanation = _("You are not authorized to perform this "
|
||||
"operation.")
|
||||
raise exc.HTTPForbidden(explanation=explanation)
|
||||
|
||||
@wsgi.response(HTTPStatus.OK)
|
||||
@wsgi.Controller.api_version(mv.DEFAULT_TYPE_OVERRIDES)
|
||||
@validation.schema(default_types.create_or_update)
|
||||
@ -63,7 +46,7 @@ class DefaultTypesController(wsgi.Controller):
|
||||
project_id = id
|
||||
volume_type_id = body['default_type']['volume_type']
|
||||
|
||||
self._validate_project_and_authorize(context, project_id,
|
||||
utils.validate_project_and_authorize(context, project_id,
|
||||
policy.CREATE_UPDATE_POLICY)
|
||||
try:
|
||||
volume_type_id = objects.VolumeType.get_by_name_or_id(
|
||||
@ -85,7 +68,7 @@ class DefaultTypesController(wsgi.Controller):
|
||||
context = req.environ['cinder.context']
|
||||
|
||||
project_id = id
|
||||
self._validate_project_and_authorize(context, project_id,
|
||||
utils.validate_project_and_authorize(context, project_id,
|
||||
policy.GET_POLICY)
|
||||
default_type = db.project_default_volume_type_get(context, project_id)
|
||||
if not default_type:
|
||||
@ -117,7 +100,7 @@ class DefaultTypesController(wsgi.Controller):
|
||||
context = req.environ['cinder.context']
|
||||
|
||||
project_id = id
|
||||
self._validate_project_and_authorize(context, project_id,
|
||||
utils.validate_project_and_authorize(context, project_id,
|
||||
policy.DELETE_POLICY)
|
||||
db.project_default_volume_type_unset(context, id)
|
||||
|
||||
|
@ -12,9 +12,6 @@
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
from keystoneauth1 import identity
|
||||
from keystoneauth1 import loading as ka_loading
|
||||
from keystoneclient import client
|
||||
from oslo_config import cfg
|
||||
from oslo_log import log as logging
|
||||
|
||||
@ -27,23 +24,6 @@ CONF.import_group('keystone_authtoken',
|
||||
LOG = logging.getLogger(__name__)
|
||||
|
||||
|
||||
class GenericProjectInfo(object):
|
||||
"""Abstraction layer for Keystone V2 and V3 project objects"""
|
||||
def __init__(self, project_id, project_keystone_api_version,
|
||||
project_parent_id=None,
|
||||
project_subtree=None,
|
||||
project_parent_tree=None,
|
||||
is_admin_project=False,
|
||||
domain_id=None):
|
||||
self.id = project_id
|
||||
self.domain_id = domain_id
|
||||
self.keystone_api_version = project_keystone_api_version
|
||||
self.parent_id = project_parent_id
|
||||
self.subtree = project_subtree
|
||||
self.parents = project_parent_tree
|
||||
self.is_admin_project = is_admin_project
|
||||
|
||||
|
||||
def get_volume_type_reservation(ctxt, volume, type_id,
|
||||
reserve_vol_type_only=False,
|
||||
negative=False):
|
||||
@ -101,82 +81,6 @@ def _filter_domain_id_from_parents(domain_id, tree):
|
||||
return new_tree
|
||||
|
||||
|
||||
def get_project_hierarchy(context, project_id, subtree_as_ids=False,
|
||||
parents_as_ids=False, is_admin_project=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 default volume type operations properly.
|
||||
"""
|
||||
keystone = _keystone_client(context)
|
||||
generic_project = GenericProjectInfo(project_id, keystone.version)
|
||||
if keystone.version == 'v3':
|
||||
project = keystone.projects.get(project_id,
|
||||
subtree_as_ids=subtree_as_ids,
|
||||
parents_as_ids=parents_as_ids)
|
||||
|
||||
generic_project.parent_id = None
|
||||
generic_project.domain_id = project.domain_id
|
||||
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 = None
|
||||
if parents_as_ids:
|
||||
generic_project.parents = _filter_domain_id_from_parents(
|
||||
project.domain_id, project.parents)
|
||||
|
||||
generic_project.is_admin_project = is_admin_project
|
||||
|
||||
return generic_project
|
||||
|
||||
|
||||
def _keystone_client(context, version=(3, 0)):
|
||||
"""Creates and returns an instance of a generic keystone client.
|
||||
|
||||
:param context: The request context
|
||||
:param version: version of Keystone to request
|
||||
:return: keystoneclient.client.Client object
|
||||
"""
|
||||
if context.system_scope is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
system_scope=context.system_scope
|
||||
)
|
||||
elif context.domain_id is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
domain_id=context.domain_id
|
||||
)
|
||||
elif context.project_id is not None:
|
||||
auth_plugin = identity.Token(
|
||||
auth_url=CONF.keystone_authtoken.auth_url,
|
||||
token=context.auth_token,
|
||||
project_id=context.project_id
|
||||
)
|
||||
else:
|
||||
# We're dealing with an unscoped token from keystone that doesn't
|
||||
# carry any authoritative power outside of the user simplify proving
|
||||
# they know their own password. This token isn't associated with any
|
||||
# authorization target (e.g., system, domain, or project).
|
||||
auth_plugin = context.get_auth_plugin()
|
||||
|
||||
client_session = ka_loading.session.Session().load_from_options(
|
||||
auth=auth_plugin,
|
||||
insecure=CONF.keystone_authtoken.insecure,
|
||||
cacert=CONF.keystone_authtoken.cafile,
|
||||
key=CONF.keystone_authtoken.keyfile,
|
||||
cert=CONF.keystone_authtoken.certfile,
|
||||
split_loggers=CONF.service_user.split_loggers)
|
||||
return client.Client(auth_url=CONF.keystone_authtoken.auth_url,
|
||||
session=client_session, version=version)
|
||||
|
||||
|
||||
OVER_QUOTA_RESOURCE_EXCEPTIONS = {'snapshots': exception.SnapshotLimitExceeded,
|
||||
'backups': exception.BackupLimitExceeded,
|
||||
'volumes': exception.VolumeLimitExceeded,
|
||||
|
@ -35,7 +35,7 @@ class DefaultVolumeTypesTest(functional_helpers._FunctionalTestBase):
|
||||
_keystone_client.version = 'v3'
|
||||
_keystone_client.projects.get.side_effect = self._get_project
|
||||
_keystone_client_get = mock.patch(
|
||||
'cinder.quota_utils._keystone_client',
|
||||
'cinder.api.api_utils._keystone_client',
|
||||
lambda *args, **kwargs: _keystone_client)
|
||||
_keystone_client_get.start()
|
||||
self.addCleanup(_keystone_client_get.stop)
|
||||
@ -44,14 +44,11 @@ class DefaultVolumeTypesTest(functional_helpers._FunctionalTestBase):
|
||||
return self.project
|
||||
|
||||
class FakeProject(object):
|
||||
_dom_id = uuid.uuid4().hex
|
||||
|
||||
def __init__(self, parent_id=None):
|
||||
def __init__(self, name=None):
|
||||
self.id = uuid.uuid4().hex
|
||||
self.parent_id = parent_id
|
||||
self.domain_id = self._dom_id
|
||||
self.subtree = None
|
||||
self.parents = None
|
||||
self.name = name
|
||||
self.description = 'fake project description'
|
||||
self.domain_id = 'default'
|
||||
|
||||
@mock.patch.object(context.RequestContext, 'authorize')
|
||||
def test_default_type_set(self, mock_authorize):
|
||||
|
@ -99,7 +99,7 @@ class QuotaSetsControllerTestBase(test.TestCase):
|
||||
|
||||
self.req.environ['cinder.context'].project_id = uuid.uuid4().hex
|
||||
|
||||
get_patcher = mock.patch('cinder.quota_utils.get_project_hierarchy',
|
||||
get_patcher = mock.patch('cinder.api.api_utils.get_project',
|
||||
self._get_project)
|
||||
get_patcher.start()
|
||||
self.addCleanup(get_patcher.stop)
|
||||
|
@ -79,7 +79,7 @@ class DefaultVolumeTypesApiTest(test.TestCase):
|
||||
self.type2 = self._create_volume_type(
|
||||
self.ctxt, 'volume_type2')
|
||||
|
||||
get_patcher = mock.patch('cinder.quota_utils.get_project_hierarchy',
|
||||
get_patcher = mock.patch('cinder.api.api_utils.get_project',
|
||||
self._get_project)
|
||||
get_patcher.start()
|
||||
self.addCleanup(get_patcher.stop)
|
||||
|
@ -15,6 +15,7 @@
|
||||
|
||||
from http import HTTPStatus
|
||||
from unittest import mock
|
||||
import uuid
|
||||
|
||||
from cinder.api import microversions as mv
|
||||
from cinder import db
|
||||
@ -38,7 +39,7 @@ class DefaultVolumeTypesPolicyTests(test_base.CinderPolicyTests):
|
||||
_keystone_client.version = 'v3'
|
||||
_keystone_client.projects.get.side_effect = self._get_project
|
||||
_keystone_client_get = mock.patch(
|
||||
'cinder.quota_utils._keystone_client',
|
||||
'cinder.api.api_utils._keystone_client',
|
||||
lambda *args, **kwargs: _keystone_client)
|
||||
_keystone_client_get.start()
|
||||
self.addCleanup(_keystone_client_get.stop)
|
||||
@ -47,14 +48,11 @@ class DefaultVolumeTypesPolicyTests(test_base.CinderPolicyTests):
|
||||
return self.project
|
||||
|
||||
class FakeProject(object):
|
||||
_dom_id = fake_constants.DOMAIN_ID
|
||||
|
||||
def __init__(self, parent_id=None):
|
||||
self.id = fake_constants.PROJECT_ID
|
||||
self.parent_id = parent_id
|
||||
self.domain_id = self._dom_id
|
||||
self.subtree = None
|
||||
self.parents = None
|
||||
def __init__(self, name=None):
|
||||
self.id = uuid.uuid4().hex
|
||||
self.name = name
|
||||
self.description = 'fake project description'
|
||||
self.domain_id = 'default'
|
||||
|
||||
def test_system_admin_can_set_default(self):
|
||||
system_admin_context = self.system_admin_context
|
||||
|
@ -18,6 +18,7 @@ from unittest import mock
|
||||
from oslo_config import cfg
|
||||
from oslo_config import fixture as config_fixture
|
||||
|
||||
from cinder.api import api_utils
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder import quota_utils
|
||||
@ -29,13 +30,6 @@ CONF = cfg.CONF
|
||||
|
||||
|
||||
class QuotaUtilsTest(test.TestCase):
|
||||
class FakeProject(object):
|
||||
def __init__(self, id='foo', parent_id=None):
|
||||
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()
|
||||
@ -49,7 +43,7 @@ class QuotaUtilsTest(test.TestCase):
|
||||
@mock.patch('keystoneauth1.session.Session')
|
||||
def test_keystone_client_instantiation(self, ksclient_session,
|
||||
ksclient_class):
|
||||
quota_utils._keystone_client(self.context)
|
||||
api_utils._keystone_client(self.context)
|
||||
ksclient_class.assert_called_once_with(auth_url=self.auth_url,
|
||||
session=ksclient_session(),
|
||||
version=(3, 0))
|
||||
@ -61,7 +55,7 @@ class QuotaUtilsTest(test.TestCase):
|
||||
self, ks_token, ksclient_session, ksclient_class):
|
||||
system_context = context.RequestContext(
|
||||
'fake_user', 'fake_proj_id', system_scope='all')
|
||||
quota_utils._keystone_client(system_context)
|
||||
api_utils._keystone_client(system_context)
|
||||
ks_token.assert_called_once_with(
|
||||
auth_url=self.auth_url, token=system_context.auth_token,
|
||||
system_scope=system_context.system_scope)
|
||||
@ -73,7 +67,7 @@ class QuotaUtilsTest(test.TestCase):
|
||||
self, ks_token, ksclient_session, ksclient_class):
|
||||
domain_context = context.RequestContext(
|
||||
'fake_user', 'fake_proj_id', domain_id='default')
|
||||
quota_utils._keystone_client(domain_context)
|
||||
api_utils._keystone_client(domain_context)
|
||||
ks_token.assert_called_once_with(
|
||||
auth_url=self.auth_url, token=domain_context.auth_token,
|
||||
domain_id=domain_context.domain_id)
|
||||
@ -85,51 +79,11 @@ class QuotaUtilsTest(test.TestCase):
|
||||
self, ks_token, ksclient_session, ksclient_class):
|
||||
project_context = context.RequestContext(
|
||||
'fake_user', project_id=fake.PROJECT_ID)
|
||||
quota_utils._keystone_client(project_context)
|
||||
api_utils._keystone_client(project_context)
|
||||
ks_token.assert_called_once_with(
|
||||
auth_url=self.auth_url, token=project_context.auth_token,
|
||||
project_id=project_context.project_id)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_project_keystoneclient_v2(self, ksclient_class):
|
||||
keystoneclient = ksclient_class.return_value
|
||||
keystoneclient.version = 'v2.0'
|
||||
expected_project = quota_utils.GenericProjectInfo(
|
||||
self.context.project_id, 'v2.0')
|
||||
project = quota_utils.get_project_hierarchy(
|
||||
self.context, self.context.project_id)
|
||||
self.assertEqual(expected_project.__dict__, project.__dict__)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_project_keystoneclient_v3(self, ksclient_class):
|
||||
keystoneclient = ksclient_class.return_value
|
||||
keystoneclient.version = 'v3'
|
||||
returned_project = self.FakeProject(self.context.project_id, 'bar')
|
||||
del returned_project.subtree
|
||||
keystoneclient.projects.get.return_value = returned_project
|
||||
expected_project = quota_utils.GenericProjectInfo(
|
||||
self.context.project_id, 'v3', 'bar', domain_id='default')
|
||||
project = quota_utils.get_project_hierarchy(
|
||||
self.context, self.context.project_id)
|
||||
self.assertEqual(expected_project.__dict__, project.__dict__)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_project_keystoneclient_v3_with_subtree(self, ksclient_class):
|
||||
keystoneclient = ksclient_class.return_value
|
||||
keystoneclient.version = 'v3'
|
||||
returned_project = self.FakeProject(self.context.project_id, 'bar')
|
||||
subtree_dict = {'baz': {'quux': None}}
|
||||
returned_project.subtree = subtree_dict
|
||||
keystoneclient.projects.get.return_value = returned_project
|
||||
expected_project = quota_utils.GenericProjectInfo(
|
||||
self.context.project_id, 'v3', 'bar', subtree_dict,
|
||||
domain_id='default')
|
||||
project = quota_utils.get_project_hierarchy(
|
||||
self.context, self.context.project_id, subtree_as_ids=True)
|
||||
keystoneclient.projects.get.assert_called_once_with(
|
||||
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
|
||||
@ -141,50 +95,6 @@ class QuotaUtilsTest(test.TestCase):
|
||||
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)
|
||||
|
||||
def _process_reserve_over_quota(self, overs, usages, quotas,
|
||||
expected_ex,
|
||||
resource='volumes'):
|
||||
|
@ -24,6 +24,7 @@ import webob.exc
|
||||
|
||||
import cinder
|
||||
from cinder.api import api_utils
|
||||
from cinder import context
|
||||
from cinder import exception
|
||||
from cinder.tests.unit import fake_constants as fake
|
||||
from cinder.tests.unit import test
|
||||
@ -1415,3 +1416,40 @@ class LimitOperationsTestCase(test.TestCase):
|
||||
mock_exec.assert_called_once_with(mocked_semaphore.__enter__)
|
||||
mocked_semaphore.__exit__.assert_not_called()
|
||||
mocked_semaphore.__exit__.assert_called_once_with(None, None, None)
|
||||
|
||||
|
||||
class TestKeystoneProjectGet(test.TestCase):
|
||||
class FakeProject(object):
|
||||
def __init__(self, id='foo', name=None):
|
||||
self.id = id
|
||||
self.name = name
|
||||
self.description = 'fake project description'
|
||||
self.domain_id = 'default'
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_project_keystoneclient_v2(self, ksclient_class):
|
||||
self.context = context.RequestContext('fake_user', 'fake_proj_id')
|
||||
keystoneclient = ksclient_class.return_value
|
||||
keystoneclient.version = 'v2.0'
|
||||
returned_project = self.FakeProject(self.context.project_id, 'bar')
|
||||
keystoneclient.projects.get.return_value = returned_project
|
||||
expected_project = api_utils.GenericProjectInfo(
|
||||
self.context.project_id, 'v2.0', domain_id='default', name='bar',
|
||||
description='fake project description')
|
||||
project = api_utils.get_project(
|
||||
self.context, self.context.project_id)
|
||||
self.assertEqual(expected_project.__dict__, project.__dict__)
|
||||
|
||||
@mock.patch('keystoneclient.client.Client')
|
||||
def test_get_project_keystoneclient_v3(self, ksclient_class):
|
||||
self.context = context.RequestContext('fake_user', 'fake_proj_id')
|
||||
keystoneclient = ksclient_class.return_value
|
||||
keystoneclient.version = 'v3'
|
||||
returned_project = self.FakeProject(self.context.project_id, 'bar')
|
||||
keystoneclient.projects.get.return_value = returned_project
|
||||
expected_project = api_utils.GenericProjectInfo(
|
||||
self.context.project_id, 'v3', domain_id='default', name='bar',
|
||||
description='fake project description')
|
||||
project = api_utils.get_project(
|
||||
self.context, self.context.project_id)
|
||||
self.assertEqual(expected_project.__dict__, project.__dict__)
|
||||
|
Loading…
Reference in New Issue
Block a user