Add project lookup utils
It is not special to specify a project of a resource. At now, find_project() is defined in OSC, but OSC plugins would like to consume it. The original idea is based on openstackclient.identity.common, but the proposed version is re-implemented on top of OpenStack SDK. sdk_connection is saved in the clientmanager in the new stype of OpenStack SDK connection and the commit assumes it. The feature depends on the OpenStack SDK patch which allows us to pass domain information to find_project(). Also adds add_project_owner_option_to_parser() to add CLI options (--project and --project-domain). It will help us make these options consistent across OSC and OSC plugins. Closes-Bug: #1632147 Depends-On: I60a8b3b83f6170b60d09c101b5c7035148283678 Change-Id: I8f59fa3f9b7c573485cd1572e5e9aae08f071e37
This commit is contained in:
parent
a8e71b0565
commit
deec32d7e0
72
osc_lib/cli/identity.py
Normal file
72
osc_lib/cli/identity.py
Normal file
@ -0,0 +1,72 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# 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 openstack import exceptions
|
||||
from openstack.identity.v3 import project
|
||||
|
||||
from osc_lib.i18n import _
|
||||
|
||||
|
||||
def add_project_owner_option_to_parser(parser):
|
||||
"""Register project and project domain options.
|
||||
|
||||
:param parser: argparse.Argument parser object.
|
||||
"""
|
||||
parser.add_argument(
|
||||
'--project',
|
||||
metavar='<project>',
|
||||
help=_("Owner's project (name or ID)")
|
||||
)
|
||||
parser.add_argument(
|
||||
'--project-domain',
|
||||
metavar='<project-domain>',
|
||||
help=_('Domain the project belongs to (name or ID). '
|
||||
'This can be used in case collisions between project names '
|
||||
'exist.'),
|
||||
)
|
||||
|
||||
|
||||
def find_project(sdk_connection, name_or_id, domain_name_or_id=None):
|
||||
"""Find a project by its name name or ID.
|
||||
|
||||
If Forbidden to find the resource (a common case if the user does not have
|
||||
permission), then return the resource by creating a local instance of
|
||||
openstack.identity.v3.Project resource.
|
||||
|
||||
:param sdk_connection: Connection object of OpenStack SDK.
|
||||
:type sdk_connection: `openstack.connection.Connection`
|
||||
:param name_or_id: Name or ID of the project
|
||||
:type name_or_id: string
|
||||
:param domain_name_or_id: Domain name or ID of the project.
|
||||
This can be used when there are multiple projects with a same name.
|
||||
:returns: the project object found
|
||||
:rtype: `openstack.identity.v3.project.Project`
|
||||
|
||||
"""
|
||||
try:
|
||||
if domain_name_or_id:
|
||||
domain = sdk_connection.identity.find_domain(domain_name_or_id,
|
||||
ignore_missing=False)
|
||||
domain_id = domain.id
|
||||
else:
|
||||
domain_id = None
|
||||
return sdk_connection.identity.find_project(name_or_id,
|
||||
ignore_missing=False,
|
||||
domain_id=domain_id)
|
||||
# NOTE: OpenStack SDK raises HttpException for 403 response code.
|
||||
# There is no specific exception class at now, so we need to catch
|
||||
# HttpException and check the status code.
|
||||
except exceptions.HttpException as e:
|
||||
if e.status_code == 403:
|
||||
return project.Project(id=name_or_id, name=name_or_id)
|
||||
raise
|
83
osc_lib/tests/cli/test_identity.py
Normal file
83
osc_lib/tests/cli/test_identity.py
Normal file
@ -0,0 +1,83 @@
|
||||
# Licensed under the Apache License, Version 2.0 (the "License"); you may
|
||||
# not use this file except in compliance with the License. You may obtain
|
||||
# a copy of the License at
|
||||
#
|
||||
# http://www.apache.org/licenses/LICENSE-2.0
|
||||
#
|
||||
# Unless required by applicable law or agreed to in writing, software
|
||||
# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
|
||||
# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
|
||||
# License for the specific language governing permissions and limitations
|
||||
# under the License.
|
||||
|
||||
import argparse
|
||||
|
||||
import mock
|
||||
from openstack import exceptions
|
||||
from openstack.identity.v3 import project
|
||||
import testtools
|
||||
|
||||
from osc_lib.cli import identity as cli_identity
|
||||
from osc_lib.tests import utils as test_utils
|
||||
|
||||
|
||||
class IdentityUtilsTestCase(test_utils.TestCase):
|
||||
|
||||
def test_add_project_owner_option_to_parser(self):
|
||||
parser = argparse.ArgumentParser()
|
||||
cli_identity.add_project_owner_option_to_parser(parser)
|
||||
parsed_args = parser.parse_args(['--project', 'project1',
|
||||
'--project-domain', 'domain1'])
|
||||
self.assertEqual('project1', parsed_args.project)
|
||||
self.assertEqual('domain1', parsed_args.project_domain)
|
||||
|
||||
def test_find_project(self):
|
||||
sdk_connection = mock.Mock()
|
||||
sdk_find_project = sdk_connection.identity.find_project
|
||||
sdk_find_project.return_value = mock.sentinel.project1
|
||||
|
||||
ret = cli_identity.find_project(sdk_connection, 'project1')
|
||||
self.assertEqual(mock.sentinel.project1, ret)
|
||||
sdk_find_project.assert_called_once_with(
|
||||
'project1', ignore_missing=False, domain_id=None)
|
||||
|
||||
def test_find_project_with_domain(self):
|
||||
domain1 = mock.Mock()
|
||||
domain1.id = 'id-domain1'
|
||||
|
||||
sdk_connection = mock.Mock()
|
||||
sdk_find_domain = sdk_connection.identity.find_domain
|
||||
sdk_find_domain.return_value = domain1
|
||||
sdk_find_project = sdk_connection.identity.find_project
|
||||
sdk_find_project.return_value = mock.sentinel.project1
|
||||
|
||||
ret = cli_identity.find_project(sdk_connection, 'project1', 'domain1')
|
||||
self.assertEqual(mock.sentinel.project1, ret)
|
||||
sdk_find_domain.assert_called_once_with(
|
||||
'domain1', ignore_missing=False)
|
||||
sdk_find_project.assert_called_once_with(
|
||||
'project1', ignore_missing=False, domain_id='id-domain1')
|
||||
|
||||
def test_find_project_with_forbidden_exception(self):
|
||||
sdk_connection = mock.Mock()
|
||||
sdk_find_project = sdk_connection.identity.find_project
|
||||
exc = exceptions.HttpException()
|
||||
exc.status_code = 403
|
||||
sdk_find_project.side_effect = exc
|
||||
|
||||
ret = cli_identity.find_project(sdk_connection, 'project1')
|
||||
|
||||
self.assertIsInstance(ret, project.Project)
|
||||
self.assertEqual('project1', ret.id)
|
||||
self.assertEqual('project1', ret.name)
|
||||
|
||||
def test_find_project_with_generic_exception(self):
|
||||
sdk_connection = mock.Mock()
|
||||
sdk_find_project = sdk_connection.identity.find_project
|
||||
exc = exceptions.HttpException()
|
||||
# Some value other than 403.
|
||||
exc.status_code = 499
|
||||
sdk_find_project.side_effect = exc
|
||||
|
||||
with testtools.ExpectedException(exceptions.HttpException):
|
||||
cli_identity.find_project(sdk_connection, 'project1')
|
13
releasenotes/notes/find-project-203bf867619c557e.yaml
Normal file
13
releasenotes/notes/find-project-203bf867619c557e.yaml
Normal file
@ -0,0 +1,13 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Adds ``osc_lib.cli.identity.find_project()``. This function can be
|
||||
used to look up a project ID from command-line options like:
|
||||
|
||||
.. code-block:: python
|
||||
|
||||
find_project(self.app.client_manager.sdk_connection,
|
||||
parsed_args.project, parsed_args.project_domain)
|
||||
- |
|
||||
Adds ``osc_lib.cli.identity.add_project_owner_option_to_parser()``
|
||||
to register project and project domain options to CLI.
|
Loading…
Reference in New Issue
Block a user