Add support for endpoint group commands

Implements the commands for endpoint group filter management.
Includes the CRUD management of the endpoint groups and the
association management between them and the projects that are
using this method.

Implements: blueprint keystone-endpoint-filter
Change-Id: I4265f7f8598d028191e90d76781b7b6ece6fef64
This commit is contained in:
Jose Castro Leon 2017-10-25 15:39:44 +02:00 committed by Dean Troyer
parent 7505831e81
commit 1eae301c4f
6 changed files with 384 additions and 0 deletions

View File

@ -0,0 +1,28 @@
==============
endpoint group
==============
A **endpoint group** is used to create groups of endpoints that then
can be used to filter the endpoints that are available to a project.
Applicable to Identity v3
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group add project
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group create
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group delete
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group list
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group remove project
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group set
.. autoprogram-cliff:: openstack.identity.v3
:command: endpoint group show

View File

@ -91,6 +91,7 @@ referring to both Compute and Volume quotas.
* ``domain``: (**Identity**) a grouping of projects
* ``ec2 credentials``: (**Identity**) AWS EC2-compatible credentials
* ``endpoint``: (**Identity**) the base URL used to contact a specific service
* ``endpoint group``: (**Identity**) group endpoints to be used as filters
* ``extension``: (**Compute**, **Identity**, **Network**, **Volume**) OpenStack server API extensions
* ``federation protocol``: (**Identity**) the underlying protocol used while federating identities
* ``flavor``: (**Compute**) predefined server configurations: ram, root disk and so on

View File

