Multiple API version support
* Use multiple entry point groups to represent each API+version combination supported * Add some tests Try it out: * Right now only '* user' commands have multiple overlapping versions; you can see the selection between v2.0 and v3 by looking at the command help output for 'tenant' vs 'project': os --os-identity-api-version=2.0 help set user os --os-identity-api-version=3 help set user Change-Id: I7114fd246843df0243d354a7cce697810bb7de62
This commit is contained in:
		
							
								
								
									
										2
									
								
								HACKING
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								HACKING
									
									
									
									
									
								
							| @@ -39,8 +39,8 @@ Human Alphabetical Order Examples | ||||
|   import logging | ||||
|   import random | ||||
|   import StringIO | ||||
|   import testtools | ||||
|   import time | ||||
|   import unittest | ||||
|  | ||||
|   from nova import flags | ||||
|   from nova import test | ||||
|   | ||||
							
								
								
									
										60
									
								
								doc/source/commands.rst
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										60
									
								
								doc/source/commands.rst
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,60 @@ | ||||
| ======== | ||||
| Commands | ||||
| ======== | ||||
|  | ||||
| Command Structure | ||||
| ================= | ||||
|  | ||||
| OpenStack Client uses a command form ``verb object``. | ||||
|  | ||||
| Note that 'object' here refers to the target of a command's action.  In coding | ||||
| discussions 'object' has its usual Python meaning.  Go figure. | ||||
|  | ||||
| Commands take the form:: | ||||
|  | ||||
|     openstack [<global-options>] <verb> <object> [<command-local-arguments>] | ||||
|  | ||||
| Command Arguments | ||||
| ----------------- | ||||
|  | ||||
|   * All long option names use two dashes ('--') as the prefix and a single dash | ||||
|     ('-') as the interpolation character.  Some common options also have the | ||||
|     traditional single letter name prefixed by a single dash ('-'). | ||||
|   * Global options generally have a corresponding environment variable that | ||||
|     may also be used to set the value. If both are present, the command-line | ||||
|     option takes priority. The environment variable names can be derived from | ||||
|     the option name by dropping the leading '--', converting all embedded dashes | ||||
|     ('-') to underscores ('_'), and converting the name to upper case. | ||||
|   * Positional arguments trail command options. In commands that require two or | ||||
|     more objects be acted upon, such as 'attach A to B', both objects appear | ||||
|     as positional arguments. If they also appear in the command object they are | ||||
|     in the same order. | ||||
|  | ||||
|  | ||||
| Implementation | ||||
| ============== | ||||
|  | ||||
| The command structure is designed to support seamless addition of extension | ||||
| command modules via entry points.  The extensions are assumed to be subclasses | ||||
| of Cliff's command.Command object. | ||||
|  | ||||
| Command Entry Points | ||||
| -------------------- | ||||
|  | ||||
| Commands are added to the client using distribute's entry points in ``setup.py``. | ||||
| There is a single common group ``openstack.cli`` for commands that are not versioned, | ||||
| and a group for each combination of OpenStack API and version that is | ||||
| supported.  For example, to support Identity API v3 there is a group called | ||||
| ``openstack.identity.v3`` that contains the individual commands.  The command | ||||
| entry points have the form:: | ||||
|  | ||||
|     "verb_object=fully.qualified.module.vXX.object:VerbObject" | ||||
|  | ||||
| For example, the 'list user' command fir the Identity API is identified in | ||||
| ``setup.py`` with:: | ||||
|  | ||||
|     'openstack.identity.v3': [ | ||||
|         # ... | ||||
|         'list_user=openstackclient.identity.v3.user:ListUser', | ||||
|         # ... | ||||
|     ], | ||||
							
								
								
									
										42
									
								
								openstackclient/common/commandmanager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										42
									
								
								openstackclient/common/commandmanager.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,42 @@ | ||||
