From b16f210bbf52bbd8f6f7ec9bb48a8a45219beb95 Mon Sep 17 00:00:00 2001 From: Doug Hellmann Date: Mon, 29 Apr 2013 21:29:07 -0400 Subject: [PATCH 0001/3547] Switch to noun-verb command forms Reverse the commands to use nouns followed by verbs to allow users to take full advantage of tab completion. Compound nouns (e.g., "floating-ip") are also reversed (e.g., "ip floating list" and "ip fixed list"). blueprint nouns-vs-verbs Change-Id: Icf09fb8d7dbd09772bddbbeb74f9a379d9189b72 Signed-off-by: Doug Hellmann --- setup.py | 278 +++++++++++++++++++++++++++---------------------------- 1 file changed, 139 insertions(+), 139 deletions(-) diff --git a/setup.py b/setup.py index 39290136b9..9181fe5739 100644 --- a/setup.py +++ b/setup.py @@ -56,225 +56,225 @@ def read(fname): 'console_scripts': ['openstack=openstackclient.shell:main'], 'openstack.cli': [ ], - 'openstack.identity.v2_0': [ - 'create_endpoint=' + 'openstack.identity.v3_0': [ + 'endpoint_create=' 'openstackclient.identity.v2_0.endpoint:CreateEndpoint', - 'delete_endpoint=' + 'endpoint_delete=' 'openstackclient.identity.v2_0.endpoint:DeleteEndpoint', - 'list_endpoint=' + 'endpoint_list=' 'openstackclient.identity.v2_0.endpoint:ListEndpoint', - 'show_endpoint=' + 'endpoint_show=' 'openstackclient.identity.v2_0.endpoint:ShowEndpoint', - 'add_role=' + 'role_add=' 'openstackclient.identity.v2_0.role:AddRole', - 'create_role=' + 'role_create=' 'openstackclient.identity.v2_0.role:CreateRole', - 'delete_role=' + 'role_delete=' 'openstackclient.identity.v2_0.role:DeleteRole', - 'list_role=openstackclient.identity.v2_0.role:ListRole', - 'remove_role=' + 'role_list=openstackclient.identity.v2_0.role:ListRole', + 'role_remove=' 'openstackclient.identity.v2_0.role:RemoveRole', - 'show_role=openstackclient.identity.v2_0.role:ShowRole', + 'role_show=openstackclient.identity.v2_0.role:ShowRole', - 'create_service=' + 'service_create=' 'openstackclient.identity.v2_0.service:CreateService', - 'delete_service=' + 'service_delete=' 'openstackclient.identity.v2_0.service:DeleteService', - 'list_service=openstackclient.identity.v2_0.service:ListService', - 'show_service=openstackclient.identity.v2_0.service:ShowService', + 'service_list=openstackclient.identity.v2_0.service:ListService', + 'service_show=openstackclient.identity.v2_0.service:ShowService', - 'create_tenant=' + 'tenant_create=' 'openstackclient.identity.v2_0.tenant:CreateTenant', - 'delete_tenant=' + 'tenant_delete=' 'openstackclient.identity.v2_0.tenant:DeleteTenant', - 'list_tenant=openstackclient.identity.v2_0.tenant:ListTenant', - 'set_tenant=openstackclient.identity.v2_0.tenant:SetTenant', - 'show_tenant=openstackclient.identity.v2_0.tenant:ShowTenant', + 'tenant_list=openstackclient.identity.v2_0.tenant:ListTenant', + 'tenant_set=openstackclient.identity.v2_0.tenant:SetTenant', + 'tenant_show=openstackclient.identity.v2_0.tenant:ShowTenant', - 'list_user-role=openstackclient.identity.v2_0.role:ListUserRole', + 'user_role_list=openstackclient.identity.v2_0.role:ListUserRole', - 'create_user=' + 'user_create=' 'openstackclient.identity.v2_0.user:CreateUser', - 'delete_user=' + 'user_delete=' 'openstackclient.identity.v2_0.user:DeleteUser', - 'list_user=openstackclient.identity.v2_0.user:ListUser', - 'set_user=openstackclient.identity.v2_0.user:SetUser', - 'show_user=openstackclient.identity.v2_0.user:ShowUser', + 'user_list=openstackclient.identity.v2_0.user:ListUser', + 'user_set=openstackclient.identity.v2_0.user:SetUser', + 'user_show=openstackclient.identity.v2_0.user:ShowUser', ], 'openstack.identity.v3': [ - 'create_credential=' + 'credential_create=' 'openstackclient.identity.v3.credential:CreateCredential', - 'delete_credential=' + 'credential_delete=' 'openstackclient.identity.v3.credential:DeleteCredential', - 'list_credential=' + 'credential_list=' 'openstackclient.identity.v3.credential:ListCredential', - 'set_credential=' + 'credential_set=' 'openstackclient.identity.v3.credential:SetCredential', - 'show_credential=' + 'credential_show=' 'openstackclient.identity.v3.credential:ShowCredential', - 'create_domain=openstackclient.identity.v3.domain:CreateDomain', - 'delete_domain=openstackclient.identity.v3.domain:DeleteDomain', - 'list_domain=openstackclient.identity.v3.domain:ListDomain', - 'set_domain=openstackclient.identity.v3.domain:SetDomain', - 'show_domain=openstackclient.identity.v3.domain:ShowDomain', + 'domain_create=openstackclient.identity.v3.domain:CreateDomain', + 'domain_delete=openstackclient.identity.v3.domain:DeleteDomain', + 'domain_list=openstackclient.identity.v3.domain:ListDomain', + 'domain_set=openstackclient.identity.v3.domain:SetDomain', + 'domain_show=openstackclient.identity.v3.domain:ShowDomain', - 'create_endpoint=' + 'endpoint_create=' 'openstackclient.identity.v3.endpoint:CreateEndpoint', - 'delete_endpoint=' + 'endpoint_delete=' 'openstackclient.identity.v3.endpoint:DeleteEndpoint', - 'set_endpoint=openstackclient.identity.v3.endpoint:SetEndpoint', - 'show_endpoint=openstackclient.identity.v3.endpoint:ShowEndpoint', - 'list_endpoint=openstackclient.identity.v3.endpoint:ListEndpoint', - - 'create_group=openstackclient.identity.v3.group:CreateGroup', - 'delete_group=openstackclient.identity.v3.group:DeleteGroup', - 'list_group=openstackclient.identity.v3.group:ListGroup', - 'set_group=openstackclient.identity.v3.group:SetGroup', - 'show_group=openstackclient.identity.v3.group:ShowGroup', - - 'create_policy=openstackclient.identity.v3.policy:CreatePolicy', - 'delete_policy=openstackclient.identity.v3.policy:DeletePolicy', - 'list_policy=openstackclient.identity.v3.policy:ListPolicy', - 'set_policy=openstackclient.identity.v3.policy:SetPolicy', - 'show_policy=openstackclient.identity.v3.policy:ShowPolicy', - - 'create_project=' + 'endpoint_set=openstackclient.identity.v3.endpoint:SetEndpoint', + 'endpoint_show=openstackclient.identity.v3.endpoint:ShowEndpoint', + 'endpoint_list=openstackclient.identity.v3.endpoint:ListEndpoint', + + 'group_create=openstackclient.identity.v3.group:CreateGroup', + 'group_delete=openstackclient.identity.v3.group:DeleteGroup', + 'group_list=openstackclient.identity.v3.group:ListGroup', + 'group_set=openstackclient.identity.v3.group:SetGroup', + 'group_show=openstackclient.identity.v3.group:ShowGroup', + + 'policy_create=openstackclient.identity.v3.policy:CreatePolicy', + 'policy_delete=openstackclient.identity.v3.policy:DeletePolicy', + 'policy_list=openstackclient.identity.v3.policy:ListPolicy', + 'policy_set=openstackclient.identity.v3.policy:SetPolicy', + 'policy_show=openstackclient.identity.v3.policy:ShowPolicy', + + 'project_create=' 'openstackclient.identity.v3.project:CreateProject', - 'delete_project=' + 'project_delete=' 'openstackclient.identity.v3.project:DeleteProject', - 'list_project=openstackclient.identity.v3.project:ListProject', - 'set_project=openstackclient.identity.v3.project:SetProject', - 'show_project=openstackclient.identity.v3.project:ShowProject', + 'project_list=openstackclient.identity.v3.project:ListProject', + 'project_set=openstackclient.identity.v3.project:SetProject', + 'project_show=openstackclient.identity.v3.project:ShowProject', - 'add_role=openstackclient.identity.v3.role:AddRole', - 'create_role=' + 'role_add=openstackclient.identity.v3.role:AddRole', + 'role_create=' 'openstackclient.identity.v3.role:CreateRole', - 'delete_role=' + 'role_delete=' 'openstackclient.identity.v3.role:DeleteRole', - 'list_role=openstackclient.identity.v3.role:ListRole', - 'show_role=openstackclient.identity.v3.role:ShowRole', - 'set_role=openstackclient.identity.v3.role:SetRole', + 'role_list=openstackclient.identity.v3.role:ListRole', + 'role_show=openstackclient.identity.v3.role:ShowRole', + 'role_set=openstackclient.identity.v3.role:SetRole', - 'create_service=' + 'service_create=' 'openstackclient.identity.v3.service:CreateService', - 'delete_service=' + 'service_delete=' 'openstackclient.identity.v3.service:DeleteService', - 'list_service=openstackclient.identity.v3.service:ListService', - 'show_service=openstackclient.identity.v3.service:ShowService', - 'set_service=openstackclient.identity.v3.service:SetService', + 'service_list=openstackclient.identity.v3.service:ListService', + 'service_show=openstackclient.identity.v3.service:ShowService', + 'service_set=openstackclient.identity.v3.service:SetService', - 'create_user=' + 'user_create=' 'openstackclient.identity.v3.user:CreateUser', - 'delete_user=' + 'user_delete=' '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', + 'user_list=openstackclient.identity.v3.user:ListUser', + 'user_set=openstackclient.identity.v3.user:SetUser', + 'user_show=openstackclient.identity.v3.user:ShowUser', ], 'openstack.image.v1': [ - 'create_image=openstackclient.image.v1.image:CreateImage', + 'image_create=openstackclient.image.v1.image:CreateImage', ], 'openstack.image.v2': [ - 'delete_image=openstackclient.image.v2.image:DeleteImage', - 'list_image=openstackclient.image.v2.image:ListImage', - 'save_image=openstackclient.image.v2.image:SaveImage', - 'show_image=openstackclient.image.v2.image:ShowImage', + 'image_delete=openstackclient.image.v2.image:DeleteImage', + 'image_list=openstackclient.image.v2.image:ListImage', + 'image_save=openstackclient.image.v2.image:SaveImage', + 'image_show=openstackclient.image.v2.image:ShowImage', ], 'openstack.compute.v2': [ - 'create_agent=openstackclient.compute.v2.agent:CreateAgent', - 'delete_agent=openstackclient.compute.v2.agent:DeleteAgent', - 'list_agent=openstackclient.compute.v2.agent:ListAgent', - 'set_agent=openstackclient.compute.v2.agent:SetAgent', + 'agent_create=openstackclient.compute.v2.agent:CreateAgent', + 'agent_delete=openstackclient.compute.v2.agent:DeleteAgent', + 'agent_list=openstackclient.compute.v2.agent:ListAgent', + 'agent_set=openstackclient.compute.v2.agent:SetAgent', - 'list_compute-service=' + 'compute_service_list=' 'openstackclient.compute.v2.service:ListService', - 'set_compute-service=' + 'compute_service_set=' 'openstackclient.compute.v2.service:SetService', - 'show_console-log=' + 'console_log_show=' 'openstackclient.compute.v2.console:ShowConsoleLog', - 'show_console-url=' + 'console_url_show=' 'openstackclient.compute.v2.console:ShowConsoleURL', - 'add_fixed-ip=openstackclient.compute.v2.fixedip:AddFixedIP', - 'remove_fixed-ip=openstackclient.compute.v2.fixedip:RemoveFixedIP', + 'ip_fixed_add=openstackclient.compute.v2.fixedip:AddFixedIP', + 'ip_fixed_remove=openstackclient.compute.v2.fixedip:RemoveFixedIP', - 'create_flavor=openstackclient.compute.v2.flavor:CreateFlavor', - 'delete_flavor=openstackclient.compute.v2.flavor:DeleteFlavor', - 'list_flavor=openstackclient.compute.v2.flavor:ListFlavor', - 'show_flavor=openstackclient.compute.v2.flavor:ShowFlavor', + 'flavor_create=openstackclient.compute.v2.flavor:CreateFlavor', + 'flavor_delete=openstackclient.compute.v2.flavor:DeleteFlavor', + 'flavor_list=openstackclient.compute.v2.flavor:ListFlavor', + 'flavor_show=openstackclient.compute.v2.flavor:ShowFlavor', - 'add_floating-ip=' + 'ip_floating_add=' 'openstackclient.compute.v2.floatingip:AddFloatingIP', - 'create_floating-ip=' + 'ip_floating_create=' 'openstackclient.compute.v2.floatingip:CreateFloatingIP', - 'delete_floating-ip=' + 'ip_floating_delete=' 'openstackclient.compute.v2.floatingip:DeleteFloatingIP', - 'list_floating-ip=' + 'ip_floating_list=' 'openstackclient.compute.v2.floatingip:ListFloatingIP', - 'remove_floating-ip=' + 'ip_floating_remove=' 'openstackclient.compute.v2.floatingip:RemoveFloatingIP', - 'list_floating-ip-pool=' + 'ip_floating_pool_list=' 'openstackclient.compute.v2.floatingippool:ListFloatingIPPool', - 'list_host=openstackclient.compute.v2.host:ListHost', - 'show_host=openstackclient.compute.v2.host:ShowHost', + 'host_list=openstackclient.compute.v2.host:ListHost', + 'host_show=openstackclient.compute.v2.host:ShowHost', - 'list_hypervisor=' + 'hypervisor_list=' 'openstackclient.compute.v2.hypervisor:ListHypervisor', - 'show_hypervisor=' + 'hypervisor_show=' 'openstackclient.compute.v2.hypervisor:ShowHypervisor', - 'create_keypair=' + 'keypair_create=' 'openstackclient.compute.v2.keypair:CreateKeypair', - 'delete_keypair=' + 'keypair_delete=' 'openstackclient.compute.v2.keypair:DeleteKeypair', - 'list_keypair=' + 'keypair_list=' 'openstackclient.compute.v2.keypair:ListKeypair', - 'show_keypair=' + 'keypair_show=' 'openstackclient.compute.v2.keypair:ShowKeypair', - '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', + 'server_create=openstackclient.compute.v2.server:CreateServer', + 'server_delete=openstackclient.compute.v2.server:DeleteServer', + 'server_list=openstackclient.compute.v2.server:ListServer', + 'server_pause=openstackclient.compute.v2.server:PauseServer', + 'server_reboot=openstackclient.compute.v2.server:RebootServer', + 'server_rebuild=openstackclient.compute.v2.server:RebuildServer', + 'server_resume=openstackclient.compute.v2.server:ResumeServer', + 'server_show=openstackclient.compute.v2.server:ShowServer', + 'server_suspend=openstackclient.compute.v2.server:SuspendServer', + 'server_unpause=openstackclient.compute.v2.server:UnpauseServer', ], 'openstack.volume.v1': [ - 'list_quota=openstackclient.volume.v1.quota:ListQuota', - 'set_quota=openstackclient.volume.v1.quota:SetQuota', - 'show_quota=openstackclient.volume.v1.quota:ShowQuota', + 'quota_list=openstackclient.volume.v1.quota:ListQuota', + 'quota_set=openstackclient.volume.v1.quota:SetQuota', + 'quota_show=openstackclient.volume.v1.quota:ShowQuota', - 'create_snapshot=' + 'snapshot_create=' 'openstackclient.volume.v1.snapshot:CreateSnapshot', - 'delete_snapshot=' + 'snapshot_delete=' 'openstackclient.volume.v1.snapshot:DeleteSnapshot', - 'list_snapshot=openstackclient.volume.v1.snapshot:ListSnapshot', - 'set_snapshot=openstackclient.volume.v1.snapshot:SetSnapshot', - 'show_snapshot=openstackclient.volume.v1.snapshot:ShowSnapshot', - - 'create_volume=openstackclient.volume.v1.volume:CreateVolume', - 'delete_volume=openstackclient.volume.v1.volume:DeleteVolume', - 'list_volume=openstackclient.volume.v1.volume:ListVolume', - 'set_volume=openstackclient.volume.v1.volume:SetVolume', - 'show_volume=openstackclient.volume.v1.volume:ShowVolume', - 'unset_volume=openstackclient.volume.v1.volume:UnsetVolume', - - 'create_volume-type=' + 'snapshot_list=openstackclient.volume.v1.snapshot:ListSnapshot', + 'snapshot_set=openstackclient.volume.v1.snapshot:SetSnapshot', + 'snapshot_show=openstackclient.volume.v1.snapshot:ShowSnapshot', + + 'volume_create=openstackclient.volume.v1.volume:CreateVolume', + 'volume_delete=openstackclient.volume.v1.volume:DeleteVolume', + 'volume_list=openstackclient.volume.v1.volume:ListVolume', + 'volume_set=openstackclient.volume.v1.volume:SetVolume', + 'volume_show=openstackclient.volume.v1.volume:ShowVolume', + 'volume_unset=openstackclient.volume.v1.volume:UnsetVolume', + + 'volume_type_create=' 'openstackclient.volume.v1.type:CreateVolumeType', - 'delete_volume-type=' + 'volume_type_delete=' 'openstackclient.volume.v1.type:DeleteVolumeType', - 'list_volume-type=openstackclient.volume.v1.type:ListVolumeType', - 'set_volume-type=openstackclient.volume.v1.type:SetVolumeType', - 'unset_volume-type=openstackclient.volume.v1.type:UnsetVolumeType', + 'volume_type_list=openstackclient.volume.v1.type:ListVolumeType', + 'volume_type_set=openstackclient.volume.v1.type:SetVolumeType', + 'volume_type_unset=openstackclient.volume.v1.type:UnsetVolumeType', ] } ) From a2e3a16221915946ee0d847b1e95187c0f628079 Mon Sep 17 00:00:00 2001 From: Josh Kearney Date: Tue, 7 May 2013 11:18:38 -0500 Subject: [PATCH 0002/3547] Rename all instances of 'metadata' to 'property'. Change-Id: I454cbe685dc5afa0a09ecc976a90d6eb6bc57d14 --- openstackclient/compute/v2/server.py | 6 +++--- openstackclient/image/v1/image.py | 8 ++++---- openstackclient/volume/v1/volume.py | 8 ++++---- 3 files changed, 11 insertions(+), 11 deletions(-) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 1abd8dd0e8..79deaca48d 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -149,11 +149,11 @@ def get_parser(self, prog_name): metavar='', help='Keypair to inject into this server (optional extension)') parser.add_argument( - '--metadata', + '--property', metavar='', action='append', default=[], - help='Metadata to store for this server ' + help='Property to store for this server ' '(repeat for multiple values)') parser.add_argument( '--file', @@ -229,7 +229,7 @@ def take_action(self, parsed_args): boot_args = [parsed_args.server_name, image, flavor] - meta = dict(v.split('=', 1) for v in parsed_args.metadata) + meta = dict(v.split('=', 1) for v in parsed_args.property) files = {} for f in parsed_args.file: diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index fa566bd916..43cae21c07 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -92,11 +92,11 @@ def get_parser(self, prog_name): help="Similar to --location, but this indicates that the image" " should immediately be copied from the data store.") parser.add_argument( - "--metadata", + "--property", metavar="", default=[], action="append", - help="Arbitrary metadata to associate with image.") + help="Arbitrary property to associate with image.") protected_group = parser.add_mutually_exclusive_group() protected_group.add_argument( "--protected", @@ -136,8 +136,8 @@ def take_action(self, parsed_args): args.pop("variables") args["properties"] = {} - for _metadata in args.pop("metadata"): - key, value = _metadata.split("=", 1) + 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: diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index 174ca3d410..f9641c5482 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -76,9 +76,9 @@ def get_parser(self, prog_name): help='Availability Zone to use', ) parser.add_argument( - '--meta-data', + '--property', metavar='', - help='Optional metadata to set on volume creation', + help='Optional property to set on volume creation', ) parser.add_argument( '--image-ref', @@ -242,8 +242,8 @@ def take_action(self, parsed_args): volume = utils.find_resource(volume_client.volumes, parsed_args.volume) meta = None - if parsed_args.meta_data: - meta = dict(v.split('=') for v in parsed_args.meta_data.split(' ')) + if parsed_args.property: + meta = dict(v.split('=') for v in parsed_args.property.split(' ')) volume_client.volumes.set_metadata(volume.id, meta) kwargs = {} From 016a0b301e0ecfea5d84b09e7f1e22a86953c1c1 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 14 May 2013 08:28:48 -0700 Subject: [PATCH 0003/3547] Fix flake8 errors in anticipation of flake8 patch. Change-Id: Ifdc4322b699f2bd91a6900e55695acd3d736568e --- openstackclient/common/command.py | 4 ++-- openstackclient/common/utils.py | 2 +- openstackclient/compute/v2/server.py | 2 +- openstackclient/identity/v2_0/service.py | 6 ++++-- openstackclient/identity/v2_0/tenant.py | 3 ++- openstackclient/identity/v2_0/user.py | 3 ++- openstackclient/identity/v3/group.py | 3 ++- openstackclient/identity/v3/project.py | 3 ++- openstackclient/identity/v3/user.py | 3 ++- openstackclient/shell.py | 14 +++++++------- openstackclient/volume/v1/quota.py | 3 ++- tests/identity/test_identity.py | 2 -- tests/test_shell.py | 2 +- 13 files changed, 28 insertions(+), 22 deletions(-) diff --git a/openstackclient/common/command.py b/openstackclient/common/command.py index 64e855df67..59cd0da281 100644 --- a/openstackclient/common/command.py +++ b/openstackclient/common/command.py @@ -15,10 +15,10 @@ """OpenStack base command""" -from cliff.command import Command +from cliff import command -class OpenStackCommand(Command): +class OpenStackCommand(command.Command): """Base class for OpenStack commands.""" api = None diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 8a7926754c..56f9cd17d5 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -55,7 +55,7 @@ def find_resource(manager, name_or_id): except Exception as ex: try: return manager.find(display_name=name_or_id) - except: + except Exception: pass if type(ex).__name__ == 'NotFound': diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index 79deaca48d..a2496fad12 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -528,7 +528,7 @@ def take_action(self, parsed_args): _password = None if parsed_args.rebuild_password is not False: - _password = args.rebuild_password + _password = parsed_args.rebuild_password kwargs = {} server = server.rebuild(image, _password, **kwargs) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index 21e32a51b1..51abfc18ce 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -141,8 +141,10 @@ def take_action(self, parsed_args): # 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." % \ - name_or_id + 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 = {} diff --git a/openstackclient/identity/v2_0/tenant.py b/openstackclient/identity/v2_0/tenant.py index 8a2f738f4a..00a6a977cf 100644 --- a/openstackclient/identity/v2_0/tenant.py +++ b/openstackclient/identity/v2_0/tenant.py @@ -16,6 +16,7 @@ """Tenant action implementations""" import logging +import sys from cliff import command from cliff import lister @@ -167,7 +168,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = parsed_args.enabled if kwargs == {}: - stdout.write("Tenant not updated, no arguments present") + sys.stdout.write("Tenant not updated, no arguments present") return 0 tenant.update(**kwargs) return diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index 840cc50084..03da60085f 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -16,6 +16,7 @@ """Identity v2.0 User action implementations""" import logging +import sys from cliff import command from cliff import lister @@ -196,7 +197,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = parsed_args.enabled if not len(kwargs): - stdout.write("User not updated, no arguments present") + sys.stdout.write("User not updated, no arguments present") return identity_client.users.update(user.id, **kwargs) return diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 3a9b80ed11..0562b76630 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -16,6 +16,7 @@ """Group action implementations""" import logging +import sys from cliff import command from cliff import lister @@ -157,7 +158,7 @@ def take_action(self, parsed_args): kwargs['domain'] = domain if not len(kwargs): - stdout.write("Group not updated, no arguments present") + sys.stdout.write("Group not updated, no arguments present") return identity_client.groups.update(group.id, **kwargs) return diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index d1e67acc2f..84271cd9a5 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -16,6 +16,7 @@ """Project action implementations""" import logging +import sys from cliff import command from cliff import lister @@ -189,7 +190,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = parsed_args.enabled if kwargs == {}: - stdout.write("Project not updated, no arguments present") + sys.stdout.write("Project not updated, no arguments present") return project.update(**kwargs) return diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index bf592d8117..7bd3706593 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -16,6 +16,7 @@ """Identity v3 User action implementations""" import logging +import sys from cliff import command from cliff import lister @@ -215,7 +216,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = parsed_args.enabled if not len(kwargs): - stdout.write("User not updated, no arguments present") + sys.stdout.write("User not updated, no arguments present") return identity_client.users.update(user.id, **kwargs) return diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 303771c6b1..cd5ab552bf 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -20,14 +20,14 @@ import os import sys -from cliff.app import App -from cliff.help import HelpAction +from cliff import app +from cliff import help from openstackclient.common import clientmanager +from openstackclient.common.commandmanager import CommandManager 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' @@ -53,7 +53,7 @@ def env(*vars, **kwargs): return kwargs.get('default', '') -class OpenStackShell(App): +class OpenStackShell(app.App): CONSOLE_MESSAGE_FORMAT = '%(levelname)s: %(name)s %(message)s' @@ -75,10 +75,10 @@ def __init__(self): # 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 + # Replace the cliff-added help.HelpAction to defer its execution self.DeferredHelpAction = None for a in self.parser._actions: - if type(a) == HelpAction: + if type(a) == help.HelpAction: # Found it, save and replace it self.DeferredHelpAction = a @@ -359,7 +359,7 @@ def clean_up(self, cmd, result, err): def main(argv=sys.argv[1:]): try: return OpenStackShell().run(argv) - except: + except Exception: return 1 diff --git a/openstackclient/volume/v1/quota.py b/openstackclient/volume/v1/quota.py index ae6c50f5e3..4f4e97e8f5 100644 --- a/openstackclient/volume/v1/quota.py +++ b/openstackclient/volume/v1/quota.py @@ -16,6 +16,7 @@ """Volume v1 Quota action implementations""" import logging +import sys from cliff import command from cliff import show @@ -79,7 +80,7 @@ def take_action(self, parsed_args): kwargs['gigabytes'] = parsed_args.gigabytes if kwargs == {}: - stdout.write("Quota not updated, no arguments present") + sys.stdout.write("Quota not updated, no arguments present") return volume_client = self.app.client_manager.volume diff --git a/tests/identity/test_identity.py b/tests/identity/test_identity.py index 5b2cf4e334..52bbd22218 100644 --- a/tests/identity/test_identity.py +++ b/tests/identity/test_identity.py @@ -13,8 +13,6 @@ # under the License. # -import mock - from openstackclient.common import clientmanager from openstackclient.identity import client as identity_client from tests import utils diff --git a/tests/test_shell.py b/tests/test_shell.py index c33068d868..d0eb5b0d39 100644 --- a/tests/test_shell.py +++ b/tests/test_shell.py @@ -13,8 +13,8 @@ # under the License. # -import os import mock +import os from openstackclient import shell from tests import utils From 967d929207fa52c00acaa0a3e1b63bbe8e7f3835 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 14 May 2013 08:29:16 -0700 Subject: [PATCH 0004/3547] Migrate to flake8. Fixes bug 1172444 Change-Id: Ieca721663aea2fd31753df4abfb5b01a7145b26a --- openstackclient/shell.py | 4 ++-- tools/test-requires | 7 ++++++- tox.ini | 8 ++++++-- 3 files changed, 14 insertions(+), 5 deletions(-) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index cd5ab552bf..35d8255ddb 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -24,7 +24,7 @@ from cliff import help from openstackclient.common import clientmanager -from openstackclient.common.commandmanager import CommandManager +from openstackclient.common import commandmanager from openstackclient.common import exceptions as exc from openstackclient.common import openstackkeyring from openstackclient.common import utils @@ -63,7 +63,7 @@ def __init__(self): super(OpenStackShell, self).__init__( description=__doc__.strip(), version=VERSION, - command_manager=CommandManager('openstack.cli')) + command_manager=commandmanager.CommandManager('openstack.cli')) # This is instantiated in initialize_app() only when using # password flow auth diff --git a/tools/test-requires b/tools/test-requires index 405b408ffa..1eb2509c73 100644 --- a/tools/test-requires +++ b/tools/test-requires @@ -1,3 +1,9 @@ +# Install bounded pep8/pyflakes first, then let flake8 install +pep8==1.4.5 +pyflakes==0.7.2 +flake8==2.0 +hacking>=0.5.3,<0.6 + distribute>=0.6.24 coverage @@ -5,7 +11,6 @@ discover fixtures>=0.3.12 mock openstack.nose_plugin -pep8==1.3.3 sphinx>=1.1.2 testrepository>=0.0.13 testtools>=0.9.26 diff --git a/tox.ini b/tox.ini index 9b5a843bf1..f6de18d137 100644 --- a/tox.ini +++ b/tox.ini @@ -11,8 +11,7 @@ deps = -r{toxinidir}/tools/pip-requires commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] -deps = pep8==1.3.3 -commands = pep8 --ignore=E126,E202,W602 --repeat --show-source openstackclient setup.py +commands = flake8 [testenv:venv] commands = {posargs} @@ -22,3 +21,8 @@ commands = python setup.py testr --coverage --testr-args='{posargs}' [tox:jenkins] downloadcache = ~/cache/pip + +[flake8] +ignore = E126,E202,W602,H402 +show-source = True +exclude = .venv,.git,.tox,dist,doc,*openstack/common*,*lib/python*,*egg,build,tools From d49fcb726d078b25566e57c7604d0d52d9998b22 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 14 May 2013 08:42:41 -0700 Subject: [PATCH 0005/3547] Migrate to pbr. Fixes bug 1179007 Change-Id: Ief74b121dcad28bb1c2b6044ef72e0cbd51e8f65 --- .gitignore | 1 - MANIFEST.in | 1 - openstack-common.conf | 2 +- openstackclient/openstack/common/setup.py | 335 ---------------------- setup.cfg | 204 +++++++++++++ setup.py | 287 +----------------- tools/pip-requires | 2 + 7 files changed, 221 insertions(+), 611 deletions(-) delete mode 100644 openstackclient/openstack/common/setup.py diff --git a/.gitignore b/.gitignore index d6f1cdbc63..a8d9f3d69d 100644 --- a/.gitignore +++ b/.gitignore @@ -16,4 +16,3 @@ build ChangeLog dist doc/build -openstackclient/versioninfo diff --git a/MANIFEST.in b/MANIFEST.in index a7f747b848..fd87b80fdf 100644 --- a/MANIFEST.in +++ b/MANIFEST.in @@ -1,7 +1,6 @@ include AUTHORS include ChangeLog include LICENSE -include openstackclient/versioninfo include README.rst recursive-include doc * diff --git a/openstack-common.conf b/openstack-common.conf index b99037bb3a..58c0dee0ef 100644 --- a/openstack-common.conf +++ b/openstack-common.conf @@ -1,7 +1,7 @@ [DEFAULT] # The list of modules to copy from openstack-common -modules=cfg,iniparser,install_venv_common,openstackkeyring,setup +modules=cfg,iniparser,install_venv_common,openstackkeyring # The base module to hold the copy of openstack.common base=openstackclient diff --git a/openstackclient/openstack/common/setup.py b/openstackclient/openstack/common/setup.py deleted file mode 100644 index fb187fff46..0000000000 --- a/openstackclient/openstack/common/setup.py +++ /dev/null @@ -1,335 +0,0 @@ -# vim: tabstop=4 shiftwidth=4 softtabstop=4 - -# Copyright 2011 OpenStack LLC. -# Copyright 2012-2013 Hewlett-Packard Development Company, L.P. -# 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. - -""" -Utilities with minimum-depends for use in setup.py -""" - -import email -import os -import re -import subprocess -import sys - -from setuptools.command import sdist - - -def parse_mailmap(mailmap='.mailmap'): - mapping = {} - if os.path.exists(mailmap): - with open(mailmap, 'r') as fp: - for l in fp: - try: - canonical_email, alias = re.match( - r'[^#]*?(<.+>).*(<.+>).*', l).groups() - except AttributeError: - continue - mapping[alias] = canonical_email - return mapping - - -def canonicalize_emails(changelog, mapping): - """Takes in a string and an email alias mapping and replaces all - instances of the aliases in the string with their real email. - """ - for alias, email_address in mapping.iteritems(): - changelog = changelog.replace(alias, email_address) - return changelog - - -# Get requirements from the first file that exists -def get_reqs_from_files(requirements_files): - for requirements_file in requirements_files: - if os.path.exists(requirements_file): - with open(requirements_file, 'r') as fil: - return fil.read().split('\n') - return [] - - -def parse_requirements(requirements_files=['requirements.txt', - 'tools/pip-requires']): - requirements = [] - for line in get_reqs_from_files(requirements_files): - # For the requirements list, we need to inject only the portion - # after egg= so that distutils knows the package it's looking for - # such as: - # -e git://github.com/openstack/nova/master#egg=nova - if re.match(r'\s*-e\s+', line): - requirements.append(re.sub(r'\s*-e\s+.*#egg=(.*)$', r'\1', - line)) - # such as: - # http://github.com/openstack/nova/zipball/master#egg=nova - elif re.match(r'\s*https?:', line): - requirements.append(re.sub(r'\s*https?:.*#egg=(.*)$', r'\1', - line)) - # -f lines are for index locations, and don't get used here - elif re.match(r'\s*-f\s+', line): - pass - # argparse is part of the standard library starting with 2.7 - # adding it to the requirements list screws distro installs - elif line == 'argparse' and sys.version_info >= (2, 7): - pass - else: - requirements.append(line) - - return requirements - - -def parse_dependency_links(requirements_files=['requirements.txt', - 'tools/pip-requires']): - dependency_links = [] - # dependency_links inject alternate locations to find packages listed - # in requirements - for line in get_reqs_from_files(requirements_files): - # skip comments and blank lines - if re.match(r'(\s*#)|(\s*$)', line): - continue - # lines with -e or -f need the whole line, minus the flag - if re.match(r'\s*-[ef]\s+', line): - dependency_links.append(re.sub(r'\s*-[ef]\s+', '', line)) - # lines that are only urls can go in unmolested - elif re.match(r'\s*https?:', line): - dependency_links.append(line) - return dependency_links - - -def _run_shell_command(cmd, throw_on_error=False): - if os.name == 'nt': - output = subprocess.Popen(["cmd.exe", "/C", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - else: - output = subprocess.Popen(["/bin/sh", "-c", cmd], - stdout=subprocess.PIPE, - stderr=subprocess.PIPE) - if output.returncode and throw_on_error: - raise Exception("%s returned %d" % cmd, output.returncode) - out = output.communicate() - if len(out) == 0: - return None - if len(out[0].strip()) == 0: - return None - return out[0].strip() - - -def write_git_changelog(): - """Write a changelog based on the git changelog.""" - new_changelog = 'ChangeLog' - if not os.getenv('SKIP_WRITE_GIT_CHANGELOG'): - if os.path.isdir('.git'): - git_log_cmd = 'git log --stat' - changelog = _run_shell_command(git_log_cmd) - mailmap = parse_mailmap() - with open(new_changelog, "w") as changelog_file: - changelog_file.write(canonicalize_emails(changelog, mailmap)) - else: - open(new_changelog, 'w').close() - - -def generate_authors(): - """Create AUTHORS file using git commits.""" - jenkins_email = 'jenkins@review.(openstack|stackforge).org' - old_authors = 'AUTHORS.in' - new_authors = 'AUTHORS' - if not os.getenv('SKIP_GENERATE_AUTHORS'): - if os.path.isdir('.git'): - # don't include jenkins email address in AUTHORS file - git_log_cmd = ("git log --format='%aN <%aE>' | sort -u | " - "egrep -v '" + jenkins_email + "'") - changelog = _run_shell_command(git_log_cmd) - mailmap = parse_mailmap() - with open(new_authors, 'w') as new_authors_fh: - new_authors_fh.write(canonicalize_emails(changelog, mailmap)) - if os.path.exists(old_authors): - with open(old_authors, "r") as old_authors_fh: - new_authors_fh.write('\n' + old_authors_fh.read()) - else: - open(new_authors, 'w').close() - - -_rst_template = """%(heading)s -%(underline)s - -.. automodule:: %(module)s - :members: - :undoc-members: - :show-inheritance: -""" - - -def get_cmdclass(): - """Return dict of commands to run from setup.py.""" - - cmdclass = dict() - - def _find_modules(arg, dirname, files): - for filename in files: - if filename.endswith('.py') and filename != '__init__.py': - arg["%s.%s" % (dirname.replace('/', '.'), - filename[:-3])] = True - - class LocalSDist(sdist.sdist): - """Builds the ChangeLog and Authors files from VC first.""" - - def run(self): - write_git_changelog() - generate_authors() - # sdist.sdist is an old style class, can't use super() - sdist.sdist.run(self) - - cmdclass['sdist'] = LocalSDist - - # If Sphinx is installed on the box running setup.py, - # enable setup.py to build the documentation, otherwise, - # just ignore it - try: - from sphinx.setup_command import BuildDoc - - class LocalBuildDoc(BuildDoc): - - builders = ['html', 'man'] - - def generate_autoindex(self): - print "**Autodocumenting from %s" % os.path.abspath(os.curdir) - modules = {} - option_dict = self.distribution.get_option_dict('build_sphinx') - source_dir = os.path.join(option_dict['source_dir'][1], 'api') - if not os.path.exists(source_dir): - os.makedirs(source_dir) - for pkg in self.distribution.packages: - if '.' not in pkg: - os.path.walk(pkg, _find_modules, modules) - module_list = modules.keys() - module_list.sort() - autoindex_filename = os.path.join(source_dir, 'autoindex.rst') - with open(autoindex_filename, 'w') as autoindex: - autoindex.write(""".. toctree:: - :maxdepth: 1 - -""") - for module in module_list: - output_filename = os.path.join(source_dir, - "%s.rst" % module) - heading = "The :mod:`%s` Module" % module - underline = "=" * len(heading) - values = dict(module=module, heading=heading, - underline=underline) - - print "Generating %s" % output_filename - with open(output_filename, 'w') as output_file: - output_file.write(_rst_template % values) - autoindex.write(" %s.rst\n" % module) - - def run(self): - if not os.getenv('SPHINX_DEBUG'): - self.generate_autoindex() - - for builder in self.builders: - self.builder = builder - self.finalize_options() - self.project = self.distribution.get_name() - self.version = self.distribution.get_version() - self.release = self.distribution.get_version() - BuildDoc.run(self) - - class LocalBuildLatex(LocalBuildDoc): - builders = ['latex'] - - cmdclass['build_sphinx'] = LocalBuildDoc - cmdclass['build_sphinx_latex'] = LocalBuildLatex - except ImportError: - pass - - return cmdclass - - -def _get_revno(): - """Return the number of commits since the most recent tag. - - We use git-describe to find this out, but if there are no - tags then we fall back to counting commits since the beginning - of time. - """ - describe = _run_shell_command("git describe --always") - if "-" in describe: - return describe.rsplit("-", 2)[-2] - - # no tags found - revlist = _run_shell_command("git rev-list --abbrev-commit HEAD") - return len(revlist.splitlines()) - - -def get_version_from_git(pre_version): - """Return a version which is equal to the tag that's on the current - revision if there is one, or tag plus number of additional revisions - if the current revision has no tag.""" - - if os.path.isdir('.git'): - if pre_version: - try: - return _run_shell_command( - "git describe --exact-match", - throw_on_error=True).replace('-', '.') - except Exception: - sha = _run_shell_command("git log -n1 --pretty=format:%h") - return "%s.a%s.g%s" % (pre_version, _get_revno(), sha) - else: - return _run_shell_command( - "git describe --always").replace('-', '.') - return None - - -def get_version_from_pkg_info(package_name): - """Get the version from PKG-INFO file if we can.""" - try: - pkg_info_file = open('PKG-INFO', 'r') - except (IOError, OSError): - return None - try: - pkg_info = email.message_from_file(pkg_info_file) - except email.MessageError: - return None - # Check to make sure we're in our own dir - if pkg_info.get('Name', None) != package_name: - return None - return pkg_info.get('Version', None) - - -def get_version(package_name, pre_version=None): - """Get the version of the project. First, try getting it from PKG-INFO, if - it exists. If it does, that means we're in a distribution tarball or that - install has happened. Otherwise, if there is no PKG-INFO file, pull the - version from git. - - We do not support setup.py version sanity in git archive tarballs, nor do - we support packagers directly sucking our git repo into theirs. We expect - that a source tarball be made from our git repo - or that if someone wants - to make a source tarball from a fork of our repo with additional tags in it - that they understand and desire the results of doing that. - """ - version = os.environ.get("OSLO_PACKAGE_VERSION", None) - if version: - return version - version = get_version_from_pkg_info(package_name) - if version: - return version - version = get_version_from_git(pre_version) - if version: - return version - raise Exception("Versioning for this project requires either an sdist" - " tarball, or access to an upstream git repository.") diff --git a/setup.cfg b/setup.cfg index 11c72013c2..1990b2c5a3 100644 --- a/setup.cfg +++ b/setup.cfg @@ -1,3 +1,207 @@ +[metadata] +name = python-openstackclient +summary = OpenStack Command-line Client +description-file = + README.rst +author = OpenStack +author-email = openstack-dev@lists.openstack.org +home-page = http://www.openstack.org/ +classifier = + Environment :: OpenStack + Intended Audience :: Information Technology + Intended Audience :: System Administrators + License :: OSI Approved :: Apache Software License + Operating System :: POSIX :: Linux + Programming Language :: Python + Programming Language :: Python :: 2 + Programming Language :: Python :: 2.7 + Programming Language :: Python :: 2.6 + +[files] +packages = + openstackclient + +[global] +setup-hooks = + pbr.hooks.setup_hook + +[entry_points] +console_scripts = + openstack = openstackclient.shell:main + +openstack.cli = + +openstack.identity.v3_0 = + endpoint_create = openstackclient.identity.v2_0.endpoint:CreateEndpoint + endpoint_delete = openstackclient.identity.v2_0.endpoint:DeleteEndpoint + endpoint_list = openstackclient.identity.v2_0.endpoint:ListEndpoint + endpoint_show = openstackclient.identity.v2_0.endpoint:ShowEndpoint + + role_add = openstackclient.identity.v2_0.role:AddRole + role_create = openstackclient.identity.v2_0.role:CreateRole + role_delete = openstackclient.identity.v2_0.role:DeleteRole + role_list =openstackclient.identity.v2_0.role:ListRole + role_remove = openstackclient.identity.v2_0.role:RemoveRole + role_show =openstackclient.identity.v2_0.role:ShowRole + + service_create = openstackclient.identity.v2_0.service:CreateService + service_delete = openstackclient.identity.v2_0.service:DeleteService + service_list =openstackclient.identity.v2_0.service:ListService + service_show =openstackclient.identity.v2_0.service:ShowService + + tenant_create = openstackclient.identity.v2_0.tenant:CreateTenant + tenant_delete = openstackclient.identity.v2_0.tenant:DeleteTenant + tenant_list = openstackclient.identity.v2_0.tenant:ListTenant + tenant_set = openstackclient.identity.v2_0.tenant:SetTenant + tenant_show = openstackclient.identity.v2_0.tenant:ShowTenant + + user_role_list = openstackclient.identity.v2_0.role:ListUserRole + + user_create = openstackclient.identity.v2_0.user:CreateUser + user_delete = openstackclient.identity.v2_0.user:DeleteUser + user_list = openstackclient.identity.v2_0.user:ListUser + user_set = openstackclient.identity.v2_0.user:SetUser + user_show = openstackclient.identity.v2_0.user:ShowUser + +openstack.identity.v3 = + credential_create = openstackclient.identity.v3.credential:CreateCredential + credential_delete = openstackclient.identity.v3.credential:DeleteCredential + credential_list = openstackclient.identity.v3.credential:ListCredential + credential_set = openstackclient.identity.v3.credential:SetCredential + credential_show = openstackclient.identity.v3.credential:ShowCredential + + domain_create = openstackclient.identity.v3.domain:CreateDomain + domain_delete = openstackclient.identity.v3.domain:DeleteDomain + domain_list = openstackclient.identity.v3.domain:ListDomain + domain_set = openstackclient.identity.v3.domain:SetDomain + domain_show = openstackclient.identity.v3.domain:ShowDomain + + endpoint_create = openstackclient.identity.v3.endpoint:CreateEndpoint + endpoint_delete = openstackclient.identity.v3.endpoint:DeleteEndpoint + endpoint_set = openstackclient.identity.v3.endpoint:SetEndpoint + endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint + endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint + + group_create = openstackclient.identity.v3.group:CreateGroup + group_delete = openstackclient.identity.v3.group:DeleteGroup + group_list = openstackclient.identity.v3.group:ListGroup + group_set = openstackclient.identity.v3.group:SetGroup + group_show = openstackclient.identity.v3.group:ShowGroup + + policy_create = openstackclient.identity.v3.policy:CreatePolicy + policy_delete = openstackclient.identity.v3.policy:DeletePolicy + policy_list = openstackclient.identity.v3.policy:ListPolicy + policy_set = openstackclient.identity.v3.policy:SetPolicy + policy_show = openstackclient.identity.v3.policy:ShowPolicy + + project_create = openstackclient.identity.v3.project:CreateProject + project_delete = openstackclient.identity.v3.project:DeleteProject + project_list = openstackclient.identity.v3.project:ListProject + project_set = openstackclient.identity.v3.project:SetProject + project_show = openstackclient.identity.v3.project:ShowProject + + role_add = openstackclient.identity.v3.role:AddRole + role_create = openstackclient.identity.v3.role:CreateRole + role_delete = openstackclient.identity.v3.role:DeleteRole + role_list = openstackclient.identity.v3.role:ListRole + role_show = openstackclient.identity.v3.role:ShowRole + role_set = openstackclient.identity.v3.role:SetRole + + service_create = openstackclient.identity.v3.service:CreateService + service_delete = openstackclient.identity.v3.service:DeleteService + service_list = openstackclient.identity.v3.service:ListService + service_show = openstackclient.identity.v3.service:ShowService + service_set = openstackclient.identity.v3.service:SetService + + user_create = openstackclient.identity.v3.user:CreateUser + user_delete = openstackclient.identity.v3.user:DeleteUser + user_list = openstackclient.identity.v3.user:ListUser + user_set = openstackclient.identity.v3.user:SetUser + user_show = openstackclient.identity.v3.user:ShowUser + +openstack.image.v1 = + image_create = openstackclient.image.v1.image:CreateImage + +openstack.image.v2 = + image_delete = openstackclient.image.v2.image:DeleteImage + image_list = openstackclient.image.v2.image:ListImage + image_save = openstackclient.image.v2.image:SaveImage + image_show = openstackclient.image.v2.image:ShowImage + +openstack.compute.v2 = + agent_create = openstackclient.compute.v2.agent:CreateAgent + agent_delete = openstackclient.compute.v2.agent:DeleteAgent + agent_list = openstackclient.compute.v2.agent:ListAgent + agent_set = openstackclient.compute.v2.agent:SetAgent + + compute_service_list = openstackclient.compute.v2.service:ListService + compute_service_set = openstackclient.compute.v2.service:SetService + + console_log_show = openstackclient.compute.v2.console:ShowConsoleLog + console_url_show = openstackclient.compute.v2.console:ShowConsoleURL + + ip_fixed_add = openstackclient.compute.v2.fixedip:AddFixedIP + ip_fixed_remove = openstackclient.compute.v2.fixedip:RemoveFixedIP + + flavor_create = openstackclient.compute.v2.flavor:CreateFlavor + flavor_delete = openstackclient.compute.v2.flavor:DeleteFlavor + flavor_list = openstackclient.compute.v2.flavor:ListFlavor + flavor_show = openstackclient.compute.v2.flavor:ShowFlavor + + ip_floating_add = openstackclient.compute.v2.floatingip:AddFloatingIP + ip_floating_create = openstackclient.compute.v2.floatingip:CreateFloatingIP + ip_floating_delete = openstackclient.compute.v2.floatingip:DeleteFloatingIP + ip_floating_list = openstackclient.compute.v2.floatingip:ListFloatingIP + ip_floating_remove = openstackclient.compute.v2.floatingip:RemoveFloatingIP + + ip_floating_pool_list = openstackclient.compute.v2.floatingippool:ListFloatingIPPool + + host_list = openstackclient.compute.v2.host:ListHost + host_show = openstackclient.compute.v2.host:ShowHost + + hypervisor_list = openstackclient.compute.v2.hypervisor:ListHypervisor + hypervisor_show = openstackclient.compute.v2.hypervisor:ShowHypervisor + + keypair_create = openstackclient.compute.v2.keypair:CreateKeypair + keypair_delete = openstackclient.compute.v2.keypair:DeleteKeypair + keypair_list = openstackclient.compute.v2.keypair:ListKeypair + keypair_show = openstackclient.compute.v2.keypair:ShowKeypair + + server_create = openstackclient.compute.v2.server:CreateServer + server_delete = openstackclient.compute.v2.server:DeleteServer + server_list = openstackclient.compute.v2.server:ListServer + server_pause = openstackclient.compute.v2.server:PauseServer + server_reboot = openstackclient.compute.v2.server:RebootServer + server_rebuild = openstackclient.compute.v2.server:RebuildServer + server_resume = openstackclient.compute.v2.server:ResumeServer + server_show = openstackclient.compute.v2.server:ShowServer + server_suspend = openstackclient.compute.v2.server:SuspendServer + 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 + snapshot_set = openstackclient.volume.v1.snapshot:SetSnapshot + snapshot_show = openstackclient.volume.v1.snapshot:ShowSnapshot + + volume_create = openstackclient.volume.v1.volume:CreateVolume + volume_delete = openstackclient.volume.v1.volume:DeleteVolume + volume_list = openstackclient.volume.v1.volume:ListVolume + volume_set = openstackclient.volume.v1.volume:SetVolume + volume_show = openstackclient.volume.v1.volume:ShowVolume + volume_unset = openstackclient.volume.v1.volume:UnsetVolume + + volume_type_create = openstackclient.volume.v1.type:CreateVolumeType + volume_type_delete = openstackclient.volume.v1.type:DeleteVolumeType + volume_type_list = openstackclient.volume.v1.type:ListVolumeType + volume_type_set = openstackclient.volume.v1.type:SetVolumeType + volume_type_unset = openstackclient.volume.v1.type:UnsetVolumeType + [build_sphinx] source-dir = doc/source build-dir = doc/build diff --git a/setup.py b/setup.py index 9181fe5739..3144d17a05 100644 --- a/setup.py +++ b/setup.py @@ -1,280 +1,21 @@ -# Copyright 2012-2013 OpenStack, LLC. +#!/usr/bin/env python +# Copyright (c) 2013 Hewlett-Packard Development Company, L.P. # -# 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 +# 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 +# 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 os +# 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 setuptools -from openstackclient.openstack.common import setup - - -project = "python-openstackclient" -requires = setup.parse_requirements() -dependency_links = setup.parse_dependency_links() - - -def read(fname): - return open(os.path.join(os.path.dirname(__file__), fname)).read() - - setuptools.setup( - name=project, - version=setup.get_version(project), - description="OpenStack command-line client", - long_description=read('README.rst'), - url='https://github.com/openstack/python-openstackclient', - license="Apache License, Version 2.0", - author='OpenStack Client Contributors', - author_email='openstack@lists.launchpad.net', - packages=setuptools.find_packages(exclude=['tests', 'tests.*']), - classifiers=[ - 'Development Status :: 2 - Pre-Alpha', - 'Environment :: Console', - 'Environment :: OpenStack', - 'Intended Audience :: Developers', - 'Intended Audience :: Information Technology', - 'License :: OSI Approved :: Apache Software License', - 'Operating System :: OS Independent', - 'Programming Language :: Python', - ], - install_requires=requires, - dependency_links=dependency_links, - cmdclass=setup.get_cmdclass(), - entry_points={ - 'console_scripts': ['openstack=openstackclient.shell:main'], - 'openstack.cli': [ - ], - 'openstack.identity.v3_0': [ - 'endpoint_create=' - 'openstackclient.identity.v2_0.endpoint:CreateEndpoint', - 'endpoint_delete=' - 'openstackclient.identity.v2_0.endpoint:DeleteEndpoint', - 'endpoint_list=' - 'openstackclient.identity.v2_0.endpoint:ListEndpoint', - 'endpoint_show=' - 'openstackclient.identity.v2_0.endpoint:ShowEndpoint', - - 'role_add=' - 'openstackclient.identity.v2_0.role:AddRole', - 'role_create=' - 'openstackclient.identity.v2_0.role:CreateRole', - 'role_delete=' - 'openstackclient.identity.v2_0.role:DeleteRole', - 'role_list=openstackclient.identity.v2_0.role:ListRole', - 'role_remove=' - 'openstackclient.identity.v2_0.role:RemoveRole', - 'role_show=openstackclient.identity.v2_0.role:ShowRole', - - 'service_create=' - 'openstackclient.identity.v2_0.service:CreateService', - 'service_delete=' - 'openstackclient.identity.v2_0.service:DeleteService', - 'service_list=openstackclient.identity.v2_0.service:ListService', - 'service_show=openstackclient.identity.v2_0.service:ShowService', - - 'tenant_create=' - 'openstackclient.identity.v2_0.tenant:CreateTenant', - 'tenant_delete=' - 'openstackclient.identity.v2_0.tenant:DeleteTenant', - 'tenant_list=openstackclient.identity.v2_0.tenant:ListTenant', - 'tenant_set=openstackclient.identity.v2_0.tenant:SetTenant', - 'tenant_show=openstackclient.identity.v2_0.tenant:ShowTenant', - - 'user_role_list=openstackclient.identity.v2_0.role:ListUserRole', - - 'user_create=' - 'openstackclient.identity.v2_0.user:CreateUser', - 'user_delete=' - 'openstackclient.identity.v2_0.user:DeleteUser', - 'user_list=openstackclient.identity.v2_0.user:ListUser', - 'user_set=openstackclient.identity.v2_0.user:SetUser', - 'user_show=openstackclient.identity.v2_0.user:ShowUser', - ], - 'openstack.identity.v3': [ - 'credential_create=' - 'openstackclient.identity.v3.credential:CreateCredential', - 'credential_delete=' - 'openstackclient.identity.v3.credential:DeleteCredential', - 'credential_list=' - 'openstackclient.identity.v3.credential:ListCredential', - 'credential_set=' - 'openstackclient.identity.v3.credential:SetCredential', - 'credential_show=' - 'openstackclient.identity.v3.credential:ShowCredential', - - 'domain_create=openstackclient.identity.v3.domain:CreateDomain', - 'domain_delete=openstackclient.identity.v3.domain:DeleteDomain', - 'domain_list=openstackclient.identity.v3.domain:ListDomain', - 'domain_set=openstackclient.identity.v3.domain:SetDomain', - 'domain_show=openstackclient.identity.v3.domain:ShowDomain', - - 'endpoint_create=' - 'openstackclient.identity.v3.endpoint:CreateEndpoint', - 'endpoint_delete=' - 'openstackclient.identity.v3.endpoint:DeleteEndpoint', - 'endpoint_set=openstackclient.identity.v3.endpoint:SetEndpoint', - 'endpoint_show=openstackclient.identity.v3.endpoint:ShowEndpoint', - 'endpoint_list=openstackclient.identity.v3.endpoint:ListEndpoint', - - 'group_create=openstackclient.identity.v3.group:CreateGroup', - 'group_delete=openstackclient.identity.v3.group:DeleteGroup', - 'group_list=openstackclient.identity.v3.group:ListGroup', - 'group_set=openstackclient.identity.v3.group:SetGroup', - 'group_show=openstackclient.identity.v3.group:ShowGroup', - - 'policy_create=openstackclient.identity.v3.policy:CreatePolicy', - 'policy_delete=openstackclient.identity.v3.policy:DeletePolicy', - 'policy_list=openstackclient.identity.v3.policy:ListPolicy', - 'policy_set=openstackclient.identity.v3.policy:SetPolicy', - 'policy_show=openstackclient.identity.v3.policy:ShowPolicy', - - 'project_create=' - 'openstackclient.identity.v3.project:CreateProject', - 'project_delete=' - 'openstackclient.identity.v3.project:DeleteProject', - 'project_list=openstackclient.identity.v3.project:ListProject', - 'project_set=openstackclient.identity.v3.project:SetProject', - 'project_show=openstackclient.identity.v3.project:ShowProject', - - 'role_add=openstackclient.identity.v3.role:AddRole', - 'role_create=' - 'openstackclient.identity.v3.role:CreateRole', - 'role_delete=' - 'openstackclient.identity.v3.role:DeleteRole', - 'role_list=openstackclient.identity.v3.role:ListRole', - 'role_show=openstackclient.identity.v3.role:ShowRole', - 'role_set=openstackclient.identity.v3.role:SetRole', - - 'service_create=' - 'openstackclient.identity.v3.service:CreateService', - 'service_delete=' - 'openstackclient.identity.v3.service:DeleteService', - 'service_list=openstackclient.identity.v3.service:ListService', - 'service_show=openstackclient.identity.v3.service:ShowService', - 'service_set=openstackclient.identity.v3.service:SetService', - - 'user_create=' - 'openstackclient.identity.v3.user:CreateUser', - 'user_delete=' - 'openstackclient.identity.v3.user:DeleteUser', - 'user_list=openstackclient.identity.v3.user:ListUser', - 'user_set=openstackclient.identity.v3.user:SetUser', - 'user_show=openstackclient.identity.v3.user:ShowUser', - ], - 'openstack.image.v1': [ - 'image_create=openstackclient.image.v1.image:CreateImage', - ], - 'openstack.image.v2': [ - 'image_delete=openstackclient.image.v2.image:DeleteImage', - 'image_list=openstackclient.image.v2.image:ListImage', - 'image_save=openstackclient.image.v2.image:SaveImage', - 'image_show=openstackclient.image.v2.image:ShowImage', - ], - 'openstack.compute.v2': [ - 'agent_create=openstackclient.compute.v2.agent:CreateAgent', - 'agent_delete=openstackclient.compute.v2.agent:DeleteAgent', - 'agent_list=openstackclient.compute.v2.agent:ListAgent', - 'agent_set=openstackclient.compute.v2.agent:SetAgent', - - 'compute_service_list=' - 'openstackclient.compute.v2.service:ListService', - 'compute_service_set=' - 'openstackclient.compute.v2.service:SetService', - - 'console_log_show=' - 'openstackclient.compute.v2.console:ShowConsoleLog', - 'console_url_show=' - 'openstackclient.compute.v2.console:ShowConsoleURL', - - 'ip_fixed_add=openstackclient.compute.v2.fixedip:AddFixedIP', - 'ip_fixed_remove=openstackclient.compute.v2.fixedip:RemoveFixedIP', - - 'flavor_create=openstackclient.compute.v2.flavor:CreateFlavor', - 'flavor_delete=openstackclient.compute.v2.flavor:DeleteFlavor', - 'flavor_list=openstackclient.compute.v2.flavor:ListFlavor', - 'flavor_show=openstackclient.compute.v2.flavor:ShowFlavor', - - 'ip_floating_add=' - 'openstackclient.compute.v2.floatingip:AddFloatingIP', - 'ip_floating_create=' - 'openstackclient.compute.v2.floatingip:CreateFloatingIP', - 'ip_floating_delete=' - 'openstackclient.compute.v2.floatingip:DeleteFloatingIP', - 'ip_floating_list=' - 'openstackclient.compute.v2.floatingip:ListFloatingIP', - 'ip_floating_remove=' - 'openstackclient.compute.v2.floatingip:RemoveFloatingIP', - - 'ip_floating_pool_list=' - 'openstackclient.compute.v2.floatingippool:ListFloatingIPPool', - - 'host_list=openstackclient.compute.v2.host:ListHost', - 'host_show=openstackclient.compute.v2.host:ShowHost', - - 'hypervisor_list=' - 'openstackclient.compute.v2.hypervisor:ListHypervisor', - 'hypervisor_show=' - 'openstackclient.compute.v2.hypervisor:ShowHypervisor', - - 'keypair_create=' - 'openstackclient.compute.v2.keypair:CreateKeypair', - 'keypair_delete=' - 'openstackclient.compute.v2.keypair:DeleteKeypair', - 'keypair_list=' - 'openstackclient.compute.v2.keypair:ListKeypair', - 'keypair_show=' - 'openstackclient.compute.v2.keypair:ShowKeypair', - - 'server_create=openstackclient.compute.v2.server:CreateServer', - 'server_delete=openstackclient.compute.v2.server:DeleteServer', - 'server_list=openstackclient.compute.v2.server:ListServer', - 'server_pause=openstackclient.compute.v2.server:PauseServer', - 'server_reboot=openstackclient.compute.v2.server:RebootServer', - 'server_rebuild=openstackclient.compute.v2.server:RebuildServer', - 'server_resume=openstackclient.compute.v2.server:ResumeServer', - 'server_show=openstackclient.compute.v2.server:ShowServer', - 'server_suspend=openstackclient.compute.v2.server:SuspendServer', - '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', - 'snapshot_set=openstackclient.volume.v1.snapshot:SetSnapshot', - 'snapshot_show=openstackclient.volume.v1.snapshot:ShowSnapshot', - - 'volume_create=openstackclient.volume.v1.volume:CreateVolume', - 'volume_delete=openstackclient.volume.v1.volume:DeleteVolume', - 'volume_list=openstackclient.volume.v1.volume:ListVolume', - 'volume_set=openstackclient.volume.v1.volume:SetVolume', - 'volume_show=openstackclient.volume.v1.volume:ShowVolume', - 'volume_unset=openstackclient.volume.v1.volume:UnsetVolume', - - 'volume_type_create=' - 'openstackclient.volume.v1.type:CreateVolumeType', - 'volume_type_delete=' - 'openstackclient.volume.v1.type:DeleteVolumeType', - 'volume_type_list=openstackclient.volume.v1.type:ListVolumeType', - 'volume_type_set=openstackclient.volume.v1.type:SetVolumeType', - 'volume_type_unset=openstackclient.volume.v1.type:UnsetVolumeType', - ] - } -) + setup_requires=['d2to1', 'pbr>=0.5,<0.6'], + d2to1=True) diff --git a/tools/pip-requires b/tools/pip-requires index 9b71d402bf..fe30e2dd91 100644 --- a/tools/pip-requires +++ b/tools/pip-requires @@ -1,3 +1,5 @@ +d2to1>=0.2.10,<0.3 +pbr>=0.5,<0.6 cliff keyring pycrypto From 02a4f16f16019a8e4efa20c183d89c030147f0bb Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Fri, 17 May 2013 06:42:48 -0500 Subject: [PATCH 0006/3547] Add domain and description to user for v3 identity * splitting the changes seen in 27142 to a few new patches * this one will just update v3 user to have description and domain Change-Id: I9b4c365703da27e26ddc702f37cf5928e19cebdc --- openstackclient/identity/v3/user.py | 45 ++++++++++++++++++++++++++--- 1 file changed, 41 insertions(+), 4 deletions(-) diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 7bd3706593..5e6282eb8c 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -53,6 +53,16 @@ def get_parser(self, prog_name): metavar='', help='New default project name or ID', ) + parser.add_argument( + '--domain', + metavar='', + help='New default domain name or ID', + ) + parser.add_argument( + '--description', + metavar='', + help='Description for new user', + ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( '--enable', @@ -72,17 +82,27 @@ 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 + if parsed_args.project: project_id = utils.find_resource( identity_client.projects, parsed_args.project).id else: project_id = None + + if parsed_args.domain: + domain_id = utils.find_resource( + identity_client.domains, parsed_args.domain).id + else: + domain_id = None + user = identity_client.users.create( parsed_args.name, + domain_id, + project_id, parsed_args.password, parsed_args.email, - project_id=project_id, - enabled=parsed_args.enabled, + parsed_args.description, + parsed_args.enabled ) info = {} @@ -138,7 +158,8 @@ def get_parser(self, prog_name): 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') + columns = ('ID', 'Name', 'Project Id', 'Domain Id', + 'Description', 'Email', 'Enabled') else: columns = ('ID', 'Name') data = self.app.client_manager.identity.users.list() @@ -177,10 +198,20 @@ def get_parser(self, prog_name): metavar='', help='New user email address', ) + parser.add_argument( + '--domain', + metavar='', + help='New domain name or ID', + ) parser.add_argument( '--project', metavar='', - help='New default project name or ID', + help='New project name or ID', + ) + parser.add_argument( + '--description', + metavar='', + help='New description', ) enable_group = parser.add_mutually_exclusive_group() enable_group.add_argument( @@ -208,10 +239,16 @@ def take_action(self, parsed_args): kwargs['name'] = parsed_args.name if parsed_args.email: kwargs['email'] = parsed_args.email + if parsed_args.description: + kwargs['description'] = parsed_args.description if parsed_args.project: project_id = utils.find_resource( identity_client.projects, parsed_args.project).id kwargs['projectId'] = project_id + if parsed_args.domain: + domain_id = utils.find_resource( + identity_client.domains, parsed_args.domain).id + kwargs['domainId'] = domain_id if 'enabled' in parsed_args: kwargs['enabled'] = parsed_args.enabled From ea9ec1c6bc00ee90b288808f6d48d2ed420cf4b5 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Thu, 11 Apr 2013 16:45:34 -0500 Subject: [PATCH 0007/3547] Tweak volume commands and add k=v argparse action Basic cleanups: * change metadata to property * add new KeyValueAction to parse the property options * multiple properties can be set using multiple --property args * consistent formatting * do lookups for volume args Change-Id: Ib6c43f01ad46b395aee8c61e886f42e2a5f5573e --- openstackclient/common/parseractions.py | 34 ++++++++ openstackclient/volume/v1/type.py | 36 +++++--- openstackclient/volume/v1/volume.py | 107 ++++++++++++++---------- tests/common/test_parseractions.py | 104 +++++++++++++++++++++++ 4 files changed, 223 insertions(+), 58 deletions(-) create mode 100644 openstackclient/common/parseractions.py create mode 100644 tests/common/test_parseractions.py diff --git a/openstackclient/common/parseractions.py b/openstackclient/common/parseractions.py new file mode 100644 index 0000000000..f111c26427 --- /dev/null +++ b/openstackclient/common/parseractions.py @@ -0,0 +1,34 @@ +# 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. +# + +"""argparse Custom Actions""" + +import argparse + + +class KeyValueAction(argparse.Action): + """A custom action to parse arguments as key=value pairs. + Ensures that dest is a dict + """ + def __call__(self, parser, namespace, values, option_string=None): + # Make sure we have an empty dict rather than None + if getattr(namespace, self.dest, None) is None: + setattr(namespace, self.dest, {}) + + # Add value if an assignment else remove it + if '=' in values: + getattr(namespace, self.dest, {}).update([values.split('=', 1)]) + else: + getattr(namespace, self.dest, {}).pop(values, None) diff --git a/openstackclient/volume/v1/type.py b/openstackclient/volume/v1/type.py index 9d79f8b3e3..e146ee3f61 100644 --- a/openstackclient/volume/v1/type.py +++ b/openstackclient/volume/v1/type.py @@ -21,6 +21,7 @@ from cliff import lister from cliff import show +from openstackclient.common import parseractions from openstackclient.common import utils @@ -118,21 +119,22 @@ def get_parser(self, prog_name): help='Volume type name or ID to update', ) parser.add_argument( - 'meta_data', + '--property', metavar='', - help='meta-data to add to volume type', + action=parseractions.KeyValueAction, + help='Property to add/change for this volume type ' + '(repeat option to set multiple properties)', ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - - meta = dict(v.split('=') for v in parsed_args.meta_data.split(' ')) volume_client = self.app.client_manager.volume volume_type = utils.find_resource( volume_client.volume_types, parsed_args.volume_type) - volume_type.set_keys(meta) + if parsed_args.property: + volume_type.set_keys(parsed_args.property) return @@ -148,12 +150,15 @@ def get_parser(self, prog_name): parser.add_argument( 'volume_type', metavar='', - help='Type ID or name to update', + help='Type ID or name to remove', ) parser.add_argument( - 'meta_data', + '--property', metavar='', - help='meta-data to remove from volume type (key only)', + action='append', + default=[], + help='Property key to remove from volume ' + '(repeat option to remove multiple properties)', ) return parser @@ -161,12 +166,17 @@ def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) volume_client = self.app.client_manager.volume volume_type = utils.find_resource( - volume_client.volume_types, parsed_args.volume_type) - - key_list = [] - key_list.append(parsed_args.meta_data) - volume_type.unset_keys(key_list) + volume_client.volume_types, + parsed_args.volume_type, + ) + if parsed_args.property: + volume_client.volumes.delete_metadata( + volume_type.id, + parsed_args.property, + ) + else: + self.app.log.error("No changes requested\n") return diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index f9641c5482..43253c4092 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -16,12 +16,12 @@ """Volume v1 Volume action implementations""" import logging -import sys from cliff import command from cliff import lister from cliff import show +from openstackclient.common import parseractions from openstackclient.common import utils @@ -63,32 +63,34 @@ def get_parser(self, prog_name): parser.add_argument( '--user-id', metavar='', - help='User id derived from context', + help='Override user id derived from context (admin only)', ) parser.add_argument( '--project-id', metavar='', - help='Project id derived from context', + help='Override project id derived from context (admin only)', ) parser.add_argument( '--availability-zone', metavar='', - help='Availability Zone to use', + help='Availability zone to use', ) parser.add_argument( '--property', metavar='', - help='Optional property to set on volume creation', + action=parseractions.KeyValueAction, + help='Property to store for this volume ' + '(repeat option to set multiple properties)', ) parser.add_argument( - '--image-ref', - metavar='', - help='reference to an image stored in glance', + '--image', + metavar='', + help='Reference to a stored image', ) parser.add_argument( - '--source-volid', - metavar='', - help='ID of source volume to clone from', + '--source', + metavar='', + help='Source for volume clone', ) return parser @@ -98,22 +100,25 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.volume - meta = None - if parsed_args.meta_data: - meta = dict(v.split('=') for v in parsed_args.meta_data.split(' ')) + source_volume = None + if parsed_args.source: + source_volume = utils.find_resource( + volume_client.volumes, + parsed_args.source, + ).id volume = volume_client.volumes.create( parsed_args.size, parsed_args.snapshot_id, - parsed_args.source_volid, + source_volume, parsed_args.name, parsed_args.description, parsed_args.volume_type, parsed_args.user_id, parsed_args.project_id, parsed_args.availability_zone, - meta, - parsed_args.image_ref + parsed_args.property, + parsed_args.image ) return zip(*sorted(volume._info.iteritems())) @@ -175,13 +180,13 @@ def get_parser(self, prog_name): '--all-tenants', action='store_true', default=False, - help='Display information from all tenants (Admin-only)', + help='Display information from all tenants (admin only)', ) parser.add_argument( '--long', action='store_true', default=False, - help='Display meta-data', + help='Display properties', ) return parser @@ -221,19 +226,25 @@ def get_parser(self, prog_name): parser.add_argument( 'volume', metavar='', - help='Name or ID of volume to change') + help='Name or ID of volume to change', + ) parser.add_argument( '--name', - metavar='', - help='New volume name') + metavar='', + help='New volume name', + ) parser.add_argument( '--description', - metavar='', - help='New volume description') + metavar='', + help='New volume description', + ) parser.add_argument( - '--meta-data', + '--property', metavar='', - help='meta-data to add to volume') + action=parseractions.KeyValueAction, + help='Property to add/change for this volume ' + '(repeat option to set multiple properties)', + ) return parser def take_action(self, parsed_args): @@ -241,21 +252,22 @@ def take_action(self, parsed_args): volume_client = self.app.client_manager.volume volume = utils.find_resource(volume_client.volumes, parsed_args.volume) - meta = None if parsed_args.property: - meta = dict(v.split('=') for v in parsed_args.property.split(' ')) - volume_client.volumes.set_metadata(volume.id, meta) + print "property: %s" % parsed_args.property + volume_client.volumes.set_metadata(volume.id, parsed_args.property) kwargs = {} if parsed_args.name: kwargs['display_name'] = parsed_args.name 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: + self.app.log.error("No changes requested\n") - if not kwargs and not meta: - sys.stdout.write("Volume not updated, no arguments present \n") - return - volume_client.volumes.update(volume.id, **kwargs) return @@ -270,7 +282,8 @@ def get_parser(self, prog_name): parser.add_argument( 'volume', metavar='', - help='Name or ID of volume to display') + help='Name or ID of volume to display', + ) return parser def take_action(self, parsed_args): @@ -292,11 +305,16 @@ def get_parser(self, prog_name): parser.add_argument( 'volume', metavar='', - help='Name or ID of volume to change') + help='Name or ID of volume to change', + ) parser.add_argument( - '--meta-data', + '--property', metavar='', - help='meta-data to remove from volume (key only)') + action='append', + default=[], + help='Property key to remove from volume ' + '(repeat to set multiple values)', + ) return parser def take_action(self, parsed_args): @@ -305,14 +323,13 @@ def take_action(self, parsed_args): volume = utils.find_resource( volume_client.volumes, parsed_args.volume) - if not parsed_args.meta_data: - sys.stdout.write("Volume not updated, no arguments present \n") - return - - key_list = [] - key_list.append(parsed_args.meta_data) - volume_client.volumes.delete_metadata(volume.id, key_list) - + if parsed_args.property: + volume_client.volumes.delete_metadata( + volume.id, + parsed_args.property, + ) + else: + self.app.log.error("No changes requested\n") return diff --git a/tests/common/test_parseractions.py b/tests/common/test_parseractions.py new file mode 100644 index 0000000000..f48c4d721b --- /dev/null +++ b/tests/common/test_parseractions.py @@ -0,0 +1,104 @@ +# 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. +# + +import argparse + +from openstackclient.common import parseractions +from tests import utils + + +class TestKeyValueAction(utils.TestCase): + def test_good_values(self): + parser = argparse.ArgumentParser() + + # Set up our typical usage + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + help='Property to store for this volume ' + '(repeat option to set multiple properties)', + ) + + results = parser.parse_args([ + '--property', 'red=', + '--property', 'green=100%', + '--property', 'blue=50%', + ]) + + actual = getattr(results, 'property', {}) + # All should pass through unmolested + expect = {'red': '', 'green': '100%', 'blue': '50%'} + self.assertDictEqual(expect, actual) + + def test_default_values(self): + parser = argparse.ArgumentParser() + + # Set up our typical usage + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + default={'green': '20%', 'format': '#rgb'}, + help='Property to store for this volume ' + '(repeat option to set multiple properties)', + ) + + results = parser.parse_args([ + '--property', 'red=', + '--property', 'green=100%', + '--property', 'blue=50%', + ]) + + actual = getattr(results, 'property', {}) + # Verify green default is changed, format default is unchanged + expect = {'red': '', 'green': '100%', 'blue': '50%', 'format': '#rgb'} + self.assertDictEqual(expect, actual) + + def test_error_values(self): + parser = argparse.ArgumentParser() + + # Set up our typical usage + parser.add_argument( + '--property', + metavar='', + action=parseractions.KeyValueAction, + default={'green': '20%', 'blue': '40%'}, + help='Property to store for this volume ' + '(repeat option to set multiple properties)', + ) + + results = parser.parse_args([ + '--property', 'red', + '--property', 'green=100%', + '--property', 'blue', + ]) + + failhere = None + actual = getattr(results, 'property', {}) + # Verify non-existant red key + try: + failhere = actual['red'] + except Exception as e: + self.assertTrue(type(e) == KeyError) + # Verify removal of blue key + try: + failhere = actual['blue'] + except Exception as e: + self.assertTrue(type(e) == KeyError) + # There should be no red or blue + expect = {'green': '100%'} + self.assertDictEqual(expect, actual) + self.assertEqual(failhere, None) From bf588ed9c4db954316b62133cc936cfcb0ea7de1 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Fri, 24 May 2013 16:59:35 -0500 Subject: [PATCH 0008/3547] Fix identity v2.0 entry point Change-Id: Ifae91a612fcd8b66660b93f6ea81d37e0f1bce1d --- setup.cfg | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup.cfg b/setup.cfg index 1990b2c5a3..eeb0c7fc28 100644 --- a/setup.cfg +++ b/setup.cfg @@ -31,7 +31,7 @@ console_scripts = openstack.cli = -openstack.identity.v3_0 = +openstack.identity.v2_0 = endpoint_create = openstackclient.identity.v2_0.endpoint:CreateEndpoint endpoint_delete = openstackclient.identity.v2_0.endpoint:DeleteEndpoint endpoint_list = openstackclient.identity.v2_0.endpoint:ListEndpoint From bac0718764ac5e7cc22adf24d035db0be0cf90d9 Mon Sep 17 00:00:00 2001 From: Zhenguo Niu Date: Wed, 29 May 2013 17:36:23 +0800 Subject: [PATCH 0009/3547] Rename requires files to standard names. Rename tools/pip-requires to requirements.txt and tools/test-requires to test-requirements.txt. These are standard files, and tools in the general world are growing intelligence about them. Change-Id: I903213fda94a833335abaa7ad9a90bbb688ec15a Fixes: bug #1179008 --- tools/pip-requires => requirements.txt | 0 tools/test-requires => test-requirements.txt | 0 tools/install_venv.py | 4 ++-- tox.ini | 4 ++-- 4 files changed, 4 insertions(+), 4 deletions(-) rename tools/pip-requires => requirements.txt (100%) rename tools/test-requires => test-requirements.txt (100%) diff --git a/tools/pip-requires b/requirements.txt similarity index 100% rename from tools/pip-requires rename to requirements.txt diff --git a/tools/test-requires b/test-requirements.txt similarity index 100% rename from tools/test-requires rename to test-requirements.txt diff --git a/tools/install_venv.py b/tools/install_venv.py index d247c02376..a5891d6710 100644 --- a/tools/install_venv.py +++ b/tools/install_venv.py @@ -48,8 +48,8 @@ def print_help(): def main(argv): root = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) venv = os.path.join(root, ".venv") - pip_requires = os.path.join(root, "tools", "pip-requires") - test_requires = os.path.join(root, "tools", "test-requires") + pip_requires = os.path.join(root, "requirements.txt") + test_requires = os.path.join(root, "test-requirements.txt") py_version = "python%s.%s" % (sys.version_info[0], sys.version_info[1]) project = "python-openstackclient" install = install_venv.InstallVenv(root, venv, pip_requires, test_requires, diff --git a/tox.ini b/tox.ini index f6de18d137..ece55df54b 100644 --- a/tox.ini +++ b/tox.ini @@ -6,8 +6,8 @@ setenv = VIRTUAL_ENV={envdir} LANG=en_US.UTF-8 LANGUAGE=en_US:en LC_ALL=C -deps = -r{toxinidir}/tools/pip-requires - -r{toxinidir}/tools/test-requires +deps = -r{toxinidir}/requirements.txt + -r{toxinidir}/test-requirements.txt commands = python setup.py testr --testr-args='{posargs}' [testenv:pep8] From 7183a11f09f2d1958ed6251ae227afc8cbb8cc45 Mon Sep 17 00:00:00 2001 From: Chuck Short Date: Sat, 1 Jun 2013 19:58:22 -0500 Subject: [PATCH 0010/3547] python3: Introduce py33 to tox.ini Introduce py33 to tox.ini to make testing with python3 easier. Change-Id: I7a775ac51e0bc0a5929184af47d51ea1cc4e3219 Signed-off-by: Chuck Short --- tox.ini | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tox.ini b/tox.ini index ece55df54b..61bdc9dec4 100644 --- a/tox.ini +++ b/tox.ini @@ -1,5 +1,5 @@ [tox] -envlist = py26,py27,pep8 +envlist = py26,py27,py33,pep8 [testenv] setenv = VIRTUAL_ENV={envdir} From ea31333c49224faac1530f258c15bdeed95a94e8 Mon Sep 17 00:00:00 2001 From: Hugh Saunders Date: Mon, 3 Jun 2013 11:10:09 +0100 Subject: [PATCH 0011/3547] Add volume backup commands Change-Id: Iedccd329ff6fb3155eb29649cd0bc84cfc5ebedf Implements: blueprint volume-backup --- openstackclient/volume/v1/backup.py | 171 ++++++++++++++++++++++++++++ setup.cfg | 6 + 2 files changed, 177 insertions(+) create mode 100644 openstackclient/volume/v1/backup.py diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py new file mode 100644 index 0000000000..cd1afd60b7 --- /dev/null +++ b/openstackclient/volume/v1/backup.py @@ -0,0 +1,171 @@ +# 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 Backup action implementations""" + +import logging + +from cliff import command +from cliff import lister +from cliff import show + +from openstackclient.common import utils + + +class CreateBackup(show.ShowOne): + """Create backup command""" + + api = 'volume' + log = logging.getLogger(__name__ + '.CreateBackup') + + def get_parser(self, prog_name): + parser = super(CreateBackup, self).get_parser(prog_name) + parser.add_argument( + 'volume', + metavar='', + help='The name or ID of the volume to backup', + ) + parser.add_argument( + '--container', + metavar='', + required=False, + help='Optional Backup container name.', + ) + parser.add_argument( + '--name', + metavar='', + required=False, + help='Name of the backup', + ) + parser.add_argument( + '--description', + metavar='', + help='Description of the backup', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + volume_client = self.app.client_manager.volume + volume_id = utils.find_resource(volume_client.volumes, + parsed_args.volume).id + backup = volume_client.backups.create( + volume_id, + parsed_args.volume, + parsed_args.name, + parsed_args.description + ) + + backup._info.pop('links') + return zip(*sorted(backup._info.iteritems())) + + +class DeleteBackup(command.Command): + """Delete backup command""" + + api = 'volume' + log = logging.getLogger(__name__ + '.DeleteBackup') + + def get_parser(self, prog_name): + parser = super(DeleteBackup, self).get_parser(prog_name) + parser.add_argument( + 'backup', + metavar='', + help='Name or ID of backup to delete', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + volume_client = self.app.client_manager.volume + backup_id = utils.find_resource(volume_client.backups, + parsed_args.backup).id + volume_client.backups.delete(backup_id) + return + + +class ListBackup(lister.Lister): + """List backup command""" + + api = 'volume' + log = logging.getLogger(__name__ + '.ListBackup') + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + columns = ( + 'ID', + 'Display Name', + 'Display Description', + 'Status', + 'Size' + ) + data = self.app.client_manager.volume.backups.list() + return (columns, + (utils.get_item_properties( + s, columns, + formatters={}, + ) for s in data)) + + +class RestoreBackup(command.Command): + """Restore backup command""" + + api = 'volume' + log = logging.getLogger(__name__ + '.RestoreBackup') + + def get_parser(self, prog_name): + parser = super(RestoreBackup, self).get_parser(prog_name) + parser.add_argument( + 'backup', + metavar='', + help='ID of backup to restore') + parser.add_argument( + 'volume', + metavar='', + help='ID of volume to restore to') + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + volume_client = self.app.client_manager.volume + backup = utils.find_resource(volume_client.backups, + parsed_args.backup) + destination_volume = utils.find_resource(volume_client.volumes, + parsed_args.volume) + return volume_client.restores.restore(backup.id, + destination_volume.id) + + +class ShowBackup(show.ShowOne): + """Show backup command""" + + api = 'volume' + log = logging.getLogger(__name__ + '.ShowBackup') + + def get_parser(self, prog_name): + parser = super(ShowBackup, self).get_parser(prog_name) + parser.add_argument( + 'backup', + metavar='', + help='Name or ID of backup to display') + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + volume_client = self.app.client_manager.volume + backup = utils.find_resource(volume_client.backups, + parsed_args.backup) + backup._info.pop('links') + return zip(*sorted(backup._info.iteritems())) diff --git a/setup.cfg b/setup.cfg index eeb0c7fc28..535fb40507 100644 --- a/setup.cfg +++ b/setup.cfg @@ -189,6 +189,12 @@ openstack.volume.v1 = snapshot_set = openstackclient.volume.v1.snapshot:SetSnapshot snapshot_show = openstackclient.volume.v1.snapshot:ShowSnapshot + backup_create = openstackclient.volume.v1.backup:CreateBackup + backup_delete = openstackclient.volume.v1.backup:DeleteBackup + backup_list = openstackclient.volume.v1.backup:ListBackup + backup_restore = openstackclient.volume.v1.backup:RestoreBackup + backup_show = openstackclient.volume.v1.backup:ShowBackup + volume_create = openstackclient.volume.v1.volume:CreateVolume volume_delete = openstackclient.volume.v1.volume:DeleteVolume volume_list = openstackclient.volume.v1.volume:ListVolume From d542def039d42d774d61800073bc9d1ba86ff4a2 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Tue, 11 Jun 2013 11:33:28 -0700 Subject: [PATCH 0012/3547] Remove explicit distribute depend. Causes issues with the recent re-merge with setuptools. Advice from upstream is to stop doing explicit depends. Change-Id: Ic83dca8c17799335b76311d1d657adcd1dc94f01 --- test-requirements.txt | 2 -- 1 file changed, 2 deletions(-) diff --git a/test-requirements.txt b/test-requirements.txt index 1eb2509c73..392348e248 100644 --- a/test-requirements.txt +++ b/test-requirements.txt @@ -4,8 +4,6 @@ pyflakes==0.7.2 flake8==2.0 hacking>=0.5.3,<0.6 -distribute>=0.6.24 - coverage discover fixtures>=0.3.12 From bc3039a43c5d4f783e8ef435ec43d4eee5e2f83a Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Mon, 10 Jun 2013 14:06:41 -0500 Subject: [PATCH 0013/3547] Fix py26 tests: assertDictEqual assertDictEqual is not present in py26 so shim it in here stolen from python-keystoneclient/tests/test_auth_token_middleware.py Change-Id: Ifd5990a8c03d11ee93cddc2f61653255970d974c --- tests/utils.py | 27 ++++++++++++++++++++++++++- 1 file changed, 26 insertions(+), 1 deletion(-) diff --git a/tests/utils.py b/tests/utils.py index 75515fad56..3e24ff4a99 100644 --- a/tests/utils.py +++ b/tests/utils.py @@ -16,12 +16,13 @@ import os import fixtures +import sys import testtools class TestCase(testtools.TestCase): def setUp(self): - super(TestCase, self).setUp() + testtools.TestCase.setUp(self) if (os.environ.get("OS_STDOUT_NOCAPTURE") == "True" and os.environ.get("OS_STDOUT_NOCAPTURE") == "1"): @@ -32,3 +33,27 @@ def setUp(self): os.environ.get("OS_STDERR_NOCAPTURE") == "1"): stderr = self.useFixture(fixtures.StringStream("stderr")).stream self.useFixture(fixtures.MonkeyPatch("sys.stderr", stderr)) + + # 2.6 doesn't have the assert dict equals so make sure that it exists + if tuple(sys.version_info)[0:2] < (2, 7): + + def assertIsInstance(self, obj, cls, msg=None): + """Same as self.assertTrue(isinstance(obj, cls)), with a nicer + default message + """ + if not isinstance(obj, cls): + standardMsg = '%s is not an instance of %r' % (obj, cls) + self.fail(self._formatMessage(msg, standardMsg)) + + def assertDictEqual(self, d1, d2, msg=None): + # Simple version taken from 2.7 + self.assertIsInstance(d1, dict, + 'First argument is not a dictionary') + self.assertIsInstance(d2, dict, + 'Second argument is not a dictionary') + if d1 != d2: + if msg: + self.fail(msg) + else: + standardMsg = '%r != %r' % (d1, d2) + self.fail(standardMsg) From dd3aa0b671fec6e6c23ae69016fdc06e7c529297 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Tue, 21 May 2013 00:57:19 -0500 Subject: [PATCH 0014/3547] Add OAuth support for Identity V3 Added client side support for: * consumer CRUD * create request token * create access token * authroize request token blueprint: delegated-auth-via-oauth Change-Id: I8d325fcab07ac4dfd124a6e55053ded8d6bf662e --- openstackclient/identity/v3/oauth.py | 264 +++++++++++++++++++++++++++ setup.cfg | 11 ++ 2 files changed, 275 insertions(+) create mode 100644 openstackclient/identity/v3/oauth.py diff --git a/openstackclient/identity/v3/oauth.py b/openstackclient/identity/v3/oauth.py new file mode 100644 index 0000000000..0b5ae4dba7 --- /dev/null +++ b/openstackclient/identity/v3/oauth.py @@ -0,0 +1,264 @@ +# 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 OAuth action implementations""" + +import logging +import sys + +from cliff import command +from cliff import lister +from cliff import show + +from openstackclient.common import utils + + +class AuthorizeRequestToken(show.ShowOne): + """Authorize request token command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.AuthorizeRequestToken') + + def get_parser(self, prog_name): + parser = super(AuthorizeRequestToken, self).get_parser(prog_name) + parser.add_argument( + '--request-key', + metavar='', + help='Consumer key', + required=True + ) + parser.add_argument( + '--user-token', + metavar='', + help='Token of authorizing user', + required=True + ) + parser.add_argument( + '--roles', + metavar='', + help='Role to authorize', + 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.oauths + + verifier_pin = oauth_client.authorize_request_token( + parsed_args.request_key, parsed_args.user_token, + parsed_args.roles) + info = {} + info.update(verifier_pin._info) + return zip(*sorted(info.iteritems())) + + +class CreateAccessToken(show.ShowOne): + """Create access token command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.CreateAccessToken') + + def get_parser(self, prog_name): + parser = super(CreateAccessToken, self).get_parser(prog_name) + parser.add_argument( + '--consumer-key', + metavar='', + help='Consumer key', + required=True + ) + parser.add_argument( + '--request-key', + metavar='', + help='Consumer key', + required=True + ) + parser.add_argument( + '--verifier', + metavar='', + help='Verifier Pin', + 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.oauths + access_token = oauth_client.create_access_token( + parsed_args.consumer_key, parsed_args.request_key, + parsed_args.verifier) + info = {} + info.update(access_token._info) + return zip(*sorted(info.iteritems())) + + +class CreateConsumer(show.ShowOne): + """Create consumer command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.CreateConsumer') + + def get_parser(self, prog_name): + parser = super(CreateConsumer, self).get_parser(prog_name) + parser.add_argument( + 'name', + metavar='', + help='New consumer name', + ) + return parser + + 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( + parsed_args.name + ) + info = {} + info.update(consumer._info) + return zip(*sorted(info.iteritems())) + + +class CreateRequestToken(show.ShowOne): + """Create request token command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.CreateRequestToken') + + def get_parser(self, prog_name): + parser = super(CreateRequestToken, self).get_parser(prog_name) + parser.add_argument( + '--consumer-key', + metavar='', + help='Consumer key', + required=True + ) + parser.add_argument( + '--roles', + metavar='', + help='Role requested', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + oauth_client = self.app.client_manager.identity.oauths + 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())) + + +class DeleteConsumer(command.Command): + """Delete consumer command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.DeleteConsumer') + + def get_parser(self, prog_name): + parser = super(DeleteConsumer, self).get_parser(prog_name) + parser.add_argument( + 'consumer', + metavar='', + help='Name or ID of consumer 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 + consumer = utils.find_resource( + identity_client.oauths, parsed_args.consumer) + identity_client.oauths.delete_consumer(consumer.id) + return + + +class ListConsumer(lister.Lister): + """List consumer command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.ListConsumer') + + 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() + return (columns, + (utils.get_item_properties( + s, columns, + formatters={}, + ) for s in data)) + + +class SetConsumer(command.Command): + """Set consumer command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.SetConsumer') + + def get_parser(self, prog_name): + parser = super(SetConsumer, self).get_parser(prog_name) + parser.add_argument( + 'consumer', + metavar='', + help='Name or ID of consumer to change', + ) + parser.add_argument( + '--name', + metavar='', + help='New consumer name', + ) + return parser + + 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) + kwargs = {} + if parsed_args.name: + kwargs['name'] = parsed_args.name + + if not len(kwargs): + sys.stdout.write("Consumer not updated, no arguments present") + return + identity_client.oauths.update_consumer(consumer.id, **kwargs) + return + + +class ShowConsumer(show.ShowOne): + """Show consumer command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.ShowConsumer') + + def get_parser(self, prog_name): + parser = super(ShowConsumer, self).get_parser(prog_name) + parser.add_argument( + 'consumer', + metavar='', + help='Name or ID of consumer 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 + consumer = utils.find_resource( + identity_client.oauths, parsed_args.consumer) + + info = {} + info.update(consumer._info) + return zip(*sorted(info.iteritems())) diff --git a/setup.cfg b/setup.cfg index 535fb40507..4b2142dd2c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -64,6 +64,14 @@ openstack.identity.v2_0 = user_show = openstackclient.identity.v2_0.user:ShowUser openstack.identity.v3 = + 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 + consumer_set = openstackclient.identity.v3.oauth:SetConsumer + consumer_show = openstackclient.identity.v3.oauth:ShowConsumer + credential_create = openstackclient.identity.v3.credential:CreateCredential credential_delete = openstackclient.identity.v3.credential:DeleteCredential credential_list = openstackclient.identity.v3.credential:ListCredential @@ -100,6 +108,9 @@ 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 108f78d98994435fbd234196b3f23948b4c8bca6 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 30 Jun 2013 23:30:27 -0400 Subject: [PATCH 0015/3547] Remove python3 incompatible exception syntax. Change-Id: I5f0687a83362ceebf03bae4a0a5b109aad7e5200 --- 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 a2496fad12..8b134aa92f 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -236,7 +236,7 @@ def take_action(self, parsed_args): dst, src = f.split('=', 1) try: files[dst] = open(src) - except IOError, e: + except IOError as e: raise exceptions.CommandError("Can't open '%s': %s" % (src, e)) if parsed_args.min > parsed_args.max: @@ -251,7 +251,7 @@ def take_action(self, parsed_args): if parsed_args.user_data: try: userdata = open(parsed_args.user_data) - except IOError, e: + except IOError as e: raise exceptions.CommandError("Can't open '%s': %s" % (parsed_args.user_data, e)) From 196daf859b1557fba24dbcf9870d3a834da86916 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Sun, 30 Jun 2013 23:01:17 -0400 Subject: [PATCH 0016/3547] Move tests into project package. There are several reasons for this. One is that the majority of OpenStack packages behave this way. The second is that it makes writing software that extends something easier to test (which is a clear usecase for openstackclient) And third, tests/__init__.py implies a global package named "tests" - which I'm pretty sure we're not providing. Change-Id: Ic708ffd92aea78c2ffc1a8579af0587af4fca4ff --- .testr.conf | 2 +- {tests => openstackclient/tests}/__init__.py | 0 {tests => openstackclient/tests}/common/test_clientmanager.py | 2 +- .../tests}/common/test_commandmanager.py | 2 +- {tests => openstackclient/tests}/common/test_parseractions.py | 2 +- {tests => openstackclient/tests}/compute/__init__.py | 0 {tests => openstackclient/tests}/compute/test_compute.py | 4 ++-- {tests => openstackclient/tests}/identity/__init__.py | 0 {tests => openstackclient/tests}/identity/test_identity.py | 4 ++-- {tests => openstackclient/tests}/image/__init__.py | 0 {tests => openstackclient/tests}/image/test_image.py | 4 ++-- {tests => openstackclient/tests}/test_shell.py | 2 +- {tests => openstackclient/tests}/utils.py | 0 {tests => openstackclient/tests}/volume/__init__.py | 0 {tests => openstackclient/tests}/volume/test_volume.py | 4 ++-- 15 files changed, 13 insertions(+), 13 deletions(-) rename {tests => openstackclient/tests}/__init__.py (100%) rename {tests => openstackclient/tests}/common/test_clientmanager.py (96%) rename {tests => openstackclient/tests}/common/test_commandmanager.py (98%) rename {tests => openstackclient/tests}/common/test_parseractions.py (98%) rename {tests => openstackclient/tests}/compute/__init__.py (100%) rename {tests => openstackclient/tests}/compute/test_compute.py (93%) rename {tests => openstackclient/tests}/identity/__init__.py (100%) rename {tests => openstackclient/tests}/identity/test_identity.py (92%) rename {tests => openstackclient/tests}/image/__init__.py (100%) rename {tests => openstackclient/tests}/image/test_image.py (93%) rename {tests => openstackclient/tests}/test_shell.py (99%) rename {tests => openstackclient/tests}/utils.py (100%) rename {tests => openstackclient/tests}/volume/__init__.py (100%) rename {tests => openstackclient/tests}/volume/test_volume.py (93%) diff --git a/.testr.conf b/.testr.conf index 2109af6ce0..d152a5aa93 100644 --- a/.testr.conf +++ b/.testr.conf @@ -1,4 +1,4 @@ [DEFAULT] -test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ ./tests $LISTOPT $IDOPTION +test_command=OS_STDOUT_CAPTURE=1 OS_STDERR_CAPTURE=1 ${PYTHON:-python} -m subunit.run discover -t ./ . $LISTOPT $IDOPTION test_id_option=--load-list $IDFILE test_list_option=--list diff --git a/tests/__init__.py b/openstackclient/tests/__init__.py similarity index 100% rename from tests/__init__.py rename to openstackclient/tests/__init__.py diff --git a/tests/common/test_clientmanager.py b/openstackclient/tests/common/test_clientmanager.py similarity index 96% rename from tests/common/test_clientmanager.py rename to openstackclient/tests/common/test_clientmanager.py index fe99c599f1..395f6ec33d 100644 --- a/tests/common/test_clientmanager.py +++ b/openstackclient/tests/common/test_clientmanager.py @@ -14,7 +14,7 @@ # from openstackclient.common import clientmanager -from tests import utils +from openstackclient.tests import utils class Container(object): diff --git a/tests/common/test_commandmanager.py b/openstackclient/tests/common/test_commandmanager.py similarity index 98% rename from tests/common/test_commandmanager.py rename to openstackclient/tests/common/test_commandmanager.py index f0a0b3418d..4953c29754 100644 --- a/tests/common/test_commandmanager.py +++ b/openstackclient/tests/common/test_commandmanager.py @@ -16,7 +16,7 @@ import mock from openstackclient.common import commandmanager -from tests import utils +from openstackclient.tests import utils class FakeCommand(object): diff --git a/tests/common/test_parseractions.py b/openstackclient/tests/common/test_parseractions.py similarity index 98% rename from tests/common/test_parseractions.py rename to openstackclient/tests/common/test_parseractions.py index f48c4d721b..705e7e9c36 100644 --- a/tests/common/test_parseractions.py +++ b/openstackclient/tests/common/test_parseractions.py @@ -16,7 +16,7 @@ import argparse from openstackclient.common import parseractions -from tests import utils +from openstackclient.tests import utils class TestKeyValueAction(utils.TestCase): diff --git a/tests/compute/__init__.py b/openstackclient/tests/compute/__init__.py similarity index 100% rename from tests/compute/__init__.py rename to openstackclient/tests/compute/__init__.py diff --git a/tests/compute/test_compute.py b/openstackclient/tests/compute/test_compute.py similarity index 93% rename from tests/compute/test_compute.py rename to openstackclient/tests/compute/test_compute.py index 2673f994c7..9d2061d284 100644 --- a/tests/compute/test_compute.py +++ b/openstackclient/tests/compute/test_compute.py @@ -17,7 +17,7 @@ from openstackclient.common import clientmanager from openstackclient.compute import client as compute_client -from tests import utils +from openstackclient.tests import utils AUTH_TOKEN = "foobar" @@ -37,7 +37,7 @@ def setUp(self): api_version = {"compute": "2"} compute_client.API_VERSIONS = { - "2": "tests.compute.test_compute.FakeClient" + "2": "openstackclient.tests.compute.test_compute.FakeClient" } self.cm = clientmanager.ClientManager(token=AUTH_TOKEN, diff --git a/tests/identity/__init__.py b/openstackclient/tests/identity/__init__.py similarity index 100% rename from tests/identity/__init__.py rename to openstackclient/tests/identity/__init__.py diff --git a/tests/identity/test_identity.py b/openstackclient/tests/identity/test_identity.py similarity index 92% rename from tests/identity/test_identity.py rename to openstackclient/tests/identity/test_identity.py index 52bbd22218..04bbd20f49 100644 --- a/tests/identity/test_identity.py +++ b/openstackclient/tests/identity/test_identity.py @@ -15,7 +15,7 @@ from openstackclient.common import clientmanager from openstackclient.identity import client as identity_client -from tests import utils +from openstackclient.tests import utils AUTH_TOKEN = "foobar" @@ -35,7 +35,7 @@ def setUp(self): api_version = {"identity": "2.0"} identity_client.API_VERSIONS = { - "2.0": "tests.identity.test_identity.FakeClient" + "2.0": "openstackclient.tests.identity.test_identity.FakeClient" } self.cm = clientmanager.ClientManager(token=AUTH_TOKEN, diff --git a/tests/image/__init__.py b/openstackclient/tests/image/__init__.py similarity index 100% rename from tests/image/__init__.py rename to openstackclient/tests/image/__init__.py diff --git a/tests/image/test_image.py b/openstackclient/tests/image/test_image.py similarity index 93% rename from tests/image/test_image.py rename to openstackclient/tests/image/test_image.py index 60b2142943..f4c8d72e37 100644 --- a/tests/image/test_image.py +++ b/openstackclient/tests/image/test_image.py @@ -17,7 +17,7 @@ from openstackclient.common import clientmanager from openstackclient.image import client as image_client -from tests import utils +from openstackclient.tests import utils AUTH_TOKEN = "foobar" @@ -38,7 +38,7 @@ def setUp(self): api_version = {"image": "2"} image_client.API_VERSIONS = { - "2": "tests.image.test_image.FakeClient" + "2": "openstackclient.tests.image.test_image.FakeClient" } self.cm = clientmanager.ClientManager(token=AUTH_TOKEN, diff --git a/tests/test_shell.py b/openstackclient/tests/test_shell.py similarity index 99% rename from tests/test_shell.py rename to openstackclient/tests/test_shell.py index d0eb5b0d39..f479b11e4b 100644 --- a/tests/test_shell.py +++ b/openstackclient/tests/test_shell.py @@ -17,7 +17,7 @@ import os from openstackclient import shell -from tests import utils +from openstackclient.tests import utils DEFAULT_USERNAME = "username" diff --git a/tests/utils.py b/openstackclient/tests/utils.py similarity index 100% rename from tests/utils.py rename to openstackclient/tests/utils.py diff --git a/tests/volume/__init__.py b/openstackclient/tests/volume/__init__.py similarity index 100% rename from tests/volume/__init__.py rename to openstackclient/tests/volume/__init__.py diff --git a/tests/volume/test_volume.py b/openstackclient/tests/volume/test_volume.py similarity index 93% rename from tests/volume/test_volume.py rename to openstackclient/tests/volume/test_volume.py index 8c60dd1242..548fbf7413 100644 --- a/tests/volume/test_volume.py +++ b/openstackclient/tests/volume/test_volume.py @@ -16,8 +16,8 @@ import mock from openstackclient.common import clientmanager +from openstackclient.tests import utils from openstackclient.volume import client as volume_client -from tests import utils AUTH_TOKEN = "foobar" @@ -38,7 +38,7 @@ def setUp(self): api_version = {"volume": "1"} volume_client.API_VERSIONS = { - "1": "tests.volume.test_volume.FakeClient" + "1": "openstackclient.tests.volume.test_volume.FakeClient" } self.cm = clientmanager.ClientManager(token=AUTH_TOKEN, From 9dbf46b8370becd1100837ee1868c7532e632b81 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Mon, 10 Jun 2013 09:19:54 -0500 Subject: [PATCH 0017/3547] Add methods for user and group interactions * Add user to group * Contains user in group * Remove user from group Change-Id: If5219fa9d4761d7b97950c39556b3e1b8aab6517 --- openstackclient/identity/v3/group.py | 117 +++++++++++++++++++++++++++ setup.cfg | 3 + 2 files changed, 120 insertions(+) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 0562b76630..de4fe2d276 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -25,6 +25,84 @@ from openstackclient.common import utils +class AddUserToGroup(command.Command): + """Add user to group""" + + api = 'identity' + log = logging.getLogger(__name__ + '.AddUserToGroup') + + def get_parser(self, prog_name): + parser = super(AddUserToGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Group name or ID that user will be added to', + ) + parser.add_argument( + 'user', + metavar='', + help='User name or ID to add to group', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + + user_id = utils.find_resource(identity_client.users, + parsed_args.user).id + group_id = utils.find_resource(identity_client.groups, + parsed_args.group).id + + try: + identity_client.users.add_to_group(user_id, group_id) + except Exception: + sys.stderr.write("%s not added to group %s\n" % + (parsed_args.user, parsed_args.group)) + else: + sys.stdout.write("%s added to group %s\n" % + (parsed_args.user, parsed_args.group)) + + +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): + parser = super(CheckUserInGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Group name or ID that user will be added to', + ) + parser.add_argument( + 'user', + metavar='', + help='User name or ID to add to group', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + + user_id = utils.find_resource(identity_client.users, + parsed_args.user).id + group_id = utils.find_resource(identity_client.groups, + parsed_args.group).id + + try: + identity_client.users.check_in_group(user_id, group_id) + except Exception: + sys.stderr.write("%s not in group %s\n" % + (parsed_args.user, parsed_args.group)) + else: + sys.stdout.write("%s in group %s\n" % + (parsed_args.user, parsed_args.group)) + + class CreateGroup(show.ShowOne): """Create group command""" @@ -117,6 +195,45 @@ def take_action(self, parsed_args): ) for s in data)) +class RemoveUserFromGroup(command.Command): + """Remove user to group""" + + api = 'identity' + log = logging.getLogger(__name__ + '.RemoveUserFromGroup') + + def get_parser(self, prog_name): + parser = super(RemoveUserFromGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + help='Group name or ID that user will be removed from', + ) + parser.add_argument( + 'user', + metavar='', + help='User name or ID to remove from group', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + + user_id = utils.find_resource(identity_client.users, + parsed_args.user).id + group_id = utils.find_resource(identity_client.groups, + parsed_args.group).id + + try: + identity_client.users.remove_from_group(user_id, group_id) + except Exception: + sys.stderr.write("%s not removed from group %s\n" % + (parsed_args.user, parsed_args.group)) + else: + sys.stdout.write("%s removed from group %s\n" % + (parsed_args.user, parsed_args.group)) + + class SetGroup(command.Command): """Set group command""" diff --git a/setup.cfg b/setup.cfg index 4b2142dd2c..a70306ea0c 100644 --- a/setup.cfg +++ b/setup.cfg @@ -90,9 +90,12 @@ openstack.identity.v3 = endpoint_show = openstackclient.identity.v3.endpoint:ShowEndpoint endpoint_list = openstackclient.identity.v3.endpoint:ListEndpoint + group_add_user = openstackclient.identity.v3.group:AddUserToGroup + group_contains_user = openstackclient.identity.v3.group:CheckUserInGroup group_create = openstackclient.identity.v3.group:CreateGroup group_delete = openstackclient.identity.v3.group:DeleteGroup group_list = openstackclient.identity.v3.group:ListGroup + group_remove_user = openstackclient.identity.v3.group:RemoveUserFromGroup group_set = openstackclient.identity.v3.group:SetGroup group_show = openstackclient.identity.v3.group:ShowGroup From f29a849ffcc203e7038fd2a026e0f755dcf2c1fc Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Thu, 18 Apr 2013 17:49:42 -0500 Subject: [PATCH 0018/3547] Finish up v3 role commands * Add remove role * Add --role to group list * Add --role to user list * Fix groups in AddRole() * Remove the tweaks to utils.find_resource for domains; will address that across domains, projects, users and groups in another patch. I want to nail down the structure of these commands and get that into place Change-Id: I8673dd8221ef88978dada5a2833c187026bdb31a --- openstackclient/common/utils.py | 11 ++- openstackclient/identity/v3/group.py | 95 +++++++++++++++++-- openstackclient/identity/v3/role.py | 135 +++++++++++++++++++++------ openstackclient/identity/v3/user.py | 92 ++++++++++++++++-- openstackclient/shell.py | 15 ++- setup.cfg | 1 + 6 files changed, 304 insertions(+), 45 deletions(-) diff --git a/openstackclient/common/utils.py b/openstackclient/common/utils.py index 56f9cd17d5..06542887e2 100644 --- a/openstackclient/common/utils.py +++ b/openstackclient/common/utils.py @@ -24,20 +24,27 @@ def find_resource(manager, name_or_id): """Helper for the _find_* methods.""" - # first try to get entity as integer id + + # Try to get entity as integer id try: if isinstance(name_or_id, int) or name_or_id.isdigit(): return manager.get(int(name_or_id)) except exceptions.NotFound: pass - # now try to get entity as uuid + # Try to get entity as uuid try: uuid.UUID(str(name_or_id)) return manager.get(name_or_id) except (ValueError, exceptions.NotFound): pass + # Try directly using the passed value + try: + return manager.get(name_or_id) + except Exception: + pass + kwargs = {} if 'NAME_ATTR' in manager.resource_class.__dict__: # novaclient does this for oddball resources diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index 0562b76630..3e7368f156 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -89,27 +89,108 @@ def take_action(self, parsed_args): class ListGroup(lister.Lister): - """List group command""" + """List groups and optionally roles assigned to groups""" api = 'identity' log = logging.getLogger(__name__ + '.ListGroup') def get_parser(self, prog_name): parser = super(ListGroup, self).get_parser(prog_name) + parser.add_argument( + 'group', + metavar='', + nargs='?', + help='Name or ID of group to list [required with --role]', + ) + parser.add_argument( + '--role', + action='store_true', + default=False, + help='List the roles assigned to ', + ) + domain_or_project = parser.add_mutually_exclusive_group() + domain_or_project.add_argument( + '--domain', + metavar='', + help='Filter list by [Only valid with --role]', + ) + domain_or_project.add_argument( + '--project', + metavar='', + help='Filter list by [Only valid with --role]', + ) parser.add_argument( '--long', action='store_true', default=False, - help='Additional fields are listed in output') + 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', 'Domain ID', 'Description') + identity_client = self.app.client_manager.identity + + if parsed_args.role: + # List roles belonging to group + + # Group is required here, bail if it is not supplied + if not parsed_args.group: + sys.stderr.write('Error: Group must be specified') + # TODO(dtroyer): This lists the commands...I want it to + # show the help for _this_ command. + self.app.DeferredHelpAction( + self.app.parser, + self.app.parser, + None, + None, + ) + return ([], []) + + group = utils.find_resource( + identity_client.groups, + parsed_args.group, + ) + + if parsed_args.domain: + columns = ('ID', 'Name', 'Domain', 'Group') + domain = utils.find_resource( + identity_client.domains, + parsed_args.domain, + ) + data = identity_client.roles.list( + group=group, + domain=domain, + ) + for group_role in data: + group_role.group = group.name + group_role.domain = domain.name + elif parsed_args.project: + columns = ('ID', 'Name', 'Project', 'Group') + project = utils.find_resource( + identity_client.projects, + parsed_args.project, + ) + data = identity_client.roles.list( + group=group, + project=project, + ) + for group_role in data: + group_role.group = group.name + group_role.project = project.name + else: + # TODO(dtroyer): raise exception here, this really is an error + sys.stderr.write("Error: Must specify --domain or --project " + "with --role\n") + return ([], []) else: - columns = ('ID', 'Name') - data = self.app.client_manager.identity.groups.list() + # List groups + if parsed_args.long: + columns = ('ID', 'Name', 'Domain ID', 'Description') + else: + columns = ('ID', 'Name') + data = identity_client.groups.list() + return (columns, (utils.get_item_properties( s, columns, @@ -158,7 +239,7 @@ def take_action(self, parsed_args): kwargs['domain'] = domain if not len(kwargs): - sys.stdout.write("Group not updated, no arguments present") + sys.stderr.write("Group not updated, no arguments present") return identity_client.groups.update(group.id, **kwargs) return diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index faff906293..7387509ae2 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -26,7 +26,7 @@ class AddRole(command.Command): - """Add role command""" + """Adds a role to a user or group on a domain or project""" api = 'identity' log = logging.getLogger(__name__ + '.AddRole') @@ -42,23 +42,24 @@ def get_parser(self, prog_name): user_or_group.add_argument( '--user', metavar='', - help='Name or ID of user to assign a role', + help='Name or ID of user to add a role', ) user_or_group.add_argument( '--group', metavar='', - help='Name or ID of group to assign a role', + help='Name or ID of group to add a role', ) domain_or_project = parser.add_mutually_exclusive_group() domain_or_project.add_argument( '--domain', metavar='', - help='Name or ID of domain where user or group resides', + default='default', + help='Name or ID of domain associated with user or group', ) domain_or_project.add_argument( '--project', metavar='', - help='Name or ID of project where user or group resides', + help='Name or ID of project associated with user or group', ) return parser @@ -68,42 +69,40 @@ def take_action(self, parsed_args): if (not parsed_args.user and not parsed_args.domain and not parsed_args.group and not parsed_args.project): - sys.stdout.write("Role not updated, no arguments present \n") + sys.stderr.write("Role not added, no arguments present\n") return role_id = utils.find_resource(identity_client.roles, parsed_args.role).id - if (parsed_args.user and parsed_args.domain): + if parsed_args.user and parsed_args.domain: user = utils.find_resource(identity_client.users, parsed_args.user) domain = utils.find_resource(identity_client.domains, parsed_args.domain) identity_client.roles.grant(role_id, user=user, domain=domain) - return - elif (parsed_args.user and parsed_args.project): + elif parsed_args.user and parsed_args.project: user = utils.find_resource(identity_client.users, parsed_args.user) project = utils.find_resource(identity_client.projects, parsed_args.project) identity_client.roles.grant(role_id, user=user, project=project) - return - elif (parsed_args.group and parsed_args.project): + elif parsed_args.group and parsed_args.domain: + group = utils.find_resource(identity_client.groups, + parsed_args.group) + domain = utils.find_resource(identity_client.domains, + parsed_args.domain) + identity_client.roles.grant(role_id, group=group, domain=domain) + elif parsed_args.group and parsed_args.project: group = utils.find_resource(identity_client.group, parsed_args.group) project = utils.find_resource(identity_client.projects, parsed_args.project) identity_client.roles.grant(role_id, group=group, project=project) - return - elif (parsed_args.group and parsed_args.domain): - group = utils.find_resource(identity_client.group, - parsed_args.group) - domain = utils.find_resource(identity_client.domains, - parsed_args.domain) - identity_client.roles.grant(role_id, group=group, domain=domain) - return else: - return + sys.stderr.write("Role not added, incorrect set of arguments \ + provided. See openstack --help for more details\n") + return class CreateRole(show.ShowOne): @@ -115,15 +114,16 @@ class CreateRole(show.ShowOne): def get_parser(self, prog_name): parser = super(CreateRole, self).get_parser(prog_name) parser.add_argument( - 'role-name', + 'name', metavar='', - help='New role name') + help='New role name', + ) return parser def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) identity_client = self.app.client_manager.identity - role = identity_client.roles.create(parsed_args.role_name) + role = identity_client.roles.create(parsed_args.name) return zip(*sorted(role._info.iteritems())) @@ -139,7 +139,8 @@ def get_parser(self, prog_name): parser.add_argument( 'role', metavar='', - help='Name or ID of role to delete') + help='Name or ID of role to delete', + ) return parser def take_action(self, parsed_args): @@ -168,6 +169,85 @@ def take_action(self, parsed_args): ) for s in data)) +class RemoveRole(command.Command): + """Remove role command""" + + api = 'identity' + log = logging.getLogger(__name__ + '.RemoveRole') + + def get_parser(self, prog_name): + parser = super(RemoveRole, self).get_parser(prog_name) + parser.add_argument( + 'role', + metavar='', + help='Name or ID of role to remove', + ) + user_or_group = parser.add_mutually_exclusive_group() + user_or_group.add_argument( + '--user', + metavar='', + help='Name or ID of user to remove a role', + ) + user_or_group.add_argument( + '--group', + metavar='', + help='Name or ID of group to remove a role', + ) + domain_or_project = parser.add_mutually_exclusive_group() + domain_or_project.add_argument( + '--domain', + metavar='', + help='Name or ID of domain associated with user or group', + ) + domain_or_project.add_argument( + '--project', + metavar='', + help='Name or ID of project associated with user or group', + ) + return parser + + def take_action(self, parsed_args): + self.log.debug('take_action(%s)' % parsed_args) + identity_client = self.app.client_manager.identity + + if (not parsed_args.user and not parsed_args.domain + and not parsed_args.group and not parsed_args.project): + sys.stdout.write("Role not updated, no arguments present\n") + return + + role_id = utils.find_resource(identity_client.roles, + parsed_args.role).id + + if parsed_args.user and parsed_args.domain: + user = utils.find_resource(identity_client.users, + parsed_args.user) + domain = utils.find_resource(identity_client.domains, + parsed_args.domain) + identity_client.roles.revoke(role_id, user=user, domain=domain) + elif parsed_args.user and parsed_args.project: + user = utils.find_resource(identity_client.users, + parsed_args.user) + project = utils.find_resource(identity_client.projects, + parsed_args.project) + identity_client.roles.revoke(role_id, user=user, project=project) + elif parsed_args.group and parsed_args.project: + group = utils.find_resource(identity_client.group, + parsed_args.group) + project = utils.find_resource(identity_client.projects, + parsed_args.project) + identity_client.roles.revoke(role_id, group=group, project=project) + elif parsed_args.group and parsed_args.domain: + group = utils.find_resource(identity_client.group, + parsed_args.group) + domain = utils.find_resource(identity_client.domains, + parsed_args.domain) + identity_client.roles.revoke(role_id, group=group, domain=domain) + else: + sys.stderr.write("Role not removed, incorrect set of arguments \ + provided. See openstack --help for more details\n") + return + + class SetRole(command.Command): """Set role command""" @@ -179,7 +259,7 @@ def get_parser(self, prog_name): parser.add_argument( 'role', metavar='', - help='Name or ID of role to change', + help='Name or ID of role to update', ) parser.add_argument( '--name', @@ -195,7 +275,7 @@ def take_action(self, parsed_args): parsed_args.role) if not parsed_args.name: - sys.stdout.write("Role not updated, no arguments present") + sys.stderr.write("Role not updated, no arguments present") return identity_client.roles.update(role_id, parsed_args.name) @@ -213,7 +293,8 @@ def get_parser(self, prog_name): parser.add_argument( 'role', metavar='', - help='Name or ID of role to display') + help='Name or ID of role to display', + ) return parser def take_action(self, parsed_args): diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 5e6282eb8c..53550cda56 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -135,7 +135,7 @@ def take_action(self, parsed_args): class ListUser(lister.Lister): - """List user command""" + """List users and optionally roles assigned to users""" api = 'identity' log = logging.getLogger(__name__ + '.ListUser') @@ -143,9 +143,27 @@ class ListUser(lister.Lister): def get_parser(self, prog_name): parser = super(ListUser, self).get_parser(prog_name) parser.add_argument( + 'user', + metavar='', + nargs='?', + help='Name or ID of user to list [required with --role]', + ) + parser.add_argument( + '--role', + action='store_true', + default=False, + help='List the roles assigned to ', + ) + domain_or_project = parser.add_mutually_exclusive_group() + domain_or_project.add_argument( + '--domain', + metavar='', + help='Filter list by [Only valid with --role]', + ) + domain_or_project.add_argument( '--project', metavar='', - help='Name or ID of project to filter users', + help='Filter list by [Only valid with --role]', ) parser.add_argument( '--long', @@ -157,12 +175,70 @@ def get_parser(self, prog_name): def take_action(self, parsed_args): self.log.debug('take_action(%s)' % parsed_args) - if parsed_args.long: - columns = ('ID', 'Name', 'Project Id', 'Domain Id', - 'Description', 'Email', 'Enabled') + identity_client = self.app.client_manager.identity + + if parsed_args.role: + # List roles belonging to user + + # User is required here, bail if it is not supplied + if not parsed_args.user: + sys.stderr.write('Error: User must be specified') + return ([], []) + + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ) + + # List a user's roles + if not parsed_args.domain and not parsed_args.project: + columns = ('ID', 'Name') + data = identity_client.roles.list( + user=user, + domain='default', + ) + # List a user's roles on a domain + elif parsed_args.user and parsed_args.domain: + columns = ('ID', 'Name', 'Domain', 'User') + domain = utils.find_resource( + identity_client.domains, + parsed_args.domain, + ) + data = identity_client.roles.list( + user=user, + domain=domain, + ) + for user_role in data: + user_role.user = user.name + user_role.domain = domain.name + # List a user's roles on a project + elif parsed_args.user and parsed_args.project: + columns = ('ID', 'Name', 'Project', 'User') + project = utils.find_resource( + identity_client.projects, + parsed_args.project, + ) + data = identity_client.roles.list( + user=user, + project=project, + ) + for user_role in data: + user_role.user = user.name + user_role.project = project.name + else: + # TODO(dtroyer): raise exception here, this really is an error + sys.stderr.write("Error: Must specify --domain or --project " + "with --role\n") + return ([], []) else: - columns = ('ID', 'Name') - data = self.app.client_manager.identity.users.list() + # List users + if parsed_args.long: + columns = ('ID', 'Name', 'Project Id', 'Domain Id', + 'Description', 'Email', 'Enabled') + else: + columns = ('ID', 'Name') + data = self.app.client_manager.identity.users.list() + return (columns, (utils.get_item_properties( s, columns, @@ -253,7 +329,7 @@ def take_action(self, parsed_args): kwargs['enabled'] = parsed_args.enabled if not len(kwargs): - sys.stdout.write("User not updated, no arguments present") + sys.stderr.write("User not updated, no arguments present") return identity_client.users.update(user.id, **kwargs) return diff --git a/openstackclient/shell.py b/openstackclient/shell.py index 35d8255ddb..e5353194cd 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -37,6 +37,7 @@ DEFAULT_IDENTITY_API_VERSION = '2.0' DEFAULT_IMAGE_API_VERSION = '2' DEFAULT_VOLUME_API_VERSION = '1' +DEFAULT_DOMAIN = 'default' def env(*vars, **kwargs): @@ -134,6 +135,15 @@ def build_option_parser(self, description, version): metavar='', default=env('OS_REGION_NAME'), help='Authentication region name (Env: OS_REGION_NAME)') + parser.add_argument( + '--os-default-domain', + metavar='', + default=env( + 'OS_DEFAULT_DOMAIN', + default=DEFAULT_DOMAIN), + help='Default domain ID, default=' + + DEFAULT_DOMAIN + + ' (Env: OS_DEFAULT_DOMAIN)') parser.add_argument( '--os-identity-api-version', metavar='', @@ -304,7 +314,10 @@ def initialize_app(self, argv): else: requests_log.setLevel(logging.WARNING) - # stash selected API versions for later + # Save default domain + self.default_domain = self.options.os_default_domain + + # Stash selected API versions for later self.api_version = { 'compute': self.options.os_compute_api_version, 'identity': self.options.os_identity_api_version, diff --git a/setup.cfg b/setup.cfg index 535fb40507..03583c59d4 100644 --- a/setup.cfg +++ b/setup.cfg @@ -104,6 +104,7 @@ openstack.identity.v3 = role_create = openstackclient.identity.v3.role:CreateRole role_delete = openstackclient.identity.v3.role:DeleteRole role_list = openstackclient.identity.v3.role:ListRole + role_remove = openstackclient.identity.v3.role:RemoveRole role_show = openstackclient.identity.v3.role:ShowRole role_set = openstackclient.identity.v3.role:SetRole From 7298dd5e72700206c4bcabdcbaa3720a4147cbe7 Mon Sep 17 00:00:00 2001 From: Dean Troyer Date: Wed, 3 Jul 2013 12:10:37 -0500 Subject: [PATCH 0019/3547] Add EC2 credentials CRUD ec2 credentials: create, delete, list, show Change-Id: I82ff84ed433cd9a2da9534bf5f584a2e1a3fe68c --- openstackclient/identity/v2_0/ec2creds.py | 185 ++++++++++++++++++++++ setup.cfg | 5 + 2 files changed, 190 insertions(+) create mode 100644 openstackclient/identity/v2_0/ec2creds.py diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py new file mode 100644 index 0000000000..6a5d2af30f --- /dev/null +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -0,0 +1,185 @@ +# 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. +# + +"""EC2 Credentials action implementations""" + +import logging + +from cliff import command +from cliff import lister +from cliff import show + +from openstackclient.common import utils + + +class CreateEC2Creds(show.ShowOne): + """Create EC2 credentials""" + + api = "identity" + log = logging.getLogger(__name__ + ".CreateEC2Creds") + + def get_parser(self, prog_name): + parser = super(CreateEC2Creds, self).get_parser(prog_name) + parser.add_argument( + '--project', + metavar='', + help='Specify a project [admin only]', + ) + parser.add_argument( + '--user', + metavar='', + help='Specify a user [admin only]', + ) + 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 = utils.find_resource( + identity_client.tenants, + parsed_args.project, + ).id + else: + # Get the project from the current auth + project = identity_client.auth_tenant_id + if parsed_args.user: + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ).id + else: + # Get the user from the current auth + user = identity_client.auth_user_id + + creds = identity_client.ec2.create(user, project) + + info = {} + info.update(creds._info) + return zip(*sorted(info.iteritems())) + + +class DeleteEC2Creds(command.Command): + """Delete EC2 credentials""" + + api = 'identity' + log = logging.getLogger(__name__ + '.DeleteEC2Creds') + + def get_parser(self, prog_name): + parser = super(DeleteEC2Creds, self).get_parser(prog_name) + parser.add_argument( + 'access_key', + metavar='', + help='Credentials access key', + ) + parser.add_argument( + '--user', + metavar='', + help='Specify a user [admin only]', + ) + 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.user: + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ).id + else: + # Get the user from the current auth + user = identity_client.auth_user_id + + identity_client.ec2.delete(user, parsed_args.access_key) + + +class ListEC2Creds(lister.Lister): + """List EC2 credentials""" + + api = 'identity' + log = logging.getLogger(__name__ + '.ListEC2Creds') + + def get_parser(self, prog_name): + parser = super(ListEC2Creds, self).get_parser(prog_name) + parser.add_argument( + '--user', + metavar='', + help='Specify a user [admin only]', + ) + 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.user: + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ).id + else: + # Get the user from the current auth + user = identity_client.auth_user_id + + columns = ('Access', 'Secret', 'Project ID', 'User ID') + data = identity_client.ec2.list(user) + return (columns, + (utils.get_item_properties( + s, columns, + formatters={}, + ) for s in data)) + + +class ShowEC2Creds(show.ShowOne): + """Show EC2 credentials""" + + api = 'identity' + log = logging.getLogger(__name__ + '.ShowEC2Creds') + + def get_parser(self, prog_name): + parser = super(ShowEC2Creds, self).get_parser(prog_name) + parser.add_argument( + 'access_key', + metavar='', + help='Credentials access key', + ) + parser.add_argument( + '--user', + metavar='', + help='Specify a user [admin only]', + ) + 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.user: + user = utils.find_resource( + identity_client.users, + parsed_args.user, + ).id + else: + # Get the user from the current auth + user = identity_client.auth_user_id + + creds = identity_client.ec2.get(user, parsed_args.access_key) + + info = {} + info.update(creds._info) + return zip(*sorted(info.iteritems())) diff --git a/setup.cfg b/setup.cfg index 4b2142dd2c..27f3e1d582 100644 --- a/setup.cfg +++ b/setup.cfg @@ -32,6 +32,11 @@ console_scripts = openstack.cli = openstack.identity.v2_0 = + ec2_credentials_create = openstackclient.identity.v2_0.ec2creds:CreateEC2Creds + ec2_credentials_delete = openstackclient.identity.v2_0.ec2creds:DeleteEC2Creds + ec2_credentials_list = openstackclient.identity.v2_0.ec2creds:ListEC2Creds + ec2_credentials_show = openstackclient.identity.v2_0.ec2creds:ShowEC2Creds + endpoint_create = openstackclient.identity.v2_0.endpoint:CreateEndpoint endpoint_delete = openstackclient.identity.v2_0.endpoint:DeleteEndpoint endpoint_list = openstackclient.identity.v2_0.endpoint:ListEndpoint From d7501c352dc95360ba9b432cdec1f1b8b822b0e6 Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Fri, 5 Jul 2013 22:30:54 -0400 Subject: [PATCH 0020/3547] Update documentation with info about setup.cfg Change-Id: If87df1e6415d0b70b6605b1d89eda639fc44a0b6 --- doc/source/commands.rst | 11 +++++------ 1 file changed, 5 insertions(+), 6 deletions(-) diff --git a/doc/source/commands.rst b/doc/source/commands.rst index 40a2425803..0c278d0525 100644 --- a/doc/source/commands.rst +++ b/doc/source/commands.rst @@ -41,20 +41,19 @@ of Cliff's command.Command object. Command Entry Points -------------------- -Commands are added to the client using distribute's entry points in ``setup.py``. +Commands are added to the client using setuptools's entry points in ``setup.cfg``. 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" + verb_object = fully.qualified.module.vXX.object:VerbObject For example, the 'list user' command fir the Identity API is identified in -``setup.py`` with:: +``setup.cfg`` with:: - 'openstack.identity.v3': [ + openstack.identity.v3 = # ... - 'list_user=openstackclient.identity.v3.user:ListUser', + list_user = openstackclient.identity.v3.user:ListUser # ... - ], From 4022e41c6420cb581da619091bd433a3d63af0ea Mon Sep 17 00:00:00 2001 From: Monty Taylor Date: Fri, 5 Jul 2013 22:31:15 -0400 Subject: [PATCH 0021/3547] Sync install_venv_common from oslo Change-Id: I0a57c658e0f89d13963862013793e12ae208c05b --- tools/install_venv_common.py | 106 ++++++++++++++--------------------- 1 file changed, 43 insertions(+), 63 deletions(-) diff --git a/tools/install_venv_common.py b/tools/install_venv_common.py index 5cef255509..f428c1e021 100644 --- a/tools/install_venv_common.py +++ b/tools/install_venv_common.py @@ -1,6 +1,6 @@ # vim: tabstop=4 shiftwidth=4 softtabstop=4 -# Copyright 2013 OpenStack, LLC +# Copyright 2013 OpenStack Foundation # Copyright 2013 IBM Corp. # # Licensed under the Apache License, Version 2.0 (the "License"); you may @@ -18,36 +18,34 @@ """Provides methods needed by installation script for OpenStack development virtual environments. +Since this script is used to bootstrap a virtualenv from the system's Python +environment, it should be kept strictly compatible with Python 2.6. + Synced in from openstack-common """ +from __future__ import print_function + +import optparse import os import subprocess import sys -possible_topdir = os.getcwd() -if os.path.exists(os.path.join(possible_topdir, "openstackclient", - "__init__.py")): - sys.path.insert(0, possible_topdir) - - -from openstackclient.openstack.common import cfg - - class InstallVenv(object): - def __init__(self, root, venv, pip_requires, test_requires, py_version, + def __init__(self, root, venv, requirements, + test_requirements, py_version, project): self.root = root self.venv = venv - self.pip_requires = pip_requires - self.test_requires = test_requires + self.requirements = requirements + self.test_requirements = test_requirements self.py_version = py_version self.project = project def die(self, message, *args): - print >> sys.stderr, message % args + print(message % args, file=sys.stderr) sys.exit(1) def check_python_version(self): @@ -58,7 +56,7 @@ def run_command_with_code(self, cmd, redirect_output=True, check_exit_code=True): """Runs a command in an out-of-process shell. - Returns the output of that command. Working directory is ROOT. + Returns the output of that command. Working directory is self.root. """ if redirect_output: stdout = subprocess.PIPE @@ -78,11 +76,13 @@ def run_command(self, cmd, redirect_output=True, check_exit_code=True): def get_distro(self): if (os.path.exists('/etc/fedora-release') or os.path.exists('/etc/redhat-release')): - return Fedora(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) + return Fedora( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) else: - return Distro(self.root, self.venv, self.pip_requires, - self.test_requires, self.py_version, self.project) + return Distro( + self.root, self.venv, self.requirements, + self.test_requirements, self.py_version, self.project) def check_dependencies(self): self.get_distro().install_virtualenv() @@ -94,20 +94,15 @@ def create_virtualenv(self, no_site_packages=True): virtual environment. """ if not os.path.isdir(self.venv): - print 'Creating venv...', + print('Creating venv...', end=' ') if no_site_packages: self.run_command(['virtualenv', '-q', '--no-site-packages', self.venv]) else: self.run_command(['virtualenv', '-q', self.venv]) - print 'done.' - print 'Installing pip in virtualenv...', - if not self.run_command(['tools/with_venv.sh', 'easy_install', - 'pip>1.0']).strip(): - self.die("Failed to install pip.") - print 'done.' + print('done.') else: - print "venv already exists..." + print("venv already exists...") pass def pip_install(self, *args): @@ -116,40 +111,27 @@ def pip_install(self, *args): redirect_output=False) def install_dependencies(self): - print 'Installing dependencies with pip (this can take a while)...' + print('Installing dependencies with pip (this can take a while)...') # First things first, make sure our venv has the latest pip and - # distribute. - # NOTE: we keep pip at version 1.1 since the most recent version causes - # the .venv creation to fail. See: - # https://bugs.launchpad.net/nova/+bug/1047120 - self.pip_install('pip==1.1') - self.pip_install('distribute') + # setuptools. + self.pip_install('pip>=1.3') + self.pip_install('setuptools') - # Install greenlet by hand - just listing it in the requires file does - # not - # get it installed in the right order - self.pip_install('greenlet') - - self.pip_install('-r', self.pip_requires) - self.pip_install('-r', self.test_requires) + self.pip_install('-r', self.requirements) + self.pip_install('-r', self.test_requirements) def post_process(self): self.get_distro().post_process() def parse_args(self, argv): """Parses command-line arguments.""" - cli_opts = [ - cfg.BoolOpt('no-site-packages', - default=False, - short='n', - help="Do not inherit packages from global Python" - "install"), - ] - CLI = cfg.ConfigOpts() - CLI.register_cli_opts(cli_opts) - CLI(argv[1:]) - return CLI + parser = optparse.OptionParser() + parser.add_option('-n', '--no-site-packages', + action='store_true', + help="Do not inherit packages from global Python " + "install") + return parser.parse_args(argv[1:])[0] class Distro(InstallVenv): @@ -163,12 +145,12 @@ def install_virtualenv(self): return if self.check_cmd('easy_install'): - print 'Installing virtualenv via easy_install...', + print('Installing virtualenv via easy_install...', end=' ') if self.run_command(['easy_install', 'virtualenv']): - print 'Succeeded' + print('Succeeded') return else: - print 'Failed' + print('Failed') self.die('ERROR: virtualenv not found.\n\n%s development' ' requires virtualenv, please install it using your' @@ -193,19 +175,16 @@ def check_pkg(self, pkg): return self.run_command_with_code(['rpm', '-q', pkg], check_exit_code=False)[1] == 0 - def yum_install(self, pkg, **kwargs): - print "Attempting to install '%s' via yum" % pkg - self.run_command(['sudo', 'yum', 'install', '-y', pkg], **kwargs) - def apply_patch(self, originalfile, patchfile): - self.run_command(['patch', originalfile, patchfile]) + self.run_command(['patch', '-N', originalfile, patchfile], + check_exit_code=False) def install_virtualenv(self): if self.check_cmd('virtualenv'): return if not self.check_pkg('python-virtualenv'): - self.yum_install('python-virtualenv', check_exit_code=False) + self.die("Please install 'python-virtualenv'.") super(Fedora, self).install_virtualenv() @@ -218,12 +197,13 @@ def post_process(self): This can be removed when the fix is applied upstream. Nova: https://bugs.launchpad.net/nova/+bug/884915 - Upstream: https://bitbucket.org/which_linden/eventlet/issue/89 + Upstream: https://bitbucket.org/eventlet/eventlet/issue/89 + RHEL: https://bugzilla.redhat.com/958868 """ # Install "patch" program if it's not there if not self.check_pkg('patch'): - self.yum_install('patch') + self.die("Please install 'patch'.") # Apply the eventlet patch self.apply_patch(os.path.join(self.venv, 'lib', self.py_version, From 713908385a183cada8c9e77bb74c2ca381a38660 Mon Sep 17 00:00:00 2001 From: Steve Martinelli Date: Wed, 3 Jul 2013 15:26:33 -0500 Subject: [PATCH 0022/3547] 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 0023/3547] 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 0024/3547] 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 0025/3547] 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 0026/3547] 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 0027/3547] 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 0028/3547] 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 0029/3547] 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 0030/3547] 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 0031/3547] 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 0032/3547] 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 0033/3547] 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 0034/3547] 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 0035/3547] 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 0036/3547] 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 0037/3547] 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 0038/3547] 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 0039/3547] 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 0040/3547] 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 0041/3547] 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 0042/3547] 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='