From 713908385a183cada8c9e77bb74c2ca381a38660 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Wed, 3 Jul 2013 15:26:33 -0500 Subject: [PATCH 0001/3699] Add authenticate method to oauth code Forgot to add one last method when I initially checked in the first set of code; the authenticate method will return an actual keystone token that the user may now use. Also, I added some changes to other methods because the client has been updated. Change-Id: Ie2707689e0df1fb1bc92177f932baf23fe1ca920 --- openstackclient/identity/v3/oauth.py | 107 ++++++++++++++++++++------- setup.cfg | 1 + 2 files changed, 81 insertions(+), 27 deletions(-) diff --git a/openstackclient/identity/v3/oauth.py b/openstackclient/identity/v3/oauth.py index 0b5ae4dba7..d3345f0021 100644 --- a/openstackclient/identity/v3/oauth.py +++ b/openstackclient/identity/v3/oauth.py @@ -25,6 +25,49 @@ from openstackclient.common import utils +class AuthenticateAccessToken(show.ShowOne): + """Authenticate access token - receive keystone token""" + + api = 'identity' + log = logging.getLogger(__name__ + '.AuthenticateAccessToken') + + def get_parser(self, prog_name): + parser = super(AuthenticateAccessToken, self).get_parser(prog_name) + parser.add_argument( + '--consumer-key', + metavar='', + help='Consumer key', + required=True + ) + parser.add_argument( + '--consumer-secret', + metavar='', + help='Consumer secret', + required=True + ) + parser.add_argument( + '--access-key', + metavar='', + help='Access token key', + required=True + ) + parser.add_argument( + '--access-secret', + metavar='', + help='Access token secret', + required=True + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + oauth_client = self.app.client_manager.identity.oauth + keystone_token = oauth_client.authenticate( + parsed_args.consumer_key, parsed_args.consumer_secret, + parsed_args.access_key, parsed_args.access_secret) + return zip(*sorted(keystone_token.iteritems())) + + class AuthorizeRequestToken(show.ShowOne): """Authorize request token command""" @@ -39,12 +82,6 @@ def get_parser(self, prog_name): help='Consumer key', required=True ) - parser.add_argument( - '--user-token', - metavar='', - help='Token of authorizing user', - required=True - ) parser.add_argument( '--roles', metavar='', @@ -55,11 +92,10 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - oauth_client = self.app.client_manager.identity.oauths + oauth_client = self.app.client_manager.identity.oauth verifier_pin = oauth_client.authorize_request_token( - parsed_args.request_key, parsed_args.user_token, - parsed_args.roles) + parsed_args.request_key, parsed_args.roles) info = {} info.update(verifier_pin._info) return zip(*sorted(info.iteritems())) @@ -79,10 +115,22 @@ def get_parser(self, prog_name): help='Consumer key', required=True ) + parser.add_argument( + '--consumer-secret', + metavar='', + help='Consumer secret', + required=True + ) parser.add_argument( '--request-key', metavar='', - help='Consumer key', + help='Request token key', + required=True + ) + parser.add_argument( + '--request-secret', + metavar='', + help='Request token secret', required=True ) parser.add_argument( @@ -95,13 +143,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - oauth_client = self.app.client_manager.identity.oauths + oauth_client = self.app.client_manager.identity.oauth access_token = oauth_client.create_access_token( - parsed_args.consumer_key, parsed_args.request_key, + parsed_args.consumer_key, parsed_args.consumer_secret, + parsed_args.request_key, parsed_args.request_secret, parsed_args.verifier) - info = {} - info.update(access_token._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(access_token.iteritems())) class CreateConsumer(show.ShowOne): @@ -122,7 +169,7 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity - consumer = identity_client.oauths.create_consumer( + consumer = identity_client.oauth.create_consumer( parsed_args.name ) info = {} @@ -144,6 +191,12 @@ def get_parser(self, prog_name): help='Consumer key', required=True ) + parser.add_argument( + '--consumer-secret', + metavar='', + help='Consumer secret', + required=True + ) parser.add_argument( '--roles', metavar='', @@ -153,12 +206,12 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - oauth_client = self.app.client_manager.identity.oauths + oauth_client = self.app.client_manager.identity.oauth request_token = oauth_client.create_request_token( - parsed_args.consumer_key, parsed_args.roles) - info = {} - info.update(request_token._info) - return zip(*sorted(info.iteritems())) + parsed_args.consumer_key, + parsed_args.consumer_secret, + parsed_args.roles) + return zip(*sorted(request_token.iteritems())) class DeleteConsumer(command.Command): @@ -180,8 +233,8 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity consumer = utils.find_resource( - identity_client.oauths, parsed_args.consumer) - identity_client.oauths.delete_consumer(consumer.id) + identity_client.oauth, parsed_args.consumer) + identity_client.oauth.delete_consumer(consumer.id) return @@ -194,7 +247,7 @@ class ListConsumer(lister.Lister): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) columns = ('ID', 'Name', 'Consumer Key', 'Consumer Secret') - data = self.app.client_manager.identity.oauths.list_consumers() + data = self.app.client_manager.identity.oauth.list_consumers() return (columns, (utils.get_item_properties( s, columns, @@ -226,7 +279,7 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity consumer = utils.find_resource( - identity_client.oauths, parsed_args.consumer) + identity_client.oauth, parsed_args.consumer) kwargs = {} if parsed_args.name: kwargs['name'] = parsed_args.name @@ -234,7 +287,7 @@ def take_action(self, parsed_args): if not len(kwargs): sys.stdout.write("Consumer not updated, no arguments present") return - identity_client.oauths.update_consumer(consumer.id, **kwargs) + identity_client.oauth.update_consumer(consumer.id, **kwargs) return @@ -257,7 +310,7 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity consumer = utils.find_resource( - identity_client.oauths, parsed_args.consumer) + identity_client.oauth, parsed_args.consumer) info = {} info.update(consumer._info) diff --git a/setup.cfg b/setup.cfg index 2068a92a5a..2aae47df48 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,6 +69,7 @@ openstack.identity.v2_0 = user_show = openstackclient.identity.v2_0.user:ShowUser openstack.identity.v3 = + access_token_authenticate = openstackclient.identity.v3.oauth:AuthenticateAccessToken access_token_create = openstackclient.identity.v3.oauth:CreateAccessToken consumer_create = openstackclient.identity.v3.oauth:CreateConsumer From 1a0d5ccc68f65394292992b48afe20241e89e7b8 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Thu, 11 Jul 2013 22:40:24 -0500 Subject: [PATCH 0002/3699] Remove api = apiName calls from each method As discussed in https://review.openstack.org/#/c/36352/ for each command, we were setting api = identity or volume... etc, this was for an old way of calling commands that are is no longer used. Also removed openstackclient/common/command.py Change-Id: I2705f35d343f2ae729dc22d6aed0b852b2f8ca19 --- openstackclient/common/command.py | 29 -------------------- openstackclient/compute/v2/agent.py | 4 --- openstackclient/compute/v2/console.py | 2 -- openstackclient/compute/v2/fixedip.py | 2 -- openstackclient/compute/v2/flavor.py | 4 --- openstackclient/compute/v2/floatingip.py | 5 ---- openstackclient/compute/v2/floatingippool.py | 1 - openstackclient/compute/v2/host.py | 2 -- openstackclient/compute/v2/hypervisor.py | 2 -- openstackclient/compute/v2/keypair.py | 4 --- openstackclient/compute/v2/server.py | 9 ------ openstackclient/compute/v2/service.py | 2 -- openstackclient/identity/v2_0/ec2creds.py | 4 --- openstackclient/identity/v2_0/endpoint.py | 4 --- openstackclient/identity/v2_0/role.py | 7 ----- openstackclient/identity/v2_0/service.py | 4 --- openstackclient/identity/v2_0/tenant.py | 5 ---- openstackclient/identity/v2_0/user.py | 5 ---- openstackclient/identity/v3/credential.py | 5 ---- openstackclient/identity/v3/domain.py | 5 ---- openstackclient/identity/v3/endpoint.py | 5 ---- openstackclient/identity/v3/group.py | 8 ------ openstackclient/identity/v3/oauth.py | 8 ------ openstackclient/identity/v3/policy.py | 5 ---- openstackclient/identity/v3/project.py | 5 ---- openstackclient/identity/v3/role.py | 7 ----- openstackclient/identity/v3/service.py | 5 ---- openstackclient/identity/v3/user.py | 5 ---- openstackclient/image/v1/image.py | 1 - openstackclient/image/v2/image.py | 4 --- openstackclient/volume/v1/backup.py | 5 ---- openstackclient/volume/v1/snapshot.py | 5 ---- openstackclient/volume/v1/type.py | 5 ---- openstackclient/volume/v1/volume.py | 6 ---- 34 files changed, 179 deletions(-) delete mode 100644 openstackclient/common/command.py diff --git a/openstackclient/common/command.py b/openstackclient/common/command.py deleted file mode 100644 index 59cd0da281..0000000000 --- a/openstackclient/common/command.py +++ /dev/null @@ -1,29 +0,0 @@ -# Copyright 2012-2013 OpenStack, LLC. -# -# 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. -# - -"""OpenStack base command""" - -from cliff import command - - -class OpenStackCommand(command.Command): - """Base class for OpenStack commands.""" - api = None - - def run(self, parsed_args): - if not self.api: - return - else: - return super(OpenStackCommand, self).run(parsed_args) diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 46ab991b4c..aac69d8a0b 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -27,7 +27,6 @@ class CreateAgent(show.ShowOne): """Create agent command""" - api = "compute" log = logging.getLogger(__name__ + ".CreateAgent") def get_parser(self, prog_name): @@ -77,7 +76,6 @@ def take_action(self, parsed_args): class DeleteAgent(command.Command): """Delete agent command""" - api = "compute" log = logging.getLogger(__name__ + ".DeleteAgent") def get_parser(self, prog_name): @@ -98,7 +96,6 @@ def take_action(self, parsed_args): class ListAgent(lister.Lister): """List agent command""" - api = "compute" log = logging.getLogger(__name__ + ".ListAgent") def get_parser(self, prog_name): @@ -131,7 +128,6 @@ def take_action(self, parsed_args): class SetAgent(show.ShowOne): """Set agent command""" - api = "compute" log = logging.getLogger(__name__ + ".SetAgent") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 8ed0d7f21a..a67b004c05 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -27,7 +27,6 @@ class ShowConsoleLog(command.Command): """Show console-log command""" - api = 'compute' log = logging.getLogger(__name__ + '.ShowConsoleLog') def get_parser(self, prog_name): @@ -65,7 +64,6 @@ def take_action(self, parsed_args): class ShowConsoleURL(show.ShowOne): """Show console-url command""" - api = 'compute' log = logging.getLogger(__name__ + '.ShowConsoleURL') def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/fixedip.py b/openstackclient/compute/v2/fixedip.py index d0687fd717..c41fed45c4 100644 --- a/openstackclient/compute/v2/fixedip.py +++ b/openstackclient/compute/v2/fixedip.py @@ -25,7 +25,6 @@ class AddFixedIP(command.Command): """Add fixed-ip command""" - api = "compute" log = logging.getLogger(__name__ + ".AddFixedIP") def get_parser(self, prog_name): @@ -59,7 +58,6 @@ def take_action(self, parsed_args): class RemoveFixedIP(command.Command): """Remove fixed-ip command""" - api = "compute" log = logging.getLogger(__name__ + ".RemoveFixedIP") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 1c6d3e3ab3..4d53a4122e 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -27,7 +27,6 @@ class CreateFlavor(show.ShowOne): """Create flavor command""" - api = "compute" log = logging.getLogger(__name__ + ".CreateFlavor") def get_parser(self, prog_name): @@ -117,7 +116,6 @@ def take_action(self, parsed_args): class DeleteFlavor(command.Command): """Delete flavor command""" - api = "compute" log = logging.getLogger(__name__ + ".DeleteFlavor") def get_parser(self, prog_name): @@ -140,7 +138,6 @@ def take_action(self, parsed_args): class ListFlavor(lister.Lister): """List flavor command""" - api = "compute" log = logging.getLogger(__name__ + ".ListFlavor") def take_action(self, parsed_args): @@ -168,7 +165,6 @@ def take_action(self, parsed_args): class ShowFlavor(show.ShowOne): """Show flavor command""" - api = "compute" log = logging.getLogger(__name__ + ".ShowFlavor") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 5a4b5f9a5d..1b07beb3d3 100644 --- a/openstackclient/compute/v2/floatingip.py +++ b/openstackclient/compute/v2/floatingip.py @@ -27,7 +27,6 @@ class AddFloatingIP(command.Command): """Add floating-ip command""" - api = "compute" log = logging.getLogger(__name__ + ".AddFloatingIP") def get_parser(self, prog_name): @@ -58,7 +57,6 @@ def take_action(self, parsed_args): class CreateFloatingIP(show.ShowOne): """Create floating-ip command""" - api = 'compute' log = logging.getLogger(__name__ + '.CreateFloatingIP') def get_parser(self, prog_name): @@ -83,7 +81,6 @@ def take_action(self, parsed_args): class DeleteFloatingIP(command.Command): """Delete floating-ip command""" - api = 'compute' log = logging.getLogger(__name__ + '.DeleteFloatingIP') def get_parser(self, prog_name): @@ -111,7 +108,6 @@ def take_action(self, parsed_args): class ListFloatingIP(lister.Lister): """List floating-ip command""" - api = 'compute' log = logging.getLogger(__name__ + '.ListFloatingIP') def take_action(self, parsed_args): @@ -132,7 +128,6 @@ def take_action(self, parsed_args): class RemoveFloatingIP(command.Command): """Remove floating-ip command""" - api = "compute" log = logging.getLogger(__name__ + ".RemoveFloatingIP") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/floatingippool.py b/openstackclient/compute/v2/floatingippool.py index 54814e046d..e1da97c3d8 100644 --- a/openstackclient/compute/v2/floatingippool.py +++ b/openstackclient/compute/v2/floatingippool.py @@ -25,7 +25,6 @@ class ListFloatingIPPool(lister.Lister): """List floating-ip-pool command""" - api = 'compute' log = logging.getLogger(__name__ + '.ListFloatingIPPool') def take_action(self, parsed_args): diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index f9c23a42cd..44f457d933 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -25,7 +25,6 @@ class ListHost(lister.Lister): """List host command""" - api = "compute" log = logging.getLogger(__name__ + ".ListHost") def get_parser(self, prog_name): @@ -54,7 +53,6 @@ def take_action(self, parsed_args): class ShowHost(lister.Lister): """Show host command""" - api = "compute" log = logging.getLogger(__name__ + ".ShowHost") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 35866aecea..ad69d3285a 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -26,7 +26,6 @@ class ListHypervisor(lister.Lister): """List hypervisor command""" - api = "compute" log = logging.getLogger(__name__ + ".ListHypervisor") def get_parser(self, prog_name): @@ -60,7 +59,6 @@ def take_action(self, parsed_args): class ShowHypervisor(show.ShowOne): """Show hypervisor command""" - api = "compute" log = logging.getLogger(__name__ + ".ShowHypervisor") def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 7987574fae..65f3679b71 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -30,7 +30,6 @@ class CreateKeypair(show.ShowOne): """Create keypair command""" - api = "compute" log = logging.getLogger(__name__ + '.CreateKeypair') def get_parser(self, prog_name): @@ -81,7 +80,6 @@ def take_action(self, parsed_args): class DeleteKeypair(command.Command): """Delete keypair command""" - api = "compute" log = logging.getLogger(__name__ + '.DeleteKeypair') def get_parser(self, prog_name): @@ -103,7 +101,6 @@ def take_action(self, parsed_args): class ListKeypair(lister.Lister): """List keypair command""" - api = "compute" log = logging.getLogger(__name__ + ".ListKeypair") def take_action(self, parsed_args): @@ -124,7 +121,6 @@ def take_action(self, parsed_args): class ShowKeypair(show.ShowOne): """Show keypair command""" - api = 'compute' log = logging.getLogger(__name__ + '.ShowKeypair') def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8b134aa92f..e78144b084 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -118,7 +118,6 @@ def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5, class CreateServer(show.ShowOne): """Create server command""" - api = "compute" log = logging.getLogger(__name__ + '.CreateServer') def get_parser(self, prog_name): @@ -318,7 +317,6 @@ def take_action(self, parsed_args): class DeleteServer(command.Command): """Delete server command""" - api = 'compute' log = logging.getLogger(__name__ + '.DeleteServer') def get_parser(self, prog_name): @@ -341,7 +339,6 @@ def take_action(self, parsed_args): class ListServer(lister.Lister): """List server command""" - api = 'compute' log = logging.getLogger(__name__ + '.ListServer') def get_parser(self, prog_name): @@ -419,7 +416,6 @@ def take_action(self, parsed_args): class PauseServer(command.Command): """Pause server command""" - api = 'compute' log = logging.getLogger(__name__ + '.PauseServer') def get_parser(self, prog_name): @@ -442,7 +438,6 @@ def take_action(self, parsed_args): class RebootServer(command.Command): """Reboot server command""" - api = 'compute' log = logging.getLogger(__name__ + '.RebootServer') def get_parser(self, prog_name): @@ -490,7 +485,6 @@ def take_action(self, parsed_args): class RebuildServer(show.ShowOne): """Rebuild server command""" - api = "compute" log = logging.getLogger(__name__ + '.RebuildServer') def get_parser(self, prog_name): @@ -545,7 +539,6 @@ def take_action(self, parsed_args): class ResumeServer(command.Command): """Resume server command""" - api = 'compute' log = logging.getLogger(__name__ + '.ResumeServer') def get_parser(self, prog_name): @@ -592,7 +585,6 @@ def take_action(self, parsed_args): class SuspendServer(command.Command): """Suspend server command""" - api = 'compute' log = logging.getLogger(__name__ + '.SuspendServer') def get_parser(self, prog_name): @@ -615,7 +607,6 @@ def take_action(self, parsed_args): class UnpauseServer(command.Command): """Unpause server command""" - api = 'compute' log = logging.getLogger(__name__ + '.UnpauseServer') def get_parser(self, prog_name): diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 5dac0e1ca9..5e57e0aa5e 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -25,7 +25,6 @@ class ListService(lister.Lister): """List service command""" - api = "compute" log = logging.getLogger(__name__ + ".ListService") def get_parser(self, prog_name): @@ -62,7 +61,6 @@ def take_action(self, parsed_args): class SetService(lister.Lister): """Set service command""" - api = "compute" log = logging.getLogger(__name__ + ".SetService") def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 6a5d2af30f..953a3de6ad 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -27,7 +27,6 @@ class CreateEC2Creds(show.ShowOne): """Create EC2 credentials""" - api = "identity" log = logging.getLogger(__name__ + ".CreateEC2Creds") def get_parser(self, prog_name): @@ -75,7 +74,6 @@ def take_action(self, parsed_args): class DeleteEC2Creds(command.Command): """Delete EC2 credentials""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteEC2Creds') def get_parser(self, prog_name): @@ -111,7 +109,6 @@ def take_action(self, parsed_args): class ListEC2Creds(lister.Lister): """List EC2 credentials""" - api = 'identity' log = logging.getLogger(__name__ + '.ListEC2Creds') def get_parser(self, prog_name): @@ -148,7 +145,6 @@ def take_action(self, parsed_args): class ShowEC2Creds(show.ShowOne): """Show EC2 credentials""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowEC2Creds') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index 4ce4fcb20a..680465ae47 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -29,7 +29,6 @@ class CreateEndpoint(show.ShowOne): """Create endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateEndpoint') def get_parser(self, prog_name): @@ -78,7 +77,6 @@ def take_action(self, parsed_args): class DeleteEndpoint(command.Command): """Delete endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteEndpoint') def get_parser(self, prog_name): @@ -99,7 +97,6 @@ def take_action(self, parsed_args): class ListEndpoint(lister.Lister): """List endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListEndpoint') def get_parser(self, prog_name): @@ -136,7 +133,6 @@ def take_action(self, parsed_args): class ShowEndpoint(show.ShowOne): """Show endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowEndpoint') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 5c905d916f..867230b6b2 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -27,7 +27,6 @@ class AddRole(show.ShowOne): """Add role to tenant:user""" - api = 'identity' log = logging.getLogger(__name__ + '.AddRole') def get_parser(self, prog_name): @@ -68,7 +67,6 @@ def take_action(self, parsed_args): class CreateRole(show.ShowOne): """Create new role""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateRole') def get_parser(self, prog_name): @@ -92,7 +90,6 @@ def take_action(self, parsed_args): class DeleteRole(command.Command): """Delete existing role""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteRole') def get_parser(self, prog_name): @@ -114,7 +111,6 @@ def take_action(self, parsed_args): class ListRole(lister.Lister): """List roles""" - api = 'identity' log = logging.getLogger(__name__ + '.ListRole') def take_action(self, parsed_args): @@ -131,7 +127,6 @@ def take_action(self, parsed_args): class ListUserRole(lister.Lister): """List user-role assignments""" - api = 'identity' log = logging.getLogger(__name__ + '.ListUserRole') def get_parser(self, prog_name): @@ -181,7 +176,6 @@ def take_action(self, parsed_args): class RemoveRole(command.Command): """Remove role from tenant:user""" - api = 'identity' log = logging.getLogger(__name__ + '.RemoveRole') def get_parser(self, prog_name): @@ -218,7 +212,6 @@ def take_action(self, parsed_args): class ShowRole(show.ShowOne): """Show single role""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowRole') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 51abfc18ce..9940cb910a 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -29,7 +29,6 @@ class CreateService(show.ShowOne): """Create service command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateService') def get_parser(self, prog_name): @@ -65,7 +64,6 @@ def take_action(self, parsed_args): class DeleteService(command.Command): """Delete service command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteService') def get_parser(self, prog_name): @@ -86,7 +84,6 @@ def take_action(self, parsed_args): class ListService(lister.Lister): """List service command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListService') def get_parser(self, prog_name): @@ -115,7 +112,6 @@ def take_action(self, parsed_args): class ShowService(show.ShowOne): """Show service command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowService') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/tenant.py b/openstackclient/identity/v2_0/tenant.py index 00a6a977cf..c9a423c5f1 100644 --- a/openstackclient/identity/v2_0/tenant.py +++ b/openstackclient/identity/v2_0/tenant.py @@ -28,7 +28,6 @@ class CreateTenant(show.ShowOne): """Create tenant command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateTenant') def get_parser(self, prog_name): @@ -71,7 +70,6 @@ def take_action(self, parsed_args): class DeleteTenant(command.Command): """Delete tenant command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteTenant') def get_parser(self, prog_name): @@ -94,7 +92,6 @@ def take_action(self, parsed_args): class ListTenant(lister.Lister): """List tenant command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListTenant') def get_parser(self, prog_name): @@ -123,7 +120,6 @@ def take_action(self, parsed_args): class SetTenant(command.Command): """Set tenant command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetTenant') def get_parser(self, prog_name): @@ -177,7 +173,6 @@ def take_action(self, parsed_args): class ShowTenant(show.ShowOne): """Show tenant command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowTenant') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 03da60085f..39be81af82 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -28,7 +28,6 @@ class CreateUser(show.ShowOne): """Create user command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateUser') def get_parser(self, prog_name): @@ -86,7 +85,6 @@ def take_action(self, parsed_args): class DeleteUser(command.Command): """Delete user command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteUser') def get_parser(self, prog_name): @@ -108,7 +106,6 @@ def take_action(self, parsed_args): class ListUser(lister.Lister): """List user command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListUser') def get_parser(self, prog_name): @@ -141,7 +138,6 @@ def take_action(self, parsed_args): class SetUser(command.Command): """Set user command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetUser') def get_parser(self, prog_name): @@ -206,7 +202,6 @@ def take_action(self, parsed_args): class ShowUser(show.ShowOne): """Show user command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowUser') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index c98260526e..a2fb43a256 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -28,7 +28,6 @@ class CreateCredential(show.ShowOne): """Create credential command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateCredential') def get_parser(self, prog_name): @@ -79,7 +78,6 @@ def take_action(self, parsed_args): class DeleteCredential(command.Command): """Delete credential command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteCredential') def get_parser(self, prog_name): @@ -101,7 +99,6 @@ def take_action(self, parsed_args): class ListCredential(lister.Lister): """List credential command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListCredential') def take_action(self, parsed_args): @@ -118,7 +115,6 @@ def take_action(self, parsed_args): class SetCredential(command.Command): """Set credential command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetCredential') def get_parser(self, prog_name): @@ -178,7 +174,6 @@ def take_action(self, parsed_args): class ShowCredential(show.ShowOne): """Show credential command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowCredential') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index e3eaec2fbc..f6064a589c 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -28,7 +28,6 @@ class CreateDomain(show.ShowOne): """Create domain command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateDomain') def get_parser(self, prog_name): @@ -72,7 +71,6 @@ def take_action(self, parsed_args): class DeleteDomain(command.Command): """Delete domain command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteDomain') def get_parser(self, prog_name): @@ -96,7 +94,6 @@ def take_action(self, parsed_args): class ListDomain(lister.Lister): """List domain command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListDomain') def take_action(self, parsed_args): @@ -113,7 +110,6 @@ def take_action(self, parsed_args): class SetDomain(command.Command): """Set domain command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetDomain') def get_parser(self, prog_name): @@ -172,7 +168,6 @@ def take_action(self, parsed_args): class ShowDomain(show.ShowOne): """Show domain command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowDomain') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 4d9b680882..c5f3ebadff 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -28,7 +28,6 @@ class CreateEndpoint(show.ShowOne): """Create endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateEndpoint') def get_parser(self, prog_name): @@ -90,7 +89,6 @@ def take_action(self, parsed_args): class DeleteEndpoint(command.Command): """Delete endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteEndpoint') def get_parser(self, prog_name): @@ -113,7 +111,6 @@ def take_action(self, parsed_args): class ListEndpoint(lister.Lister): """List endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListEndpoint') def get_parser(self, prog_name): @@ -150,7 +147,6 @@ def take_action(self, parsed_args): class SetEndpoint(command.Command): """Set endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetEndpoint') def get_parser(self, prog_name): @@ -220,7 +216,6 @@ def take_action(self, parsed_args): class ShowEndpoint(show.ShowOne): """Show endpoint command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowEndpoint') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 21d73966aa..ca0493ebbb 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -28,7 +28,6 @@ class AddUserToGroup(command.Command): """Add user to group""" - api = 'identity' log = logging.getLogger(__name__ + '.AddUserToGroup') def get_parser(self, prog_name): @@ -67,7 +66,6 @@ def take_action(self, parsed_args): class CheckUserInGroup(command.Command): """Checks that user is in a specific group""" - api = 'identity' log = logging.getLogger(__name__ + '.CheckUserInGroup') def get_parser(self, prog_name): @@ -106,7 +104,6 @@ def take_action(self, parsed_args): class CreateGroup(show.ShowOne): """Create group command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateGroup') def get_parser(self, prog_name): @@ -147,7 +144,6 @@ def take_action(self, parsed_args): class DeleteGroup(command.Command): """Delete group command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteGroup') def get_parser(self, prog_name): @@ -169,7 +165,6 @@ def take_action(self, parsed_args): class ListGroup(lister.Lister): """List groups and optionally roles assigned to groups""" - api = 'identity' log = logging.getLogger(__name__ + '.ListGroup') def get_parser(self, prog_name): @@ -279,7 +274,6 @@ def take_action(self, parsed_args): class RemoveUserFromGroup(command.Command): """Remove user to group""" - api = 'identity' log = logging.getLogger(__name__ + '.RemoveUserFromGroup') def get_parser(self, prog_name): @@ -318,7 +312,6 @@ def take_action(self, parsed_args): class SetGroup(command.Command): """Set group command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetGroup') def get_parser(self, prog_name): @@ -365,7 +358,6 @@ def take_action(self, parsed_args): class ShowGroup(show.ShowOne): """Show group command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowGroup') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/oauth.py b/openstackclient/identity/v3/oauth.py index 0b5ae4dba7..1e803e15cf 100644 --- a/openstackclient/identity/v3/oauth.py +++ b/openstackclient/identity/v3/oauth.py @@ -28,7 +28,6 @@ class AuthorizeRequestToken(show.ShowOne): """Authorize request token command""" - api = 'identity' log = logging.getLogger(__name__ + '.AuthorizeRequestToken') def get_parser(self, prog_name): @@ -68,7 +67,6 @@ def take_action(self, parsed_args): class CreateAccessToken(show.ShowOne): """Create access token command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateAccessToken') def get_parser(self, prog_name): @@ -107,7 +105,6 @@ def take_action(self, parsed_args): class CreateConsumer(show.ShowOne): """Create consumer command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateConsumer') def get_parser(self, prog_name): @@ -133,7 +130,6 @@ def take_action(self, parsed_args): class CreateRequestToken(show.ShowOne): """Create request token command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateRequestToken') def get_parser(self, prog_name): @@ -164,7 +160,6 @@ def take_action(self, parsed_args): class DeleteConsumer(command.Command): """Delete consumer command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteConsumer') def get_parser(self, prog_name): @@ -188,7 +183,6 @@ def take_action(self, parsed_args): class ListConsumer(lister.Lister): """List consumer command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListConsumer') def take_action(self, parsed_args): @@ -205,7 +199,6 @@ def take_action(self, parsed_args): class SetConsumer(command.Command): """Set consumer command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetConsumer') def get_parser(self, prog_name): @@ -241,7 +234,6 @@ def take_action(self, parsed_args): class ShowConsumer(show.ShowOne): """Show consumer command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowConsumer') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index ccfc363569..ac14cc464d 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -28,7 +28,6 @@ class CreatePolicy(show.ShowOne): """Create policy command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreatePolicy') def get_parser(self, prog_name): @@ -61,7 +60,6 @@ def take_action(self, parsed_args): class DeletePolicy(command.Command): """Delete policy command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeletePolicy') def get_parser(self, prog_name): @@ -83,7 +81,6 @@ def take_action(self, parsed_args): class ListPolicy(lister.Lister): """List policy command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListPolicy') def get_parser(self, prog_name): @@ -113,7 +110,6 @@ def take_action(self, parsed_args): class SetPolicy(command.Command): """Set policy command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetPolicy') def get_parser(self, prog_name): @@ -159,7 +155,6 @@ def take_action(self, parsed_args): class ShowPolicy(show.ShowOne): """Show policy command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowPolicy') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 84271cd9a5..9d1e360bac 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -28,7 +28,6 @@ class CreateProject(show.ShowOne): """Create project command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateProject') def get_parser(self, prog_name): @@ -85,7 +84,6 @@ def take_action(self, parsed_args): class DeleteProject(command.Command): """Delete project command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteProject') def get_parser(self, prog_name): @@ -108,7 +106,6 @@ def take_action(self, parsed_args): class ListProject(lister.Lister): """List project command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListProject') def get_parser(self, prog_name): @@ -137,7 +134,6 @@ def take_action(self, parsed_args): class SetProject(command.Command): """Set project command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetProject') def get_parser(self, prog_name): @@ -199,7 +195,6 @@ def take_action(self, parsed_args): class ShowProject(show.ShowOne): """Show project command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowProject') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 7387509ae2..9be3b78410 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -28,7 +28,6 @@ class AddRole(command.Command): """Adds a role to a user or group on a domain or project""" - api = 'identity' log = logging.getLogger(__name__ + '.AddRole') def get_parser(self, prog_name): @@ -108,7 +107,6 @@ def take_action(self, parsed_args): class CreateRole(show.ShowOne): """Create new role""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateRole') def get_parser(self, prog_name): @@ -131,7 +129,6 @@ def take_action(self, parsed_args): class DeleteRole(command.Command): """Delete existing role""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteRole') def get_parser(self, prog_name): @@ -155,7 +152,6 @@ def take_action(self, parsed_args): class ListRole(lister.Lister): """List roles""" - api = 'identity' log = logging.getLogger(__name__ + '.ListRole') def take_action(self, parsed_args): @@ -172,7 +168,6 @@ def take_action(self, parsed_args): class RemoveRole(command.Command): """Remove role command""" - api = 'identity' log = logging.getLogger(__name__ + '.RemoveRole') def get_parser(self, prog_name): @@ -251,7 +246,6 @@ def take_action(self, parsed_args): class SetRole(command.Command): """Set role command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetRole') def get_parser(self, prog_name): @@ -285,7 +279,6 @@ def take_action(self, parsed_args): class ShowRole(show.ShowOne): """Show single role""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowRole') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 023c858e84..5c82284c52 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -28,7 +28,6 @@ class CreateService(show.ShowOne): """Create service command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateService') def get_parser(self, prog_name): @@ -69,7 +68,6 @@ def take_action(self, parsed_args): class DeleteService(command.Command): """Delete service command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteService') def get_parser(self, prog_name): @@ -94,7 +92,6 @@ def take_action(self, parsed_args): class ListService(lister.Lister): """List service command""" - api = 'identity' log = logging.getLogger(__name__ + '.ListService') def take_action(self, parsed_args): @@ -111,7 +108,6 @@ def take_action(self, parsed_args): class SetService(show.ShowOne): """Set service command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetService') def get_parser(self, prog_name): @@ -165,7 +161,6 @@ def take_action(self, parsed_args): class ShowService(show.ShowOne): """Show service command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowService') def get_parser(self, prog_name): diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 53550cda56..aac7027450 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -28,7 +28,6 @@ class CreateUser(show.ShowOne): """Create user command""" - api = 'identity' log = logging.getLogger(__name__ + '.CreateUser') def get_parser(self, prog_name): @@ -113,7 +112,6 @@ def take_action(self, parsed_args): class DeleteUser(command.Command): """Delete user command""" - api = 'identity' log = logging.getLogger(__name__ + '.DeleteUser') def get_parser(self, prog_name): @@ -137,7 +135,6 @@ def take_action(self, parsed_args): class ListUser(lister.Lister): """List users and optionally roles assigned to users""" - api = 'identity' log = logging.getLogger(__name__ + '.ListUser') def get_parser(self, prog_name): @@ -249,7 +246,6 @@ def take_action(self, parsed_args): class SetUser(command.Command): """Set user command""" - api = 'identity' log = logging.getLogger(__name__ + '.SetUser') def get_parser(self, prog_name): @@ -338,7 +334,6 @@ def take_action(self, parsed_args): class ShowUser(show.ShowOne): """Show user command""" - api = 'identity' log = logging.getLogger(__name__ + '.ShowUser') def get_parser(self, prog_name): diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 43cae21c07..5b4a939daa 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -30,7 +30,6 @@ class CreateImage(show.ShowOne): """Create image command""" - api = "image" log = logging.getLogger(__name__ + ".CreateImage") def get_parser(self, prog_name): diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 3b9b349d30..61273aa2c3 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -28,7 +28,6 @@ class DeleteImage(command.Command): """Delete image command""" - api = "image" log = logging.getLogger(__name__ + ".DeleteImage") def get_parser(self, prog_name): @@ -49,7 +48,6 @@ def take_action(self, parsed_args): class ListImage(lister.Lister): """List image command""" - api = "image" log = logging.getLogger(__name__ + ".ListImage") def get_parser(self, prog_name): @@ -78,7 +76,6 @@ def take_action(self, parsed_args): class SaveImage(command.Command): """Save image command""" - api = "image" log = logging.getLogger(__name__ + ".SaveImage") def get_parser(self, prog_name): @@ -107,7 +104,6 @@ def take_action(self, parsed_args): class ShowImage(show.ShowOne): """Show image command""" - api = "image" log = logging.getLogger(__name__ + ".ShowImage") def get_parser(self, prog_name): diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py index cd1afd60b7..8ef666c1fa 100644 --- a/openstackclient/volume/v1/backup.py +++ b/openstackclient/volume/v1/backup.py @@ -27,7 +27,6 @@ class CreateBackup(show.ShowOne): """Create backup command""" - api = 'volume' log = logging.getLogger(__name__ + '.CreateBackup') def get_parser(self, prog_name): @@ -75,7 +74,6 @@ def take_action(self, parsed_args): class DeleteBackup(command.Command): """Delete backup command""" - api = 'volume' log = logging.getLogger(__name__ + '.DeleteBackup') def get_parser(self, prog_name): @@ -99,7 +97,6 @@ def take_action(self, parsed_args): class ListBackup(lister.Lister): """List backup command""" - api = 'volume' log = logging.getLogger(__name__ + '.ListBackup') def take_action(self, parsed_args): @@ -122,7 +119,6 @@ def take_action(self, parsed_args): class RestoreBackup(command.Command): """Restore backup command""" - api = 'volume' log = logging.getLogger(__name__ + '.RestoreBackup') def get_parser(self, prog_name): @@ -151,7 +147,6 @@ def take_action(self, parsed_args): class ShowBackup(show.ShowOne): """Show backup command""" - api = 'volume' log = logging.getLogger(__name__ + '.ShowBackup') def get_parser(self, prog_name): diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py index a4cf86bfd5..6055a4d8af 100644 --- a/openstackclient/volume/v1/snapshot.py +++ b/openstackclient/volume/v1/snapshot.py @@ -28,7 +28,6 @@ class CreateSnapshot(show.ShowOne): """Create snapshot command""" - api = 'volume' log = logging.getLogger(__name__ + '.CreateSnapshot') def get_parser(self, prog_name): @@ -76,7 +75,6 @@ def take_action(self, parsed_args): class DeleteSnapshot(command.Command): """Delete snapshot command""" - api = 'volume' log = logging.getLogger(__name__ + '.DeleteSnapshot') def get_parser(self, prog_name): @@ -100,7 +98,6 @@ def take_action(self, parsed_args): class ListSnapshot(lister.Lister): """List snapshot command""" - api = 'volume' log = logging.getLogger(__name__ + '.ListSnapshot') def take_action(self, parsed_args): @@ -123,7 +120,6 @@ def take_action(self, parsed_args): class SetSnapshot(command.Command): """Set snapshot command""" - api = 'volume' log = logging.getLogger(__name__ + '.SetSnapshot') def get_parser(self, prog_name): @@ -163,7 +159,6 @@ def take_action(self, parsed_args): class ShowSnapshot(show.ShowOne): """Show snapshot command""" - api = 'volume' log = logging.getLogger(__name__ + '.ShowSnapshot') def get_parser(self, prog_name): diff --git a/openstackclient/volume/v1/type.py b/openstackclient/volume/v1/type.py index e146ee3f61..dab21d9911 100644 --- a/openstackclient/volume/v1/type.py +++ b/openstackclient/volume/v1/type.py @@ -28,7 +28,6 @@ class CreateVolumeType(show.ShowOne): """Create volume type command""" - api = 'volume' log = logging.getLogger(__name__ + '.CreateVolumeType') def get_parser(self, prog_name): @@ -55,7 +54,6 @@ def take_action(self, parsed_args): class DeleteVolumeType(command.Command): """Delete volume type command""" - api = 'volume' log = logging.getLogger(__name__ + '.DeleteVolumeType') def get_parser(self, prog_name): @@ -79,7 +77,6 @@ def take_action(self, parsed_args): class ListVolumeType(lister.Lister): """List volume type command""" - api = 'volume' log = logging.getLogger(__name__ + '.ListVolumeType') def get_parser(self, prog_name): @@ -108,7 +105,6 @@ def take_action(self, parsed_args): class SetVolumeType(command.Command): """Set volume type command""" - api = 'volume' log = logging.getLogger(__name__ + '.SetVolumeType') def get_parser(self, prog_name): @@ -142,7 +138,6 @@ def take_action(self, parsed_args): class UnsetVolumeType(command.Command): """Unset volume type command""" - api = 'volume' log = logging.getLogger(__name__ + '.UnsetVolumeType') def get_parser(self, prog_name): diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 43253c4092..f1e421f488 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -28,7 +28,6 @@ class CreateVolume(show.ShowOne): """Create volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.CreateVolume') def get_parser(self, prog_name): @@ -127,7 +126,6 @@ def take_action(self, parsed_args): class DeleteVolume(command.Command): """Delete volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.DeleteVolume') def get_parser(self, prog_name): @@ -161,7 +159,6 @@ def take_action(self, parsed_args): class ListVolume(lister.Lister): """List volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.ListVolume') def get_parser(self, prog_name): @@ -218,7 +215,6 @@ def take_action(self, parsed_args): class SetVolume(command.Command): """Set volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.SetVolume') def get_parser(self, prog_name): @@ -274,7 +270,6 @@ def take_action(self, parsed_args): class ShowVolume(show.ShowOne): """Show volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.ShowVolume') def get_parser(self, prog_name): @@ -297,7 +292,6 @@ def take_action(self, parsed_args): class UnsetVolume(command.Command): """Unset volume command""" - api = 'volume' log = logging.getLogger(__name__ + '.UnsetVolume') def get_parser(self, prog_name): From 75dcdb0c6637afe5c14e5c60f7719182bda7ab4a Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Tue, 9 Jul 2013 17:10:33 -0500 Subject: [PATCH 0003/3699] Add show limits command * This is a combination of the compute and volume API limits as they are very similar. As such, the command lives in a new command group 'openstack.common' that is unversioned. * Implements 'limits show [--absolute|--rate] Updated for https://review.openstack.org/#/c/36772/ Bug: 1172057 Change-Id: I2bd181cd0d098f7143360ae67944c2f221379af5 --- openstackclient/common/limits.py | 79 ++++++++++++++++++++++++++++++++ openstackclient/shell.py | 4 ++ setup.cfg | 3 ++ 3 files changed, 86 insertions(+) create mode 100644 openstackclient/common/limits.py diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py new file mode 100644 index 0000000000..bbc15228e8 --- /dev/null +++ b/openstackclient/common/limits.py @@ -0,0 +1,79 @@ +# Copyright 2012-2013 OpenStack Foundation +# +# 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. +# + +"""Limits Action Implementation""" + +import itertools +import logging + +from cliff import lister + +from openstackclient.common import utils + + +class ShowLimits(lister.Lister): + """Show compute and volume limits""" + + log = logging.getLogger(__name__ + '.ShowLimits') + + def get_parser(self, prog_name): + parser = super(ShowLimits, self).get_parser(prog_name) + type_group = parser.add_mutually_exclusive_group() + type_group.add_argument( + "--absolute", + dest="is_absolute", + action="store_true", + default=False, + help="Show absolute limits") + type_group.add_argument( + "--rate", + dest="is_rate", + action="store_true", + default=False, + help="Show rate limits") + parser.add_argument( + "--reserved", + dest="is_reserved", + action="store_true", + default=False, + help="Include reservations count [only valid with --absolute]") + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.volume + + compute_limits = compute_client.limits.get(parsed_args.is_reserved) + volume_limits = volume_client.limits.get() + + if parsed_args.is_absolute: + compute_limits = compute_limits.absolute + volume_limits = volume_limits.absolute + columns = ["Name", "Value"] + return (columns, (utils.get_item_properties(s, columns) + for s in itertools.chain(compute_limits, volume_limits))) + + elif parsed_args.is_rate: + compute_limits = compute_limits.rate + volume_limits = volume_limits.rate + columns = ["Verb", "URI", "Value", "Remain", "Unit", + "Next Available"] + return (columns, (utils.get_item_properties(s, columns) + for s in itertools.chain(compute_limits, volume_limits))) + + else: + return ({}, {}) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index e5353194cd..561b8ddf6c 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -331,6 +331,10 @@ def initialize_app(self, argv): self.command_manager.add_command_group( 'openstack.' + api + version) + # Commands that span multiple APIs + self.command_manager.add_command_group( + 'openstack.common') + # This is the naive extension implementation referred to in # blueprint 'client-extensions' # Extension modules can register their commands in an diff --git a/setup.cfg b/setup.cfg index 2068a92a5a..1e7911bda6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,6 +31,9 @@ console_scripts = openstack.cli = +openstack.common = + limits_show = openstackclient.common.limits:ShowLimits + openstack.identity.v2_0 = ec2_credentials_create = openstackclient.identity.v2_0.ec2creds:CreateEC2Creds ec2_credentials_delete = openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds From f768ea2b228257030de4696bfefe61d9fc917723 Mon Sep 17 00:00:00 2001 From: Alessio Ababilov Date: Tue, 16 Jul 2013 15:20:03 +0300 Subject: [PATCH 0004/3699] Update openstack-common.conf format Change-Id: Ifa6718331d3da91f0e9515a809484808bd6317f9 --- openstack-common.conf | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/openstack-common.conf b/openstack-common.conf index 58c0dee0ef..867e204af7 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -1,7 +1,10 @@ [DEFAULT] # The list of modules to copy from openstack-common -modules=cfg,iniparser,install_venv_common,openstackkeyring +module=cfg +module=iniparser +module=install_venv_common +module=openstackkeyring # The base module to hold the copy of openstack.common base=openstackclient From bbb71e7ce2cb8bc81318858823018ff494c61a40 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 17 Jan 2013 14:16:32 -0600 Subject: [PATCH 0005/3699] Add --catalog to service show Shows endpoints from the service catalog rather than the system services. Change-Id: I842916af9f7c0a76c4d3e27e419bf0fec059ec78 --- openstackclient/identity/v2_0/service.py | 67 ++++++++++++++++-------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 9940cb910a..629475df00 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -110,7 +110,7 @@ def take_action(self, parsed_args): class ShowService(show.ShowOne): - """Show service command""" + """Show cloud service information""" log = logging.getLogger(__name__ + '.ShowService') @@ -119,30 +119,51 @@ def get_parser(self, prog_name): parser.add_argument( 'service', metavar='', - help='Type, name or ID of service to display') + help='Type, name or ID of service to display', + ) + parser.add_argument( + '--catalog', + action='store_true', + default=False, + help='Show service catalog information', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity - try: - # search for the usual ID or name - service = utils.find_resource(identity_client.services, - parsed_args.service) - except exceptions.CommandError: - try: - # search for service type - service = identity_client.services.find( - type=parsed_args.service) - # FIXME(dtroyer): This exception should eventually come from - # common client exceptions - except identity_exc.NotFound: - msg = "No service with exists." - # TODO(mordred): Where does name_or_id come from? - # msg = ("No service with a type, name or ID of '%s' exists." % - # name_or_id) - raise exceptions.CommandError(msg) - info = {} - info.update(service._info) - return zip(*sorted(info.iteritems())) + if parsed_args.catalog: + endpoints = identity_client.service_catalog.get_endpoints( + service_type=parsed_args.service) + for (service, service_endpoints) in endpoints.iteritems(): + if service_endpoints: + info = {"type": service} + info.update(service_endpoints[0]) + return zip(*sorted(info.iteritems())) + + msg = ("No service catalog with a type, name or ID of '%s' " + "exists." % (parsed_args.service)) + raise exceptions.CommandError(msg) + else: + try: + # search for the usual ID or name + service = utils.find_resource( + identity_client.services, + parsed_args.service, + ) + except exceptions.CommandError: + try: + # search for service type + service = identity_client.services.find( + type=parsed_args.service) + # FIXME(dtroyer): This exception should eventually come from + # common client exceptions + except identity_exc.NotFound: + msg = ("No service with a type, name or ID of '%s' exists." + % parsed_args.service) + raise exceptions.CommandError(msg) + + info = {} + info.update(service._info) + return zip(*sorted(info.iteritems())) From 6146213e327729a2a48a09de35087ca2be9786e5 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Tue, 16 Jul 2013 13:52:59 -0500 Subject: [PATCH 0006/3699] Add list and delete authorizations for oauth commands * List user authorizations * Delete user authorization * Grouped the commands with oauth prefix Change-Id: I032ffa25181aad0fb4689f69cdca5a7adc6e29f1 --- openstackclient/identity/v3/oauth.py | 87 +++++++++++++++++++++++++++- setup.cfg | 14 +++-- 2 files changed, 94 insertions(+), 7 deletions(-) diff --git a/openstackclient/identity/v3/oauth.py b/openstackclient/identity/v3/oauth.py index b8f01e6458..bcbbdf7e09 100644 --- a/openstackclient/identity/v3/oauth.py +++ b/openstackclient/identity/v3/oauth.py @@ -26,7 +26,7 @@ class AuthenticateAccessToken(show.ShowOne): - """Authenticate access token - receive keystone token""" + """Authenticate access token to receive keystone token""" api = 'identity' log = logging.getLogger(__name__ + '.AuthenticateAccessToken') @@ -233,6 +233,36 @@ def take_action(self, parsed_args): return +class DeleteUserAuthorization(command.Command): + """Delete user authorization command""" + + log = logging.getLogger(__name__ + '.DeleteUserAuthorization') + + def get_parser(self, prog_name): + parser = super(DeleteUserAuthorization, self).get_parser(prog_name) + parser.add_argument( + 'user', + metavar='', + help='Name or Id of user', + ) + parser.add_argument( + 'access_id', + metavar='', + help='Access Id to be deleted', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + identity_client = self.app.client_manager.identity + user = utils.find_resource( + identity_client.users, parsed_args.user).id + identity_client.oauth.delete_authorization(user, + parsed_args.access_id) + return + + class ListConsumer(lister.Lister): """List consumer command""" @@ -249,6 +279,37 @@ def take_action(self, parsed_args): ) for s in data)) +class ListUserAuthorizations(lister.Lister): + """List user authorizations command""" + + log = logging.getLogger(__name__ + '.ListUserAuthorizations') + + def get_parser(self, prog_name): + parser = super(ListUserAuthorizations, self).get_parser(prog_name) + parser.add_argument( + 'user', + metavar='', + help='Name or Id of user', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + identity_client = self.app.client_manager.identity + user = utils.find_resource( + identity_client.users, parsed_args.user).id + + columns = ('Access Key', 'Consumer Key', 'Issued At', + 'Project Id', 'User Id', 'Requested Roles') + data = identity_client.oauth.list_authorizations(user) + return (columns, + (utils.get_item_properties( + s, columns, + formatters={}, + ) for s in data)) + + class SetConsumer(command.Command): """Set consumer command""" @@ -284,6 +345,30 @@ def take_action(self, parsed_args): return +class ShowAuthorizationPin(show.ShowOne): + """Show Authorization pin command""" + + log = logging.getLogger(__name__ + '.ShowAuthorizationPin') + + def get_parser(self, prog_name): + parser = super(ShowAuthorizationPin, self).get_parser(prog_name) + parser.add_argument( + 'request_id', + metavar='', + help='Show pin for request token', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + data = identity_client.oauth.get_authorization_pin( + parsed_args.request_id) + info = {} + info.update(data._info) + return zip(*sorted(info.iteritems())) + + class ShowConsumer(show.ShowOne): """Show consumer command""" diff --git a/setup.cfg b/setup.cfg index 2aae47df48..7447dbc50c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -69,9 +69,6 @@ openstack.identity.v2_0 = user_show = openstackclient.identity.v2_0.user:ShowUser openstack.identity.v3 = - access_token_authenticate = openstackclient.identity.v3.oauth:AuthenticateAccessToken - access_token_create = openstackclient.identity.v3.oauth:CreateAccessToken - consumer_create = openstackclient.identity.v3.oauth:CreateConsumer consumer_delete = openstackclient.identity.v3.oauth:DeleteConsumer consumer_list = openstackclient.identity.v3.oauth:ListConsumer @@ -105,6 +102,14 @@ openstack.identity.v3 = group_set = openstackclient.identity.v3.group:SetGroup group_show = openstackclient.identity.v3.group:ShowGroup + oauth_access_token_authenticate = openstackclient.identity.v3.oauth:AuthenticateAccessToken + oauth_access_token_create = openstackclient.identity.v3.oauth:CreateAccessToken + oauth_request_token_authorize = openstackclient.identity.v3.oauth:AuthorizeRequestToken + oauth_request_token_create = openstackclient.identity.v3.oauth:CreateRequestToken + oauth_authorization_delete = openstackclient.identity.v3.oauth:DeleteUserAuthorization + oauth_authorization_list = openstackclient.identity.v3.oauth:ListUserAuthorizations + oauth_authorization_show = openstackclient.identity.v3.oauth:ShowAuthorizationPin + policy_create = openstackclient.identity.v3.policy:CreatePolicy policy_delete = openstackclient.identity.v3.policy:DeletePolicy policy_list = openstackclient.identity.v3.policy:ListPolicy @@ -117,9 +122,6 @@ openstack.identity.v3 = project_set = openstackclient.identity.v3.project:SetProject project_show = openstackclient.identity.v3.project:ShowProject - request_token_authorize = openstackclient.identity.v3.oauth:AuthorizeRequestToken - request_token_create = openstackclient.identity.v3.oauth:CreateRequestToken - role_add = openstackclient.identity.v3.role:AddRole role_create = openstackclient.identity.v3.role:CreateRole role_delete = openstackclient.identity.v3.role:DeleteRole From 87104a28d75bd77df8b7ceb44600cd2b3971b4ae Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Wed, 3 Jul 2013 16:46:00 -0500 Subject: [PATCH 0007/3699] Add quota commands * Add quota set and quota show commands; these work on both the compute and volume APIs * Add the --class variation on the above commands Note: this replaces the existing volume-only quota commands and eliminates quota list Blueprint: cinder-client Bug: 1172064 Change-Id: I766d40e410e48f05e36e17e567a4f01a9411b40e --- openstackclient/common/quota.py | 192 +++++++++++++++++++++++++++++ openstackclient/volume/v1/quota.py | 114 ----------------- setup.cfg | 6 +- 3 files changed, 194 insertions(+), 118 deletions(-) create mode 100644 openstackclient/common/quota.py delete mode 100644 openstackclient/volume/v1/quota.py diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py new file mode 100644 index 0000000000..fd482da9e6 --- /dev/null +++ b/openstackclient/common/quota.py @@ -0,0 +1,192 @@ +# Copyright 2012 OpenStack Foundation +# +# 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. +# + +"""Quota action implementations""" + +import itertools +import logging +import six +import sys + +from cliff import command +from cliff import show + + +# List the quota items, map the internal argument name to the option +# name that the user sees. + +COMPUTE_QUOTAS = { + 'cores': 'cores', + 'fixed_ips': 'fixed-ips', + 'floating_ips': 'floating-ips', + 'injected_file_content_bytes': 'injected-file-size', + 'injected_file_path_bytes': 'injected-path-size', + 'injected_files': 'injected-files', + 'instances': 'instances', + 'key_pairs': 'key-pairs', + 'metadata_items': 'properties', + 'ram': 'ram', + 'security_group_rules': 'secgroup-rules', + 'security_groups': 'secgroups', +} + +VOLUME_QUOTAS = { + 'gigabytes': 'gigabytes', + 'snapshots': 'snapshots', + 'volumes': 'volumes', +} + + +class SetQuota(command.Command): + """Set quotas for project or class""" + + log = logging.getLogger(__name__ + '.SetQuota') + + def get_parser(self, prog_name): + parser = super(SetQuota, self).get_parser(prog_name) + parser.add_argument( + 'project', + metavar='', + help='Set quotas for this project or class (name/ID)', + ) + parser.add_argument( + '--class', + dest='quota_class', + action='store_true', + default=False, + help='Set quotas for ', + ) + for k, v in itertools.chain( + COMPUTE_QUOTAS.items(), VOLUME_QUOTAS.items()): + parser.add_argument( + '--%s' % v, + metavar='' % v, + type=int, + help='New value for the %s quota' % v, + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.volume + + compute_kwargs = {} + for k, v in COMPUTE_QUOTAS.items(): + if v in parsed_args: + compute_kwargs[k] = getattr(parsed_args, v, None) + + volume_kwargs = {} + for k, v in VOLUME_QUOTAS.items(): + if v in parsed_args: + volume_kwargs[k] = getattr(parsed_args, v, None) + + if compute_kwargs == {} and volume_kwargs == {}: + sys.stderr.write("No quotas updated") + return + + if parsed_args.quota_class: + if compute_kwargs: + compute_client.quota_classes.update( + parsed_args.project, + **compute_kwargs) + if volume_kwargs: + volume_client.quota_classes.update( + parsed_args.project, + **volume_kwargs) + else: + if compute_kwargs: + compute_client.quotas.update( + parsed_args.project, + **compute_kwargs) + if volume_kwargs: + volume_client.quotas.update( + parsed_args.project, + **volume_kwargs) + + +class ShowQuota(show.ShowOne): + """Show quotas for project or class""" + + log = logging.getLogger(__name__ + '.ShowQuota') + + def get_parser(self, prog_name): + parser = super(ShowQuota, self).get_parser(prog_name) + parser.add_argument( + 'project', + metavar='', + help='Show this project or class (name/ID)', + ) + type_group = parser.add_mutually_exclusive_group() + type_group.add_argument( + '--class', + dest='quota_class', + action='store_true', + default=False, + help='Show quotas for ', + ) + type_group.add_argument( + '--default', + dest='default', + action='store_true', + default=False, + help='Show default quotas for ' + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.volume + + # NOTE(dtroyer): These quota API calls do not validate the project + # or class arguments and return what appears to be + # the default quota values if the project or class + # does not exist. If this is determined to be the + # intended behaviour of the API we will validate + # the argument with Identity ourselves later. + if parsed_args.quota_class: + compute_quota = compute_client.quota_classes.get( + parsed_args.project) + volume_quota = volume_client.quota_classes.get( + parsed_args.project) + elif parsed_args.default: + compute_quota = compute_client.quotas.defaults( + parsed_args.project) + volume_quota = volume_client.quotas.defaults( + parsed_args.project) + else: + compute_quota = compute_client.quotas.get(parsed_args.project) + volume_quota = volume_client.quotas.get(parsed_args.project) + + info = {} + info.update(compute_quota._info) + info.update(volume_quota._info) + + # Map the internal quota names to the external ones + for k, v in itertools.chain( + COMPUTE_QUOTAS.items(), VOLUME_QUOTAS.items()): + if not k == v and info[k]: + info[v] = info[k] + info.pop(k) + + # Handle project ID special as it only appears in output + if info['id']: + info['project'] = info['id'] + info.pop('id') + + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/volume/v1/quota.py b/openstackclient/volume/v1/quota.py deleted file mode 100644 index 4f4e97e8f5..0000000000 --- a/openstackclient/volume/v1/quota.py +++ /dev/null @@ -1,114 +0,0 @@ -# Copyright 2012-2013 OpenStack, LLC. -# -# 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. -# - -"""Volume v1 Quota action implementations""" - -import logging -import sys - -from cliff import command -from cliff import show - -from openstackclient.common import utils - - -class ListQuota(show.ShowOne): - """List quota command""" - - api = 'volume' - log = logging.getLogger(__name__ + '.ListQuota') - - def get_parser(self, prog_name): - parser = super(ListQuota, self).get_parser(prog_name) - parser.add_argument( - 'tenant', - metavar='', - help='ID of tenant to list the default quotas for') - return parser - - def take_action(self, parsed_args): - self.log.debug('take_action(%s)' % parsed_args) - volume_client = self.app.client_manager.volume - defaults = volume_client.quotas.defaults(parsed_args.tenant) - - return zip(*sorted(defaults._info.iteritems())) - - -class SetQuota(command.Command): - """Set quota command""" - - api = 'volume' - log = logging.getLogger(__name__ + '.SetQuota') - - def get_parser(self, prog_name): - parser = super(SetQuota, self).get_parser(prog_name) - parser.add_argument( - 'tenant', - metavar='', - help='ID of tenant to set the quotas for') - parser.add_argument( - '--volumes', - metavar='', - type=int, - help='New value for the volumes quota') - parser.add_argument( - '--gigabytes', - metavar='', - type=int, - help='New value for the gigabytes quota') - return parser - - def take_action(self, parsed_args): - self.log.debug('take_action(%s)' % parsed_args) - - kwargs = {} - if parsed_args.volumes: - kwargs['volumes'] = parsed_args.volumes - if parsed_args.gigabytes: - kwargs['gigabytes'] = parsed_args.gigabytes - - if kwargs == {}: - sys.stdout.write("Quota not updated, no arguments present") - return - - volume_client = self.app.client_manager.volume - volume_client.quotas.update(parsed_args.tenant, - parsed_args.volumes, - parsed_args.gigabytes) - - return - - -class ShowQuota(show.ShowOne): - """Show quota command""" - - api = 'volume' - log = logging.getLogger(__name__ + '.ShowQuota') - - def get_parser(self, prog_name): - parser = super(ShowQuota, self).get_parser(prog_name) - parser.add_argument( - 'tenant', - metavar='', - help='ID of tenant to list the quotas for') - return parser - - def take_action(self, parsed_args): - self.log.debug('take_action(%s)' % parsed_args) - volume_client = self.app.client_manager.volume - quota = utils.find_resource(volume_client.quotas, - parsed_args.tenant) - - return zip(*sorted(quota._info.iteritems())) diff --git a/setup.cfg b/setup.cfg index bbda913db1..475e2077bb 100644 --- a/setup.cfg +++ b/setup.cfg @@ -33,6 +33,8 @@ openstack.cli = openstack.common = limits_show = openstackclient.common.limits:ShowLimits + quota_set = openstackclient.common.quota:SetQuota + quota_show = openstackclient.common.quota:ShowQuota openstack.identity.v2_0 = ec2_credentials_create = openstackclient.identity.v2_0.ec2creds:CreateEC2Creds @@ -203,10 +205,6 @@ openstack.compute.v2 = server_unpause = openstackclient.compute.v2.server:UnpauseServer openstack.volume.v1 = - quota_list = openstackclient.volume.v1.quota:ListQuota - quota_set = openstackclient.volume.v1.quota:SetQuota - quota_show = openstackclient.volume.v1.quota:ShowQuota - snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot snapshot_delete = openstackclient.volume.v1.snapshot:DeleteSnapshot snapshot_list = openstackclient.volume.v1.snapshot:ListSnapshot From cdaee1b71e21df56e6127689801240274af9d847 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Mon, 8 Jul 2013 16:48:10 -0500 Subject: [PATCH 0008/3699] Complete Image v1 * Add v1 versions of image delete, list, save, set, show * Change default Image API to v1 Rebased for https://review.openstack.org/#/c/36772/ Change-Id: Ie2bfe660aac8a0fcf651c67fd1ea4842e76ce377 --- openstackclient/image/client.py | 60 +++++- openstackclient/image/v1/image.py | 307 +++++++++++++++++++++++++--- openstackclient/image/v2/image.py | 65 +++--- openstackclient/shell.py | 4 +- openstackclient/tests/test_shell.py | 2 +- setup.cfg | 5 + 6 files changed, 383 insertions(+), 60 deletions(-) diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index 371605692a..70bef1c8a6 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -15,6 +15,9 @@ import logging +from glanceclient import exc as gc_exceptions +from glanceclient.v1 import client as gc_v1_client +from glanceclient.v1 import images as gc_v1_images from openstackclient.common import utils @@ -22,8 +25,8 @@ API_NAME = "image" API_VERSIONS = { - "1": "glanceclient.v1.client.Client", - "2": "glanceclient.v2.client.Client" + "1": "openstackclient.image.client.Client_v1", + "2": "glanceclient.v2.client.Client", } @@ -38,3 +41,54 @@ def make_client(instance): instance._url = instance.get_endpoint_for_service_type(API_NAME) return image_client(instance._url, token=instance._token) + + +# NOTE(dtroyer): glanceclient.v1.image.ImageManager() doesn't have a find() +# method so add one here until the common client libs arrive +# A similar subclass will be required for v2 + +class Client_v1(gc_v1_client.Client): + """An image v1 client that uses ImageManager_v1""" + + def __init__(self, *args, **kwargs): + super(Client_v1, self).__init__(*args, **kwargs) + self.images = ImageManager_v1(self) + + +class ImageManager_v1(gc_v1_images.ImageManager): + """Add find() and findall() to the ImageManager class""" + + def find(self, **kwargs): + """Find a single item with attributes matching ``**kwargs``. + + This isn't very efficient: it loads the entire list then filters on + the Python side. + """ + rl = self.findall(**kwargs) + num = len(rl) + + if num == 0: + raise gc_exceptions.NotFound + elif num > 1: + raise gc_exceptions.NoUniqueMatch + else: + return rl[0] + + def findall(self, **kwargs): + """Find all items with attributes matching ``**kwargs``. + + This isn't very efficient: it loads the entire list then filters on + the Python side. + """ + found = [] + searches = kwargs.items() + + for obj in self.list(): + try: + if all(getattr(obj, attr) == value + for (attr, value) in searches): + found.append(obj) + except AttributeError: + continue + + return found diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 5b4a939daa..0213ed1e28 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -1,4 +1,4 @@ -# Copyright 2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -17,6 +17,7 @@ import logging import os +import six import sys if os.name == "nt": @@ -24,11 +25,18 @@ else: msvcrt = None +from cliff import command +from cliff import lister from cliff import show +from glanceclient.common import utils as gc_utils +from openstackclient.common import exceptions +from openstackclient.common import parseractions +from openstackclient.common import utils + class CreateImage(show.ShowOne): - """Create image command""" + """Create/upload an image""" log = logging.getLogger(__name__ + ".CreateImage") @@ -37,89 +45,108 @@ def get_parser(self, prog_name): parser.add_argument( "name", metavar="", - help="Name of image.") + help="Name of image", + ) parser.add_argument( "--disk_format", default="raw", metavar="", - help="Disk format of image.") + help="Disk format of image", + ) parser.add_argument( "--id", metavar="", - help="ID of image to reserve.") + help="ID of image to reserve", + ) parser.add_argument( "--store", metavar="", - help="Store to upload image to.") + help="Store to upload image to", + ) parser.add_argument( "--container-format", default="bare", metavar="", - help="Container format of image.") + help="Container format of image", + ) parser.add_argument( "--owner", - metavar="", - help="Owner of the image.") + metavar="", + help="Owner of the image", + ) parser.add_argument( "--size", metavar="", help="Size of image in bytes. Only used with --location and" - " --copy-from.") + " --copy-from", + ) parser.add_argument( "--min-disk", metavar="", - help="Minimum size of disk needed to boot image in gigabytes.") + help="Minimum size of disk needed to boot image in gigabytes", + ) parser.add_argument( "--min-ram", metavar="", - help="Minimum amount of ram needed to boot image in megabytes.") + help="Minimum amount of ram needed to boot image in megabytes", + ) parser.add_argument( "--location", metavar="", - help="URL where the data for this image already resides.") + help="URL where the data for this image already resides", + ) parser.add_argument( "--file", metavar="", - help="Local file that contains disk image.") + help="Local file that contains disk image", + ) parser.add_argument( "--checksum", metavar="", - help="Hash of image data used for verification.") + help="Hash of image data used for verification", + ) parser.add_argument( "--copy-from", metavar="", help="Similar to --location, but this indicates that the image" - " should immediately be copied from the data store.") + " should immediately be copied from the data store", + ) parser.add_argument( "--property", + dest="properties", metavar="", - default=[], - action="append", - help="Arbitrary property to associate with image.") + action=parseractions.KeyValueAction, + help="Set property on this image " + '(repeat option to set multiple properties)', + ) protected_group = parser.add_mutually_exclusive_group() protected_group.add_argument( "--protected", dest="protected", action="store_true", - help="Prevent image from being deleted (default: False).") + help="Prevent image from being deleted (default: False)", + ) protected_group.add_argument( "--unprotected", dest="protected", action="store_false", default=False, - help="Allow images to be deleted (default: True).") + help="Allow images to be deleted (default: True)", + ) public_group = parser.add_mutually_exclusive_group() public_group.add_argument( "--public", dest="is_public", action="store_true", default=True, - help="Image is accessible to the public (default).") + help="Image is accessible to the public (default)", + ) public_group.add_argument( "--private", dest="is_public", action="store_false", - help="Image is inaccessible to the public.") + help="Image is inaccessible to the public", + ) return parser def take_action(self, parsed_args): @@ -134,11 +161,6 @@ def take_action(self, parsed_args): args.pop("prefix") args.pop("variables") - args["properties"] = {} - for _property in args.pop("property"): - key, value = _property.split("=", 1) - args["properties"][key] = value - if "location" not in args and "copy_from" not in args: if "file" in args: args["data"] = open(args.pop("file"), "rb") @@ -150,6 +172,231 @@ def take_action(self, parsed_args): args["data"] = sys.stdin image_client = self.app.client_manager.image - data = image_client.images.create(**args)._info.copy() + try: + image = utils.find_resource( + image_client.images, + parsed_args.name, + ) + except exceptions.CommandError: + # This is normal for a create or reserve (create w/o an image) + image = image_client.images.create(**args) + else: + # It must be an update + # If an image is specified via --file, --location or --copy-from + # let the API handle it + image = image_client.images.update(image, **args) + + info = {} + info.update(image._info) + return zip(*sorted(six.iteritems(info))) + + +class DeleteImage(command.Command): + """Delete an image""" + + log = logging.getLogger(__name__ + ".DeleteImage") + + def get_parser(self, prog_name): + parser = super(DeleteImage, self).get_parser(prog_name) + parser.add_argument( + "image", + metavar="", + help="Name or ID of image to delete", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + image_client = self.app.client_manager.image + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) + image_client.images.delete(image) + + +class ListImage(lister.Lister): + """List available images""" + + log = logging.getLogger(__name__ + ".ListImage") + + def get_parser(self, prog_name): + parser = super(ListImage, self).get_parser(prog_name) + parser.add_argument( + "--page-size", + metavar="", + help="Number of images to request in each paginated request", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + image_client = self.app.client_manager.image + + kwargs = {} + if parsed_args.page_size is not None: + kwargs["page_size"] = parsed_args.page_size + + data = image_client.images.list(**kwargs) + columns = ["ID", "Name"] + + return (columns, (utils.get_item_properties(s, columns) for s in data)) + + +class SaveImage(command.Command): + """Save an image locally""" + + log = logging.getLogger(__name__ + ".SaveImage") + + def get_parser(self, prog_name): + parser = super(SaveImage, self).get_parser(prog_name) + parser.add_argument( + "--file", + metavar="", + help="Downloaded image save filename [default: stdout]", + ) + parser.add_argument( + "image", + metavar="", + help="Name or ID of image to delete", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + image_client = self.app.client_manager.image + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) + data = image_client.images.data(image) + + gc_utils.save_image(data, parsed_args.file) + + +class SetImage(show.ShowOne): + """Change image properties""" + + log = logging.getLogger(__name__ + ".SetImage") + + def get_parser(self, prog_name): + parser = super(SetImage, self).get_parser(prog_name) + parser.add_argument( + "image", + metavar="", + help="Name or ID of image to change", + ) + parser.add_argument( + "--name", + metavar="", + help="Name of image", + ) + parser.add_argument( + "--owner", + metavar="", + help="Owner of the image", + ) + parser.add_argument( + "--min-disk", + metavar="", + help="Minimum size of disk needed to boot image in gigabytes", + ) + parser.add_argument( + "--min-ram", + metavar="", + help="Minimum amount of ram needed to boot image in megabytes", + ) + parser.add_argument( + "--property", + dest="properties", + metavar="", + action=parseractions.KeyValueAction, + help="Set property on this image " + '(repeat option to set multiple properties)', + ) + protected_group = parser.add_mutually_exclusive_group() + protected_group.add_argument( + "--protected", + dest="protected", + action="store_true", + help="Prevent image from being deleted (default: False)", + ) + protected_group.add_argument( + "--unprotected", + dest="protected", + action="store_false", + default=False, + help="Allow images to be deleted (default: True)", + ) + public_group = parser.add_mutually_exclusive_group() + public_group.add_argument( + "--public", + dest="is_public", + action="store_true", + default=True, + help="Image is accessible to the public (default)", + ) + public_group.add_argument( + "--private", + dest="is_public", + action="store_false", + help="Image is inaccessible to the public", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + # NOTE(jk0): Since create() takes kwargs, it's easiest to just make a + # copy of parsed_args and remove what we don't need. + args = vars(parsed_args) + args = dict(filter(lambda x: x[1] is not None, args.items())) + args.pop("columns") + args.pop("formatter") + args.pop("prefix") + args.pop("variables") + image_arg = args.pop("image") + + image_client = self.app.client_manager.image + image = utils.find_resource( + image_client.images, + image_arg, + ) + # Merge properties + args["properties"].update(image.properties) + image = image_client.images.update(image, **args) + + info = {} + info.update(image._info) + return zip(*sorted(six.iteritems(info))) + + +class ShowImage(show.ShowOne): + """Show image details""" + + log = logging.getLogger(__name__ + ".ShowImage") + + def get_parser(self, prog_name): + parser = super(ShowImage, self).get_parser(prog_name) + parser.add_argument( + "image", + metavar="", + help="Name or ID of image to display", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + image_client = self.app.client_manager.image + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) - return zip(*sorted(data.iteritems())) + info = {} + info.update(image._info) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 61273aa2c3..e84e0d0191 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Image V2 Action Implementations""" import logging +import six from cliff import command from cliff import lister @@ -26,27 +27,32 @@ class DeleteImage(command.Command): - """Delete image command""" + """Delete an image""" log = logging.getLogger(__name__ + ".DeleteImage") def get_parser(self, prog_name): parser = super(DeleteImage, self).get_parser(prog_name) parser.add_argument( - "id", - metavar="", - help="ID of image to delete.") + "image", + metavar="", + help="Name or ID of image to delete", + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) image_client = self.app.client_manager.image - image_client.images.delete(parsed_args.id) + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) + image_client.images.delete(image) class ListImage(lister.Lister): - """List image command""" + """List available images""" log = logging.getLogger(__name__ + ".ListImage") @@ -55,7 +61,8 @@ def get_parser(self, prog_name): parser.add_argument( "--page-size", metavar="", - help="Number of images to request in each paginated request.") + help="Number of images to request in each paginated request", + ) return parser def take_action(self, parsed_args): @@ -74,7 +81,7 @@ def take_action(self, parsed_args): class SaveImage(command.Command): - """Save image command""" + """Save an image locally""" log = logging.getLogger(__name__ + ".SaveImage") @@ -82,42 +89,52 @@ def get_parser(self, prog_name): parser = super(SaveImage, self).get_parser(prog_name) parser.add_argument( "--file", - metavar="", - help="Local file to save downloaded image data " - "to. If this is not specified the image " - "data will be written to stdout.") + metavar="", + help="Downloaded image save filename [default: stdout]", + ) parser.add_argument( - "id", - metavar="", - help="ID of image to describe.") + "image", + metavar="", + help="Name or ID of image to delete", + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) image_client = self.app.client_manager.image - data = image_client.images.data(parsed_args.id) + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) + data = image_client.images.data(image) gc_utils.save_image(data, parsed_args.file) class ShowImage(show.ShowOne): - """Show image command""" + """Show image details""" log = logging.getLogger(__name__ + ".ShowImage") def get_parser(self, prog_name): parser = super(ShowImage, self).get_parser(prog_name) parser.add_argument( - "id", - metavar="", - help="ID of image to describe.") + "image", + metavar="", + help="Name or ID of image to display", + ) return parser def take_action(self, parsed_args): self.log.debug("take_action(%s)" % parsed_args) image_client = self.app.client_manager.image - data = image_client.images.get(parsed_args.id) - - return zip(*sorted(data.iteritems())) + image = utils.find_resource( + image_client.images, + parsed_args.image, + ) + + info = {} + info.update(image._info) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index e5353194cd..cdfe2b1dbb 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -35,7 +35,7 @@ DEFAULT_COMPUTE_API_VERSION = '2' DEFAULT_IDENTITY_API_VERSION = '2.0' -DEFAULT_IMAGE_API_VERSION = '2' +DEFAULT_IMAGE_API_VERSION = '1' DEFAULT_VOLUME_API_VERSION = '1' DEFAULT_DOMAIN = 'default' diff --git a/openstackclient/tests/test_shell.py b/openstackclient/tests/test_shell.py index f479b11e4b..ca87997f67 100644 --- a/openstackclient/tests/test_shell.py +++ b/openstackclient/tests/test_shell.py @@ -36,7 +36,7 @@ LIB_COMPUTE_API_VERSION = "2" LIB_IDENTITY_API_VERSION = "2.0" -LIB_IMAGE_API_VERSION = "2" +LIB_IMAGE_API_VERSION = "1" LIB_VOLUME_API_VERSION = "1" diff --git a/setup.cfg b/setup.cfg index 2068a92a5a..027e7be4a0 100644 --- a/setup.cfg +++ b/setup.cfg @@ -141,6 +141,11 @@ openstack.identity.v3 = openstack.image.v1 = image_create = openstackclient.image.v1.image:CreateImage + image_delete = openstackclient.image.v1.image:DeleteImage + image_list = openstackclient.image.v1.image:ListImage + image_save = openstackclient.image.v1.image:SaveImage + image_set = openstackclient.image.v1.image:SetImage + image_show = openstackclient.image.v1.image:ShowImage openstack.image.v2 = image_delete = openstackclient.image.v2.image:DeleteImage From 2cc996356c438e45a876a6cb51a332d070044c1e Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 11 Jul 2013 15:41:18 -0500 Subject: [PATCH 0009/3699] Add aggregate commands * Add aggregate: add host, create, delete, list, remove host, set, show * Add list --long option * Filter 'availability_zone' from the metadata fields * Rename 'metadata' column to 'properties' in all output Bug: 1172032 Blueprint: nova-client Change-Id: Icd408c2b34af07f5102f53d3778d8546952a12c5 --- openstackclient/compute/v2/aggregate.py | 322 ++++++++++++++++++++++++ setup.cfg | 8 + 2 files changed, 330 insertions(+) create mode 100644 openstackclient/compute/v2/aggregate.py diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py new file mode 100644 index 0000000000..d786d7e5b0 --- /dev/null +++ b/openstackclient/compute/v2/aggregate.py @@ -0,0 +1,322 @@ +# Copyright 2012 OpenStack Foundation +# Copyright 2013 Nebula Inc. +# +# 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. +# + +"""Compute v2 Aggregate action implementations""" + +import logging +import six + +from cliff import command +from cliff import lister +from cliff import show + +from openstackclient.common import parseractions +from openstackclient.common import utils + + +class AddAggregateHost(show.ShowOne): + """Add host to aggregate""" + + log = logging.getLogger(__name__ + '.AddAggregateHost') + + def get_parser(self, prog_name): + parser = super(AddAggregateHost, self).get_parser(prog_name) + parser.add_argument( + 'aggregate', + metavar='', + help='Name or ID of aggregate', + ) + parser.add_argument( + 'host', + metavar='', + help='Host to add to aggregate', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + aggregate = utils.find_resource( + compute_client.aggregates, + parsed_args.aggregate, + ) + data = compute_client.aggregates.add_host(aggregate, parsed_args.host) + + info = {} + info.update(data._info) + return zip(*sorted(six.iteritems(info))) + + +class CreateAggregate(show.ShowOne): + """Create a new aggregate""" + + log = logging.getLogger(__name__ + ".CreateAggregate") + + def get_parser(self, prog_name): + parser = super(CreateAggregate, self).get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + help="New aggregate name", + ) + parser.add_argument( + "--zone", + metavar="", + help="Availability zone name", + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + help='Property to add to this aggregate ' + '(repeat option to set multiple properties)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + info = {} + data = compute_client.aggregates.create( + parsed_args.name, + parsed_args.zone, + ) + info.update(data._info) + + if parsed_args.property: + info.update(compute_client.aggregates.set_metadata( + data, + parsed_args.property, + )._info) + + return zip(*sorted(six.iteritems(info))) + + +class DeleteAggregate(command.Command): + """Delete an existing aggregate""" + + log = logging.getLogger(__name__ + '.DeleteAggregate') + + def get_parser(self, prog_name): + parser = super(DeleteAggregate, self).get_parser(prog_name) + parser.add_argument( + 'aggregate', + metavar='', + help='Name or ID of aggregate to delete', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + data = utils.find_resource( + compute_client.aggregates, + parsed_args.aggregate, + ) + compute_client.aggregates.delete(data.id) + return + + +class ListAggregate(lister.Lister): + """List all aggregates""" + + log = logging.getLogger(__name__ + ".ListAggregate") + + def get_parser(self, prog_name): + parser = super(ListAggregate, self).get_parser(prog_name) + parser.add_argument( + '--long', + action='store_true', + default=False, + help='List additional fields in output') + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + data = compute_client.aggregates.list() + + if parsed_args.long: + # Remove availability_zone from metadata because Nova doesn't + for d in data: + if 'availability_zone' in d.metadata: + d.metadata.pop('availability_zone') + # This is the easiest way to change column headers + column_headers = ( + "ID", + "Name", + "Availability Zone", + "Properties", + ) + columns = ( + "ID", + "Name", + "Availability Zone", + "Metadata", + ) + else: + column_headers = columns = ( + "ID", + "Name", + "Availability Zone", + ) + + return (column_headers, + (utils.get_item_properties( + s, columns, + ) for s in data)) + + +class RemoveAggregateHost(show.ShowOne): + """Remove host from aggregate""" + + log = logging.getLogger(__name__ + '.RemoveAggregateHost') + + def get_parser(self, prog_name): + parser = super(RemoveAggregateHost, self).get_parser(prog_name) + parser.add_argument( + 'aggregate', + metavar='', + help='Name or ID of aggregate', + ) + parser.add_argument( + 'host', + metavar='', + help='Host to remove from aggregate', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + aggregate = utils.find_resource( + compute_client.aggregates, + parsed_args.aggregate, + ) + data = compute_client.aggregates.remove_host( + aggregate, + parsed_args.host, + ) + + info = {} + info.update(data._info) + return zip(*sorted(six.iteritems(info))) + + +class SetAggregate(show.ShowOne): + """Set aggregate properties""" + + log = logging.getLogger(__name__ + '.SetAggregate') + + def get_parser(self, prog_name): + parser = super(SetAggregate, self).get_parser(prog_name) + parser.add_argument( + 'aggregate', + metavar='', + help='Name or ID of aggregate to display', + ) + parser.add_argument( + '--name', + metavar='', + help='New aggregate name', + ) + parser.add_argument( + "--zone", + metavar="", + help="Availability zone name", + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + help='Property to add/change for this aggregate ' + '(repeat option to set multiple properties)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + aggregate = utils.find_resource( + compute_client.aggregates, + parsed_args.aggregate, + ) + + info = {} + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + if parsed_args.zone: + kwargs['availability_zone'] = parsed_args.zone + if kwargs: + info.update(compute_client.aggregates.update( + aggregate, + kwargs + )._info) + + if parsed_args.property: + info.update(compute_client.aggregates.set_metadata( + aggregate, + parsed_args.property, + )._info) + + if info: + return zip(*sorted(six.iteritems(info))) + else: + return ({}, {}) + + +class ShowAggregate(show.ShowOne): + """Show a specific aggregate""" + + log = logging.getLogger(__name__ + '.ShowAggregate') + + def get_parser(self, prog_name): + parser = super(ShowAggregate, self).get_parser(prog_name) + parser.add_argument( + 'aggregate', + metavar='', + help='Name or ID of aggregate to display', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + data = utils.find_resource( + compute_client.aggregates, + parsed_args.aggregate, + ) + # Remove availability_zone from metadata because Nova doesn't + if 'availability_zone' in data.metadata: + data.metadata.pop('availability_zone') + # Map 'metadata' column to 'properties' + data._info.update({'properties': data._info.pop('metadata')}) + + info = {} + info.update(data._info) + return zip(*sorted(six.iteritems(info))) diff --git a/setup.cfg b/setup.cfg index 2068a92a5a..dade8c5885 100644 --- a/setup.cfg +++ b/setup.cfg @@ -154,6 +154,14 @@ openstack.compute.v2 = agent_list = openstackclient.compute.v2.agent:ListAgent agent_set = openstackclient.compute.v2.agent:SetAgent + aggregate_add_host = openstackclient.compute.v2.aggregate:AddAggregateHost + aggregate_create = openstackclient.compute.v2.aggregate:CreateAggregate + aggregate_delete = openstackclient.compute.v2.aggregate:DeleteAggregate + aggregate_list = openstackclient.compute.v2.aggregate:ListAggregate + aggregate_remove_host = openstackclient.compute.v2.aggregate:RemoveAggregateHost + aggregate_set = openstackclient.compute.v2.aggregate:SetAggregate + aggregate_show = openstackclient.compute.v2.aggregate:ShowAggregate + compute_service_list = openstackclient.compute.v2.service:ListService compute_service_set = openstackclient.compute.v2.service:SetService From b4904b0a4a83db57e9ed70dad5ebefab85201e2f Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sat, 20 Jul 2013 16:19:43 -0500 Subject: [PATCH 0010/3699] Add password field to set user Noticed this was missing in set user, the password from parsed args wasn't being passed in. Change-Id: I0eb748444b86b374265b6e1dd02f69a922a9b043 --- openstackclient/identity/v3/user.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index aac7027450..8ee535dc33 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -311,6 +311,8 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.email: kwargs['email'] = parsed_args.email + if parsed_args.password: + kwargs['password'] = parsed_args.password if parsed_args.description: kwargs['description'] = parsed_args.description if parsed_args.project: From 818c94875221f606ed56f276c1cbd320a9106754 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 18 Jul 2013 11:10:34 -0500 Subject: [PATCH 0011/3699] Clean up properties (metadata) formatting * Reformat default dict output to key='value' using utils.format_dict() * Changes utils.get_item_properties() to pass the specific field to the formatter function rather than the entire resource object, this allows the formatter to handle multiple attributes. * Updates server, volume, volume type commands Change-Id: I90eebf6b84ae200532f09cd925f371598ea54a64 --- openstackclient/common/utils.py | 28 +++++++--- openstackclient/compute/v2/server.py | 76 +++++++++++++++++++------- openstackclient/volume/v1/type.py | 46 ++++++++-------- openstackclient/volume/v1/volume.py | 82 ++++++++++++++++++---------- 4 files changed, 154 insertions(+), 78 deletions(-) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 06542887e2..2f2f5718f5 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -73,6 +73,20 @@ def find_resource(manager, name_or_id): raise +def format_dict(data): + """Return a formatted string of key value pairs + + :param data: a dict + :param format: optional formatting hints + :rtype: a string formatted to key='value' + """ + + output = "" + for s in data: + output = output + s + "='" + data[s] + "', " + return output[:-2] + + def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): """Return a tuple containing the item properties. @@ -85,14 +99,14 @@ def get_item_properties(item, fields, mixed_case_fields=[], formatters={}): row = [] for field in fields: + if field in mixed_case_fields: + field_name = field.replace(' ', '_') + else: + field_name = field.lower().replace(' ', '_') + data = getattr(item, field_name, '') if field in formatters: - row.append(formatters[field](item)) + row.append(formatters[field](data)) else: - if field in mixed_case_fields: - field_name = field.replace(' ', '_') - else: - field_name = field.lower().replace(' ', '_') - data = getattr(item, field_name, '') row.append(data) return tuple(row) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index e78144b084..a8c86a2e66 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -13,7 +13,7 @@ # under the License. # -"""Server action implementations""" +"""Compute v2 Server action implementations""" import logging import os @@ -25,17 +25,18 @@ from novaclient.v1_1 import servers from openstackclient.common import exceptions +from openstackclient.common import parseractions from openstackclient.common import utils -def _format_servers_list_networks(server): - """Return a string containing the networks a server is attached to. +def _format_servers_list_networks(networks): + """Return a formatted string of a server's networks - :param server: a single Server resource + :param server: a Server.networks field :rtype: a string of formatted network addresses """ output = [] - for (network, addresses) in server.networks.items(): + for (network, addresses) in networks.items(): if not addresses: continue addresses_csv = ', '.join(addresses) @@ -73,7 +74,12 @@ def _prep_server_detail(compute_client, server): # NOTE(dtroyer): novaclient splits these into separate entries... # Format addresses in a useful way - info['addresses'] = _format_servers_list_networks(server) + info['addresses'] = _format_servers_list_networks(server.networks) + + # Map 'metadata' field to 'properties' + info.update( + {'properties': utils.format_dict(info.pop('metadata'))} + ) # Remove values that are long and not too useful info.pop('links', None) @@ -116,7 +122,7 @@ def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5, class CreateServer(show.ShowOne): - """Create server command""" + """Create a new server""" log = logging.getLogger(__name__ + '.CreateServer') @@ -150,9 +156,8 @@ def get_parser(self, prog_name): parser.add_argument( '--property', metavar='', - action='append', - default=[], - help='Property to store for this server ' + action=parseractions.KeyValueAction, + help='Set a property on this server ' '(repeat for multiple values)') parser.add_argument( '--file', @@ -228,8 +233,6 @@ def take_action(self, parsed_args): boot_args = [parsed_args.server_name, image, flavor] - meta = dict(v.split('=', 1) for v in parsed_args.property) - files = {} for f in parsed_args.file: dst, src = f.split('=', 1) @@ -288,7 +291,7 @@ def take_action(self, parsed_args): config_drive = parsed_args.config_drive boot_kwargs = dict( - meta=meta, + meta=parsed_args.property, files=files, reservation_id=None, min_count=parsed_args.min, @@ -337,7 +340,7 @@ def take_action(self, parsed_args): class ListServer(lister.Lister): - """List server command""" + """List servers""" log = logging.getLogger(__name__ + '.ListServer') @@ -385,6 +388,11 @@ def get_parser(self, prog_name): action='store_true', default=bool(int(os.environ.get("ALL_TENANTS", 0))), help='display information from all tenants (admin only)') + parser.add_argument( + '--long', + action='store_true', + default=False, + help='Additional fields are listed in output') return parser def take_action(self, parsed_args): @@ -403,13 +411,43 @@ def take_action(self, parsed_args): 'all_tenants': parsed_args.all_tenants, } self.log.debug('search options: %s', search_opts) - # FIXME(dhellmann): Consider adding other columns - columns = ('ID', 'Name', 'Status', 'Networks') + + if parsed_args.long: + columns = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + 'Metadata', + ) + column_headers = ( + 'ID', + 'Name', + 'Status', + 'Networks', + 'Availability Zone', + 'Host', + 'Properties', + ) + mixed_case_fields = [ + 'OS-EXT-AZ:availability_zone', + 'OS-EXT-SRV-ATTR:host', + ] + else: + columns = ('ID', 'Name', 'Status', 'Networks') + column_headers = columns + mixed_case_fields = [] data = compute_client.servers.list(search_opts=search_opts) - return (columns, + return (column_headers, (utils.get_item_properties( s, columns, - formatters={'Networks': _format_servers_list_networks}, + mixed_case_fields=mixed_case_fields, + formatters={ + 'Networks': _format_servers_list_networks, + 'Metadata': utils.format_dict, + }, ) for s in data)) diff --git a/openstackclient/volume/v1/type.py b/openstackclient/volume/v1/type.py index dab21d9911..0d9fdb39de 100644 --- a/openstackclient/volume/v1/type.py +++ b/openstackclient/volume/v1/type.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -26,7 +26,7 @@ class CreateVolumeType(show.ShowOne): - """Create volume type command""" + """Create new volume type""" log = logging.getLogger(__name__ + '.CreateVolumeType') @@ -37,6 +37,13 @@ def get_parser(self, prog_name): metavar='', help='New volume type name', ) + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + help='Property to add for this volume type ' + '(repeat option to set multiple properties)', + ) return parser def take_action(self, parsed_args): @@ -45,6 +52,13 @@ def take_action(self, parsed_args): volume_type = volume_client.volume_types.create( parsed_args.name ) + if parsed_args.property: + volume_type.set_keys(parsed_args.property) + # Map 'extra_specs' column to 'properties' + volume_type._info.update( + {'properties': utils.format_dict( + volume_type._info.pop('extra_specs'))} + ) info = {} info.update(volume_type._info) @@ -52,7 +66,7 @@ def take_action(self, parsed_args): class DeleteVolumeType(command.Command): - """Delete volume type command""" + """Delete volume type""" log = logging.getLogger(__name__ + '.DeleteVolumeType') @@ -75,7 +89,7 @@ def take_action(self, parsed_args): class ListVolumeType(lister.Lister): - """List volume type command""" + """List volume types""" log = logging.getLogger(__name__ + '.ListVolumeType') @@ -92,18 +106,20 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) if parsed_args.long: columns = ('ID', 'Name', 'Extra Specs') + column_headers = ('ID', 'Name', 'Properties') else: columns = ('ID', 'Name') + column_headers = columns data = self.app.client_manager.volume.volume_types.list() - return (columns, + return (column_headers, (utils.get_item_properties( s, columns, - formatters={'Extra Specs': _format_type_list_extra_specs}, + formatters={'Extra Specs': utils.format_dict}, ) for s in data)) class SetVolumeType(command.Command): - """Set volume type command""" + """Set volume type property""" log = logging.getLogger(__name__ + '.SetVolumeType') @@ -136,7 +152,7 @@ def take_action(self, parsed_args): class UnsetVolumeType(command.Command): - """Unset volume type command""" + """Unset volume type property""" log = logging.getLogger(__name__ + '.UnsetVolumeType') @@ -173,17 +189,3 @@ def take_action(self, parsed_args): else: self.app.log.error("No changes requested\n") return - - -def _format_type_list_extra_specs(vol_type): - """Return a string containing the key value pairs - - :param server: a single VolumeType resource - :rtype: a string formatted to key=value - """ - - keys = vol_type.get_keys() - output = "" - for s in keys: - output = output + s + "=" + keys[s] + "; " - return output diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index f1e421f488..b74fe45249 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -26,7 +26,7 @@ class CreateVolume(show.ShowOne): - """Create volume command""" + """Create new volume""" log = logging.getLogger(__name__ + '.CreateVolume') @@ -119,12 +119,16 @@ def take_action(self, parsed_args): parsed_args.property, parsed_args.image ) + # Map 'metadata' column to 'properties' + volume._info.update( + {'properties': utils.format_dict(volume._info.pop('metadata'))} + ) return zip(*sorted(volume._info.iteritems())) class DeleteVolume(command.Command): - """Delete volume command""" + """Delete volume""" log = logging.getLogger(__name__ + '.DeleteVolume') @@ -157,7 +161,7 @@ def take_action(self, parsed_args): class ListVolume(lister.Lister): - """List volume command""" + """List volumes""" log = logging.getLogger(__name__ + '.ListVolume') @@ -190,12 +194,42 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - columns = ('ID', 'Status', 'Display Name', 'Size', - 'Volume Type', 'Bootable', 'Attached to') if parsed_args.long: - columns = ('ID', 'Status', 'Display Name', 'Size', - 'Volume Type', 'Bootable', 'Attached to', 'Meta-data') - + columns = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Volume Type', + 'Bootable', + 'Attached to', + 'Metadata', + ) + column_headers = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Type', + 'Bootable', + 'Attached', + 'Properties', + ) + else: + columns = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Attached to', + ) + column_headers = ( + 'ID', + 'Display Name', + 'Status', + 'Size', + 'Attached', + ) search_opts = { 'all_tenants': parsed_args.all_tenants, 'display_name': parsed_args.name, @@ -205,15 +239,15 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.volume data = volume_client.volumes.list(search_opts=search_opts) - return (columns, + return (column_headers, (utils.get_item_properties( s, columns, - formatters={'Meta-data': _format_meta_data}, + formatters={'Metadata': utils.format_dict}, ) for s in data)) class SetVolume(command.Command): - """Set volume command""" + """Set volume properties""" log = logging.getLogger(__name__ + '.SetVolume') @@ -249,7 +283,6 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) if parsed_args.property: - print "property: %s" % parsed_args.property volume_client.volumes.set_metadata(volume.id, parsed_args.property) kwargs = {} @@ -258,7 +291,6 @@ def take_action(self, parsed_args): if parsed_args.description: kwargs['display_description'] = parsed_args.description if kwargs: - print "kwargs: %s" % kwargs volume_client.volumes.update(volume.id, **kwargs) if not kwargs and not parsed_args.property: @@ -268,7 +300,7 @@ def take_action(self, parsed_args): class ShowVolume(show.ShowOne): - """Show volume command""" + """Show specific volume""" log = logging.getLogger(__name__ + '.ShowVolume') @@ -285,12 +317,16 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) volume_client = self.app.client_manager.volume volume = utils.find_resource(volume_client.volumes, parsed_args.volume) + # Map 'metadata' column to 'properties' + volume._info.update( + {'properties': utils.format_dict(volume._info.pop('metadata'))} + ) return zip(*sorted(volume._info.iteritems())) class UnsetVolume(command.Command): - """Unset volume command""" + """Unset volume properties""" log = logging.getLogger(__name__ + '.UnsetVolume') @@ -325,17 +361,3 @@ def take_action(self, parsed_args): else: self.app.log.error("No changes requested\n") return - - -def _format_meta_data(volume): - """Return a string containing the key value pairs - - :param server: a single volume resource - :rtype: a string formatted to key=value - """ - - keys = volume.metadata - output = "" - for s in keys: - output = output + s + "=" + keys[s] + "; " - return output From 8dd9feb6434243c72f8bd27994418be6409c6c96 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 21 Jul 2013 14:45:22 -0500 Subject: [PATCH 0012/3699] Change volume manager to volume type, unset property for type In the unset method in volume_type, it was calling the volume manager, instead of the volume_type. Bug: 1203561 Change-Id: Iea1a9214db90f15815a456955040c0c5a795ff3d --- openstackclient/volume/v1/type.py | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) diff --git a/openstackclient/volume/v1/type.py b/openstackclient/volume/v1/type.py index dab21d9911..78fc322ed2 100644 --- a/openstackclient/volume/v1/type.py +++ b/openstackclient/volume/v1/type.py @@ -166,10 +166,7 @@ def take_action(self, parsed_args): ) if parsed_args.property: - volume_client.volumes.delete_metadata( - volume_type.id, - parsed_args.property, - ) + volume_type.unset_keys(parsed_args.property) else: self.app.log.error("No changes requested\n") return From 61beeb7e203eda719e167d1fd2740f0b3225e11f Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Mon, 22 Jul 2013 10:23:51 -0500 Subject: [PATCH 0013/3699] Fix --password in server rebuild Use correct attribute to get password in server rebuild command. Fixes bug 1190722 Change-Id: Ibe2ccb8840a385319781885b8aadca6e1ba4cc43 --- openstackclient/compute/v2/server.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index e78144b084..08742f37d8 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -521,8 +521,8 @@ def take_action(self, parsed_args): compute_client.servers, parsed_args.server) _password = None - if parsed_args.rebuild_password is not False: - _password = parsed_args.rebuild_password + if parsed_args.password is not False: + _password = parsed_args.password kwargs = {} server = server.rebuild(image, _password, **kwargs) From 3789cdfebe2d84fd77a4549dbdb08346f5d8e280 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 21 Jul 2013 03:28:27 -0500 Subject: [PATCH 0014/3699] Add server diagnose for compute api Add server diagnose for compute api as per blueprint: nova-client Change-Id: I0a2c13e36e1e13f61ef4ba00ec146634f9644648 --- openstackclient/compute/v2/server.py | 18 +++++++++++++++--- 1 file changed, 15 insertions(+), 3 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index a8c86a2e66..5922f759c7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -17,6 +17,7 @@ import logging import os +import sys import time from cliff import command @@ -599,7 +600,6 @@ def take_action(self, parsed_args): class ShowServer(show.ShowOne): """Show server command""" - api = 'compute' log = logging.getLogger(__name__ + '.ShowServer') def get_parser(self, prog_name): @@ -608,6 +608,11 @@ def get_parser(self, prog_name): 'server', metavar='', help='Name or ID of server to display') + parser.add_argument( + '--diagnostics', + action='store_true', + default=False, + help='Display diagnostics information for a given server') return parser def take_action(self, parsed_args): @@ -616,8 +621,15 @@ def take_action(self, parsed_args): server = utils.find_resource(compute_client.servers, parsed_args.server) - details = _prep_server_detail(compute_client, server) - return zip(*sorted(details.iteritems())) + if parsed_args.diagnostics: + (resp, data) = server.diagnostics() + if not resp.status_code == 200: + sys.stderr.write("Error retrieving diagnostics data") + return ({}, {}) + else: + data = _prep_server_detail(compute_client, server) + + return zip(*sorted(data.iteritems())) class SuspendServer(command.Command): From fcc34652ecad4029998e45c32016af1b03256fc5 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Sun, 21 Jul 2013 02:52:25 -0500 Subject: [PATCH 0015/3699] Add usage command for compute api As per the blueprint: nova-client, adding usage command for compute Change-Id: Ib694b0b1ebf56b2a62b6f09c67ffaa6959911605 --- openstackclient/compute/v2/usage.py | 91 +++++++++++++++++++++++++++++ setup.cfg | 2 + 2 files changed, 93 insertions(+) create mode 100644 openstackclient/compute/v2/usage.py diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py new file mode 100644 index 0000000000..0bfbc9b84d --- /dev/null +++ b/openstackclient/compute/v2/usage.py @@ -0,0 +1,91 @@ +# Copyright 2013 OpenStack Foundation. +# +# 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. +# + +"""Usage action implementations""" + +import datetime +import logging + +from cliff import lister + +from openstackclient.common import utils + + +class ListUsage(lister.Lister): + """List resource usage per project. """ + + log = logging.getLogger(__name__ + ".ListUsage") + + def get_parser(self, prog_name): + parser = super(ListUsage, self).get_parser(prog_name) + parser.add_argument( + "--start", + metavar="", + default=None, + help="Usage range start date ex 2012-01-20" + " (default: 4 weeks ago)." + ) + parser.add_argument( + "--end", + metavar="", + default=None, + help="Usage range end date, ex 2012-01-20 (default: tomorrow)" + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + compute_client = self.app.client_manager.compute + columns = ( + "tenant_id", + "total_memory_mb_usage", + "total_vcpus_usage", + "total_local_gb_usage" + ) + column_headers = ( + "Project ID", + "RAM MB-Hours", + "CPU Hours", + "Disk GB-Hours" + ) + + dateformat = "%Y-%m-%d" + now = datetime.datetime.utcnow() + + if parsed_args.start: + start = datetime.datetime.strptime(parsed_args.start, dateformat) + else: + start = now - datetime.timedelta(weeks=4) + + if parsed_args.end: + end = datetime.datetime.strptime(parsed_args.end, dateformat) + else: + end = now + datetime.timedelta(days=1) + + usage_list = compute_client.usage.list(start, end) + + if len(usage_list) > 0: + print("Usage from %s to %s:" % (start.strftime(dateformat), + end.strftime(dateformat))) + + return (column_headers, + (utils.get_item_properties( + s, columns, + formatters={ + 'total_memory_mb_usage': lambda x: float("%.2f" % x), + 'total_vcpus_usage': lambda x: float("%.2f" % x), + 'total_local_gb_usage': lambda x: float("%.2f" % x), + }, + ) for s in usage_list)) diff --git a/setup.cfg b/setup.cfg index b3a6657bf7..60fa17f897 100644 --- a/setup.cfg +++ b/setup.cfg @@ -208,6 +208,8 @@ openstack.compute.v2 = keypair_list = openstackclient.compute.v2.keypair:ListKeypair keypair_show = openstackclient.compute.v2.keypair:ShowKeypair + project_usage_list = openstackclient.compute.v2.usage:ListUsage + server_create = openstackclient.compute.v2.server:CreateServer server_delete = openstackclient.compute.v2.server:DeleteServer server_list = openstackclient.compute.v2.server:ListServer From 3ff6378c23ab103647310d5d64b1e51bf3cdcf04 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 25 Jul 2013 12:17:25 -0500 Subject: [PATCH 0016/3699] Add server commands: (un)lock, (un)rescue, (un)set, add/remove volume * server lock/unlock, rescue/unrescue, set/unset * add/remove volume Blueprint: nova-client Change-Id: I3709ecdb297ab15ad44df09d89af840164271a66 --- openstackclient/compute/v2/server.py | 343 +++++++++++++++++++++++++-- setup.cfg | 8 + 2 files changed, 325 insertions(+), 26 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index ce2390d7d3..009b49799e 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -15,8 +15,10 @@ """Compute v2 Server action implementations""" +import getpass import logging import os +import six import sys import time @@ -122,6 +124,52 @@ def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5, return retval +class AddServerVolume(command.Command): + """Add volume to server""" + + log = logging.getLogger(__name__ + '.AddServerVolume') + + def get_parser(self, prog_name): + parser = super(AddServerVolume, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + parser.add_argument( + 'volume', + metavar='', + help='Volume to add (name or ID)', + ) + parser.add_argument( + '--device', + metavar='', + help='Server internal device name for volume', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.volume + + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + volume = utils.find_resource( + volume_client.volumes, + parsed_args.volume, + ) + + compute_client.volumes.create_server_volume( + server.id, + volume.id, + parsed_args.device, + ) + + class CreateServer(show.ShowOne): """Create a new server""" @@ -452,8 +500,32 @@ def take_action(self, parsed_args): ) for s in data)) +class LockServer(command.Command): + """Lock server""" + + log = logging.getLogger(__name__ + '.LockServer') + + def get_parser(self, prog_name): + parser = super(LockServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).lock() + + class PauseServer(command.Command): - """Pause server command""" + """Pause server""" log = logging.getLogger(__name__ + '.PauseServer') @@ -462,16 +534,18 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to pause') + help='Server (name or ID)', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) + compute_client = self.app.client_manager.compute - server = utils.find_resource( - compute_client.servers, parsed_args.server) - server.pause() - return + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).pause() class RebootServer(command.Command): @@ -575,8 +649,73 @@ def take_action(self, parsed_args): return zip(*sorted(details.iteritems())) +class RemoveServerVolume(command.Command): + """Remove volume from server""" + + log = logging.getLogger(__name__ + '.RemoveServerVolume') + + def get_parser(self, prog_name): + parser = super(RemoveServerVolume, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + parser.add_argument( + 'volume', + metavar='', + help='Volume to remove (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + volume_client = self.app.client_manager.volume + + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + volume = utils.find_resource( + volume_client.volumes, + parsed_args.volume, + ) + + compute_client.volumes.delete_server_volume( + server.id, + volume.id, + ) + + +class RescueServer(show.ShowOne): + """Put server in rescue mode""" + + log = logging.getLogger(__name__ + '.RescueServer') + + def get_parser(self, prog_name): + parser = super(RescueServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ).rescue() + return zip(*sorted(six.iteritems(server._info))) + + class ResumeServer(command.Command): - """Resume server command""" + """Resume server""" log = logging.getLogger(__name__ + '.ResumeServer') @@ -585,20 +724,81 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to resume') + help='Server (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + utils.find_resource( + compute_client.servers, + parsed_args.server, + ) .resume() + + +class SetServer(command.Command): + """Set server properties""" + + log = logging.getLogger(__name__ + '.SetServer') + + def get_parser(self, prog_name): + parser = super(SetServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + parser.add_argument( + '--name', + metavar='', + help='New server name', + ) + parser.add_argument( + '--root-password', + action="store_true", + help='Set new root password (interactive only)', + ) + parser.add_argument( + "--property", + metavar="", + action=parseractions.KeyValueAction, + help='Property to add/change for this server ' + '(repeat option to set multiple properties)', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) + compute_client = self.app.client_manager.compute server = utils.find_resource( - compute_client.servers, parsed_args.server) - server.resume() - return + compute_client.servers, + parsed_args.server, + ) + + if parsed_args.name: + server.update(name=parsed_args.name) + + if parsed_args.property: + compute_client.servers.set_meta( + server, + parsed_args.property, + ) + + if parsed_args.root_password: + p1 = getpass.getpass('New password: ') + p2 = getpass.getpass('Retype new password: ') + if p1 == p2: + server.change_password(p1) + else: + raise exceptions.CommandError( + "Passwords do not match, password unchanged") class ShowServer(show.ShowOne): - """Show server command""" + """Show server details""" log = logging.getLogger(__name__ + '.ShowServer') @@ -607,12 +807,14 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to display') + help='Server to show (name or ID)', + ) parser.add_argument( '--diagnostics', action='store_true', default=False, - help='Display diagnostics information for a given server') + help='Display diagnostics information for a given server', + ) return parser def take_action(self, parsed_args): @@ -633,7 +835,7 @@ def take_action(self, parsed_args): class SuspendServer(command.Command): - """Suspend server command""" + """Suspend server""" log = logging.getLogger(__name__ + '.SuspendServer') @@ -642,20 +844,46 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to suspend') + help='Server (name or ID)', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) + compute_client = self.app.client_manager.compute - server = utils.find_resource(compute_client.servers, - parsed_args.server) - server.suspend() - return + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).suspend() + + +class UnlockServer(command.Command): + """Unlock server""" + + log = logging.getLogger(__name__ + '.UnlockServer') + + def get_parser(self, prog_name): + parser = super(UnlockServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).unlock() class UnpauseServer(command.Command): - """Unpause server command""" + """Unpause server""" log = logging.getLogger(__name__ + '.UnpauseServer') @@ -664,13 +892,76 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to unpause') + help='Server (name or ID)', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) + compute_client = self.app.client_manager.compute - server = utils.find_resource(compute_client.servers, - parsed_args.server) - server.unpause() - return + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).unpause() + + +class UnrescueServer(command.Command): + """Restore server from rescue mode""" + + log = logging.getLogger(__name__ + '.UnrescueServer') + + def get_parser(self, prog_name): + parser = super(UnrescueServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + utils.find_resource( + compute_client.servers, + parsed_args.server, + ).unrescue() + + +class UnsetServer(command.Command): + """Unset server properties""" + + log = logging.getLogger(__name__ + '.UnsetServer') + + def get_parser(self, prog_name): + parser = super(UnsetServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + parser.add_argument( + '--property', + metavar='', + action='append', + default=[], + help='Property key to remove from server ' + '(repeat to set multiple values)', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + compute_client = self.app.client_manager.compute + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + + if parsed_args.property: + compute_client.servers.delete_meta( + server, + parsed_args.property, + ) diff --git a/setup.cfg b/setup.cfg index 60fa17f897..2fc34f37ea 100644 --- a/setup.cfg +++ b/setup.cfg @@ -210,16 +210,24 @@ openstack.compute.v2 = project_usage_list = openstackclient.compute.v2.usage:ListUsage + server_add_volume = openstackclient.compute.v2.server:AddServerVolume server_create = openstackclient.compute.v2.server:CreateServer server_delete = openstackclient.compute.v2.server:DeleteServer server_list = openstackclient.compute.v2.server:ListServer + server_lock = openstackclient.compute.v2.server:LockServer server_pause = openstackclient.compute.v2.server:PauseServer server_reboot = openstackclient.compute.v2.server:RebootServer server_rebuild = openstackclient.compute.v2.server:RebuildServer + server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume + server_rescue = openstackclient.compute.v2.server:RescueServer server_resume = openstackclient.compute.v2.server:ResumeServer + server_set = openstackclient.compute.v2.server:SetServer server_show = openstackclient.compute.v2.server:ShowServer server_suspend = openstackclient.compute.v2.server:SuspendServer + server_unlock = openstackclient.compute.v2.server:UnlockServer server_unpause = openstackclient.compute.v2.server:UnpauseServer + server_unrescue = openstackclient.compute.v2.server:UnrescueServer + server_unset = openstackclient.compute.v2.server:UnsetServer openstack.volume.v1 = snapshot_create = openstackclient.volume.v1.snapshot:CreateSnapshot From 3cc313a60db6d1a5a89c572ed10bca11402912d4 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Fri, 26 Jul 2013 15:00:21 -0500 Subject: [PATCH 0017/3699] Add server migrate command Blueprint: nova-client Note: I've tested that the API calls are made correctly but do not have an environment with migration proerly enabled to watch it complete... Change-Id: Ideaf0985d43aa2be22390cf0d2850124c549632d --- openstackclient/compute/v2/server.py | 92 ++++++++++++++++++++++++++++ setup.cfg | 1 + 2 files changed, 93 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 009b49799e..8c31e5d9e7 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -524,6 +524,98 @@ def take_action(self, parsed_args): ).lock() +# FIXME(dtroyer): Here is what I want, how with argparse/cliff? +# server migrate [--wait] \ +# [--live +# [--shared-migration | --block-migration] +# [--disk-overcommit | --no-disk-overcommit]] +# +# +# live_parser = parser.add_argument_group(title='Live migration options') +# then adding the groups doesn't seem to work + +class MigrateServer(command.Command): + """Migrate server to different host""" + + log = logging.getLogger(__name__ + '.MigrateServer') + + def get_parser(self, prog_name): + parser = super(MigrateServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server to migrate (name or ID)', + ) + parser.add_argument( + '--wait', + action='store_true', + help='Wait for resize to complete', + ) + parser.add_argument( + '--live', + metavar='', + help='Target hostname', + ) + migration_group = parser.add_mutually_exclusive_group() + migration_group.add_argument( + '--shared-migration', + dest='shared_migration', + action='store_true', + default=True, + help='Perform a shared live migration (default)', + ) + migration_group.add_argument( + '--block-migration', + dest='shared_migration', + action='store_false', + help='Perform a block live migration', + ) + disk_group = parser.add_mutually_exclusive_group() + disk_group.add_argument( + '--no-disk-overcommit', + dest='disk_overcommit', + action='store_false', + default=False, + help='Do not over-commit disk on the destination host (default)', + ) + disk_group.add_argument( + '--disk-overcommit', + action='store_true', + default=False, + help='Allow disk over-commit on the destination host', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + if parsed_args.live: + server.live_migrate( + parsed_args.live, + parsed_args.shared_migration, + parsed_args.disk_overcommit, + ) + else: + server.migrate() + + if parsed_args.wait: + if utils.wait_for_status( + compute_client.servers.get, + server.id, + #callback=_show_progress, + ): + sys.stdout.write('Complete\n') + else: + sys.stdout.write('\nError migrating server') + raise SystemExit + + class PauseServer(command.Command): """Pause server""" diff --git a/setup.cfg b/setup.cfg index 2fc34f37ea..c2a744f3d6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -215,6 +215,7 @@ openstack.compute.v2 = server_delete = openstackclient.compute.v2.server:DeleteServer server_list = openstackclient.compute.v2.server:ListServer server_lock = openstackclient.compute.v2.server:LockServer + server_migrate = openstackclient.compute.v2.server:MigrateServer server_pause = openstackclient.compute.v2.server:PauseServer server_reboot = openstackclient.compute.v2.server:RebootServer server_rebuild = openstackclient.compute.v2.server:RebuildServer From 65d2a14e3e834ce0c57c879ec7d42715058254bf Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 25 Jul 2013 18:00:33 -0500 Subject: [PATCH 0018/3699] Add server resize command * add server resize * update --wait handling for server create, reboot, rebuild * move _wait_for_status to utils Blueprint: nova-client Rebased after https://review.openstack.org/38162 was committed Change-Id: I7a43b996feecadc7628fcfe20cd5b17333762739 --- openstackclient/common/utils.py | 33 +++++ openstackclient/compute/v2/server.py | 179 +++++++++++++++++---------- setup.cfg | 1 + 3 files changed, 149 insertions(+), 64 deletions(-) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 2f2f5718f5..fd504ea17d 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -17,6 +17,7 @@ import os import sys +import time import uuid from openstackclient.common import exceptions @@ -155,3 +156,35 @@ def get_client_class(api_name, version, version_map): raise exceptions.UnsupportedVersion(msg) return import_class(client_path) + + +def wait_for_status(status_f, + res_id, + status_field='status', + success_status=['active'], + sleep_time=5, + callback=None): + """Wait for status change on a resource during a long-running operation + + :param status_f: a status function that takes a single id argument + :param res_id: the resource id to watch + :param success_status: a list of status strings for successful completion + :param status_field: the status attribute in the returned resource object + :param sleep_time: wait this long (seconds) + :param callback: called per sleep cycle, useful to display progress + :rtype: True on success + """ + while True: + res = status_f(res_id) + status = getattr(res, status_field, '').lower() + if status in success_status: + retval = True + break + elif status == 'error': + retval = False + break + if callback: + progress = getattr(res, 'progress', None) or 0 + callback(progress) + time.sleep(sleep_time) + return retval diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8c31e5d9e7..8f81dfdb2f 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -20,7 +20,6 @@ import os import six import sys -import time from cliff import command from cliff import lister @@ -90,38 +89,10 @@ def _prep_server_detail(compute_client, server): return info -def _wait_for_status(poll_fn, obj_id, final_ok_states, poll_period=5, - status_field="status"): - """Block while an action is being performed - - :param poll_fn: a function to retrieve the state of the object - :param obj_id: the id of the object - :param final_ok_states: a tuple of the states of the object that end the - wait as success, ex ['active'] - :param poll_period: the wait time between checks of object status - :param status_field: field name containing the status to be checked - """ - log = logging.getLogger(__name__ + '._wait_for_status') - while True: - obj = poll_fn(obj_id) - - status = getattr(obj, status_field) - - if status: - status = status.lower() - - if status in final_ok_states: - log.debug('Wait terminated with success') - retval = True - break - elif status == "error": - log.error('Wait terminated with an error') - retval = False - break - - time.sleep(poll_period) - - return retval +def _show_progress(progress): + if progress: + sys.stdout.write('\rProgress: %s' % progress) + sys.stdout.flush() class AddServerVolume(command.Command): @@ -263,9 +234,9 @@ def get_parser(self, prog_name): help='Maximum number of servers to launch (default=1)') parser.add_argument( '--wait', - dest='wait', action='store_true', - help='Wait for servers to become active') + help='Wait for build to complete', + ) return parser def take_action(self, parsed_args): @@ -359,8 +330,17 @@ def take_action(self, parsed_args): server = compute_client.servers.create(*boot_args, **boot_kwargs) if parsed_args.wait: - _wait_for_status(compute_client.servers.get, server._info['id'], - ['active']) + if utils.wait_for_status( + compute_client.servers.get, + server.id, + callback=_show_progress, + ): + sys.stdout.write('\n') + else: + self.log.error('Error creating server: %s' % + parsed_args.server_name) + sys.stdout.write('\nError creating server') + raise SystemExit details = _prep_server_detail(compute_client, server) return zip(*sorted(details.iteritems())) @@ -641,7 +621,7 @@ def take_action(self, parsed_args): class RebootServer(command.Command): - """Reboot server command""" + """Perform a hard or soft server reboot""" log = logging.getLogger(__name__ + '.RebootServer') @@ -650,7 +630,8 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Name or ID of server to reboot') + help='Server (name or ID)', + ) group = parser.add_mutually_exclusive_group() group.add_argument( '--hard', @@ -658,19 +639,21 @@ def get_parser(self, prog_name): action='store_const', const=servers.REBOOT_HARD, default=servers.REBOOT_SOFT, - help='Perform a hard reboot') + help='Perform a hard reboot', + ) group.add_argument( '--soft', dest='reboot_type', action='store_const', const=servers.REBOOT_SOFT, default=servers.REBOOT_SOFT, - help='Perform a soft reboot') + help='Perform a soft reboot', + ) parser.add_argument( '--wait', - dest='wait', action='store_true', - help='Wait for server to become active to return') + help='Wait for reboot to complete', + ) return parser def take_action(self, parsed_args): @@ -681,14 +664,19 @@ def take_action(self, parsed_args): server.reboot(parsed_args.reboot_type) if parsed_args.wait: - _wait_for_status(compute_client.servers.get, server.id, - ['active']) - - return + if utils.wait_for_status( + compute_client.servers.get, + server.id, + callback=_show_progress, + ): + sys.stdout.write('\nReboot complete\n') + else: + sys.stdout.write('\nError rebooting server\n') + raise SystemExit class RebuildServer(show.ShowOne): - """Rebuild server command""" + """Rebuild server""" log = logging.getLogger(__name__ + '.RebuildServer') @@ -697,22 +685,24 @@ def get_parser(self, prog_name): parser.add_argument( 'server', metavar='', - help='Server name or ID') + help='Server (name or ID)', + ) parser.add_argument( '--image', metavar='', required=True, - help='Recreate server from this image') + help='Recreate server from this image', + ) parser.add_argument( '--password', metavar='', - default=False, - help="Set the provided password on the rebuild instance") + help="Set the password on the rebuilt instance", + ) parser.add_argument( '--wait', - dest='wait', action='store_true', - help='Wait for server to become active to return') + help='Wait for rebuild to complete', + ) return parser def take_action(self, parsed_args): @@ -725,17 +715,17 @@ def take_action(self, parsed_args): server = utils.find_resource( compute_client.servers, parsed_args.server) - _password = None - if parsed_args.password is not False: - _password = parsed_args.password - - kwargs = {} - server = server.rebuild(image, _password, **kwargs) - - # TODO(dtroyer): force silent=True if output filter != table + server = server.rebuild(image, parsed_args.password) if parsed_args.wait: - _wait_for_status(compute_client.servers.get, server._info['id'], - ['active']) + if utils.wait_for_status( + compute_client.servers.get, + server.id, + callback=_show_progress, + ): + sys.stdout.write('\nComplete\n') + else: + sys.stdout.write('\nError rebuilding server') + raise SystemExit details = _prep_server_detail(compute_client, server) return zip(*sorted(details.iteritems())) @@ -806,6 +796,67 @@ def take_action(self, parsed_args): return zip(*sorted(six.iteritems(server._info))) +class ResizeServer(command.Command): + """Convert server to a new flavor""" + + log = logging.getLogger(__name__ + '.ResizeServer') + + def get_parser(self, prog_name): + parser = super(ResizeServer, self).get_parser(prog_name) + phase_group = parser.add_mutually_exclusive_group() + phase_group.add_argument( + '--flavor', + metavar='', + help='Resize server to this flavor', + ) + phase_group.add_argument( + '--verify', + action="store_true", + help='Verify previous server resize', + ) + phase_group.add_argument( + '--revert', + action="store_true", + help='Restore server before resize', + ) + parser.add_argument( + '--wait', + action='store_true', + help='Wait for resize to complete', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + if parsed_args.flavor: + flavor = utils.find_resource( + compute_client.flavors, + parsed_args.flavor, + ) + server.resize(flavor) + if parsed_args.wait: + if utils.wait_for_status( + compute_client.servers.get, + server.id, + success_status=['active', 'verify_resize'], + callback=_show_progress, + ): + sys.stdout.write('Complete\n') + else: + sys.stdout.write('\nError resizing server') + raise SystemExit + elif parsed_args.verify: + server.confirm_resize() + elif parsed_args.revert: + server.revert_resize() + + class ResumeServer(command.Command): """Resume server""" diff --git a/setup.cfg b/setup.cfg index c2a744f3d6..7fd06a2ee6 100644 --- a/setup.cfg +++ b/setup.cfg @@ -221,6 +221,7 @@ openstack.compute.v2 = server_rebuild = openstackclient.compute.v2.server:RebuildServer server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume server_rescue = openstackclient.compute.v2.server:RescueServer + server_resize = openstackclient.compute.v2.server:ResizeServer server_resume = openstackclient.compute.v2.server:ResumeServer server_set = openstackclient.compute.v2.server:SetServer server_show = openstackclient.compute.v2.server:ShowServer From c94e262df8d2d37e6c2043a3c3d0bc1cb78348a5 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Fri, 12 Jul 2013 15:49:03 -0500 Subject: [PATCH 0019/3699] Add security group commands * Add security group: create, delete, list, set, show * Add server: add secgroup, remove secgroup * Add security group rule: create, delete, list * Add Oslo's strutils and gettextutils * Adds parseractions.RangeAction() to handle option arguments of either a single number or a range of numbers: '--port 25' or '--port 1024:65535' Blueprint: nova-client Change-Id: Iad2de1b273ba29197709fc4c6a1036b4ae99725f --- openstack-common.conf | 1 + openstackclient/common/parseractions.py | 24 ++ openstackclient/common/utils.py | 5 +- openstackclient/compute/v2/security_group.py | 394 ++++++++++++++++++ openstackclient/compute/v2/server.py | 73 ++++ openstackclient/identity/client.py | 13 +- .../openstack/common/gettextutils.py | 259 ++++++++++++ openstackclient/openstack/common/strutils.py | 218 ++++++++++ setup.cfg | 11 + 9 files changed, 996 insertions(+), 2 deletions(-) create mode 100644 openstackclient/compute/v2/security_group.py create mode 100644 openstackclient/openstack/common/gettextutils.py create mode 100644 openstackclient/openstack/common/strutils.py diff --git a/openstack-common.conf b/openstack-common.conf index 867e204af7..5e55d5867c 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -5,6 +5,7 @@ module=cfg module=iniparser module=install_venv_common module=openstackkeyring +module=strutils # The base module to hold the copy of openstack.common base=openstackclient diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py index f111c26427..644472d88e 100644 --- a/openstackclient/common/parseractions.py +++ b/openstackclient/common/parseractions.py @@ -32,3 +32,27 @@ def __call__(self, parser, namespace, values, option_string=None): getattr(namespace, self.dest, {}).update([values.split('=', 1)]) else: getattr(namespace, self.dest, {}).pop(values, None) + + +class RangeAction(argparse.Action): + """A custom action to parse a single value or a range of values.""" + def __call__(self, parser, namespace, values, option_string=None): + range = values.split(':') + if len(range) == 0: + # Nothing passed, return a zero default + setattr(namespace, self.dest, (0, 0)) + elif len(range) == 1: + # Only a single value is present + setattr(namespace, self.dest, (int(range[0]), int(range[0]))) + elif len(range) == 2: + # Range of two values + if int(range[0]) <= int(range[1]): + setattr(namespace, self.dest, (int(range[0]), int(range[1]))) + else: + msg = "Invalid range, %s is not less than %s" % \ + (range[0], range[1]) + raise argparse.ArgumentError(self, msg) + else: + # Too many values + msg = "Invalid range, too many values" + raise argparse.ArgumentError(self, msg) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index fd504ea17d..4d2afd15b3 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -16,11 +16,13 @@ """Common client utilities""" import os +import six import sys import time import uuid from openstackclient.common import exceptions +from openstackclient.openstack.common import strutils def find_resource(manager, name_or_id): @@ -84,7 +86,8 @@ def format_dict(data): output = "" for s in data: - output = output + s + "='" + data[s] + "', " + output = output + s + "='" + \ + strutils.safe_encode(six.text_type(data[s])) + "', " return output[:-2] diff --git a/openstackclient/compute/v2/security_group.py b/openstackclient/compute/v2/security_group.py new file mode 100644 index 0000000000..a1dc786d71 --- /dev/null +++ b/openstackclient/compute/v2/security_group.py @@ -0,0 +1,394 @@ +# Copyright 2012 OpenStack Foundation +# Copyright 2013 Nebula Inc +# +# 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. +# + +"""Compute v2 Security Group action implementations""" + +import logging +import six + +from cliff import command +from cliff import lister +from cliff import show + +from novaclient.v1_1 import security_group_rules +from openstackclient.common import parseractions +from openstackclient.common import utils + + +def _xform_security_group_rule(sgroup): + info = {} + info.update(sgroup) + info.update( + {'port_range': "%u:%u" % ( + info.pop('from_port'), + info.pop('to_port'), + )} + ) + info['ip_range'] = info['ip_range']['cidr'] + if info['ip_protocol'] == 'icmp': + info['port_range'] = '' + return info + + +class CreateSecurityGroup(show.ShowOne): + """Create a new security group""" + + log = logging.getLogger(__name__ + ".CreateSecurityGroup") + + def get_parser(self, prog_name): + parser = super(CreateSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + "name", + metavar="", + help="New security group name", + ) + parser.add_argument( + "--description", + metavar="", + help="Security group description", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + data = compute_client.security_groups.create( + parsed_args.name, + parsed_args.description, + ) + + info = {} + info.update(data._info) + return zip(*sorted(six.iteritems(info))) + + +class DeleteSecurityGroup(command.Command): + """Delete a security group""" + + log = logging.getLogger(__name__ + '.DeleteSecurityGroup') + + def get_parser(self, prog_name): + parser = super(DeleteSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Name or ID of security group to delete', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + data = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + compute_client.security_groups.delete(data.id) + return + + +class ListSecurityGroup(lister.Lister): + """List all security groups""" + + log = logging.getLogger(__name__ + ".ListSecurityGroup") + + def get_parser(self, prog_name): + parser = super(ListSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help='Display information from all projects (admin only)', + ) + return parser + + def take_action(self, parsed_args): + + def _get_project(project_id): + try: + return getattr(project_hash[project_id], 'name', project_id) + except KeyError: + return project_id + + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + columns = ( + "ID", + "Name", + "Description", + ) + column_headers = columns + if parsed_args.all_projects: + # TODO(dtroyer): Translate Project_ID to Project (name) + columns = columns + ('Tenant ID',) + column_headers = column_headers + ('Project',) + search = {'all_tenants': parsed_args.all_projects} + data = compute_client.security_groups.list(search_opts=search) + + projects = self.app.client_manager.identity.projects.list() + project_hash = {} + for project in projects: + project_hash[project.id] = project + + return (column_headers, + (utils.get_item_properties( + s, columns, + formatters={'Tenant ID': _get_project}, + ) for s in data)) + + +class SetSecurityGroup(show.ShowOne): + """Set security group properties""" + + log = logging.getLogger(__name__ + '.SetSecurityGroup') + + def get_parser(self, prog_name): + parser = super(SetSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Name or ID of security group to change', + ) + parser.add_argument( + '--name', + metavar='', + help='New security group name', + ) + parser.add_argument( + "--description", + metavar="", + help="New security group name", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + data = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + + if parsed_args.name: + data.name = parsed_args.name + if parsed_args.description: + data.description = parsed_args.description + + info = {} + info.update(compute_client.security_groups.update( + data, + data.name, + data.description, + )._info) + + if info: + return zip(*sorted(six.iteritems(info))) + else: + return ({}, {}) + + +class ShowSecurityGroup(show.ShowOne): + """Show a specific security group""" + + log = logging.getLogger(__name__ + '.ShowSecurityGroup') + + def get_parser(self, prog_name): + parser = super(ShowSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Name or ID of security group to change', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + info = {} + info.update(utils.find_resource( + compute_client.security_groups, + parsed_args.group, + )._info) + rules = [] + for r in info['rules']: + rules.append(utils.format_dict(_xform_security_group_rule(r))) + + # Format rules into a list of strings + info.update( + {'rules': rules} + ) + # Map 'tenant_id' column to 'project_id' + info.update( + {'project_id': info.pop('tenant_id')} + ) + + return zip(*sorted(six.iteritems(info))) + + +class CreateSecurityGroupRule(show.ShowOne): + """Create a new security group rule""" + + log = logging.getLogger(__name__ + ".CreateSecurityGroupRule") + + def get_parser(self, prog_name): + parser = super(CreateSecurityGroupRule, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Create rule in this security group', + ) + parser.add_argument( + "--proto", + metavar="", + default="tcp", + help="IP protocol (icmp, tcp, udp; default: tcp)", + ) + parser.add_argument( + "--src-ip", + metavar="", + default="0.0.0.0/0", + help="Source IP (may use CIDR notation; default: 0.0.0.0/0)", + ) + parser.add_argument( + "--dst-port", + metavar="", + action=parseractions.RangeAction, + help="Destination port, may be a range: 137:139 (default: 0; " + "only required for proto tcp and udp)", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + group = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + from_port, to_port = parsed_args.dst_port + data = compute_client.security_group_rules.create( + group.id, + parsed_args.proto, + from_port, + to_port, + parsed_args.src_ip, + ) + + info = _xform_security_group_rule(data._info) + return zip(*sorted(six.iteritems(info))) + + +class DeleteSecurityGroupRule(command.Command): + """Delete a security group rule""" + + log = logging.getLogger(__name__ + '.DeleteSecurityGroupRule') + + def get_parser(self, prog_name): + parser = super(DeleteSecurityGroupRule, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Create rule in this security group', + ) + parser.add_argument( + "--proto", + metavar="", + default="tcp", + help="IP protocol (icmp, tcp, udp; default: tcp)", + ) + parser.add_argument( + "--src-ip", + metavar="", + default="0.0.0.0/0", + help="Source IP (may use CIDR notation; default: 0.0.0.0/0)", + ) + parser.add_argument( + "--dst-port", + metavar="", + action=parseractions.RangeAction, + help="Destination port, may be a range: 137:139 (default: 0; " + "only required for proto tcp and udp)", + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + + compute_client = self.app.client_manager.compute + group = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + from_port, to_port = parsed_args.dst_port + # sigh...delete by ID? + compute_client.security_group_rules.delete( + group.id, + parsed_args.proto, + from_port, + to_port, + parsed_args.src_ip, + ) + return + + +class ListSecurityGroupRule(lister.Lister): + """List all security group rules""" + + log = logging.getLogger(__name__ + ".ListSecurityGroupRule") + + def get_parser(self, prog_name): + parser = super(ListSecurityGroupRule, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Create rule in this security group', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + group = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + + # Argh, the rules are not Resources... + rules = [] + for rule in group.rules: + rules.append(security_group_rules.SecurityGroupRule( + compute_client.security_group_rules, + _xform_security_group_rule(rule), + )) + + columns = column_headers = ( + "ID", + "IP Protocol", + "IP Range", + "Port Range", + ) + return (column_headers, + (utils.get_item_properties( + s, columns, + ) for s in rules)) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8f81dfdb2f..6be97981a9 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -141,6 +141,43 @@ def take_action(self, parsed_args): ) +class AddServerSecurityGroup(command.Command): + """Add security group to server""" + + log = logging.getLogger(__name__ + '.AddServerSecurityGroup') + + def get_parser(self, prog_name): + parser = super(AddServerSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Name or ID of server to use', + ) + parser.add_argument( + 'group', + metavar='', + help='Name or ID of security group to add to server', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + security_group = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + + server.add_security_group(security_group) + return + + class CreateServer(show.ShowOne): """Create a new server""" @@ -731,6 +768,42 @@ def take_action(self, parsed_args): return zip(*sorted(details.iteritems())) +class RemoveServerSecurityGroup(command.Command): + """Remove security group from server""" + + log = logging.getLogger(__name__ + '.RemoveServerSecurityGroup') + + def get_parser(self, prog_name): + parser = super(RemoveServerSecurityGroup, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Name or ID of server to use', + ) + parser.add_argument( + 'group', + metavar='', + help='Name or ID of security group to remove from server', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug("take_action(%s)" % parsed_args) + + compute_client = self.app.client_manager.compute + + server = utils.find_resource( + compute_client.servers, + parsed_args.server, + ) + security_group = utils.find_resource( + compute_client.security_groups, + parsed_args.group, + ) + + server.remove_security_group(security_group) + + class RemoveServerVolume(command.Command): """Remove volume from server""" diff --git a/openstackclient/identity/client.py b/openstackclient/identity/client.py index 748d166683..0f8fbb815d 100644 --- a/openstackclient/identity/client.py +++ b/openstackclient/identity/client.py @@ -15,6 +15,7 @@ import logging +from keystoneclient.v2_0 import client as identity_client_v2_0 from openstackclient.common import utils @@ -22,7 +23,7 @@ API_NAME = 'identity' API_VERSIONS = { - '2.0': 'keystoneclient.v2_0.client.Client', + '2.0': 'openstackclient.identity.client.IdentityClientv2_0', '3': 'keystoneclient.v3.client.Client', } @@ -48,3 +49,13 @@ def make_client(instance): auth_url=instance._auth_url, region_name=instance._region_name) return client + + +class IdentityClientv2_0(identity_client_v2_0.Client): + """Tweak the earlier client class to deal with some changes""" + def __getattr__(self, name): + # Map v3 'projects' back to v2 'tenants' + if name == "projects": + return self.tenants + else: + raise AttributeError, name diff --git a/openstackclient/openstack/common/gettextutils.py b/openstackclient/openstack/common/gettextutils.py new file mode 100644 index 0000000000..2dd5449e6c --- /dev/null +++ b/openstackclient/openstack/common/gettextutils.py @@ -0,0 +1,259 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2012 Red Hat, Inc. +# All Rights Reserved. +# Copyright 2013 IBM Corp. +# +# 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. + +""" +gettext for openstack-common modules. + +Usual usage in an openstack.common module: + + from openstackclient.openstack.common.gettextutils import _ +""" + +import copy +import gettext +import logging.handlers +import os +import re +import UserString + +import six + +_localedir = os.environ.get('openstackclient'.upper() + '_LOCALEDIR') +_t = gettext.translation('openstackclient', localedir=_localedir, fallback=True) + + +def _(msg): + return _t.ugettext(msg) + + +def install(domain): + """Install a _() function using the given translation domain. + + Given a translation domain, install a _() function using gettext's + install() function. + + The main difference from gettext.install() is that we allow + overriding the default localedir (e.g. /usr/share/locale) using + a translation-domain-specific environment variable (e.g. + NOVA_LOCALEDIR). + """ + gettext.install(domain, + localedir=os.environ.get(domain.upper() + '_LOCALEDIR'), + unicode=True) + + +""" +Lazy gettext functionality. + +The following is an attempt to introduce a deferred way +to do translations on messages in OpenStack. We attempt to +override the standard _() function and % (format string) operation +to build Message objects that can later be translated when we have +more information. Also included is an example LogHandler that +translates Messages to an associated locale, effectively allowing +many logs, each with their own locale. +""" + + +def get_lazy_gettext(domain): + """Assemble and return a lazy gettext function for a given domain. + + Factory method for a project/module to get a lazy gettext function + for its own translation domain (i.e. nova, glance, cinder, etc.) + """ + + def _lazy_gettext(msg): + """Create and return a Message object. + + Message encapsulates a string so that we can translate it later when + needed. + """ + return Message(msg, domain) + + return _lazy_gettext + + +class Message(UserString.UserString, object): + """Class used to encapsulate translatable messages.""" + def __init__(self, msg, domain): + # _msg is the gettext msgid and should never change + self._msg = msg + self._left_extra_msg = '' + self._right_extra_msg = '' + self.params = None + self.locale = None + self.domain = domain + + @property + def data(self): + # NOTE(mrodden): this should always resolve to a unicode string + # that best represents the state of the message currently + + localedir = os.environ.get(self.domain.upper() + '_LOCALEDIR') + if self.locale: + lang = gettext.translation(self.domain, + localedir=localedir, + languages=[self.locale], + fallback=True) + else: + # use system locale for translations + lang = gettext.translation(self.domain, + localedir=localedir, + fallback=True) + + full_msg = (self._left_extra_msg + + lang.ugettext(self._msg) + + self._right_extra_msg) + + if self.params is not None: + full_msg = full_msg % self.params + + return six.text_type(full_msg) + + def _save_dictionary_parameter(self, dict_param): + full_msg = self.data + # look for %(blah) fields in string; + # ignore %% and deal with the + # case where % is first character on the line + keys = re.findall('(?:[^%]|^)%\((\w*)\)[a-z]', full_msg) + + # if we don't find any %(blah) blocks but have a %s + if not keys and re.findall('(?:[^%]|^)%[a-z]', full_msg): + # apparently the full dictionary is the parameter + params = copy.deepcopy(dict_param) + else: + params = {} + for key in keys: + try: + params[key] = copy.deepcopy(dict_param[key]) + except TypeError: + # cast uncopyable thing to unicode string + params[key] = unicode(dict_param[key]) + + return params + + def _save_parameters(self, other): + # we check for None later to see if + # we actually have parameters to inject, + # so encapsulate if our parameter is actually None + if other is None: + self.params = (other, ) + elif isinstance(other, dict): + self.params = self._save_dictionary_parameter(other) + else: + # fallback to casting to unicode, + # this will handle the problematic python code-like + # objects that cannot be deep-copied + try: + self.params = copy.deepcopy(other) + except TypeError: + self.params = unicode(other) + + return self + + # overrides to be more string-like + def __unicode__(self): + return self.data + + def __str__(self): + return self.data.encode('utf-8') + + def __getstate__(self): + to_copy = ['_msg', '_right_extra_msg', '_left_extra_msg', + 'domain', 'params', 'locale'] + new_dict = self.__dict__.fromkeys(to_copy) + for attr in to_copy: + new_dict[attr] = copy.deepcopy(self.__dict__[attr]) + + return new_dict + + def __setstate__(self, state): + for (k, v) in state.items(): + setattr(self, k, v) + + # operator overloads + def __add__(self, other): + copied = copy.deepcopy(self) + copied._right_extra_msg += other.__str__() + return copied + + def __radd__(self, other): + copied = copy.deepcopy(self) + copied._left_extra_msg += other.__str__() + return copied + + def __mod__(self, other): + # do a format string to catch and raise + # any possible KeyErrors from missing parameters + self.data % other + copied = copy.deepcopy(self) + return copied._save_parameters(other) + + def __mul__(self, other): + return self.data * other + + def __rmul__(self, other): + return other * self.data + + def __getitem__(self, key): + return self.data[key] + + def __getslice__(self, start, end): + return self.data.__getslice__(start, end) + + def __getattribute__(self, name): + # NOTE(mrodden): handle lossy operations that we can't deal with yet + # These override the UserString implementation, since UserString + # uses our __class__ attribute to try and build a new message + # after running the inner data string through the operation. + # At that point, we have lost the gettext message id and can just + # safely resolve to a string instead. + ops = ['capitalize', 'center', 'decode', 'encode', + 'expandtabs', 'ljust', 'lstrip', 'replace', 'rjust', 'rstrip', + 'strip', 'swapcase', 'title', 'translate', 'upper', 'zfill'] + if name in ops: + return getattr(self.data, name) + else: + return UserString.UserString.__getattribute__(self, name) + + +class LocaleHandler(logging.Handler): + """Handler that can have a locale associated to translate Messages. + + A quick example of how to utilize the Message class above. + LocaleHandler takes a locale and a target logging.Handler object + to forward LogRecord objects to after translating the internal Message. + """ + + def __init__(self, locale, target): + """Initialize a LocaleHandler + + :param locale: locale to use for translating messages + :param target: logging.Handler object to forward + LogRecord objects to after translation + """ + logging.Handler.__init__(self) + self.locale = locale + self.target = target + + def emit(self, record): + if isinstance(record.msg, Message): + # set the locale and resolve to a string + record.msg.locale = self.locale + + self.target.emit(record) diff --git a/openstackclient/openstack/common/strutils.py b/openstackclient/openstack/common/strutils.py new file mode 100644 index 0000000000..e3f26a7876 --- /dev/null +++ b/openstackclient/openstack/common/strutils.py @@ -0,0 +1,218 @@ +# vim: tabstop=4 shiftwidth=4 softtabstop=4 + +# Copyright 2011 OpenStack Foundation. +# All Rights Reserved. +# +# 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. + +""" +System-level utilities and helper functions. +""" + +import re +import sys +import unicodedata + +import six + +from openstackclient.openstack.common.gettextutils import _ # noqa + + +# Used for looking up extensions of text +# to their 'multiplied' byte amount +BYTE_MULTIPLIERS = { + '': 1, + 't': 1024 ** 4, + 'g': 1024 ** 3, + 'm': 1024 ** 2, + 'k': 1024, +} +BYTE_REGEX = re.compile(r'(^-?\d+)(\D*)') + +TRUE_STRINGS = ('1', 't', 'true', 'on', 'y', 'yes') +FALSE_STRINGS = ('0', 'f', 'false', 'off', 'n', 'no') + +SLUGIFY_STRIP_RE = re.compile(r"[^\w\s-]") +SLUGIFY_HYPHENATE_RE = re.compile(r"[-\s]+") + + +def int_from_bool_as_string(subject): + """Interpret a string as a boolean and return either 1 or 0. + + Any string value in: + + ('True', 'true', 'On', 'on', '1') + + is interpreted as a boolean True. + + Useful for JSON-decoded stuff and config file parsing + """ + return bool_from_string(subject) and 1 or 0 + + +def bool_from_string(subject, strict=False): + """Interpret a string as a boolean. + + A case-insensitive match is performed such that strings matching 't', + 'true', 'on', 'y', 'yes', or '1' are considered True and, when + `strict=False`, anything else is considered False. + + Useful for JSON-decoded stuff and config file parsing. + + If `strict=True`, unrecognized values, including None, will raise a + ValueError which is useful when parsing values passed in from an API call. + Strings yielding False are 'f', 'false', 'off', 'n', 'no', or '0'. + """ + if not isinstance(subject, six.string_types): + subject = str(subject) + + lowered = subject.strip().lower() + + if lowered in TRUE_STRINGS: + return True + elif lowered in FALSE_STRINGS: + return False + elif strict: + acceptable = ', '.join( + "'%s'" % s for s in sorted(TRUE_STRINGS + FALSE_STRINGS)) + msg = _("Unrecognized value '%(val)s', acceptable values are:" + " %(acceptable)s") % {'val': subject, + 'acceptable': acceptable} + raise ValueError(msg) + else: + return False + + +def safe_decode(text, incoming=None, errors='strict'): + """Decodes incoming str using `incoming` if they're not already unicode. + + :param incoming: Text's current encoding + :param errors: Errors handling policy. See here for valid + values http://docs.python.org/2/library/codecs.html + :returns: text or a unicode `incoming` encoded + representation of it. + :raises TypeError: If text is not an isntance of str + """ + if not isinstance(text, six.string_types): + raise TypeError("%s can't be decoded" % type(text)) + + if isinstance(text, six.text_type): + return text + + if not incoming: + incoming = (sys.stdin.encoding or + sys.getdefaultencoding()) + + try: + return text.decode(incoming, errors) + except UnicodeDecodeError: + # Note(flaper87) If we get here, it means that + # sys.stdin.encoding / sys.getdefaultencoding + # didn't return a suitable encoding to decode + # text. This happens mostly when global LANG + # var is not set correctly and there's no + # default encoding. In this case, most likely + # python will use ASCII or ANSI encoders as + # default encodings but they won't be capable + # of decoding non-ASCII characters. + # + # Also, UTF-8 is being used since it's an ASCII + # extension. + return text.decode('utf-8', errors) + + +def safe_encode(text, incoming=None, + encoding='utf-8', errors='strict'): + """Encodes incoming str/unicode using `encoding`. + + If incoming is not specified, text is expected to be encoded with + current python's default encoding. (`sys.getdefaultencoding`) + + :param incoming: Text's current encoding + :param encoding: Expected encoding for text (Default UTF-8) + :param errors: Errors handling policy. See here for valid + values http://docs.python.org/2/library/codecs.html + :returns: text or a bytestring `encoding` encoded + representation of it. + :raises TypeError: If text is not an isntance of str + """ + if not isinstance(text, six.string_types): + raise TypeError("%s can't be encoded" % type(text)) + + if not incoming: + incoming = (sys.stdin.encoding or + sys.getdefaultencoding()) + + if isinstance(text, six.text_type): + return text.encode(encoding, errors) + elif text and encoding != incoming: + # Decode text before encoding it with `encoding` + text = safe_decode(text, incoming, errors) + return text.encode(encoding, errors) + + return text + + +def to_bytes(text, default=0): + """Converts a string into an integer of bytes. + + Looks at the last characters of the text to determine + what conversion is needed to turn the input text into a byte number. + Supports "B, K(B), M(B), G(B), and T(B)". (case insensitive) + + :param text: String input for bytes size conversion. + :param default: Default return value when text is blank. + + """ + match = BYTE_REGEX.search(text) + if match: + magnitude = int(match.group(1)) + mult_key_org = match.group(2) + if not mult_key_org: + return magnitude + elif text: + msg = _('Invalid string format: %s') % text + raise TypeError(msg) + else: + return default + mult_key = mult_key_org.lower().replace('b', '', 1) + multiplier = BYTE_MULTIPLIERS.get(mult_key) + if multiplier is None: + msg = _('Unknown byte multiplier: %s') % mult_key_org + raise TypeError(msg) + return magnitude * multiplier + + +def to_slug(value, incoming=None, errors="strict"): + """Normalize string. + + Convert to lowercase, remove non-word characters, and convert spaces + to hyphens. + + Inspired by Django's `slugify` filter. + + :param value: Text to slugify + :param incoming: Text's current encoding + :param errors: Errors handling policy. See here for valid + values http://docs.python.org/2/library/codecs.html + :returns: slugified unicode representation of `value` + :raises TypeError: If text is not an instance of str + """ + value = safe_decode(value, incoming, errors) + # NOTE(aababilov): no need to use safe_(encode|decode) here: + # encodings are always "ascii", error handling is always "ignore" + # and types are always known (first: unicode; second: str) + value = unicodedata.normalize("NFKD", value).encode( + "ascii", "ignore").decode("ascii") + value = SLUGIFY_STRIP_RE.sub("", value).strip().lower() + return SLUGIFY_HYPHENATE_RE.sub("-", value) diff --git a/setup.cfg b/setup.cfg index 7fd06a2ee6..0fa59c4020 100644 --- a/setup.cfg +++ b/setup.cfg @@ -210,6 +210,16 @@ openstack.compute.v2 = project_usage_list = openstackclient.compute.v2.usage:ListUsage + security_group_create = openstackclient.compute.v2.secgroup:CreateSecurityGroup + security_group_delete = openstackclient.compute.v2.secgroup:DeleteSecurityGroup + security_group_list = openstackclient.compute.v2.secgroup:ListSecurityGroup + security_group_set = openstackclient.compute.v2.secgroup:SetSecurityGroup + security_group_show = openstackclient.compute.v2.secgroup:ShowSecurityGroup + security_group_rule_create = openstackclient.compute.v2.secgroup:CreateSecurityGroupRule + security_group_rule_delete = openstackclient.compute.v2.secgroup:DeleteSecurityGroupRule + security_group_rule_list = openstackclient.compute.v2.secgroup:ListSecurityGroupRule + + server_add_security_group = openstackclient.compute.v2.server:AddServerSecurityGroup server_add_volume = openstackclient.compute.v2.server:AddServerVolume server_create = openstackclient.compute.v2.server:CreateServer server_delete = openstackclient.compute.v2.server:DeleteServer @@ -219,6 +229,7 @@ openstack.compute.v2 = server_pause = openstackclient.compute.v2.server:PauseServer server_reboot = openstackclient.compute.v2.server:RebootServer server_rebuild = openstackclient.compute.v2.server:RebuildServer + server_remove_security_group = openstackclient.compute.v2.server:RemoveServerSecurityGroup server_remove_volume = openstackclient.compute.v2.server:RemoveServerVolume server_rescue = openstackclient.compute.v2.server:RescueServer server_resize = openstackclient.compute.v2.server:ResizeServer From dfb0e3e3c1b5b5563279bebfe222ed4762f79494 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Wed, 3 Jul 2013 18:12:58 -0500 Subject: [PATCH 0020/3699] Begin Python 3 compatability * use six.iteritems() * replace basestring with six.string_types * convert print statements to functions (they're all debugging and should be removed eventually anyway) * clean up OpenStack copyright: LLC -> Foundation Change-Id: Icb14212bcb408e63816bfec3922a697bc1a6c946 --- openstackclient/compute/v2/agent.py | 7 ++++--- openstackclient/compute/v2/console.py | 7 +++---- openstackclient/compute/v2/flavor.py | 7 ++++--- openstackclient/compute/v2/floatingip.py | 3 ++- openstackclient/compute/v2/hypervisor.py | 3 ++- openstackclient/compute/v2/keypair.py | 5 +++-- openstackclient/compute/v2/server.py | 8 ++++---- openstackclient/identity/v2_0/ec2creds.py | 8 +++++--- openstackclient/identity/v2_0/endpoint.py | 9 +++++---- openstackclient/identity/v2_0/role.py | 9 +++++---- openstackclient/identity/v2_0/service.py | 9 +++++---- openstackclient/identity/v2_0/tenant.py | 7 ++++--- openstackclient/identity/v2_0/user.py | 7 ++++--- openstackclient/identity/v3/credential.py | 7 ++++--- openstackclient/identity/v3/domain.py | 7 ++++--- openstackclient/identity/v3/endpoint.py | 7 ++++--- openstackclient/identity/v3/group.py | 7 ++++--- openstackclient/identity/v3/oauth.py | 17 +++++++++-------- openstackclient/identity/v3/policy.py | 7 ++++--- openstackclient/identity/v3/project.py | 7 ++++--- openstackclient/identity/v3/role.py | 7 ++++--- openstackclient/identity/v3/service.py | 7 ++++--- openstackclient/identity/v3/user.py | 7 ++++--- openstackclient/volume/v1/backup.py | 7 ++++--- openstackclient/volume/v1/snapshot.py | 5 +++-- openstackclient/volume/v1/type.py | 3 ++- openstackclient/volume/v1/volume.py | 5 +++-- 27 files changed, 107 insertions(+), 82 deletions(-) diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index aac69d8a0b..b79ebbe761 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -1,4 +1,4 @@ -# Copyright 2013 OpenStack, LLC. +# Copyright 2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Agent action implementations""" import logging +import six from cliff import command from cliff import lister @@ -70,7 +71,7 @@ def take_action(self, parsed_args): parsed_args.hypervisor ) agent = compute_client.agents.create(*args)._info.copy() - return zip(*sorted(agent.iteritems())) + return zip(*sorted(six.iteritems(agent))) class DeleteAgent(command.Command): @@ -160,4 +161,4 @@ def take_action(self, parsed_args): parsed_args.md5hash ) agent = compute_client.agents.update(*args)._info.copy() - return zip(*sorted(agent.iteritems())) + return zip(*sorted(six.iteritems(agent))) diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index a67b004c05..8f49c5134a 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -13,9 +13,10 @@ # under the License. # -"""Console action implementations""" +"""Compute v2 Console action implementations""" import logging +import six import sys from cliff import command @@ -106,7 +107,6 @@ def take_action(self, parsed_args): parsed_args.server, ) - print "type: %s" % parsed_args.url_type if parsed_args.url_type in ['novnc', 'xvpvnc']: data = server.get_vnc_console(parsed_args.url_type) if parsed_args.url_type in ['spice']: @@ -114,8 +114,7 @@ def take_action(self, parsed_args): if not data: return ({}, {}) - print "data: %s" % data['console'] info = {} info.update(data['console']) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index 4d53a4122e..d1d08d8df0 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -1,4 +1,4 @@ -# Copyright 2013 OpenStack, LLC. +# Copyright 2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Flavor action implementations""" import logging +import six from cliff import command from cliff import lister @@ -110,7 +111,7 @@ def take_action(self, parsed_args): flavor = compute_client.flavors.create(*args)._info.copy() flavor.pop("links") - return zip(*sorted(flavor.iteritems())) + return zip(*sorted(six.iteritems(flavor))) class DeleteFlavor(command.Command): @@ -182,4 +183,4 @@ def take_action(self, parsed_args): parsed_args.flavor)._info.copy() flavor.pop("links") - return zip(*sorted(flavor.iteritems())) + return zip(*sorted(six.iteritems(flavor))) diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 1b07beb3d3..7ed847f5f4 100644 --- a/openstackclient/compute/v2/floatingip.py +++ b/openstackclient/compute/v2/floatingip.py @@ -16,6 +16,7 @@ """Floating IP action implementations""" import logging +import six from cliff import command from cliff import lister @@ -75,7 +76,7 @@ def take_action(self, parsed_args): info = {} info.update(floating_ip._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteFloatingIP(command.Command): diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index ad69d3285a..535062e8da 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -16,6 +16,7 @@ """Hypervisor action implementations""" import logging +import six from cliff import lister from cliff import show @@ -79,4 +80,4 @@ def take_action(self, parsed_args): hypervisor["service_host"] = hypervisor["service"]["host"] del hypervisor["service"] - return zip(*sorted(hypervisor.iteritems())) + return zip(*sorted(six.iteritems(hypervisor))) diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index 65f3679b71..d68dae0643 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -17,6 +17,7 @@ import logging import os +import six import sys from cliff import command @@ -71,7 +72,7 @@ def take_action(self, parsed_args): if public_key: info.update(keypair._info) del info['public_key'] - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) else: sys.stdout.write(keypair.private_key) return ({}, {}) @@ -148,7 +149,7 @@ def take_action(self, parsed_args): info.update(keypair._info['keypair']) if not parsed_args.public_key: del info['public_key'] - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) else: # NOTE(dtroyer): a way to get the public key in a similar form # as the private key in the create command diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8f81dfdb2f..42529af586 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -293,7 +293,7 @@ def take_action(self, parsed_args): # NOTE(vish): multiple copies of the same hint will # result in a list of values if key in hints: - if isinstance(hints[key], basestring): + if isinstance(hints[key], six.string_types): hints[key] = [hints[key]] hints[key] += [value] else: @@ -343,7 +343,7 @@ def take_action(self, parsed_args): raise SystemExit details = _prep_server_detail(compute_client, server) - return zip(*sorted(details.iteritems())) + return zip(*sorted(six.iteritems(details))) class DeleteServer(command.Command): @@ -728,7 +728,7 @@ def take_action(self, parsed_args): raise SystemExit details = _prep_server_detail(compute_client, server) - return zip(*sorted(details.iteritems())) + return zip(*sorted(six.iteritems(details))) class RemoveServerVolume(command.Command): @@ -974,7 +974,7 @@ def take_action(self, parsed_args): else: data = _prep_server_detail(compute_client, server) - return zip(*sorted(data.iteritems())) + return zip(*sorted(six.iteritems(data))) class SuspendServer(command.Command): diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 953a3de6ad..cb3a51658f 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -1,3 +1,4 @@ +# Copyright 2012 OpenStack Foundation # Copyright 2013 Nebula Inc. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -13,9 +14,10 @@ # under the License. # -"""EC2 Credentials action implementations""" +"""Identity v2 EC2 Credentials action implementations""" import logging +import six from cliff import command from cliff import lister @@ -68,7 +70,7 @@ def take_action(self, parsed_args): info = {} info.update(creds._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteEC2Creds(command.Command): @@ -178,4 +180,4 @@ def take_action(self, parsed_args): info = {} info.update(creds._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index 680465ae47..5a050fa190 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Endpoint action implementations""" import logging +import six from cliff import command from cliff import lister @@ -71,7 +72,7 @@ def take_action(self, parsed_args): info.update(endpoint._info) info['service_name'] = service.name info['service_type'] = service.type - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteEndpoint(command.Command): @@ -183,7 +184,7 @@ def take_action(self, parsed_args): url = identity_client.service_catalog.url_for(**kwargs) info = {'%s.%s' % (parsed_args.service, parsed_args.type): url} - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) else: # The Identity 2.0 API doesn't support retrieving a single # endpoint so we have to do this ourselves @@ -211,4 +212,4 @@ def take_action(self, parsed_args): ep.service_id) info['service_name'] = service.name info['service_type'] = service.type - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index 867230b6b2..61a83af575 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Role action implementations""" import logging +import six from cliff import command from cliff import lister @@ -61,7 +62,7 @@ def take_action(self, parsed_args): info = {} info.update(role._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class CreateRole(show.ShowOne): @@ -84,7 +85,7 @@ def take_action(self, parsed_args): info = {} info.update(role._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteRole(command.Command): @@ -229,4 +230,4 @@ def take_action(self, parsed_args): info = {} info.update(role._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 629475df00..2e81805bb2 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -16,6 +16,7 @@ """Service action implementations""" import logging +import six from cliff import command from cliff import lister @@ -58,7 +59,7 @@ def take_action(self, parsed_args): info = {} info.update(service._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteService(command.Command): @@ -136,11 +137,11 @@ def take_action(self, parsed_args): if parsed_args.catalog: endpoints = identity_client.service_catalog.get_endpoints( service_type=parsed_args.service) - for (service, service_endpoints) in endpoints.iteritems(): + for (service, service_endpoints) in six.iteritems(endpoints): if service_endpoints: info = {"type": service} info.update(service_endpoints[0]) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) msg = ("No service catalog with a type, name or ID of '%s' " "exists." % (parsed_args.service)) @@ -166,4 +167,4 @@ def take_action(self, parsed_args): info = {} info.update(service._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v2_0/tenant.py b/openstackclient/identity/v2_0/tenant.py index c9a423c5f1..d535716cac 100644 --- a/openstackclient/identity/v2_0/tenant.py +++ b/openstackclient/identity/v2_0/tenant.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Tenant action implementations""" import logging +import six import sys from cliff import command @@ -64,7 +65,7 @@ def take_action(self, parsed_args): info = {} info.update(tenant._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteTenant(command.Command): @@ -191,4 +192,4 @@ def take_action(self, parsed_args): info = {} info.update(tenant._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 39be81af82..8c8e2622db 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v2.0 User action implementations""" import logging +import six import sys from cliff import command @@ -79,7 +80,7 @@ def take_action(self, parsed_args): info = {} info.update(user._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteUser(command.Command): @@ -219,4 +220,4 @@ def take_action(self, parsed_args): info = {} info.update(user._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index a2fb43a256..b82825f00b 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Credential action implementations""" import logging +import six import sys from cliff import command @@ -72,7 +73,7 @@ def take_action(self, parsed_args): parsed_args.data, project=project) - return zip(*sorted(credential._info.iteritems())) + return zip(*sorted(six.iteritems(credential._info))) class DeleteCredential(command.Command): @@ -191,4 +192,4 @@ def take_action(self, parsed_args): credential = utils.find_resource(identity_client.credentials, parsed_args.credential) - return zip(*sorted(credential._info.iteritems())) + return zip(*sorted(six.iteritems(credential._info))) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index f6064a589c..1e9a4a2a0b 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Domain action implementations""" import logging +import six import sys from cliff import command @@ -65,7 +66,7 @@ def take_action(self, parsed_args): enabled=parsed_args.enabled, ) - return zip(*sorted(domain._info.iteritems())) + return zip(*sorted(six.iteritems(domain._info))) class DeleteDomain(command.Command): @@ -185,4 +186,4 @@ def take_action(self, parsed_args): domain = utils.find_resource(identity_client.domains, parsed_args.domain) - return zip(*sorted(domain._info.iteritems())) + return zip(*sorted(six.iteritems(domain._info))) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index c5f3ebadff..43da07aaad 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Endpoint action implementations""" import logging +import six import sys from cliff import command @@ -83,7 +84,7 @@ def take_action(self, parsed_args): info.update(endpoint._info) info['service_name'] = service.name info['service_type'] = service.type - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteEndpoint(command.Command): @@ -239,4 +240,4 @@ def take_action(self, parsed_args): info.update(endpoint._info) info['service_name'] = service.name info['service_type'] = service.type - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index ca0493ebbb..b5d24ef5f8 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Group action implementations""" import logging +import six import sys from cliff import command @@ -138,7 +139,7 @@ def take_action(self, parsed_args): info = {} info.update(group._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteGroup(command.Command): @@ -375,4 +376,4 @@ def take_action(self, parsed_args): info = {} info.update(group._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v3/oauth.py b/openstackclient/identity/v3/oauth.py index bcbbdf7e09..1672cd24f3 100644 --- a/openstackclient/identity/v3/oauth.py +++ b/openstackclient/identity/v3/oauth.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 OAuth action implementations""" import logging +import six import sys from cliff import command @@ -65,7 +66,7 @@ def take_action(self, parsed_args): keystone_token = oauth_client.authenticate( parsed_args.consumer_key, parsed_args.consumer_secret, parsed_args.access_key, parsed_args.access_secret) - return zip(*sorted(keystone_token.iteritems())) + return zip(*sorted(six.iteritems(keystone_token))) class AuthorizeRequestToken(show.ShowOne): @@ -97,7 +98,7 @@ def take_action(self, parsed_args): parsed_args.request_key, parsed_args.roles) info = {} info.update(verifier_pin._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class CreateAccessToken(show.ShowOne): @@ -146,7 +147,7 @@ def take_action(self, parsed_args): parsed_args.consumer_key, parsed_args.consumer_secret, parsed_args.request_key, parsed_args.request_secret, parsed_args.verifier) - return zip(*sorted(access_token.iteritems())) + return zip(*sorted(six.iteritems(access_token))) class CreateConsumer(show.ShowOne): @@ -171,7 +172,7 @@ def take_action(self, parsed_args): ) info = {} info.update(consumer._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class CreateRequestToken(show.ShowOne): @@ -207,7 +208,7 @@ def take_action(self, parsed_args): parsed_args.consumer_key, parsed_args.consumer_secret, parsed_args.roles) - return zip(*sorted(request_token.iteritems())) + return zip(*sorted(six.iteritems(request_token))) class DeleteConsumer(command.Command): @@ -366,7 +367,7 @@ def take_action(self, parsed_args): parsed_args.request_id) info = {} info.update(data._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class ShowConsumer(show.ShowOne): @@ -391,4 +392,4 @@ def take_action(self, parsed_args): info = {} info.update(consumer._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index ac14cc464d..cdbb1cf299 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Policy action implementations""" import logging +import six import sys from cliff import command @@ -54,7 +55,7 @@ def take_action(self, parsed_args): blob, type=parsed_args.type ) - return zip(*sorted(policy._info.iteritems())) + return zip(*sorted(six.iteritems(policy._info))) class DeletePolicy(command.Command): @@ -172,7 +173,7 @@ def take_action(self, parsed_args): policy = utils.find_resource(identity_client.policies, parsed_args.policy) - return zip(*sorted(policy._info.iteritems())) + return zip(*sorted(six.iteritems(policy._info))) def _read_blob_file_contents(blob_file): diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 9d1e360bac..05722b54d1 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Project action implementations""" import logging +import six import sys from cliff import command @@ -78,7 +79,7 @@ def take_action(self, parsed_args): info = {} info.update(project._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteProject(command.Command): @@ -213,4 +214,4 @@ def take_action(self, parsed_args): info = {} info.update(project._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 9be3b78410..5403d4cb8c 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Role action implementations""" import logging +import six import sys from cliff import command @@ -123,7 +124,7 @@ def take_action(self, parsed_args): identity_client = self.app.client_manager.identity role = identity_client.roles.create(parsed_args.name) - return zip(*sorted(role._info.iteritems())) + return zip(*sorted(six.iteritems(role._info))) class DeleteRole(command.Command): @@ -296,4 +297,4 @@ def take_action(self, parsed_args): role = utils.find_resource(identity_client.roles, parsed_args.role) - return zip(*sorted(role._info.iteritems())) + return zip(*sorted(six.iteritems(role._info))) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 5c82284c52..77efbeadfc 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 Service action implementations""" import logging +import six import sys from cliff import command @@ -62,7 +63,7 @@ def take_action(self, parsed_args): parsed_args.type, parsed_args.enabled) - return zip(*sorted(service._info.iteritems())) + return zip(*sorted(six.iteritems(service._info))) class DeleteService(command.Command): @@ -178,4 +179,4 @@ def take_action(self, parsed_args): service = utils.find_resource(identity_client.services, parsed_args.service) - return zip(*sorted(service._info.iteritems())) + return zip(*sorted(six.iteritems(service._info))) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 8ee535dc33..b90527a3f5 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Identity v3 User action implementations""" import logging +import six import sys from cliff import command @@ -106,7 +107,7 @@ def take_action(self, parsed_args): info = {} info.update(user._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteUser(command.Command): @@ -355,4 +356,4 @@ def take_action(self, parsed_args): info = {} info.update(user._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py index 8ef666c1fa..dbf6eb7370 100644 --- a/openstackclient/volume/v1/backup.py +++ b/openstackclient/volume/v1/backup.py @@ -1,4 +1,4 @@ -# Copyright 2012-2013 OpenStack, LLC. +# Copyright 2012-2013 OpenStack Foundation # # 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 @@ -16,6 +16,7 @@ """Volume v1 Backup action implementations""" import logging +import six from cliff import command from cliff import lister @@ -68,7 +69,7 @@ def take_action(self, parsed_args): ) backup._info.pop('links') - return zip(*sorted(backup._info.iteritems())) + return zip(*sorted(six.iteritems(backup._info))) class DeleteBackup(command.Command): @@ -163,4 +164,4 @@ def take_action(self, parsed_args): backup = utils.find_resource(volume_client.backups, parsed_args.backup) backup._info.pop('links') - return zip(*sorted(backup._info.iteritems())) + return zip(*sorted(six.iteritems(backup._info))) diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py index 6055a4d8af..d3a56b759e 100644 --- a/openstackclient/volume/v1/snapshot.py +++ b/openstackclient/volume/v1/snapshot.py @@ -16,6 +16,7 @@ """Volume v1 Snapshot action implementations""" import logging +import six import sys from cliff import command @@ -69,7 +70,7 @@ def take_action(self, parsed_args): parsed_args.description ) - return zip(*sorted(snapshot._info.iteritems())) + return zip(*sorted(six.iteritems(snapshot._info))) class DeleteSnapshot(command.Command): @@ -175,4 +176,4 @@ def take_action(self, parsed_args): snapshot = utils.find_resource(volume_client.volume_snapshots, parsed_args.snapshot) - return zip(*sorted(snapshot._info.iteritems())) + return zip(*sorted(six.iteritems(snapshot._info))) diff --git a/openstackclient/volume/v1/type.py b/openstackclient/volume/v1/type.py index 895f237776..ecf22781b4 100644 --- a/openstackclient/volume/v1/type.py +++ b/openstackclient/volume/v1/type.py @@ -16,6 +16,7 @@ """Volume v1 Type action implementations""" import logging +import six from cliff import command from cliff import lister @@ -62,7 +63,7 @@ def take_action(self, parsed_args): info = {} info.update(volume_type._info) - return zip(*sorted(info.iteritems())) + return zip(*sorted(six.iteritems(info))) class DeleteVolumeType(command.Command): diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index b74fe45249..97f8d31d0a 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -16,6 +16,7 @@ """Volume v1 Volume action implementations""" import logging +import six from cliff import command from cliff import lister @@ -124,7 +125,7 @@ def take_action(self, parsed_args): {'properties': utils.format_dict(volume._info.pop('metadata'))} ) - return zip(*sorted(volume._info.iteritems())) + return zip(*sorted(six.iteritems(volume._info))) class DeleteVolume(command.Command): @@ -322,7 +323,7 @@ def take_action(self, parsed_args): {'properties': utils.format_dict(volume._info.pop('metadata'))} ) - return zip(*sorted(volume._info.iteritems())) + return zip(*sorted(six.iteritems(volume._info))) class UnsetVolume(command.Command): From 978c2e7dec712c1dd355cf9f5eb6a7270fa2c385 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Fri, 26 Jul 2013 16:26:50 -0500 Subject: [PATCH 0021/3699] Add server ssh command Change-Id: I9317ad6a47818d5479a046b4be8c5adbbce613ef --- openstackclient/compute/v2/server.py | 159 +++++++++++++++++++++++++++ setup.cfg | 1 + 2 files changed, 160 insertions(+) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 8f81dfdb2f..eb4ccaba23 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -15,6 +15,7 @@ """Compute v2 Server action implementations""" +import argparse import getpass import logging import os @@ -977,6 +978,164 @@ def take_action(self, parsed_args): return zip(*sorted(data.iteritems())) +class SshServer(command.Command): + """Ssh to server""" + + log = logging.getLogger(__name__ + '.SshServer') + + def get_parser(self, prog_name): + parser = super(SshServer, self).get_parser(prog_name) + parser.add_argument( + 'server', + metavar='', + help='Server (name or ID)', + ) + parser.add_argument( + '--login', + metavar='', + help='Login name (ssh -l option)', + ) + parser.add_argument( + '-l', + metavar='', + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--port', + metavar='', + type=int, + help='Destination port (ssh -p option)', + ) + parser.add_argument( + '-p', + metavar='', + dest='port', + type=int, + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--identity', + metavar='', + help='Private key file (ssh -i option)', + ) + parser.add_argument( + '-i', + metavar='', + dest='identity', + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--option', + metavar='', + help='Options in ssh_config(5) format (ssh -o option)', + ) + parser.add_argument( + '-o', + metavar='