| #   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. | ||||
| # | ||||
|  | ||||
| """Modify Cliff's CommandManager""" | ||||
|  | ||||
| import logging | ||||
| import pkg_resources | ||||
|  | ||||
| import cliff.commandmanager | ||||
|  | ||||
|  | ||||
| LOG = logging.getLogger(__name__) | ||||
|  | ||||
|  | ||||
| class CommandManager(cliff.commandmanager.CommandManager): | ||||
|     """Alters Cliff's default CommandManager behaviour to load additiona | ||||
|        command groups after initialization. | ||||
|     """ | ||||
|     def _load_commands(self, group=None): | ||||
|         if not group: | ||||
|             group = self.namespace | ||||
|         for ep in pkg_resources.iter_entry_points(group): | ||||
|             LOG.debug('found command %r' % ep.name) | ||||
|             self.commands[ep.name.replace('_', ' ')] = ep | ||||
|         return | ||||
|  | ||||
|     def add_command_group(self, group=None): | ||||
|         """Adds another group of command entrypoints""" | ||||
|         if group: | ||||
|             self._load_commands(group) | ||||
| @@ -13,7 +13,7 @@ | ||||
| #   under the License. | ||||
| # | ||||
|  | ||||
| """User action implementations""" | ||||
| """Identity v2.0 User action implementations""" | ||||
|  | ||||
| import logging | ||||
|  | ||||
| @@ -126,7 +126,7 @@ class ListUser(lister.Lister): | ||||
|     def take_action(self, parsed_args): | ||||
|         self.log.debug('take_action(%s)' % parsed_args) | ||||
|         if parsed_args.long: | ||||
|             columns = ('ID', 'Name', 'TenantId', 'Email', 'Enabled') | ||||
|             columns = ('ID', 'Name', 'Tenant Id', 'Email', 'Enabled') | ||||
|         else: | ||||
|             columns = ('ID', 'Name') | ||||
|         data = self.app.client_manager.identity.users.list() | ||||
|   | ||||
							
								
								
									
										247
									
								
								openstackclient/identity/v3/user.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										247
									
								
								openstackclient/identity/v3/user.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,247 @@ | ||||
