From 85f5ec262c1b996a8a096719f432f9cedcf560ba Mon Sep 17 00:00:00 2001 From: Juan Antonio Osorio Robles Date: Mon, 6 Jul 2015 16:59:43 +0300 Subject: [PATCH] Create Openstack CLI plugin for Barbican This is the first implementation of the barbicanclient plugin for the OpenStack CLI. Since we chose to use cliff in for the client, the integration with the OCC is quite seamless. Only change that needed to be done was that, since the OCC uses a ClientManager class to fetch the specific libraries for the components, we needed to simulate that in the barbicanclient. Currently, the plugin can be used if the barbican server is configured with keystone authentication. This is because currently there is no way for us to set the X-Project-Id header manually from the client (if the plugin is being used) but that functionality is out of the scope of this first implementation. Change-Id: I37fd158af24e785bc0b7125c6b4c1a9795927a10 --- barbicanclient/barbican.py | 7 ++- barbicanclient/barbican_cli/cas.py | 5 ++- barbicanclient/barbican_cli/containers.py | 19 ++++---- barbicanclient/barbican_cli/orders.py | 12 +++--- barbicanclient/barbican_cli/secrets.py | 22 +++++----- barbicanclient/osc_plugin.py | 43 +++++++++++++++++++ .../cli/v1/behaviors/container_behaviors.py | 8 ++-- setup.cfg | 21 +++++---- test-requirements.txt | 1 + 9 files changed, 96 insertions(+), 42 deletions(-) create mode 100644 barbicanclient/osc_plugin.py diff --git a/barbicanclient/barbican.py b/barbicanclient/barbican.py index e8e49dfd..dd21b944 100644 --- a/barbicanclient/barbican.py +++ b/barbicanclient/barbican.py @@ -18,6 +18,7 @@ Command-line interface to the Barbican API. """ import sys +from collections import namedtuple from cliff import app from cliff import command @@ -52,7 +53,8 @@ class Barbican(app.App): super(Barbican, self).__init__( description=__doc__.strip(), version=version.__version__, - command_manager=commandmanager.CommandManager('barbican.client'), + command_manager=commandmanager.CommandManager( + 'openstack.key_manager.v1'), deferred_help=True, **kwargs ) @@ -309,8 +311,9 @@ class Barbican(app.App): client interface. This is inherited from the framework. """ + self.client_manager = namedtuple('ClientManager', 'key_manager') if cmd.auth_required: - self.client = self.create_client(self.options) + self.client_manager.key_manager = self.create_client(self.options) def run(self, argv): # If no arguments are provided, usage is displayed diff --git a/barbicanclient/barbican_cli/cas.py b/barbicanclient/barbican_cli/cas.py index 1aa12a3c..573ded9d 100644 --- a/barbicanclient/barbican_cli/cas.py +++ b/barbicanclient/barbican_cli/cas.py @@ -28,7 +28,7 @@ class GetCA(show.ShowOne): return parser def take_action(self, args): - entity = self.app.client.cas.get(ca_ref=args.URI) + entity = self.app.client_manager.key_manager.cas.get(ca_ref=args.URI) return entity._get_formatted_entity() @@ -52,5 +52,6 @@ class ListCA(lister.Lister): return parser def take_action(self, args): - obj_list = self.app.client.cas.list(args.limit, args.offset, args.name) + obj_list = self.app.client_manager.key_manager.cas.list( + args.limit, args.offset, args.name) return cas.CA._list_objects(obj_list) diff --git a/barbicanclient/barbican_cli/containers.py b/barbicanclient/barbican_cli/containers.py index 91efb66c..5c4d946c 100644 --- a/barbicanclient/barbican_cli/containers.py +++ b/barbicanclient/barbican_cli/containers.py @@ -31,7 +31,7 @@ class DeleteContainer(command.Command): return parser def take_action(self, args): - self.app.client.containers.delete(args.URI) + self.app.client_manager.key_manager.containers.delete(args.URI) class GetContainer(show.ShowOne): @@ -43,7 +43,8 @@ class GetContainer(show.ShowOne): return parser def take_action(self, args): - entity = self.app.client.containers.get(args.URI) + entity = self.app.client_manager.key_manager.containers.get( + args.URI) return entity._get_formatted_entity() @@ -70,8 +71,8 @@ class ListContainer(lister.Lister): return parser def take_action(self, args): - obj_list = self.app.client.containers.list(args.limit, args.offset, - args.name, args.type) + obj_list = self.app.client_manager.key_manager.containers.list( + args.limit, args.offset, args.name, args.type) return Container._list_objects(obj_list) @@ -93,8 +94,8 @@ class CreateContainer(show.ShowOne): return parser def take_action(self, args): - container_type = self.app.client.containers._container_map.get( - args.type) + client = self.app.client_manager.key_manager + container_type = client.containers._container_map.get(args.type) if not container_type: raise ValueError('Invalid container type specified.') secret_refs = CreateContainer._parse_secrets(args.secret) @@ -103,7 +104,7 @@ class CreateContainer(show.ShowOne): private_key_ref = secret_refs.get('private_key') private_key_pass_ref = secret_refs.get('private_key_passphrase') entity = RSAContainer( - api=self.app.client.containers._api, + api=client.containers._api, name=args.name, public_key_ref=public_key_ref, private_key_ref=private_key_ref, @@ -115,7 +116,7 @@ class CreateContainer(show.ShowOne): private_key_ref = secret_refs.get('private_key') private_key_pass_ref = secret_refs.get('private_key_passphrase') entity = CertificateContainer( - api=self.app.client.containers._api, + api=client.containers._api, name=args.name, certificate_ref=certificate_ref, intermediates_ref=intermediates_ref, @@ -123,7 +124,7 @@ class CreateContainer(show.ShowOne): private_key_passphrase_ref=private_key_pass_ref, ) else: - entity = container_type(api=self.app.client.containers._api, + entity = container_type(api=client.containers._api, name=args.name, secret_refs=secret_refs) entity.store() return entity._get_formatted_entity() diff --git a/barbicanclient/barbican_cli/orders.py b/barbicanclient/barbican_cli/orders.py index 60c81a66..d6f35835 100644 --- a/barbicanclient/barbican_cli/orders.py +++ b/barbicanclient/barbican_cli/orders.py @@ -73,14 +73,14 @@ class CreateOrder(show.ShowOne): raise ValueError( 'Couldn\'t read request file %s.' % args.request_file) - entity = self.app.client.orders.create( + entity = self.app.client_manager.key_manager.orders.create( name=args.name, type=args.type, subject_dn=args.subject_dn, request_type=args.request_type, source_container_ref=args.source_container_ref, ca_id=args.ca_id, profile=args.profile, request_data=request_data) else: - entity = self.app.client.orders.create( + entity = self.app.client_manager.key_manager.orders.create( name=args.name, type=args.type, payload_content_type=args.payload_content_type, algorithm=args.algorithm, bit_length=args.bit_length, @@ -98,7 +98,7 @@ class DeleteOrder(command.Command): return parser def take_action(self, args): - self.app.client.orders.delete(args.URI) + self.app.client_manager.key_manager.orders.delete(args.URI) class GetOrder(show.ShowOne): @@ -110,7 +110,8 @@ class GetOrder(show.ShowOne): return parser def take_action(self, args): - entity = self.app.client.orders.get(order_ref=args.URI) + entity = self.app.client_manager.key_manager.orders.get( + order_ref=args.URI) return entity._get_formatted_entity() @@ -131,7 +132,8 @@ class ListOrder(lister.Lister): return parser def take_action(self, args): - obj_list = self.app.client.orders.list(args.limit, args.offset) + obj_list = self.app.client_manager.key_manager.orders.list( + args.limit, args.offset) if not obj_list: return [], [] columns = obj_list[0]._get_generic_columns() diff --git a/barbicanclient/barbican_cli/secrets.py b/barbicanclient/barbican_cli/secrets.py index 24f58908..2a5d5202 100644 --- a/barbicanclient/barbican_cli/secrets.py +++ b/barbicanclient/barbican_cli/secrets.py @@ -29,7 +29,7 @@ class DeleteSecret(command.Command): return parser def take_action(self, args): - self.app.client.secrets.delete(args.URI) + self.app.client_manager.key_manager.secrets.delete(args.URI) class GetSecret(show.ShowOne): @@ -62,12 +62,13 @@ class GetSecret(show.ShowOne): def take_action(self, args): if args.decrypt or args.payload: - entity = self.app.client.secrets.get(args.URI, - args.payload_content_type) + entity = self.app.client_manager.key_manager.secrets.get( + args.URI, args.payload_content_type) return (('Payload',), (entity.payload,)) else: - entity = self.app.client.secrets.get(secret_ref=args.URI) + entity = self.app.client_manager.key_manager.secrets.get( + secret_ref=args.URI) return entity._get_formatted_entity() @@ -82,8 +83,8 @@ class UpdateSecret(show.ShowOne): return parser def take_action(self, args): - self.app.client.secrets.update(args.URI, - args.payload) + self.app.client_manager.key_manager.secrets.update(args.URI, + args.payload) class ListSecret(lister.Lister): @@ -116,10 +117,9 @@ class ListSecret(lister.Lister): return parser def take_action(self, args): - obj_list = self.app.client.secrets.list(args.limit, args.offset, - args.name, args.mode, - args.algorithm, - args.bit_length) + obj_list = self.app.client_manager.key_manager.secrets.list( + args.limit, args.offset, args.name, args.mode, args.algorithm, + args.bit_length) return secrets.Secret._list_objects(obj_list) @@ -162,7 +162,7 @@ class StoreSecret(show.ShowOne): return parser def take_action(self, args): - entity = self.app.client.secrets.create( + entity = self.app.client_manager.key_manager.secrets.create( name=args.name, payload=args.payload, payload_content_type=args.payload_content_type, payload_content_encoding=args.payload_content_encoding, diff --git a/barbicanclient/osc_plugin.py b/barbicanclient/osc_plugin.py new file mode 100644 index 00000000..95f70761 --- /dev/null +++ b/barbicanclient/osc_plugin.py @@ -0,0 +1,43 @@ +# 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 logging + +from barbicanclient import client + +LOG = logging.getLogger(__name__) + +DEFAULT_API_VERSION = '1' +API_VERSION_OPTION = 'os_key_manager_api_version' +API_NAME = 'key_manager' +API_VERSIONS = { + '1': 'barbicanclient.client.Client', +} + + +def make_client(instance): + """Returns a Barbican service client.""" + return client.Client(session=instance.session, + region_name=instance._region_name) + + +def build_option_parser(parser): + """Hook to add global options.""" + parser.add_argument('--os-key-manager-api-version', + metavar='', + default=client.env( + 'OS_KEY_MANAGER_API_VERSION', + default=DEFAULT_API_VERSION), + help=('Barbican API version, default=' + + DEFAULT_API_VERSION + + ' (Env: OS_KEY_MANAGER_API_VERSION)')) + return parser diff --git a/functionaltests/cli/v1/behaviors/container_behaviors.py b/functionaltests/cli/v1/behaviors/container_behaviors.py index 21c97827..278717dd 100644 --- a/functionaltests/cli/v1/behaviors/container_behaviors.py +++ b/functionaltests/cli/v1/behaviors/container_behaviors.py @@ -30,7 +30,7 @@ class ContainerBehaviors(base_behaviors.BaseBehaviors): :param container_href the href to the container to delete """ - argv = ['container', 'delete'] + argv = ['secret', 'container', 'delete'] self.add_auth_and_endpoint(argv) argv.extend([container_href]) @@ -44,7 +44,7 @@ class ContainerBehaviors(base_behaviors.BaseBehaviors): :return: the href to the newly created container """ - argv = ['container', 'create'] + argv = ['secret', 'container', 'create'] self.add_auth_and_endpoint(argv) for secret_href in secret_hrefs: argv.extend(['--secret', secret_href]) @@ -64,7 +64,7 @@ class ContainerBehaviors(base_behaviors.BaseBehaviors): :return dict of container values, or an empty dict if the container is not found. """ - argv = ['container', 'get'] + argv = ['secret', 'container', 'get'] self.add_auth_and_endpoint(argv) argv.extend([container_href]) @@ -81,7 +81,7 @@ class ContainerBehaviors(base_behaviors.BaseBehaviors): :return: a list of containers """ - argv = ['container', 'list'] + argv = ['secret', 'container', 'list'] self.add_auth_and_endpoint(argv) stdout, stderr = self.issue_barbican_command(argv) diff --git a/setup.cfg b/setup.cfg index 311ff632..a6314f76 100644 --- a/setup.cfg +++ b/setup.cfg @@ -27,12 +27,15 @@ packages = console_scripts = barbican = barbicanclient.barbican:main -barbican.client = +openstack.cli.extension = + key_manager = barbicanclient.osc_plugin - order_create = barbicanclient.barbican_cli.orders:CreateOrder - order_delete = barbicanclient.barbican_cli.orders:DeleteOrder - order_get = barbicanclient.barbican_cli.orders:GetOrder - order_list = barbicanclient.barbican_cli.orders:ListOrder +openstack.key_manager.v1 = + + secret_order_create = barbicanclient.barbican_cli.orders:CreateOrder + secret_order_delete = barbicanclient.barbican_cli.orders:DeleteOrder + secret_order_get = barbicanclient.barbican_cli.orders:GetOrder + secret_order_list = barbicanclient.barbican_cli.orders:ListOrder secret_delete = barbicanclient.barbican_cli.secrets:DeleteSecret secret_get = barbicanclient.barbican_cli.secrets:GetSecret @@ -40,10 +43,10 @@ barbican.client = secret_store = barbicanclient.barbican_cli.secrets:StoreSecret secret_update = barbicanclient.barbican_cli.secrets:UpdateSecret - container_delete = barbicanclient.barbican_cli.containers:DeleteContainer - container_get = barbicanclient.barbican_cli.containers:GetContainer - container_list = barbicanclient.barbican_cli.containers:ListContainer - container_create = barbicanclient.barbican_cli.containers:CreateContainer + secret_container_delete = barbicanclient.barbican_cli.containers:DeleteContainer + secret_container_get = barbicanclient.barbican_cli.containers:GetContainer + secret_container_list = barbicanclient.barbican_cli.containers:ListContainer + secret_container_create = barbicanclient.barbican_cli.containers:CreateContainer ca_get = barbicanclient.barbican_cli.cas:GetCA ca_list = barbicanclient.barbican_cli.cas:ListCA diff --git a/test-requirements.txt b/test-requirements.txt index eb41af1a..eb738d00 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -11,6 +11,7 @@ testrepository>=0.0.18 testtools>=1.4.0 oslotest>=1.10.0 # Apache-2.0 nose +python-openstackclient>=1.5.0 # Documentation build requirements sphinx!=1.2.0,!=1.3b1,<1.3,>=1.1.2