@ -0,0 +1,324 @@
# 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.
#
"""Identity v3 Endpoint Group action implementations"""
import json
import logging
from osc_lib.command import command
from osc_lib import exceptions
from osc_lib import utils
import six
from openstackclient.i18n import _
from openstackclient.identity import common
LOG = logging.getLogger(__name__)
class _FiltersReader(object):
_description = _("Helper class capable of reading filters from files")
def _read_filters(self, path):
"""Read and parse rules from path
Expect the file to contain a valid JSON structure.
:param path: path to the file
:return: loaded and valid dictionary with filters
:raises exception.CommandError: In case the file cannot be
accessed or the content is not a valid JSON.
Example of the content of the file:
{
"interface": "admin",
"service_id": "1b501a"
}
"""
blob = utils.read_blob_file_contents(path)
try:
rules = json.loads(blob)
except ValueError as e:
msg = _("An error occurred when reading filters from file "
"%(path)s: %(error)s") % {"path": path, "error": e}
raise exceptions.CommandError(msg)
else:
return rules
class AddProjectToEndpointGroup(command.Command):
_description = _("Add a project to an endpoint group")
def get_parser(self, prog_name):
parser = super(
AddProjectToEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'endpointgroup',
metavar='<endpoint-group>',
help=_('Endpoint group (name or ID)'),
)
parser.add_argument(
'project',
metavar='<project>',
help=_('Project to associate (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
endpointgroup = utils.find_resource(client.endpoint_groups,
parsed_args.endpointgroup)
project = common.find_project(client,
parsed_args.project,
parsed_args.project_domain)
client.endpoint_filter.add_endpoint_group_to_project(
endpoint_group=endpointgroup.id,
project=project.id)
class CreateEndpointGroup(command.ShowOne, _FiltersReader):
_description = _("Create new endpoint group")
def get_parser(self, prog_name):
parser = super(CreateEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<name>',
help=_('Name of the endpoint group'),
)
parser.add_argument(
'filters',
metavar='<filename>',
help=_('Filename that contains a new set of filters'),
)
parser.add_argument(
'--description',
help=_('Description of the endpoint group'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
filters = None
if parsed_args.filters:
filters = self._read_filters(parsed_args.filters)
endpoint_group = identity_client.endpoint_groups.create(
name=parsed_args.name,
filters=filters,
description=parsed_args.description
)
info = {}
endpoint_group._info.pop('links')
info.update(endpoint_group._info)
return zip(*sorted(six.iteritems(info)))
class DeleteEndpointGroup(command.Command):
_description = _("Delete endpoint group(s)")
def get_parser(self, prog_name):
parser = super(DeleteEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'endpointgroup',
metavar='<endpoint-group>',
nargs='+',
help=_('Endpoint group(s) to delete (name or ID)'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
result = 0
for i in parsed_args.endpointgroup:
try:
endpoint_id = utils.find_resource(
identity_client.endpoint_groups, i).id
identity_client.endpoint_groups.delete(endpoint_id)
except Exception as e:
result += 1
LOG.error(_("Failed to delete endpoint group with "
"ID '%(endpointgroup)s': %(e)s"),
{'endpointgroup': i, 'e': e})
if result > 0:
total = len(parsed_args.endpointgroup)
msg = (_("%(result)s of %(total)s endpointgroups failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListEndpointGroup(command.Lister):
_description = _("List endpoint groups")
def get_parser(self, prog_name):
parser = super(ListEndpointGroup, self).get_parser(prog_name)
list_group = parser.add_mutually_exclusive_group()
list_group.add_argument(
'--endpointgroup',
metavar='<endpoint-group>',
help=_('Endpoint Group (name or ID)'),
)
list_group.add_argument(
'--project',
metavar='<project>',
help=_('Project (name or ID)'),
)
parser.add_argument(
'--domain',
metavar='<domain>',
help=_('Domain owning <project> (name or ID)'),
)
return parser
def take_action(self, parsed_args):
client = self.app.client_manager.identity
endpointgroup = None
if parsed_args.endpointgroup:
endpointgroup = utils.find_resource(client.endpoint_groups,
parsed_args.endpointgroup)
project = None
if parsed_args.project:
project = common.find_project(client,
parsed_args.project,
parsed_args.domain)
if endpointgroup:
# List projects associated to the endpoint group
columns = ('ID', 'Name')
data = client.endpoint_filter.list_projects_for_endpoint_group(
endpoint_group=endpointgroup.id)
elif project:
columns = ('ID', 'Name')
data = client.endpoint_filter.list_endpoint_groups_for_project(
project=project.id)
else:
columns = ('ID', 'Name', 'Description')
data = client.endpoint_groups.list()
return (columns,
(utils.get_item_properties(
s, columns,
formatters={},
) for s in data))
class RemoveProjectFromEndpointGroup(command.Command):
_description = _("Remove project from endpoint group")
def get_parser(self, prog_name):
parser = super(
RemoveProjectFromEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'endpointgroup',
metavar='<endpoint-group>',
help=_('Endpoint group (name or ID)'),
)
parser.add_argument(
'project',
metavar='<project>',
help=_('Project to remove (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
endpointgroup = utils.find_resource(client.endpoint_groups,
parsed_args.endpointgroup)
project = common.find_project(client,
parsed_args.project,
parsed_args.project_domain)
client.endpoint_filter.delete_endpoint_group_to_project(
endpoint_group=endpointgroup.id,
project=project.id)
class SetEndpointGroup(command.Command, _FiltersReader):
_description = _("Set endpoint group properties")
def get_parser(self, prog_name):
parser = super(SetEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'endpointgroup',
metavar='<endpoint-group>',
help=_('Endpoint Group to modify (name or ID)'),
)
parser.add_argument(
'--name',
metavar='<name>',
help=_('New enpoint group name'),
)
parser.add_argument(
'--filters',
metavar='<filename>',
help=_('Filename that contains a new set of filters'),
)
parser.add_argument(
'--description',
metavar='<description>',
default='',
help=_('New endpoint group description'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
endpointgroup = utils.find_resource(identity_client.endpoint_groups,
parsed_args.endpointgroup)
filters = None
if parsed_args.filters:
filters = self._read_filters(parsed_args.filters)
identity_client.endpoint_groups.update(
endpointgroup.id,
name=parsed_args.name,
filters=filters,
description=parsed_args.description
)
class ShowEndpointGroup(command.ShowOne):
_description = _("Display endpoint group details")
def get_parser(self, prog_name):
parser = super(ShowEndpointGroup, self).get_parser(prog_name)
parser.add_argument(
'endpointgroup',
metavar='<endpointgroup>',
help=_('Endpoint group (name or ID)'),
)
return parser
def take_action(self, parsed_args):
identity_client = self.app.client_manager.identity
endpoint_group = utils.find_resource(identity_client.endpoint_groups,
parsed_args.endpointgroup)
info = {}
endpoint_group._info.pop('links')
info.update(endpoint_group._info)
return zip(*sorted(six.iteritems(info)))

View File

@ -223,6 +223,20 @@ ENDPOINT = {
'links': base_url + 'endpoints/' + endpoint_id,
}
endpoint_group_id = 'eg-123'
endpoint_group_description = 'eg 123 description'
endpoint_group_filters = {
'service_id': service_id,
'region_id': endpoint_region,
}
ENDPOINT_GROUP = {
'id': endpoint_group_id,
'filters': endpoint_group_filters,
'description': endpoint_group_description,
'links': base_url + 'endpoint_groups/' + endpoint_group_id,
}
user_id = 'bbbbbbb-aaaa-aaaa-aaaa-bbbbbbbaaaa'
user_name = 'paul'
user_description = 'Sir Paul'
@ -495,6 +509,8 @@ class FakeIdentityv3Client(object):
self.endpoints.resource_class = fakes.FakeResource(None, {})
self.endpoint_filter = mock.Mock()
self.endpoint_filter.resource_class = fakes.FakeResource(None, {})
self.endpoint_groups = mock.Mock()
self.endpoint_groups.resource_class = fakes.FakeResource(None, {})
self.groups = mock.Mock()
self.groups.resource_class = fakes.FakeResource(None, {})
self.oauth1 = mock.Mock()

View File

@ -0,0 +1,7 @@
---
features:
- |
Add endpoint group commands: ``endpoint group add project``, ``endpoint group create``,
``endpoint group delete``, ``endpoint group list``, ``endpoint group remove project``,
``endpoint group set`` and ``endpoint group show``.
[Blueprint `keystone-endpoint-filter <https://blueprints.launchpad.net/python-openstackclient/+spec/keystone-endpoint-filter>`_]

View File

@ -236,6 +236,14 @@ openstack.identity.v3 =
endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint
endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint
endpoint_group_add_project = openstackclient.identity.v3.endpoint_group:AddProjectToEndpointGroup
endpoint_group_create = openstackclient.identity.v3.endpoint_group:CreateEndpointGroup
endpoint_group_delete = openstackclient.identity.v3.endpoint_group:DeleteEndpointGroup
endpoint_group_list = openstackclient.identity.v3.endpoint_group:ListEndpointGroup
endpoint_group_remove_project = openstackclient.identity.v3.endpoint_group:RemoveProjectFromEndpointGroup
endpoint_group_set = openstackclient.identity.v3.endpoint_group:SetEndpointGroup
endpoint_group_show = openstackclient.identity.v3.endpoint_group:ShowEndpointGroup
group_add_user = openstackclient.identity.v3.group:AddUserToGroup
group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup
group_create = openstackclient.identity.v3.group:CreateGroup