| #   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. | ||||
| # | ||||
|  | ||||
| """Identity v3 User action implementations""" | ||||
|  | ||||
| import logging | ||||
|  | ||||
| from cliff import command | ||||
| from cliff import lister | ||||
| from cliff import show | ||||
|  | ||||
| from openstackclient.common import utils | ||||
|  | ||||
|  | ||||
| class CreateUser(show.ShowOne): | ||||
|     """Create user command""" | ||||
|  | ||||
|     api = 'identity' | ||||
|     log = logging.getLogger(__name__ + '.CreateUser') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(CreateUser, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             'name', | ||||
|             metavar='<user-name>', | ||||
|             help='New user name', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--password', | ||||
|             metavar='<user-password>', | ||||
|             help='New user password', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--email', | ||||
|             metavar='<user-email>', | ||||
|             help='New user email address', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--project', | ||||
|             metavar='<project>', | ||||
|             help='New default project name or ID', | ||||
|         ) | ||||
|         enable_group = parser.add_mutually_exclusive_group() | ||||
|         enable_group.add_argument( | ||||
|             '--enable', | ||||
|             dest='enabled', | ||||
|             action='store_true', | ||||
|             default=True, | ||||
|             help='Enable user', | ||||
|         ) | ||||
|         enable_group.add_argument( | ||||
|             '--disable', | ||||
|             dest='enabled', | ||||
|             action='store_false', | ||||
|             help='Disable user', | ||||
|         ) | ||||
|         return parser | ||||
|  | ||||
|     def take_action(self, parsed_args): | ||||
|         self.log.debug('take_action(%s)' % parsed_args) | ||||
|         identity_client = self.app.client_manager.identity | ||||
|         if parsed_args.project: | ||||
|             project_id = utils.find_resource( | ||||
|                 identity_client.projects, parsed_args.project).id | ||||
|         else: | ||||
|             project_id = None | ||||
|         user = identity_client.users.create( | ||||
|             parsed_args.name, | ||||
|             parsed_args.password, | ||||
|             parsed_args.email, | ||||
|             project_id=project_id, | ||||
|             enabled=parsed_args.enabled, | ||||
|         ) | ||||
|  | ||||
|         info = {} | ||||
|         info.update(user._info) | ||||
|         return zip(*sorted(info.iteritems())) | ||||
|  | ||||
|  | ||||
| class DeleteUser(command.Command): | ||||
|     """Delete user command""" | ||||
|  | ||||
|     api = 'identity' | ||||
|     log = logging.getLogger(__name__ + '.DeleteUser') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(DeleteUser, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             'user', | ||||
|             metavar='<user>', | ||||
|             help='Name or ID of user to delete', | ||||
|         ) | ||||
|         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) | ||||
|         identity_client.users.delete(user.id) | ||||
|         return | ||||
|  | ||||
|  | ||||
| class ListUser(lister.Lister): | ||||
|     """List user command""" | ||||
|  | ||||
|     api = 'identity' | ||||
|     log = logging.getLogger(__name__ + '.ListUser') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(ListUser, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             '--project', | ||||
|             metavar='<project>', | ||||
|             help='Name or ID of project to filter users', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--long', | ||||
|             action='store_true', | ||||
|             default=False, | ||||
|             help='Additional fields are listed in output', | ||||
|         ) | ||||
|         return parser | ||||
|  | ||||
|     def take_action(self, parsed_args): | ||||
|         self.log.debug('take_action(%s)' % parsed_args) | ||||
|         if parsed_args.long: | ||||
|             columns = ('ID', 'Name', 'Project Id', 'Email', 'Enabled') | ||||
|         else: | ||||
|             columns = ('ID', 'Name') | ||||
|         data = self.app.client_manager.identity.users.list() | ||||
|         return (columns, | ||||
|                 (utils.get_item_properties( | ||||
|                     s, columns, | ||||
|                     formatters={}, | ||||
|                 ) for s in data)) | ||||
|  | ||||
|  | ||||
| class SetUser(command.Command): | ||||
|     """Set user command""" | ||||
|  | ||||
|     api = 'identity' | ||||
|     log = logging.getLogger(__name__ + '.SetUser') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(SetUser, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             'user', | ||||
|             metavar='<user>', | ||||
|             help='Name or ID of user to change', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--name', | ||||
|             metavar='<new-user-name>', | ||||
|             help='New user name', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--password', | ||||
|             metavar='<user-password>', | ||||
|             help='New user password', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--email', | ||||
|             metavar='<user-email>', | ||||
|             help='New user email address', | ||||
|         ) | ||||
|         parser.add_argument( | ||||
|             '--project', | ||||
|             metavar='<project>', | ||||
|             help='New default project name or ID', | ||||
|         ) | ||||
|         enable_group = parser.add_mutually_exclusive_group() | ||||
|         enable_group.add_argument( | ||||
|             '--enable', | ||||
|             dest='enabled', | ||||
|             action='store_true', | ||||
|             default=True, | ||||
|             help='Enable user (default)', | ||||
|         ) | ||||
|         enable_group.add_argument( | ||||
|             '--disable', | ||||
|             dest='enabled', | ||||
|             action='store_false', | ||||
|             help='Disable 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) | ||||
|         kwargs = {} | ||||
|         if parsed_args.name: | ||||
|             kwargs['name'] = parsed_args.name | ||||
|         if parsed_args.email: | ||||
|             kwargs['email'] = parsed_args.email | ||||
|         if parsed_args.project: | ||||
|             project_id = utils.find_resource( | ||||
|                 identity_client.projects, parsed_args.project).id | ||||
|             kwargs['projectId'] = project_id | ||||
|         if 'enabled' in parsed_args: | ||||
|             kwargs['enabled'] = parsed_args.enabled | ||||
|  | ||||
|         if not len(kwargs): | ||||
|             stdout.write("User not updated, no arguments present") | ||||
|             return | ||||
|         identity_client.users.update(user.id, **kwargs) | ||||
|         return | ||||
|  | ||||
|  | ||||
| class ShowUser(show.ShowOne): | ||||
|     """Show user command""" | ||||
|  | ||||
|     api = 'identity' | ||||
|     log = logging.getLogger(__name__ + '.ShowUser') | ||||
|  | ||||
|     def get_parser(self, prog_name): | ||||
|         parser = super(ShowUser, self).get_parser(prog_name) | ||||
|         parser.add_argument( | ||||
|             'user', | ||||
|             metavar='<user>', | ||||
|             help='Name or ID of user to display', | ||||
|         ) | ||||
|         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) | ||||
|  | ||||
|         info = {} | ||||
|         info.update(user._info) | ||||
|         return zip(*sorted(info.iteritems())) | ||||
| @@ -21,17 +21,22 @@ import os | ||||
| import sys | ||||
|  | ||||
| from cliff.app import App | ||||
| from cliff.commandmanager import CommandManager | ||||
| from cliff.help import HelpAction | ||||
|  | ||||
| from openstackclient.common import clientmanager | ||||
| from openstackclient.common import exceptions as exc | ||||
| from openstackclient.common import openstackkeyring | ||||
| from openstackclient.common import utils | ||||
| from openstackclient.common.commandmanager import CommandManager | ||||
|  | ||||
|  | ||||
| VERSION = '0.1' | ||||
| KEYRING_SERVICE = 'openstack' | ||||
|  | ||||
| DEFAULT_COMPUTE_API_VERSION = '2' | ||||
| DEFAULT_IDENTITY_API_VERSION = '2.0' | ||||
| DEFAULT_IMAGE_API_VERSION = '1.0' | ||||
|  | ||||
|  | ||||
| def env(*vars, **kwargs): | ||||
|     """Search for the first defined of possibly many env vars | ||||
| @@ -63,6 +68,35 @@ class OpenStackShell(App): | ||||
|         # password flow auth | ||||
|         self.auth_client = None | ||||
|  | ||||
|         # NOTE(dtroyer): This hack changes the help action that Cliff | ||||
|         #                automatically adds to the parser so we can defer | ||||
|         #                its execution until after the api-versioned commands | ||||
|         #                have been loaded.  There doesn't seem to be a | ||||
|         #                way to edit/remove anything from an existing parser. | ||||
|  | ||||
|         # Replace the cliff-added HelpAction to defer its execution | ||||
|         self.DeferredHelpAction = None | ||||
|         for a in self.parser._actions: | ||||
|             if type(a) == HelpAction: | ||||
|                 # Found it, save and replace it | ||||
|                 self.DeferredHelpAction = a | ||||
|  | ||||
|                 # These steps are argparse-implementation-dependent | ||||
|                 self.parser._actions.remove(a) | ||||
|                 if self.parser._option_string_actions['-h']: | ||||
|                     del self.parser._option_string_actions['-h'] | ||||
|                 if self.parser._option_string_actions['--help']: | ||||
|                     del self.parser._option_string_actions['--help'] | ||||
|  | ||||
|                 # Make a new help option to just set a flag | ||||
|                 self.parser.add_argument( | ||||
|                     '-h', '--help', | ||||
|                     action='store_true', | ||||
|                     dest='deferred_help', | ||||
|                     default=False, | ||||
|                     help="show this help message and exit", | ||||
|                 ) | ||||
|  | ||||
|     def build_option_parser(self, description, version): | ||||
|         parser = super(OpenStackShell, self).build_option_parser( | ||||
|             description, | ||||
| @@ -102,20 +136,30 @@ class OpenStackShell(App): | ||||
|         parser.add_argument( | ||||
|             '--os-identity-api-version', | ||||
|             metavar='<identity-api-version>', | ||||
|             default=env('OS_IDENTITY_API_VERSION', default='2.0'), | ||||
|             help='Identity API version, default=2.0 ' | ||||
|                  '(Env: OS_IDENTITY_API_VERSION)') | ||||
|             default=env( | ||||
|                 'OS_IDENTITY_API_VERSION', | ||||
|                 default=DEFAULT_IDENTITY_API_VERSION), | ||||
|             help='Identity API version, default=' + | ||||
|                  DEFAULT_IDENTITY_API_VERSION + | ||||
|                  ' (Env: OS_IDENTITY_API_VERSION)') | ||||
|         parser.add_argument( | ||||
|             '--os-compute-api-version', | ||||
|             metavar='<compute-api-version>', | ||||
|             default=env('OS_COMPUTE_API_VERSION', default='2'), | ||||
|             help='Compute API version, default=2 ' | ||||
|                  '(Env: OS_COMPUTE_API_VERSION)') | ||||
|             default=env( | ||||
|                 'OS_COMPUTE_API_VERSION', | ||||
|                 default=DEFAULT_COMPUTE_API_VERSION), | ||||
|             help='Compute API version, default=' + | ||||
|                  DEFAULT_COMPUTE_API_VERSION + | ||||
|                  ' (Env: OS_COMPUTE_API_VERSION)') | ||||
|         parser.add_argument( | ||||
|             '--os-image-api-version', | ||||
|             metavar='<image-api-version>', | ||||
|             default=env('OS_IMAGE_API_VERSION', default='1.0'), | ||||
|             help='Image API version, default=1.0 (Env: OS_IMAGE_API_VERSION)') | ||||
|             default=env( | ||||
|                 'OS_IMAGE_API_VERSION', | ||||
|                 default=DEFAULT_IMAGE_API_VERSION), | ||||
|             help='Image API version, default=' + | ||||
|                  DEFAULT_IMAGE_API_VERSION + | ||||
|                  ' (Env: OS_IMAGE_API_VERSION)') | ||||
|         parser.add_argument( | ||||
|             '--os-token', | ||||
|             metavar='<token>', | ||||
| @@ -251,6 +295,16 @@ class OpenStackShell(App): | ||||
|             'image': self.options.os_image_api_version, | ||||
|         } | ||||
|  | ||||
|         # Add the API version-specific commands | ||||
|         for api in self.api_version.keys(): | ||||
|             version = '.v' + self.api_version[api].replace('.', '_') | ||||
|             self.command_manager.add_command_group( | ||||
|                 'openstack.' + api + version) | ||||
|  | ||||
|         # Handle deferred help and exit | ||||
|         if self.options.deferred_help: | ||||
|             self.DeferredHelpAction(self.parser, self.parser, None, None) | ||||
|  | ||||
|         # If the user is not asking for help, make sure they | ||||
|         # have given us auth. | ||||
|         cmd_name = None | ||||
|   | ||||
							
								
								
									
										45
									
								
								setup.py
									
									
									
									
									
								
							
							
						
						
									
										45
									
								
								setup.py
									
									
									
									
									
								
							| @@ -55,6 +55,8 @@ setuptools.setup( | ||||
|     entry_points={ | ||||
|         'console_scripts': ['openstack=openstackclient.shell:main'], | ||||
|         'openstack.cli': [ | ||||
|         ], | ||||
|         'openstack.identity.v2_0': [ | ||||
|             'create_endpoint=' + | ||||
|             'openstackclient.identity.v2_0.endpoint:CreateEndpoint', | ||||
|             'delete_endpoint=' + | ||||
| @@ -73,16 +75,6 @@ setuptools.setup( | ||||
|             'remove_role=' + | ||||
|             'openstackclient.identity.v2_0.role:RemoveRole', | ||||
|             'show_role=openstackclient.identity.v2_0.role:ShowRole', | ||||
|             'create_server=openstackclient.compute.v2.server:CreateServer', | ||||
|             'delete_server=openstackclient.compute.v2.server:DeleteServer', | ||||
|             'list_server=openstackclient.compute.v2.server:ListServer', | ||||
|             'pause_server=openstackclient.compute.v2.server:PauseServer', | ||||
|             'reboot_server=openstackclient.compute.v2.server:RebootServer', | ||||
|             'rebuild_server=openstackclient.compute.v2.server:RebuildServer', | ||||
|             'resume_server=openstackclient.compute.v2.server:ResumeServer', | ||||
|             'show_server=openstackclient.compute.v2.server:ShowServer', | ||||
|             'suspend_server=openstackclient.compute.v2.server:SuspendServer', | ||||
|             'unpause_server=openstackclient.compute.v2.server:UnpauseServer', | ||||
|             'create_service=' + | ||||
|             'openstackclient.identity.v2_0.service:CreateService', | ||||
|             'delete_service=' + | ||||
| @@ -96,6 +88,7 @@ setuptools.setup( | ||||
|             'list_tenant=openstackclient.identity.v2_0.tenant:ListTenant', | ||||
|             'set_tenant=openstackclient.identity.v2_0.tenant:SetTenant', | ||||
|             'show_tenant=openstackclient.identity.v2_0.tenant:ShowTenant', | ||||
|             'list_user-role=openstackclient.identity.v2_0.role:ListUserRole', | ||||
|             'create_user=' + | ||||
|             'openstackclient.identity.v2_0.user:CreateUser', | ||||
|             'delete_user=' + | ||||
| @@ -103,10 +96,8 @@ setuptools.setup( | ||||
|             'list_user=openstackclient.identity.v2_0.user:ListUser', | ||||
|             'set_user=openstackclient.identity.v2_0.user:SetUser', | ||||
|             'show_user=openstackclient.identity.v2_0.user:ShowUser', | ||||
|             'list_user-role=openstackclient.identity.v2_0.role:ListUserRole', | ||||
|             'list_image=openstackclient.image.v2.image:ListImage', | ||||
|             'show_image=openstackclient.image.v2.image:ShowImage', | ||||
|             'save_image=openstackclient.image.v2.image:SaveImage', | ||||
|         ], | ||||
|         'openstack.identity.v3': [ | ||||
|             'create_group=openstackclient.identity.v3.group:CreateGroup', | ||||
|             'delete_group=openstackclient.identity.v3.group:DeleteGroup', | ||||
|             'set_group=openstackclient.identity.v3.group:SetGroup', | ||||
| @@ -119,6 +110,30 @@ setuptools.setup( | ||||
|             'set_project=openstackclient.identity.v3.project:SetProject', | ||||
|             'show_project=openstackclient.identity.v3.project:ShowProject', | ||||
|             'list_project=openstackclient.identity.v3.project:ListProject', | ||||
|         ] | ||||
|             'create_user=' + | ||||
|             'openstackclient.identity.v3.user:CreateUser', | ||||
|             'delete_user=' + | ||||
|             'openstackclient.identity.v3.user:DeleteUser', | ||||
|             'list_user=openstackclient.identity.v3.user:ListUser', | ||||
|             'set_user=openstackclient.identity.v3.user:SetUser', | ||||
|             'show_user=openstackclient.identity.v3.user:ShowUser', | ||||
|         ], | ||||
|         'openstack.image.v2': [ | ||||
|             'list_image=openstackclient.image.v2.image:ListImage', | ||||
|             'show_image=openstackclient.image.v2.image:ShowImage', | ||||
|             'save_image=openstackclient.image.v2.image:SaveImage', | ||||
|         ], | ||||
|         'openstack.compute.v2': [ | ||||
|             'create_server=openstackclient.compute.v2.server:CreateServer', | ||||
|             'delete_server=openstackclient.compute.v2.server:DeleteServer', | ||||
|             'list_server=openstackclient.compute.v2.server:ListServer', | ||||
|             'pause_server=openstackclient.compute.v2.server:PauseServer', | ||||
|             'reboot_server=openstackclient.compute.v2.server:RebootServer', | ||||
|             'rebuild_server=openstackclient.compute.v2.server:RebuildServer', | ||||
|             'resume_server=openstackclient.compute.v2.server:ResumeServer', | ||||
|             'show_server=openstackclient.compute.v2.server:ShowServer', | ||||
|             'suspend_server=openstackclient.compute.v2.server:SuspendServer', | ||||
|             'unpause_server=openstackclient.compute.v2.server:UnpauseServer', | ||||
|         ], | ||||
|     } | ||||
| ) | ||||
|   | ||||
							
								
								
									
										71
									
								
								tests/common/test_commandmanager.py
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										71
									
								
								tests/common/test_commandmanager.py
									
									
									
									
									
										Normal file
									
								
							| @@ -0,0 +1,71 @@ | ||||
| #   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. | ||||
| # | ||||
|  | ||||
| import mock | ||||
|  | ||||
| from openstackclient.common import commandmanager | ||||
| from tests import utils | ||||
|  | ||||
|  | ||||
| class FakeCommand(object): | ||||
|     @classmethod | ||||
|     def load(cls): | ||||
|         return cls | ||||
|  | ||||
|     def __init__(self): | ||||
|         return | ||||
|  | ||||
| FAKE_CMD_ONE = FakeCommand | ||||
| FAKE_CMD_TWO = FakeCommand | ||||
| FAKE_CMD_ALPHA = FakeCommand | ||||
| FAKE_CMD_BETA = FakeCommand | ||||
|  | ||||
|  | ||||
| class FakeCommandManager(commandmanager.CommandManager): | ||||
|     commands = {} | ||||
|  | ||||
|     def _load_commands(self, group=None): | ||||
|         if not group: | ||||
|             self.commands['one'] = FAKE_CMD_ONE | ||||
|             self.commands['two'] = FAKE_CMD_TWO | ||||
|         else: | ||||
|             self.commands['alpha'] = FAKE_CMD_ALPHA | ||||
|             self.commands['beta'] = FAKE_CMD_BETA | ||||
|  | ||||
|  | ||||
| class TestCommandManager(utils.TestCase): | ||||
|     def test_add_command_group(self): | ||||
|         mgr = FakeCommandManager('test') | ||||
|  | ||||
|         # Make sure add_command() still functions | ||||
|         mock_cmd_one = mock.Mock() | ||||
|         mgr.add_command('mock', mock_cmd_one) | ||||
|         cmd_mock, name, args = mgr.find_command(['mock']) | ||||
|         self.assertEqual(cmd_mock, mock_cmd_one) | ||||
|  | ||||
|         # Find a command added in initialization | ||||
|         cmd_one, name, args = mgr.find_command(['one']) | ||||
|         self.assertEqual(cmd_one, FAKE_CMD_ONE) | ||||
|  | ||||
|         # Load another command group | ||||
|         mgr.add_command_group('latin') | ||||
|  | ||||
|         # Find a new command | ||||
|         cmd_alpha, name, args = mgr.find_command(['alpha']) | ||||
|         self.assertEqual(cmd_alpha, FAKE_CMD_ALPHA) | ||||
|  | ||||
|         # Ensure that the original commands were not overwritten | ||||
|         cmd_two, name, args = mgr.find_command(['two']) | ||||
|         self.assertEqual(cmd_two, FAKE_CMD_TWO) | ||||
| @@ -108,6 +108,30 @@ class TestShell(utils.TestCase): | ||||
|                              default_args["image_api_version"]) | ||||
|  | ||||
|  | ||||
| class TestShellHelp(TestShell): | ||||
|     """Test the deferred help flag""" | ||||
|     def setUp(self): | ||||
|         super(TestShellHelp, self).setUp() | ||||
|         self.orig_env, os.environ = os.environ, {} | ||||
|  | ||||
|     def tearDown(self): | ||||
|         super(TestShellHelp, self).tearDown() | ||||
|         os.environ = self.orig_env | ||||
|  | ||||
|     def test_help_options(self): | ||||
|         flag = "-h list server" | ||||
|         kwargs = { | ||||
|             "deferred_help": True, | ||||
|         } | ||||
|         with mock.patch("openstackclient.shell.OpenStackShell.initialize_app", | ||||
|                         self.app): | ||||
|             _shell, _cmd = make_shell(), flag | ||||
|             fake_execute(_shell, _cmd) | ||||
|  | ||||
|             self.assertEqual(_shell.options.deferred_help, | ||||
|                              kwargs["deferred_help"]) | ||||
|  | ||||
|  | ||||
| class TestShellPasswordAuth(TestShell): | ||||
|     def setUp(self): | ||||
|         super(TestShellPasswordAuth, self).setUp() | ||||
|   | ||||
		Reference in New Issue
	
	Block a user
	 Dean Troyer
					Dean Troyer