Merge "Add support for endpoing filter commands"
This commit is contained in:
commit
21b656ca4a
@ -4,6 +4,34 @@ endpoint
|
||||
|
||||
Identity v2, v3
|
||||
|
||||
endpoint add project
|
||||
--------------------
|
||||
|
||||
Associate a project to and endpoint for endpoint filtering
|
||||
|
||||
.. program:: endpoint add project
|
||||
.. code:: bash
|
||||
|
||||
openstack endpoint add project
|
||||
[--project-domain <project-domain>]
|
||||
<endpoint>
|
||||
<project>
|
||||
|
||||
.. option:: --project-domain <project-domain>
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
.. _endpoint_add_project-endpoint:
|
||||
.. describe:: <endpoint>
|
||||
|
||||
Endpoint to associate with specified project (name or ID)
|
||||
|
||||
.. _endpoint_add_project-project:
|
||||
.. describe:: <project>
|
||||
|
||||
Project to associate with specified endpoint (name or ID)
|
||||
|
||||
endpoint create
|
||||
---------------
|
||||
|
||||
@ -107,6 +135,8 @@ List endpoints
|
||||
[--interface <interface>]
|
||||
[--region <region-id>]
|
||||
[--long]
|
||||
[--endpoint <endpoint> |
|
||||
--project <project> [--project-domain <project-domain>]]
|
||||
|
||||
.. option:: --service <service>
|
||||
|
||||
@ -132,6 +162,55 @@ List endpoints
|
||||
|
||||
*Identity version 2 only*
|
||||
|
||||
.. option:: --endpoint
|
||||
|
||||
List projects that have access to that endpoint using
|
||||
endpoint filtering
|
||||
|
||||
*Identity version 3 only*
|
||||
|
||||
.. option:: --project
|
||||
|
||||
List endpoints available for the project using
|
||||
endpoint filtering
|
||||
|
||||
*Identity version 3 only*
|
||||
|
||||
.. option:: --project-domain
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
*Identity version 3 only*
|
||||
|
||||
endpoint remove project
|
||||
-----------------------
|
||||
|
||||
Dissociate a project from an endpoint.
|
||||
|
||||
.. program:: endpoint remove project
|
||||
.. code:: bash
|
||||
|
||||
openstack endpoint remove project
|
||||
[--project-domain <project-domain>]
|
||||
<endpoint>
|
||||
<project>
|
||||
|
||||
.. option:: --project-domain <project-domain>
|
||||
|
||||
Domain the project belongs to (name or ID).
|
||||
This can be used in case collisions between project names exist.
|
||||
|
||||
.. _endpoint_remove_project-endpoint:
|
||||
.. describe:: <endpoint>
|
||||
|
||||
Endpoint to dissociate with specified project (name or ID)
|
||||
|
||||
.. _endpoint_remove_project-project:
|
||||
.. describe:: <project>
|
||||
|
||||
Project to dissociate with specified endpoint (name or ID)
|
||||
|
||||
endpoint set
|
||||
------------
|
||||
|
||||
|
@ -36,6 +36,42 @@ def get_service_name(service):
|
||||
return ''
|
||||
|
||||
|
||||
class AddProjectToEndpoint(command.Command):
|
||||
_description = _("Associate a project to an endpoint")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(
|
||||
AddProjectToEndpoint, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'endpoint',
|
||||
metavar='<endpoint>',
|
||||
help=_('Endpoint to associate with '
|
||||
'specified project (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'project',
|
||||
metavar='<project>',
|
||||
help=_('Project to associate with '
|
||||
'specified endpoint name or ID)'),
|
||||
)
|
||||
common.add_project_domain_option_to_parser(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.identity
|
||||
|
||||
endpoint = utils.find_resource(client.endpoints,
|
||||
parsed_args.endpoint)
|
||||
|
||||
project = common.find_project(client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain)
|
||||
|
||||
client.endpoint_filter.add_endpoint_to_project(
|
||||
project=project.id,
|
||||
endpoint=endpoint.id)
|
||||
|
||||
|
||||
class CreateEndpoint(command.ShowOne):
|
||||
_description = _("Create new endpoint")
|
||||
|
||||
@ -152,27 +188,68 @@ class ListEndpoint(command.Lister):
|
||||
metavar='<region-id>',
|
||||
help=_('Filter by region ID'),
|
||||
)
|
||||
list_group = parser.add_mutually_exclusive_group()
|
||||
list_group.add_argument(
|
||||
'--endpoint',
|
||||
metavar='<endpoint-group>',
|
||||
help=_('Endpoint to list filters'),
|
||||
)
|
||||
list_group.add_argument(
|
||||
'--project',
|
||||
metavar='<project>',
|
||||
help=_('Project to list filters (name or ID)'),
|
||||
)
|
||||
common.add_project_domain_option_to_parser(list_group)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
identity_client = self.app.client_manager.identity
|
||||
columns = ('ID', 'Region', 'Service Name', 'Service Type',
|
||||
'Enabled', 'Interface', 'URL')
|
||||
kwargs = {}
|
||||
if parsed_args.service:
|
||||
service = common.find_service(identity_client, parsed_args.service)
|
||||
kwargs['service'] = service.id
|
||||
if parsed_args.interface:
|
||||
kwargs['interface'] = parsed_args.interface
|
||||
if parsed_args.region:
|
||||
kwargs['region'] = parsed_args.region
|
||||
data = identity_client.endpoints.list(**kwargs)
|
||||
service_list = identity_client.services.list()
|
||||
|
||||
for ep in data:
|
||||
service = common.find_service_in_list(service_list, ep.service_id)
|
||||
ep.service_name = get_service_name(service)
|
||||
ep.service_type = service.type
|
||||
endpoint = None
|
||||
if parsed_args.endpoint:
|
||||
endpoint = utils.find_resource(identity_client.endpoints,
|
||||
parsed_args.endpoint)
|
||||
project = None
|
||||
if parsed_args.project:
|
||||
project = common.find_project(identity_client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain)
|
||||
|
||||
if endpoint:
|
||||
columns = ('ID', 'Name')
|
||||
data = (
|
||||
identity_client.endpoint_filter
|
||||
.list_projects_for_endpoint(endpoint=endpoint.id)
|
||||
)
|
||||
else:
|
||||
columns = ('ID', 'Region', 'Service Name', 'Service Type',
|
||||
'Enabled', 'Interface', 'URL')
|
||||
kwargs = {}
|
||||
if parsed_args.service:
|
||||
service = common.find_service(identity_client,
|
||||
parsed_args.service)
|
||||
kwargs['service'] = service.id
|
||||
if parsed_args.interface:
|
||||
kwargs['interface'] = parsed_args.interface
|
||||
if parsed_args.region:
|
||||
kwargs['region'] = parsed_args.region
|
||||
|
||||
if project:
|
||||
data = (
|
||||
identity_client.endpoint_filter
|
||||
.list_endpoints_for_project(project=project.id)
|
||||
)
|
||||
else:
|
||||
data = identity_client.endpoints.list(**kwargs)
|
||||
|
||||
service_list = identity_client.services.list()
|
||||
|
||||
for ep in data:
|
||||
service = common.find_service_in_list(service_list,
|
||||
ep.service_id)
|
||||
ep.service_name = get_service_name(service)
|
||||
ep.service_type = service.type
|
||||
|
||||
return (columns,
|
||||
(utils.get_item_properties(
|
||||
s, columns,
|
||||
@ -180,6 +257,42 @@ class ListEndpoint(command.Lister):
|
||||
) for s in data))
|
||||
|
||||
|
||||
class RemoveProjectFromEndpoint(command.Command):
|
||||
_description = _("Dissociate a project from an endpoint")
|
||||
|
||||
def get_parser(self, prog_name):
|
||||
parser = super(
|
||||
RemoveProjectFromEndpoint, self).get_parser(prog_name)
|
||||
parser.add_argument(
|
||||
'endpoint',
|
||||
metavar='<endpoint>',
|
||||
help=_('Endpoint to dissociate from '
|
||||
'specified project (name or ID)'),
|
||||
)
|
||||
parser.add_argument(
|
||||
'project',
|
||||
metavar='<project>',
|
||||
help=_('Project to dissociate from '
|
||||
'specified endpoint name or ID)'),
|
||||
)
|
||||
common.add_project_domain_option_to_parser(parser)
|
||||
return parser
|
||||
|
||||
def take_action(self, parsed_args):
|
||||
client = self.app.client_manager.identity
|
||||
|
||||
endpoint = utils.find_resource(client.endpoints,
|
||||
parsed_args.endpoint)
|
||||
|
||||
project = common.find_project(client,
|
||||
parsed_args.project,
|
||||
parsed_args.project_domain)
|
||||
|
||||
client.endpoint_filter.delete_endpoint_from_project(
|
||||
project=project.id,
|
||||
endpoint=endpoint.id)
|
||||
|
||||
|
||||
class SetEndpoint(command.Command):
|
||||
_description = _("Set endpoint properties")
|
||||
|
||||
|
@ -42,6 +42,7 @@ class IdentityTests(base.TestCase):
|
||||
REGION_LIST_HEADERS = ['Region', 'Parent Region', 'Description']
|
||||
ENDPOINT_LIST_HEADERS = ['ID', 'Region', 'Service Name', 'Service Type',
|
||||
'Enabled', 'Interface', 'URL']
|
||||
ENDPOINT_LIST_PROJECT_HEADERS = ['ID', 'Name']
|
||||
|
||||
IDENTITY_PROVIDER_FIELDS = ['description', 'enabled', 'id', 'remote_ids',
|
||||
'domain_id']
|
||||
|
@ -42,6 +42,29 @@ class EndpointTests(common.IdentityTests):
|
||||
items = self.parse_listing(raw_output)
|
||||
self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
|
||||
|
||||
def test_endpoint_list_filter(self):
|
||||
endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
|
||||
project_id = self._create_dummy_project(add_clean_up=False)
|
||||
raw_output = self.openstack(
|
||||
'endpoint add project '
|
||||
'%(endpoint_id)s '
|
||||
'%(project_id)s' % {
|
||||
'project_id': project_id,
|
||||
'endpoint_id': endpoint_id})
|
||||
self.assertEqual(0, len(raw_output))
|
||||
raw_output = self.openstack(
|
||||
'endpoint list --endpoint %s' % endpoint_id)
|
||||
self.assertIn(project_id, raw_output)
|
||||
items = self.parse_listing(raw_output)
|
||||
self.assert_table_structure(items,
|
||||
self.ENDPOINT_LIST_PROJECT_HEADERS)
|
||||
|
||||
raw_output = self.openstack(
|
||||
'endpoint list --project %s' % project_id)
|
||||
self.assertIn(endpoint_id, raw_output)
|
||||
items = self.parse_listing(raw_output)
|
||||
self.assert_table_structure(items, self.ENDPOINT_LIST_HEADERS)
|
||||
|
||||
def test_endpoint_set(self):
|
||||
endpoint_id = self._create_dummy_endpoint()
|
||||
new_endpoint_url = data_utils.rand_url()
|
||||
@ -65,3 +88,22 @@ class EndpointTests(common.IdentityTests):
|
||||
raw_output = self.openstack('endpoint show %s' % endpoint_id)
|
||||
items = self.parse_show(raw_output)
|
||||
self.assert_show_fields(items, self.ENDPOINT_FIELDS)
|
||||
|
||||
def test_endpoint_add_remove_project(self):
|
||||
endpoint_id = self._create_dummy_endpoint(add_clean_up=False)
|
||||
project_id = self._create_dummy_project(add_clean_up=False)
|
||||
raw_output = self.openstack(
|
||||
'endpoint add project '
|
||||
'%(endpoint_id)s '
|
||||
'%(project_id)s' % {
|
||||
'project_id': project_id,
|
||||
'endpoint_id': endpoint_id})
|
||||
self.assertEqual(0, len(raw_output))
|
||||
|
||||
raw_output = self.openstack(
|
||||
'endpoint remove project '
|
||||
'%(endpoint_id)s '
|
||||
'%(project_id)s' % {
|
||||
'project_id': project_id,
|
||||
'endpoint_id': endpoint_id})
|
||||
self.assertEqual(0, len(raw_output))
|
||||
|
@ -493,6 +493,8 @@ class FakeIdentityv3Client(object):
|
||||
self.credentials.resource_class = fakes.FakeResource(None, {})
|
||||
self.endpoints = mock.Mock()
|
||||
self.endpoints.resource_class = fakes.FakeResource(None, {})
|
||||
self.endpoint_filter = mock.Mock()
|
||||
self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
|
||||
self.groups = mock.Mock()
|
||||
self.groups.resource_class = fakes.FakeResource(None, {})
|
||||
self.oauth1 = mock.Mock()
|
||||
@ -911,6 +913,31 @@ class FakeEndpoint(object):
|
||||
loaded=True)
|
||||
return endpoint
|
||||
|
||||
@staticmethod
|
||||
def create_one_endpoint_filter(attrs=None):
|
||||
"""Create a fake endpoint project relationship.
|
||||
|
||||
:param Dictionary attrs:
|
||||
A dictionary with all attributes of endpoint filter
|
||||
:return:
|
||||
A FakeResource object with project, endpoint and so on
|
||||
"""
|
||||
attrs = attrs or {}
|
||||
|
||||
# Set default attribute
|
||||
endpoint_filter_info = {
|
||||
'project': 'project-id-' + uuid.uuid4().hex,
|
||||
'endpoint': 'endpoint-id-' + uuid.uuid4().hex,
|
||||
}
|
||||
|
||||
# Overwrite default attributes if there are some attributes set
|
||||
endpoint_filter_info.update(attrs)
|
||||
|
||||
endpoint_filter = fakes.FakeModel(
|
||||
copy.deepcopy(endpoint_filter_info))
|
||||
|
||||
return endpoint_filter
|
||||
|
||||
|
||||
class FakeService(object):
|
||||
"""Fake one or more service."""
|
||||
|
@ -22,11 +22,23 @@ class TestEndpoint(identity_fakes.TestIdentityv3):
|
||||
# Get a shortcut to the EndpointManager Mock
|
||||
self.endpoints_mock = self.app.client_manager.identity.endpoints
|
||||
self.endpoints_mock.reset_mock()
|
||||
self.ep_filter_mock = (
|
||||
self.app.client_manager.identity.endpoint_filter
|
||||
)
|
||||
self.ep_filter_mock.reset_mock()
|
||||
|
||||
# Get a shortcut to the ServiceManager Mock
|
||||
self.services_mock = self.app.client_manager.identity.services
|
||||
self.services_mock.reset_mock()
|
||||
|
||||
# Get a shortcut to the DomainManager Mock
|
||||
self.domains_mock = self.app.client_manager.identity.domains
|
||||
self.domains_mock.reset_mock()
|
||||
|
||||
# Get a shortcut to the ProjectManager Mock
|
||||
self.projects_mock = self.app.client_manager.identity.projects
|
||||
self.projects_mock.reset_mock()
|
||||
|
||||
|
||||
class TestEndpointCreate(TestEndpoint):
|
||||
|
||||
@ -750,3 +762,130 @@ class TestEndpointShowServiceWithoutName(TestEndpointShow):
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = endpoint.ShowEndpoint(self.app, None)
|
||||
|
||||
|
||||
class TestAddProjectToEndpoint(TestEndpoint):
|
||||
|
||||
project = identity_fakes.FakeProject.create_one_project()
|
||||
domain = identity_fakes.FakeDomain.create_one_domain()
|
||||
service = identity_fakes.FakeService.create_one_service()
|
||||
endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(
|
||||
attrs={'service_id': service.id})
|
||||
|
||||
new_ep_filter = identity_fakes.FakeEndpoint.create_one_endpoint_filter(
|
||||
attrs={'endpoint': endpoint.id,
|
||||
'project': project.id}
|
||||
)
|
||||
|
||||
def setUp(self):
|
||||
super(TestAddProjectToEndpoint, self).setUp()
|
||||
|
||||
# This is the return value for utils.find_resource()
|
||||
self.endpoints_mock.get.return_value = self.endpoint
|
||||
|
||||
# Update the image_id in the MEMBER dict
|
||||
self.ep_filter_mock.create.return_value = self.new_ep_filter
|
||||
self.projects_mock.get.return_value = self.project
|
||||
self.domains_mock.get.return_value = self.domain
|
||||
# Get the command object to test
|
||||
self.cmd = endpoint.AddProjectToEndpoint(self.app, None)
|
||||
|
||||
def test_add_project_to_endpoint_no_option(self):
|
||||
arglist = [
|
||||
self.endpoint.id,
|
||||
self.project.id,
|
||||
]
|
||||
verifylist = [
|
||||
('endpoint', self.endpoint.id),
|
||||
('project', self.project.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.ep_filter_mock.add_endpoint_to_project.assert_called_with(
|
||||
project=self.project.id,
|
||||
endpoint=self.endpoint.id
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_add_project_to_endpoint_with_option(self):
|
||||
arglist = [
|
||||
self.endpoint.id,
|
||||
self.project.id,
|
||||
'--project-domain', self.domain.id,
|
||||
]
|
||||
verifylist = [
|
||||
('endpoint', self.endpoint.id),
|
||||
('project', self.project.id),
|
||||
('project_domain', self.domain.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
self.ep_filter_mock.add_endpoint_to_project.assert_called_with(
|
||||
project=self.project.id,
|
||||
endpoint=self.endpoint.id
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
|
||||
class TestRemoveProjectEndpoint(TestEndpoint):
|
||||
|
||||
project = identity_fakes.FakeProject.create_one_project()
|
||||
domain = identity_fakes.FakeDomain.create_one_domain()
|
||||
service = identity_fakes.FakeService.create_one_service()
|
||||
endpoint = identity_fakes.FakeEndpoint.create_one_endpoint(
|
||||
attrs={'service_id': service.id})
|
||||
|
||||
def setUp(self):
|
||||
super(TestRemoveProjectEndpoint, self).setUp()
|
||||
|
||||
# This is the return value for utils.find_resource()
|
||||
self.endpoints_mock.get.return_value = self.endpoint
|
||||
|
||||
self.projects_mock.get.return_value = self.project
|
||||
self.domains_mock.get.return_value = self.domain
|
||||
self.ep_filter_mock.delete.return_value = None
|
||||
|
||||
# Get the command object to test
|
||||
self.cmd = endpoint.RemoveProjectFromEndpoint(self.app, None)
|
||||
|
||||
def test_remove_project_endpoint_no_options(self):
|
||||
arglist = [
|
||||
self.endpoint.id,
|
||||
self.project.id,
|
||||
]
|
||||
verifylist = [
|
||||
('endpoint', self.endpoint.id),
|
||||
('project', self.project.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.ep_filter_mock.delete_endpoint_from_project.assert_called_with(
|
||||
project=self.project.id,
|
||||
endpoint=self.endpoint.id,
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
||||
def test_remove_project_endpoint_with_options(self):
|
||||
arglist = [
|
||||
self.endpoint.id,
|
||||
self.project.id,
|
||||
'--project-domain', self.domain.id,
|
||||
]
|
||||
verifylist = [
|
||||
('endpoint', self.endpoint.id),
|
||||
('project', self.project.id),
|
||||
('project_domain', self.domain.id),
|
||||
]
|
||||
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
|
||||
|
||||
result = self.cmd.take_action(parsed_args)
|
||||
|
||||
self.ep_filter_mock.delete_endpoint_from_project.assert_called_with(
|
||||
project=self.project.id,
|
||||
endpoint=self.endpoint.id,
|
||||
)
|
||||
self.assertIsNone(result)
|
||||
|
@ -0,0 +1,5 @@
|
||||
---
|
||||
features:
|
||||
- |
|
||||
Add ``endpoint add project``, ``endpoint remove project`` and ``endpoint
|
||||
list`` commands to manage endpoint filters in identity v3.
|
@ -228,11 +228,13 @@ openstack.identity.v3 =
|
||||
ec2_credentials_list = openstackclient.identity.v3.ec2creds:ListEC2Creds
|
||||
ec2_credentials_show = openstackclient.identity.v3.ec2creds:ShowEC2Creds
|
||||
|
||||
endpoint_add_project = openstackclient.identity.v3.endpoint:AddProjectToEndpoint
|
||||
endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint
|
||||
endpoint_delete = openstackclient.identity.v3.endpoint:DeleteEndpoint
|
||||
endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint
|
||||
endpoint_remove_project = openstackclient.identity.v3.endpoint:RemoveProjectFromEndpoint
|
||||
endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint
|
||||
endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint
|
||||
endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint
|
||||
|
||||
group_add_user = openstackclient.identity.v3.group:AddUserToGroup
|
||||
group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup
|
||||
|
Loading…
Reference in New Issue
Block a user