diff options
Diffstat (limited to 'openstackclient')
118 files changed, 3520 insertions, 791 deletions
diff --git a/openstackclient/common/availability_zone.py b/openstackclient/common/availability_zone.py index 63c55370..b2385ef7 100644 --- a/openstackclient/common/availability_zone.py +++ b/openstackclient/common/availability_zone.py @@ -88,7 +88,7 @@ def _xform_network_availability_zone(az): class ListAvailabilityZone(command.Lister): - """List availability zones and their status""" + _description = _("List availability zones and their status") def get_parser(self, prog_name): parser = super(ListAvailabilityZone, self).get_parser(prog_name) diff --git a/openstackclient/common/client_config.py b/openstackclient/common/client_config.py index d6297753..1ac53f7f 100644 --- a/openstackclient/common/client_config.py +++ b/openstackclient/common/client_config.py @@ -151,6 +151,7 @@ class OSC_Config(config.OpenStackConfig): # present, then do not change the behaviour. Otherwise, set the # PROJECT_DOMAIN_ID to 'OS_DEFAULT_DOMAIN' for better usability. if ( + auth_type in ("password", "v3password", "v3totp") and not config['auth'].get('project_domain_id') and not config['auth'].get('project_domain_name') ): diff --git a/openstackclient/common/configuration.py b/openstackclient/common/configuration.py index 016e9191..57825bb0 100644 --- a/openstackclient/common/configuration.py +++ b/openstackclient/common/configuration.py @@ -23,7 +23,7 @@ REDACTED = "<redacted>" class ShowConfiguration(command.ShowOne): - """Display configuration details""" + _description = _("Display configuration details") def get_parser(self, prog_name): parser = super(ShowConfiguration, self).get_parser(prog_name) diff --git a/openstackclient/common/extension.py b/openstackclient/common/extension.py index de480016..991f3afc 100644 --- a/openstackclient/common/extension.py +++ b/openstackclient/common/extension.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) class ListExtension(command.Lister): - """List API extensions""" + _description = _("List API extensions") def get_parser(self, prog_name): parser = super(ListExtension, self).get_parser(prog_name) diff --git a/openstackclient/common/limits.py b/openstackclient/common/limits.py index f7aa82f6..957f1d02 100644 --- a/openstackclient/common/limits.py +++ b/openstackclient/common/limits.py @@ -25,7 +25,7 @@ from openstackclient.identity import common as identity_common class ShowLimits(command.Lister): - """Show compute and block storage limits""" + _description = _("Show compute and block storage limits") def get_parser(self, prog_name): parser = super(ShowLimits, self).get_parser(prog_name) diff --git a/openstackclient/common/module.py b/openstackclient/common/module.py index 7c5fcd55..15719a30 100644 --- a/openstackclient/common/module.py +++ b/openstackclient/common/module.py @@ -25,7 +25,7 @@ from openstackclient.i18n import _ class ListCommand(command.Lister): - """List recognized commands by group""" + _description = _("List recognized commands by group") auth_required = False @@ -53,7 +53,7 @@ class ListCommand(command.Lister): class ListModule(command.ShowOne): - """List module versions""" + _description = _("List module versions") auth_required = False diff --git a/openstackclient/common/quota.py b/openstackclient/common/quota.py index 8f099cc9..58368c56 100644 --- a/openstackclient/common/quota.py +++ b/openstackclient/common/quota.py @@ -80,7 +80,7 @@ NETWORK_QUOTAS = { class SetQuota(command.Command): - """Set quotas for project or class""" + _description = _("Set quotas for project or class") def _build_options_list(self): if self.app.client_manager.is_network_endpoint_enabled(): @@ -186,7 +186,7 @@ class SetQuota(command.Command): class ShowQuota(command.ShowOne): - """Show quotas for project or class""" + _description = _("Show quotas for project or class") def get_parser(self, prog_name): parser = super(ShowQuota, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/agent.py b/openstackclient/compute/v2/agent.py index 76c1b3b7..151dcc1e 100644 --- a/openstackclient/compute/v2/agent.py +++ b/openstackclient/compute/v2/agent.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreateAgent(command.ShowOne): - """Create compute agent""" + _description = _("Create compute agent") def get_parser(self, prog_name): parser = super(CreateAgent, self).get_parser(prog_name) @@ -81,7 +81,7 @@ class CreateAgent(command.ShowOne): class DeleteAgent(command.Command): - """Delete compute agent(s)""" + _description = _("Delete compute agent(s)") def get_parser(self, prog_name): parser = super(DeleteAgent, self).get_parser(prog_name) @@ -112,7 +112,7 @@ class DeleteAgent(command.Command): class ListAgent(command.Lister): - """List compute agents""" + _description = _("List compute agents") def get_parser(self, prog_name): parser = super(ListAgent, self).get_parser(prog_name) @@ -142,7 +142,7 @@ class ListAgent(command.Lister): class SetAgent(command.Command): - """Set compute agent properties""" + _description = _("Set compute agent properties") def get_parser(self, prog_name): parser = super(SetAgent, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/aggregate.py b/openstackclient/compute/v2/aggregate.py index 58d529e9..76ba5cc6 100644 --- a/openstackclient/compute/v2/aggregate.py +++ b/openstackclient/compute/v2/aggregate.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class AddAggregateHost(command.ShowOne): - """Add host to aggregate""" + _description = _("Add host to aggregate") def get_parser(self, prog_name): parser = super(AddAggregateHost, self).get_parser(prog_name) @@ -62,7 +62,7 @@ class AddAggregateHost(command.ShowOne): class CreateAggregate(command.ShowOne): - """Create a new aggregate""" + _description = _("Create a new aggregate") def get_parser(self, prog_name): parser = super(CreateAggregate, self).get_parser(prog_name) @@ -105,7 +105,7 @@ class CreateAggregate(command.ShowOne): class DeleteAggregate(command.Command): - """Delete existing aggregate(s)""" + _description = _("Delete existing aggregate(s)") def get_parser(self, prog_name): parser = super(DeleteAggregate, self).get_parser(prog_name) @@ -139,7 +139,7 @@ class DeleteAggregate(command.Command): class ListAggregate(command.Lister): - """List all aggregates""" + _description = _("List all aggregates") def get_parser(self, prog_name): parser = super(ListAggregate, self).get_parser(prog_name) @@ -188,7 +188,7 @@ class ListAggregate(command.Lister): class RemoveAggregateHost(command.ShowOne): - """Remove host from aggregate""" + _description = _("Remove host from aggregate") def get_parser(self, prog_name): parser = super(RemoveAggregateHost, self).get_parser(prog_name) @@ -222,7 +222,7 @@ class RemoveAggregateHost(command.ShowOne): class SetAggregate(command.Command): - """Set aggregate properties""" + _description = _("Set aggregate properties") def get_parser(self, prog_name): parser = super(SetAggregate, self).get_parser(prog_name) @@ -298,7 +298,7 @@ class SetAggregate(command.Command): class ShowAggregate(command.ShowOne): - """Display aggregate details""" + _description = _("Display aggregate details") def get_parser(self, prog_name): parser = super(ShowAggregate, self).get_parser(prog_name) @@ -334,7 +334,7 @@ class ShowAggregate(command.ShowOne): class UnsetAggregate(command.Command): - """Unset aggregate properties""" + _description = _("Unset aggregate properties") def get_parser(self, prog_name): parser = super(UnsetAggregate, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/console.py b/openstackclient/compute/v2/console.py index 02be99d5..358df501 100644 --- a/openstackclient/compute/v2/console.py +++ b/openstackclient/compute/v2/console.py @@ -26,7 +26,7 @@ from openstackclient.i18n import _ class ShowConsoleLog(command.Command): - """Show server's console output""" + _description = _("Show server's console output") def get_parser(self, prog_name): parser = super(ShowConsoleLog, self).get_parser(prog_name) @@ -64,7 +64,7 @@ class ShowConsoleLog(command.Command): class ShowConsoleURL(command.ShowOne): - """Show server's remote console URL""" + _description = _("Show server's remote console URL") def get_parser(self, prog_name): parser = super(ShowConsoleURL, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/fixedip.py b/openstackclient/compute/v2/fixedip.py index c14d29fa..0c0b619e 100644 --- a/openstackclient/compute/v2/fixedip.py +++ b/openstackclient/compute/v2/fixedip.py @@ -24,7 +24,7 @@ from openstackclient.i18n import _ class AddFixedIP(command.Command): - """Add fixed IP address to server""" + _description = _("Add fixed IP address to server") # TODO(tangchen): Remove this class and ``ip fixed add`` command # two cycles after Mitaka. @@ -64,7 +64,7 @@ class AddFixedIP(command.Command): class RemoveFixedIP(command.Command): - """Remove fixed IP address from server""" + _description = _("Remove fixed IP address from server") # TODO(tangchen): Remove this class and ``ip fixed remove`` command # two cycles after Mitaka. diff --git a/openstackclient/compute/v2/flavor.py b/openstackclient/compute/v2/flavor.py index b3f09ce5..f20d154b 100644 --- a/openstackclient/compute/v2/flavor.py +++ b/openstackclient/compute/v2/flavor.py @@ -49,7 +49,7 @@ def _find_flavor(compute_client, flavor): class CreateFlavor(command.ShowOne): - """Create new flavor""" + _description = _("Create new flavor") def get_parser(self, prog_name): parser = super(CreateFlavor, self).get_parser(prog_name) @@ -186,7 +186,7 @@ class CreateFlavor(command.ShowOne): class DeleteFlavor(command.Command): - """Delete flavor(s)""" + _description = _("Delete flavor(s)") def get_parser(self, prog_name): parser = super(DeleteFlavor, self).get_parser(prog_name) @@ -219,7 +219,7 @@ class DeleteFlavor(command.Command): class ListFlavor(command.Lister): - """List flavors""" + _description = _("List flavors") def get_parser(self, prog_name): parser = super(ListFlavor, self).get_parser(prog_name) @@ -303,7 +303,7 @@ class ListFlavor(command.Lister): class SetFlavor(command.Command): - """Set flavor properties""" + _description = _("Set flavor properties") def get_parser(self, prog_name): parser = super(SetFlavor, self).get_parser(prog_name) @@ -366,7 +366,7 @@ class SetFlavor(command.Command): class ShowFlavor(command.ShowOne): - """Display flavor details""" + _description = _("Display flavor details") def get_parser(self, prog_name): parser = super(ShowFlavor, self).get_parser(prog_name) @@ -409,7 +409,7 @@ class ShowFlavor(command.ShowOne): class UnsetFlavor(command.Command): - """Unset flavor properties""" + _description = _("Unset flavor properties") def get_parser(self, prog_name): parser = super(UnsetFlavor, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/floatingip.py b/openstackclient/compute/v2/floatingip.py index 8398ea57..69595bed 100644 --- a/openstackclient/compute/v2/floatingip.py +++ b/openstackclient/compute/v2/floatingip.py @@ -24,7 +24,7 @@ from openstackclient.i18n import _ class AddFloatingIP(command.Command): - """Add floating IP address to server""" + _description = _("Add floating IP address to server") # TODO(tangchen): Remove this class and ``ip floating add`` command # two cycles after Mitaka. @@ -61,7 +61,7 @@ class AddFloatingIP(command.Command): class RemoveFloatingIP(command.Command): - """Remove floating IP address from server""" + _description = _("Remove floating IP address from server") # TODO(tangchen): Remove this class and ``ip floating remove`` command # two cycles after Mitaka. diff --git a/openstackclient/compute/v2/host.py b/openstackclient/compute/v2/host.py index 4785377e..a495b367 100644 --- a/openstackclient/compute/v2/host.py +++ b/openstackclient/compute/v2/host.py @@ -22,7 +22,7 @@ from openstackclient.i18n import _ class ListHost(command.Lister): - """List hosts""" + _description = _("List hosts") def get_parser(self, prog_name): parser = super(ListHost, self).get_parser(prog_name) @@ -48,7 +48,8 @@ class ListHost(command.Lister): class SetHost(command.Command): - """Set host properties""" + _description = _("Set host properties") + def get_parser(self, prog_name): parser = super(SetHost, self).get_parser(prog_name) parser.add_argument( @@ -107,7 +108,7 @@ class SetHost(command.Command): class ShowHost(command.Lister): - """Display host details""" + _description = _("Display host details") def get_parser(self, prog_name): parser = super(ShowHost, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/hypervisor.py b/openstackclient/compute/v2/hypervisor.py index 69b5d137..406aa917 100644 --- a/openstackclient/compute/v2/hypervisor.py +++ b/openstackclient/compute/v2/hypervisor.py @@ -26,7 +26,7 @@ from openstackclient.i18n import _ class ListHypervisor(command.Lister): - """List hypervisors""" + _description = _("List hypervisors") def get_parser(self, prog_name): parser = super(ListHypervisor, self).get_parser(prog_name) @@ -66,7 +66,7 @@ class ListHypervisor(command.Lister): class ShowHypervisor(command.ShowOne): - """Display hypervisor details""" + _description = _("Display hypervisor details") def get_parser(self, prog_name): parser = super(ShowHypervisor, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/hypervisor_stats.py b/openstackclient/compute/v2/hypervisor_stats.py index c6fd2992..b0413005 100644 --- a/openstackclient/compute/v2/hypervisor_stats.py +++ b/openstackclient/compute/v2/hypervisor_stats.py @@ -17,9 +17,11 @@ from osc_lib.command import command import six +from openstackclient.i18n import _ + class ShowHypervisorStats(command.ShowOne): - """Display hypervisor stats details""" + _description = _("Display hypervisor stats details") def take_action(self, parsed_args): compute_client = self.app.client_manager.compute diff --git a/openstackclient/compute/v2/keypair.py b/openstackclient/compute/v2/keypair.py index d5c682f4..a63cbfec 100644 --- a/openstackclient/compute/v2/keypair.py +++ b/openstackclient/compute/v2/keypair.py @@ -32,7 +32,7 @@ LOG = logging.getLogger(__name__) class CreateKeypair(command.ShowOne): - """Create new public or private key for server ssh access""" + _description = _("Create new public or private key for server ssh access") def get_parser(self, prog_name): parser = super(CreateKeypair, self).get_parser(prog_name) @@ -83,7 +83,7 @@ class CreateKeypair(command.ShowOne): class DeleteKeypair(command.Command): - """Delete public or private key(s)""" + _description = _("Delete public or private key(s)") def get_parser(self, prog_name): parser = super(DeleteKeypair, self).get_parser(prog_name) @@ -117,7 +117,7 @@ class DeleteKeypair(command.Command): class ListKeypair(command.Lister): - """List key fingerprints""" + _description = _("List key fingerprints") def take_action(self, parsed_args): compute_client = self.app.client_manager.compute @@ -134,7 +134,7 @@ class ListKeypair(command.Lister): class ShowKeypair(command.ShowOne): - """Display key details""" + _description = _("Display key details") def get_parser(self, prog_name): parser = super(ShowKeypair, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py index df46c7df..48d8b2d0 100644 --- a/openstackclient/compute/v2/server.py +++ b/openstackclient/compute/v2/server.py @@ -175,7 +175,7 @@ def _show_progress(progress): class AddFixedIP(command.Command): - """Add fixed IP address to server""" + _description = _("Add fixed IP address to server") def get_parser(self, prog_name): parser = super(AddFixedIP, self).get_parser(prog_name) @@ -205,7 +205,7 @@ class AddFixedIP(command.Command): class AddFloatingIP(command.Command): - """Add floating IP address to server""" + _description = _("Add floating IP address to server") def get_parser(self, prog_name): parser = super(AddFloatingIP, self).get_parser(prog_name) @@ -232,7 +232,7 @@ class AddFloatingIP(command.Command): class AddServerSecurityGroup(command.Command): - """Add security group to server""" + _description = _("Add security group to server") def get_parser(self, prog_name): parser = super(AddServerSecurityGroup, self).get_parser(prog_name) @@ -264,7 +264,7 @@ class AddServerSecurityGroup(command.Command): class AddServerVolume(command.Command): - """Add volume to server""" + _description = _("Add volume to server") def get_parser(self, prog_name): parser = super(AddServerVolume, self).get_parser(prog_name) @@ -306,7 +306,7 @@ class AddServerVolume(command.Command): class CreateServer(command.ShowOne): - """Create a new server""" + _description = _("Create a new server") def get_parser(self, prog_name): parser = super(CreateServer, self).get_parser(prog_name) @@ -642,7 +642,7 @@ class CreateServerDump(command.Command): class DeleteServer(command.Command): - """Delete server(s)""" + _description = _("Delete server(s)") def get_parser(self, prog_name): parser = super(DeleteServer, self).get_parser(prog_name) @@ -680,7 +680,7 @@ class DeleteServer(command.Command): class ListServer(command.Lister): - """List servers""" + _description = _("List servers") def get_parser(self, prog_name): parser = super(ListServer, self).get_parser(prog_name) @@ -923,7 +923,8 @@ class ListServer(command.Lister): class LockServer(command.Command): - """Lock server(s). A non-admin user will not be able to execute actions""" + _description = _("Lock server(s). A non-admin user will not be able to " + "execute actions") def get_parser(self, prog_name): parser = super(LockServer, self).get_parser(prog_name) @@ -956,7 +957,7 @@ class LockServer(command.Command): # then adding the groups doesn't seem to work class MigrateServer(command.Command): - """Migrate server to different host""" + _description = _("Migrate server to different host") def get_parser(self, prog_name): parser = super(MigrateServer, self).get_parser(prog_name) @@ -1038,7 +1039,7 @@ class MigrateServer(command.Command): class PauseServer(command.Command): - """Pause server(s)""" + _description = _("Pause server(s)") def get_parser(self, prog_name): parser = super(PauseServer, self).get_parser(prog_name) @@ -1060,7 +1061,7 @@ class PauseServer(command.Command): class RebootServer(command.Command): - """Perform a hard or soft server reboot""" + _description = _("Perform a hard or soft server reboot") def get_parser(self, prog_name): parser = super(RebootServer, self).get_parser(prog_name) @@ -1114,7 +1115,7 @@ class RebootServer(command.Command): class RebuildServer(command.ShowOne): - """Rebuild server""" + _description = _("Rebuild server") def get_parser(self, prog_name): parser = super(RebuildServer, self).get_parser(prog_name) @@ -1170,7 +1171,7 @@ class RebuildServer(command.ShowOne): class RemoveFixedIP(command.Command): - """Remove fixed IP address from server""" + _description = _("Remove fixed IP address from server") def get_parser(self, prog_name): parser = super(RemoveFixedIP, self).get_parser(prog_name) @@ -1197,7 +1198,7 @@ class RemoveFixedIP(command.Command): class RemoveFloatingIP(command.Command): - """Remove floating IP address from server""" + _description = _("Remove floating IP address from server") def get_parser(self, prog_name): parser = super(RemoveFloatingIP, self).get_parser(prog_name) @@ -1225,7 +1226,7 @@ class RemoveFloatingIP(command.Command): class RemoveServerSecurityGroup(command.Command): - """Remove security group from server""" + _description = _("Remove security group from server") def get_parser(self, prog_name): parser = super(RemoveServerSecurityGroup, self).get_parser(prog_name) @@ -1257,7 +1258,7 @@ class RemoveServerSecurityGroup(command.Command): class RemoveServerVolume(command.Command): - """Remove volume from server""" + _description = _("Remove volume from server") def get_parser(self, prog_name): parser = super(RemoveServerVolume, self).get_parser(prog_name) @@ -1293,7 +1294,7 @@ class RemoveServerVolume(command.Command): class RescueServer(command.ShowOne): - """Put server in rescue mode""" + _description = _("Put server in rescue mode") def get_parser(self, prog_name): parser = super(RescueServer, self).get_parser(prog_name) @@ -1315,7 +1316,7 @@ class RescueServer(command.ShowOne): class ResizeServer(command.Command): - """Scale server to a new flavor""" + _description = _("Scale server to a new flavor") def get_parser(self, prog_name): parser = super(ResizeServer, self).get_parser(prog_name) @@ -1380,7 +1381,7 @@ class ResizeServer(command.Command): class RestoreServer(command.Command): - """Restore server(s)""" + _description = _("Restore server(s)") def get_parser(self, prog_name): parser = super(RestoreServer, self).get_parser(prog_name) @@ -1402,7 +1403,7 @@ class RestoreServer(command.Command): class ResumeServer(command.Command): - """Resume server(s)""" + _description = _("Resume server(s)") def get_parser(self, prog_name): parser = super(ResumeServer, self).get_parser(prog_name) @@ -1425,7 +1426,7 @@ class ResumeServer(command.Command): class SetServer(command.Command): - """Set server properties""" + _description = _("Set server properties") def get_parser(self, prog_name): parser = super(SetServer, self).get_parser(prog_name) @@ -1490,7 +1491,7 @@ class SetServer(command.Command): class ShelveServer(command.Command): - """Shelve server(s)""" + _description = _("Shelve server(s)") def get_parser(self, prog_name): parser = super(ShelveServer, self).get_parser(prog_name) @@ -1512,7 +1513,7 @@ class ShelveServer(command.Command): class ShowServer(command.ShowOne): - """Show server details""" + _description = _("Show server details") def get_parser(self, prog_name): parser = super(ShowServer, self).get_parser(prog_name) @@ -1546,7 +1547,7 @@ class ShowServer(command.ShowOne): class SshServer(command.Command): - """SSH to server""" + _description = _("SSH to server") def get_parser(self, prog_name): parser = super(SshServer, self).get_parser(prog_name) @@ -1690,7 +1691,7 @@ class SshServer(command.Command): class StartServer(command.Command): - """Start server(s).""" + _description = _("Start server(s).") def get_parser(self, prog_name): parser = super(StartServer, self).get_parser(prog_name) @@ -1712,7 +1713,7 @@ class StartServer(command.Command): class StopServer(command.Command): - """Stop server(s).""" + _description = _("Stop server(s).") def get_parser(self, prog_name): parser = super(StopServer, self).get_parser(prog_name) @@ -1734,7 +1735,7 @@ class StopServer(command.Command): class SuspendServer(command.Command): - """Suspend server(s)""" + _description = _("Suspend server(s)") def get_parser(self, prog_name): parser = super(SuspendServer, self).get_parser(prog_name) @@ -1757,7 +1758,7 @@ class SuspendServer(command.Command): class UnlockServer(command.Command): - """Unlock server(s)""" + _description = _("Unlock server(s)") def get_parser(self, prog_name): parser = super(UnlockServer, self).get_parser(prog_name) @@ -1780,7 +1781,7 @@ class UnlockServer(command.Command): class UnpauseServer(command.Command): - """Unpause server(s)""" + _description = _("Unpause server(s)") def get_parser(self, prog_name): parser = super(UnpauseServer, self).get_parser(prog_name) @@ -1803,7 +1804,7 @@ class UnpauseServer(command.Command): class UnrescueServer(command.Command): - """Restore server from rescue mode""" + _description = _("Restore server from rescue mode") def get_parser(self, prog_name): parser = super(UnrescueServer, self).get_parser(prog_name) @@ -1824,7 +1825,7 @@ class UnrescueServer(command.Command): class UnsetServer(command.Command): - """Unset server properties""" + _description = _("Unset server properties") def get_parser(self, prog_name): parser = super(UnsetServer, self).get_parser(prog_name) @@ -1858,7 +1859,7 @@ class UnsetServer(command.Command): class UnshelveServer(command.Command): - """Unshelve server(s)""" + _description = _("Unshelve server(s)") def get_parser(self, prog_name): parser = super(UnshelveServer, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/server_backup.py b/openstackclient/compute/v2/server_backup.py index c0e2e5ee..ddcf9101 100644 --- a/openstackclient/compute/v2/server_backup.py +++ b/openstackclient/compute/v2/server_backup.py @@ -33,7 +33,7 @@ def _show_progress(progress): class CreateServerBackup(command.ShowOne): - """Create a server backup image""" + _description = _("Create a server backup image") IMAGE_API_VERSIONS = { "1": "openstackclient.image.v1.image", diff --git a/openstackclient/compute/v2/server_group.py b/openstackclient/compute/v2/server_group.py index d51b1ec2..6bcfc6ae 100644 --- a/openstackclient/compute/v2/server_group.py +++ b/openstackclient/compute/v2/server_group.py @@ -43,7 +43,7 @@ def _get_columns(info): class CreateServerGroup(command.ShowOne): - """Create a new server group.""" + _description = _("Create a new server group.") def get_parser(self, prog_name): parser = super(CreateServerGroup, self).get_parser(prog_name) @@ -77,7 +77,7 @@ class CreateServerGroup(command.ShowOne): class DeleteServerGroup(command.Command): - """Delete existing server group(s).""" + _description = _("Delete existing server group(s).") def get_parser(self, prog_name): parser = super(DeleteServerGroup, self).get_parser(prog_name) @@ -112,7 +112,7 @@ class DeleteServerGroup(command.Command): class ListServerGroup(command.Lister): - """List all server groups.""" + _description = _("List all server groups.") def get_parser(self, prog_name): parser = super(ListServerGroup, self).get_parser(prog_name) @@ -161,7 +161,7 @@ class ListServerGroup(command.Lister): class ShowServerGroup(command.ShowOne): - """Display server group details.""" + _description = _("Display server group details.") def get_parser(self, prog_name): parser = super(ShowServerGroup, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/server_image.py b/openstackclient/compute/v2/server_image.py index 285c7fd2..c66e0674 100644 --- a/openstackclient/compute/v2/server_image.py +++ b/openstackclient/compute/v2/server_image.py @@ -37,7 +37,7 @@ def _show_progress(progress): class CreateServerImage(command.ShowOne): - """Create a new server disk image from an existing server""" + _description = _("Create a new server disk image from an existing server") IMAGE_API_VERSIONS = { "1": "openstackclient.image.v1.image", diff --git a/openstackclient/compute/v2/service.py b/openstackclient/compute/v2/service.py index 53624f55..9c384f05 100644 --- a/openstackclient/compute/v2/service.py +++ b/openstackclient/compute/v2/service.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class DeleteService(command.Command): - """Delete compute service(s)""" + _description = _("Delete compute service(s)") def get_parser(self, prog_name): parser = super(DeleteService, self).get_parser(prog_name) @@ -61,7 +61,7 @@ class DeleteService(command.Command): class ListService(command.Lister): - """List compute services""" + _description = _("List compute services") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) @@ -115,7 +115,7 @@ class ListService(command.Lister): class SetService(command.Command): - """Set compute service properties""" + _description = _("Set compute service properties") def get_parser(self, prog_name): parser = super(SetService, self).get_parser(prog_name) diff --git a/openstackclient/compute/v2/usage.py b/openstackclient/compute/v2/usage.py index 2f35b01b..89601ae3 100644 --- a/openstackclient/compute/v2/usage.py +++ b/openstackclient/compute/v2/usage.py @@ -26,7 +26,7 @@ from openstackclient.i18n import _ class ListUsage(command.Lister): - """List resource usage per project""" + _description = _("List resource usage per project") def get_parser(self, prog_name): parser = super(ListUsage, self).get_parser(prog_name) @@ -115,7 +115,7 @@ class ListUsage(command.Lister): class ShowUsage(command.ShowOne): - """Show resource usage for a single project""" + _description = _("Show resource usage for a single project") def get_parser(self, prog_name): parser = super(ShowUsage, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/catalog.py b/openstackclient/identity/v2_0/catalog.py index 7a15cf3a..993bdd53 100644 --- a/openstackclient/identity/v2_0/catalog.py +++ b/openstackclient/identity/v2_0/catalog.py @@ -43,7 +43,7 @@ def _format_endpoints(eps=None): class ListCatalog(command.Lister): - """List services in the service catalog""" + _description = _("List services in the service catalog") def take_action(self, parsed_args): @@ -66,7 +66,7 @@ class ListCatalog(command.Lister): class ShowCatalog(command.ShowOne): - """Display service catalog details""" + _description = _("Display service catalog details") def get_parser(self, prog_name): parser = super(ShowCatalog, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/ec2creds.py b/openstackclient/identity/v2_0/ec2creds.py index 4873cd55..2572c4f0 100644 --- a/openstackclient/identity/v2_0/ec2creds.py +++ b/openstackclient/identity/v2_0/ec2creds.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateEC2Creds(command.ShowOne): - """Create EC2 credentials""" + _description = _("Create EC2 credentials") def get_parser(self, prog_name): parser = super(CreateEC2Creds, self).get_parser(prog_name) @@ -86,7 +86,7 @@ class CreateEC2Creds(command.ShowOne): class DeleteEC2Creds(command.Command): - """Delete EC2 credentials""" + _description = _("Delete EC2 credentials") def get_parser(self, prog_name): parser = super(DeleteEC2Creds, self).get_parser(prog_name) @@ -133,7 +133,7 @@ class DeleteEC2Creds(command.Command): class ListEC2Creds(command.Lister): - """List EC2 credentials""" + _description = _("List EC2 credentials") def get_parser(self, prog_name): parser = super(ListEC2Creds, self).get_parser(prog_name) @@ -168,7 +168,7 @@ class ListEC2Creds(command.Lister): class ShowEC2Creds(command.ShowOne): - """Display EC2 credentials details""" + _description = _("Display EC2 credentials details") def get_parser(self, prog_name): parser = super(ShowEC2Creds, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/endpoint.py b/openstackclient/identity/v2_0/endpoint.py index 5a3b3186..7e0751a7 100644 --- a/openstackclient/identity/v2_0/endpoint.py +++ b/openstackclient/identity/v2_0/endpoint.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateEndpoint(command.ShowOne): - """Create new endpoint""" + _description = _("Create new endpoint") def get_parser(self, prog_name): parser = super(CreateEndpoint, self).get_parser(prog_name) @@ -80,7 +80,7 @@ class CreateEndpoint(command.ShowOne): class DeleteEndpoint(command.Command): - """Delete endpoint(s)""" + _description = _("Delete endpoint(s)") def get_parser(self, prog_name): parser = super(DeleteEndpoint, self).get_parser(prog_name) @@ -113,7 +113,7 @@ class DeleteEndpoint(command.Command): class ListEndpoint(command.Lister): - """List endpoints""" + _description = _("List endpoints") def get_parser(self, prog_name): parser = super(ListEndpoint, self).get_parser(prog_name) @@ -146,7 +146,7 @@ class ListEndpoint(command.Lister): class ShowEndpoint(command.ShowOne): - """Display endpoint details""" + _description = _("Display endpoint details") def get_parser(self, prog_name): parser = super(ShowEndpoint, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/project.py b/openstackclient/identity/v2_0/project.py index fc5c9201..8526d6bd 100644 --- a/openstackclient/identity/v2_0/project.py +++ b/openstackclient/identity/v2_0/project.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateProject(command.ShowOne): - """Create new project""" + _description = _("Create new project") def get_parser(self, prog_name): parser = super(CreateProject, self).get_parser(prog_name) @@ -102,7 +102,7 @@ class CreateProject(command.ShowOne): class DeleteProject(command.Command): - """Delete project(s)""" + _description = _("Delete project(s)") def get_parser(self, prog_name): parser = super(DeleteProject, self).get_parser(prog_name) @@ -126,7 +126,7 @@ class DeleteProject(command.Command): class ListProject(command.Lister): - """List projects""" + _description = _("List projects") def get_parser(self, prog_name): parser = super(ListProject, self).get_parser(prog_name) @@ -152,7 +152,7 @@ class ListProject(command.Lister): class SetProject(command.Command): - """Set project properties""" + _description = _("Set project properties") def get_parser(self, prog_name): parser = super(SetProject, self).get_parser(prog_name) @@ -221,7 +221,7 @@ class SetProject(command.Command): class ShowProject(command.ShowOne): - """Display project details""" + _description = _("Display project details") def get_parser(self, prog_name): parser = super(ShowProject, self).get_parser(prog_name) @@ -279,7 +279,7 @@ class ShowProject(command.ShowOne): class UnsetProject(command.Command): - """Unset project properties""" + _description = _("Unset project properties") def get_parser(self, prog_name): parser = super(UnsetProject, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/role.py b/openstackclient/identity/v2_0/role.py index b4b67bad..0a28a70a 100644 --- a/openstackclient/identity/v2_0/role.py +++ b/openstackclient/identity/v2_0/role.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class AddRole(command.ShowOne): - """Add role to project:user""" + _description = _("Add role to project:user") def get_parser(self, prog_name): parser = super(AddRole, self).get_parser(prog_name) @@ -73,7 +73,7 @@ class AddRole(command.ShowOne): class CreateRole(command.ShowOne): - """Create new role""" + _description = _("Create new role") def get_parser(self, prog_name): parser = super(CreateRole, self).get_parser(prog_name) @@ -109,7 +109,7 @@ class CreateRole(command.ShowOne): class DeleteRole(command.Command): - """Delete role(s)""" + _description = _("Delete role(s)") def get_parser(self, prog_name): parser = super(DeleteRole, self).get_parser(prog_name) @@ -133,7 +133,7 @@ class DeleteRole(command.Command): class ListRole(command.Lister): - """List roles""" + _description = _("List roles") def get_parser(self, prog_name): parser = super(ListRole, self).get_parser(prog_name) @@ -223,7 +223,7 @@ class ListRole(command.Lister): class ListUserRole(command.Lister): - """List user-role assignments""" + _description = _("List user-role assignments") def get_parser(self, prog_name): parser = super(ListUserRole, self).get_parser(prog_name) @@ -293,7 +293,7 @@ class ListUserRole(command.Lister): class RemoveRole(command.Command): - """Remove role from project : user""" + _description = _("Remove role from project : user") def get_parser(self, prog_name): parser = super(RemoveRole, self).get_parser(prog_name) @@ -331,7 +331,7 @@ class RemoveRole(command.Command): class ShowRole(command.ShowOne): - """Display role details""" + _description = _("Display role details") def get_parser(self, prog_name): parser = super(ShowRole, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/role_assignment.py b/openstackclient/identity/v2_0/role_assignment.py index 44f55c3b..8236bbfc 100644 --- a/openstackclient/identity/v2_0/role_assignment.py +++ b/openstackclient/identity/v2_0/role_assignment.py @@ -21,7 +21,7 @@ from openstackclient.i18n import _ # noqa class ListRoleAssignment(command.Lister): - """List role assignments""" + _description = _("List role assignments") def get_parser(self, prog_name): parser = super(ListRoleAssignment, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/service.py b/openstackclient/identity/v2_0/service.py index e318643c..f70f0fa9 100644 --- a/openstackclient/identity/v2_0/service.py +++ b/openstackclient/identity/v2_0/service.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class CreateService(command.ShowOne): - """Create new service""" + _description = _("Create new service") def get_parser(self, prog_name): parser = super(CreateService, self).get_parser(prog_name) @@ -91,7 +91,7 @@ class CreateService(command.ShowOne): class DeleteService(command.Command): - """Delete service(s)""" + _description = _("Delete service(s)") def get_parser(self, prog_name): parser = super(DeleteService, self).get_parser(prog_name) @@ -125,7 +125,7 @@ class DeleteService(command.Command): class ListService(command.Lister): - """List services""" + _description = _("List services") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class ListService(command.Lister): class ShowService(command.ShowOne): - """Display service details""" + _description = _("Display service details") def get_parser(self, prog_name): parser = super(ShowService, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/token.py b/openstackclient/identity/v2_0/token.py index 4b7e988b..3b08b475 100644 --- a/openstackclient/identity/v2_0/token.py +++ b/openstackclient/identity/v2_0/token.py @@ -23,7 +23,7 @@ from openstackclient.i18n import _ class IssueToken(command.ShowOne): - """Issue new token""" + _description = _("Issue new token") # scoped token is optional required_scope = False @@ -53,7 +53,7 @@ class IssueToken(command.ShowOne): class RevokeToken(command.Command): - """Revoke existing token""" + _description = _("Revoke existing token") def get_parser(self, prog_name): parser = super(RevokeToken, self).get_parser(prog_name) diff --git a/openstackclient/identity/v2_0/user.py b/openstackclient/identity/v2_0/user.py index bc091ce7..ddd5b981 100644 --- a/openstackclient/identity/v2_0/user.py +++ b/openstackclient/identity/v2_0/user.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreateUser(command.ShowOne): - """Create new user""" + _description = _("Create new user") def get_parser(self, prog_name): parser = super(CreateUser, self).get_parser(prog_name) @@ -130,7 +130,7 @@ class CreateUser(command.ShowOne): class DeleteUser(command.Command): - """Delete user(s)""" + _description = _("Delete user(s)") def get_parser(self, prog_name): parser = super(DeleteUser, self).get_parser(prog_name) @@ -154,7 +154,7 @@ class DeleteUser(command.Command): class ListUser(command.Lister): - """List users""" + _description = _("List users") def get_parser(self, prog_name): parser = super(ListUser, self).get_parser(prog_name) @@ -242,7 +242,7 @@ class ListUser(command.Lister): class SetUser(command.Command): - """Set user properties""" + _description = _("Set user properties") def get_parser(self, prog_name): parser = super(SetUser, self).get_parser(prog_name) @@ -336,7 +336,7 @@ class SetUser(command.Command): class ShowUser(command.ShowOne): - """Display user details""" + _description = _("Display user details") def get_parser(self, prog_name): parser = super(ShowUser, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/catalog.py b/openstackclient/identity/v3/catalog.py index a62d0a93..28f4fada 100644 --- a/openstackclient/identity/v3/catalog.py +++ b/openstackclient/identity/v3/catalog.py @@ -38,7 +38,7 @@ def _format_endpoints(eps=None): class ListCatalog(command.Lister): - """List services in the service catalog""" + _description = _("List services in the service catalog") def take_action(self, parsed_args): @@ -61,7 +61,7 @@ class ListCatalog(command.Lister): class ShowCatalog(command.ShowOne): - """Display service catalog details""" + _description = _("Display service catalog details") def get_parser(self, prog_name): parser = super(ShowCatalog, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/consumer.py b/openstackclient/identity/v3/consumer.py index b41a37ca..bcb29db4 100644 --- a/openstackclient/identity/v3/consumer.py +++ b/openstackclient/identity/v3/consumer.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreateConsumer(command.ShowOne): - """Create new consumer""" + _description = _("Create new consumer") def get_parser(self, prog_name): parser = super(CreateConsumer, self).get_parser(prog_name) @@ -50,7 +50,7 @@ class CreateConsumer(command.ShowOne): class DeleteConsumer(command.Command): - """Delete consumer(s)""" + _description = _("Delete consumer(s)") def get_parser(self, prog_name): parser = super(DeleteConsumer, self).get_parser(prog_name) @@ -84,7 +84,7 @@ class DeleteConsumer(command.Command): class ListConsumer(command.Lister): - """List consumers""" + _description = _("List consumers") def take_action(self, parsed_args): columns = ('ID', 'Description') @@ -97,7 +97,7 @@ class ListConsumer(command.Lister): class SetConsumer(command.Command): - """Set consumer properties""" + _description = _("Set consumer properties") def get_parser(self, prog_name): parser = super(SetConsumer, self).get_parser(prog_name) @@ -126,7 +126,7 @@ class SetConsumer(command.Command): class ShowConsumer(command.ShowOne): - """Display consumer details""" + _description = _("Display consumer details") def get_parser(self, prog_name): parser = super(ShowConsumer, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/credential.py b/openstackclient/identity/v3/credential.py index 0ef94cf4..68287f2a 100644 --- a/openstackclient/identity/v3/credential.py +++ b/openstackclient/identity/v3/credential.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreateCredential(command.ShowOne): - """Create new credential""" + _description = _("Create new credential") def get_parser(self, prog_name): parser = super(CreateCredential, self).get_parser(prog_name) @@ -78,7 +78,7 @@ class CreateCredential(command.ShowOne): class DeleteCredential(command.Command): - """Delete credential(s)""" + _description = _("Delete credential(s)") def get_parser(self, prog_name): parser = super(DeleteCredential, self).get_parser(prog_name) @@ -110,7 +110,7 @@ class DeleteCredential(command.Command): class ListCredential(command.Lister): - """List credentials""" + _description = _("List credentials") def take_action(self, parsed_args): columns = ('ID', 'Type', 'User ID', 'Blob', 'Project ID') @@ -124,7 +124,7 @@ class ListCredential(command.Lister): class SetCredential(command.Command): - """Set credential properties""" + _description = _("Set credential properties") def get_parser(self, prog_name): parser = super(SetCredential, self).get_parser(prog_name) @@ -180,7 +180,7 @@ class SetCredential(command.Command): class ShowCredential(command.ShowOne): - """Display credential details""" + _description = _("Display credential details") def get_parser(self, prog_name): parser = super(ShowCredential, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/domain.py b/openstackclient/identity/v3/domain.py index 76e47d32..59ab0f07 100644 --- a/openstackclient/identity/v3/domain.py +++ b/openstackclient/identity/v3/domain.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class CreateDomain(command.ShowOne): - """Create new domain""" + _description = _("Create new domain") def get_parser(self, prog_name): parser = super(CreateDomain, self).get_parser(prog_name) @@ -89,7 +89,7 @@ class CreateDomain(command.ShowOne): class DeleteDomain(command.Command): - """Delete domain(s)""" + _description = _("Delete domain(s)") def get_parser(self, prog_name): parser = super(DeleteDomain, self).get_parser(prog_name) @@ -122,7 +122,7 @@ class DeleteDomain(command.Command): class ListDomain(command.Lister): - """List domains""" + _description = _("List domains") def take_action(self, parsed_args): columns = ('ID', 'Name', 'Enabled', 'Description') @@ -135,7 +135,7 @@ class ListDomain(command.Lister): class SetDomain(command.Command): - """Set domain properties""" + _description = _("Set domain properties") def get_parser(self, prog_name): parser = super(SetDomain, self).get_parser(prog_name) @@ -186,7 +186,7 @@ class SetDomain(command.Command): class ShowDomain(command.ShowOne): - """Display domain details""" + _description = _("Display domain details") def get_parser(self, prog_name): parser = super(ShowDomain, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/ec2creds.py b/openstackclient/identity/v3/ec2creds.py index 7ad01719..9854efda 100644 --- a/openstackclient/identity/v3/ec2creds.py +++ b/openstackclient/identity/v3/ec2creds.py @@ -55,7 +55,7 @@ def _determine_ec2_user(parsed_args, client_manager): class CreateEC2Creds(command.ShowOne): - """Create EC2 credentials""" + _description = _("Create EC2 credentials") def get_parser(self, prog_name): parser = super(CreateEC2Creds, self).get_parser(prog_name) @@ -112,7 +112,7 @@ class CreateEC2Creds(command.ShowOne): class DeleteEC2Creds(command.Command): - """Delete EC2 credentials""" + _description = _("Delete EC2 credentials") def get_parser(self, prog_name): parser = super(DeleteEC2Creds, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class DeleteEC2Creds(command.Command): class ListEC2Creds(command.Lister): - """List EC2 credentials""" + _description = _("List EC2 credentials") def get_parser(self, prog_name): parser = super(ListEC2Creds, self).get_parser(prog_name) @@ -179,7 +179,7 @@ class ListEC2Creds(command.Lister): class ShowEC2Creds(command.ShowOne): - """Display EC2 credentials details""" + _description = _("Display EC2 credentials details") def get_parser(self, prog_name): parser = super(ShowEC2Creds, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/endpoint.py b/openstackclient/identity/v3/endpoint.py index 73b37a43..39fd49ef 100644 --- a/openstackclient/identity/v3/endpoint.py +++ b/openstackclient/identity/v3/endpoint.py @@ -37,7 +37,7 @@ def get_service_name(service): class CreateEndpoint(command.ShowOne): - """Create new endpoint""" + _description = _("Create new endpoint") def get_parser(self, prog_name): parser = super(CreateEndpoint, self).get_parser(prog_name) @@ -99,7 +99,7 @@ class CreateEndpoint(command.ShowOne): class DeleteEndpoint(command.Command): - """Delete endpoint(s)""" + _description = _("Delete endpoint(s)") def get_parser(self, prog_name): parser = super(DeleteEndpoint, self).get_parser(prog_name) @@ -133,7 +133,7 @@ class DeleteEndpoint(command.Command): class ListEndpoint(command.Lister): - """List endpoints""" + _description = _("List endpoints") def get_parser(self, prog_name): parser = super(ListEndpoint, self).get_parser(prog_name) @@ -181,7 +181,7 @@ class ListEndpoint(command.Lister): class SetEndpoint(command.Command): - """Set endpoint properties""" + _description = _("Set endpoint properties") def get_parser(self, prog_name): parser = super(SetEndpoint, self).get_parser(prog_name) @@ -252,7 +252,7 @@ class SetEndpoint(command.Command): class ShowEndpoint(command.ShowOne): - """Display endpoint details""" + _description = _("Display endpoint details") def get_parser(self, prog_name): parser = super(ShowEndpoint, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/federation_protocol.py b/openstackclient/identity/v3/federation_protocol.py index 3fde9027..0752e8f6 100644 --- a/openstackclient/identity/v3/federation_protocol.py +++ b/openstackclient/identity/v3/federation_protocol.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) class CreateProtocol(command.ShowOne): - """Create new federation protocol""" + _description = _("Create new federation protocol") def get_parser(self, prog_name): parser = super(CreateProtocol, self).get_parser(prog_name) @@ -72,7 +72,7 @@ class CreateProtocol(command.ShowOne): class DeleteProtocol(command.Command): - """Delete federation protocol(s)""" + _description = _("Delete federation protocol(s)") def get_parser(self, prog_name): parser = super(DeleteProtocol, self).get_parser(prog_name) @@ -113,7 +113,7 @@ class DeleteProtocol(command.Command): class ListProtocols(command.Lister): - """List federation protocols""" + _description = _("List federation protocols") def get_parser(self, prog_name): parser = super(ListProtocols, self).get_parser(prog_name) @@ -139,7 +139,7 @@ class ListProtocols(command.Lister): class SetProtocol(command.Command): - """Set federation protocol properties""" + _description = _("Set federation protocol properties") def get_parser(self, prog_name): parser = super(SetProtocol, self).get_parser(prog_name) @@ -179,7 +179,7 @@ class SetProtocol(command.Command): class ShowProtocol(command.ShowOne): - """Display federation protocol details""" + _description = _("Display federation protocol details") def get_parser(self, prog_name): parser = super(ShowProtocol, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/group.py b/openstackclient/identity/v3/group.py index f780810a..df684c12 100644 --- a/openstackclient/identity/v3/group.py +++ b/openstackclient/identity/v3/group.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class AddUserToGroup(command.Command): - """Add user to group""" + _description = _("Add user to group") def get_parser(self, prog_name): parser = super(AddUserToGroup, self).get_parser(prog_name) @@ -76,7 +76,7 @@ class AddUserToGroup(command.Command): class CheckUserInGroup(command.Command): - """Check user membership in group""" + _description = _("Check user membership in group") def get_parser(self, prog_name): parser = super(CheckUserInGroup, self).get_parser(prog_name) @@ -121,7 +121,7 @@ class CheckUserInGroup(command.Command): class CreateGroup(command.ShowOne): - """Create new group""" + _description = _("Create new group") def get_parser(self, prog_name): parser = super(CreateGroup, self).get_parser(prog_name) @@ -174,7 +174,7 @@ class CreateGroup(command.ShowOne): class DeleteGroup(command.Command): - """Delete group(s)""" + _description = _("Delete group(s)") def get_parser(self, prog_name): parser = super(DeleteGroup, self).get_parser(prog_name) @@ -202,7 +202,7 @@ class DeleteGroup(command.Command): class ListGroup(command.Lister): - """List groups""" + _description = _("List groups") def get_parser(self, prog_name): parser = super(ListGroup, self).get_parser(prog_name) @@ -262,7 +262,7 @@ class ListGroup(command.Lister): class RemoveUserFromGroup(command.Command): - """Remove user from group""" + _description = _("Remove user from group") def get_parser(self, prog_name): parser = super(RemoveUserFromGroup, self).get_parser(prog_name) @@ -307,7 +307,7 @@ class RemoveUserFromGroup(command.Command): class SetGroup(command.Command): - """Set group properties""" + _description = _("Set group properties") def get_parser(self, prog_name): parser = super(SetGroup, self).get_parser(prog_name) @@ -347,7 +347,7 @@ class SetGroup(command.Command): class ShowGroup(command.ShowOne): - """Display group details""" + _description = _("Display group details") def get_parser(self, prog_name): parser = super(ShowGroup, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/identity_provider.py b/openstackclient/identity/v3/identity_provider.py index b6b03188..163dcb5f 100644 --- a/openstackclient/identity/v3/identity_provider.py +++ b/openstackclient/identity/v3/identity_provider.py @@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__) class CreateIdentityProvider(command.ShowOne): - """Create new identity provider""" + _description = _("Create new identity provider") def get_parser(self, prog_name): parser = super(CreateIdentityProvider, self).get_parser(prog_name) @@ -94,7 +94,7 @@ class CreateIdentityProvider(command.ShowOne): class DeleteIdentityProvider(command.Command): - """Delete identity provider(s)""" + _description = _("Delete identity provider(s)") def get_parser(self, prog_name): parser = super(DeleteIdentityProvider, self).get_parser(prog_name) @@ -126,7 +126,7 @@ class DeleteIdentityProvider(command.Command): class ListIdentityProvider(command.Lister): - """List identity providers""" + _description = _("List identity providers") def take_action(self, parsed_args): columns = ('ID', 'Enabled', 'Description') @@ -140,7 +140,7 @@ class ListIdentityProvider(command.Lister): class SetIdentityProvider(command.Command): - """Set identity provider properties""" + _description = _("Set identity provider properties") def get_parser(self, prog_name): parser = super(SetIdentityProvider, self).get_parser(prog_name) @@ -211,7 +211,7 @@ class SetIdentityProvider(command.Command): class ShowIdentityProvider(command.ShowOne): - """Display identity provider details""" + _description = _("Display identity provider details") def get_parser(self, prog_name): parser = super(ShowIdentityProvider, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/mapping.py b/openstackclient/identity/v3/mapping.py index 09181a0b..dbb1b068 100644 --- a/openstackclient/identity/v3/mapping.py +++ b/openstackclient/identity/v3/mapping.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class _RulesReader(object): - """Helper class capable of reading rules from files""" + _description = _("Helper class capable of reading rules from files") def _read_rules(self, path): """Read and parse rules from path @@ -82,7 +82,7 @@ class _RulesReader(object): class CreateMapping(command.ShowOne, _RulesReader): - """Create new mapping""" + _description = _("Create new mapping") def get_parser(self, prog_name): parser = super(CreateMapping, self).get_parser(prog_name) @@ -111,7 +111,7 @@ class CreateMapping(command.ShowOne, _RulesReader): class DeleteMapping(command.Command): - """Delete mapping(s)""" + _description = _("Delete mapping(s)") def get_parser(self, prog_name): parser = super(DeleteMapping, self).get_parser(prog_name) @@ -143,7 +143,7 @@ class DeleteMapping(command.Command): class ListMapping(command.Lister): - """List mappings""" + _description = _("List mappings") def take_action(self, parsed_args): # NOTE(marek-denis): Since rules can be long and tedious I have decided @@ -157,7 +157,7 @@ class ListMapping(command.Lister): class SetMapping(command.Command, _RulesReader): - """Set mapping properties""" + _description = _("Set mapping properties") def get_parser(self, prog_name): parser = super(SetMapping, self).get_parser(prog_name) @@ -187,7 +187,7 @@ class SetMapping(command.Command, _RulesReader): class ShowMapping(command.ShowOne): - """Display mapping details""" + _description = _("Display mapping details") def get_parser(self, prog_name): parser = super(ShowMapping, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/policy.py b/openstackclient/identity/v3/policy.py index 596eae01..c511652e 100644 --- a/openstackclient/identity/v3/policy.py +++ b/openstackclient/identity/v3/policy.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreatePolicy(command.ShowOne): - """Create new policy""" + _description = _("Create new policy") def get_parser(self, prog_name): parser = super(CreatePolicy, self).get_parser(prog_name) @@ -61,7 +61,7 @@ class CreatePolicy(command.ShowOne): class DeletePolicy(command.Command): - """Delete policy(s)""" + _description = _("Delete policy(s)") def get_parser(self, prog_name): parser = super(DeletePolicy, self).get_parser(prog_name) @@ -93,7 +93,7 @@ class DeletePolicy(command.Command): class ListPolicy(command.Lister): - """List policies""" + _description = _("List policies") def get_parser(self, prog_name): parser = super(ListPolicy, self).get_parser(prog_name) @@ -121,7 +121,7 @@ class ListPolicy(command.Lister): class SetPolicy(command.Command): - """Set policy properties""" + _description = _("Set policy properties") def get_parser(self, prog_name): parser = super(SetPolicy, self).get_parser(prog_name) @@ -159,7 +159,7 @@ class SetPolicy(command.Command): class ShowPolicy(command.ShowOne): - """Display policy details""" + _description = _("Display policy details") def get_parser(self, prog_name): parser = super(ShowPolicy, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/project.py b/openstackclient/identity/v3/project.py index 56c4fbc8..a6348659 100644 --- a/openstackclient/identity/v3/project.py +++ b/openstackclient/identity/v3/project.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class CreateProject(command.ShowOne): - """Create new project""" + _description = _("Create new project") def get_parser(self, prog_name): parser = super(CreateProject, self).get_parser(prog_name) @@ -125,7 +125,7 @@ class CreateProject(command.ShowOne): class DeleteProject(command.Command): - """Delete project(s)""" + _description = _("Delete project(s)") def get_parser(self, prog_name): parser = super(DeleteProject, self).get_parser(prog_name) @@ -160,7 +160,7 @@ class DeleteProject(command.Command): class ListProject(command.Lister): - """List projects""" + _description = _("List projects") def get_parser(self, prog_name): parser = super(ListProject, self).get_parser(prog_name) @@ -216,7 +216,7 @@ class ListProject(command.Lister): class SetProject(command.Command): - """Set project properties""" + _description = _("Set project properties") def get_parser(self, prog_name): parser = super(SetProject, self).get_parser(prog_name) @@ -283,7 +283,7 @@ class SetProject(command.Command): class ShowProject(command.ShowOne): - """Display project details""" + _description = _("Display project details") def get_parser(self, prog_name): parser = super(ShowProject, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/region.py b/openstackclient/identity/v3/region.py index b7c51f93..d3e712e3 100644 --- a/openstackclient/identity/v3/region.py +++ b/openstackclient/identity/v3/region.py @@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__) class CreateRegion(command.ShowOne): - """Create new region""" + _description = _("Create new region") def get_parser(self, prog_name): parser = super(CreateRegion, self).get_parser(prog_name) @@ -66,7 +66,7 @@ class CreateRegion(command.ShowOne): class DeleteRegion(command.Command): - """Delete region(s)""" + _description = _("Delete region(s)") def get_parser(self, prog_name): parser = super(DeleteRegion, self).get_parser(prog_name) @@ -98,7 +98,7 @@ class DeleteRegion(command.Command): class ListRegion(command.Lister): - """List regions""" + _description = _("List regions") def get_parser(self, prog_name): parser = super(ListRegion, self).get_parser(prog_name) @@ -128,7 +128,7 @@ class ListRegion(command.Lister): class SetRegion(command.Command): - """Set region properties""" + _description = _("Set region properties") def get_parser(self, prog_name): parser = super(SetRegion, self).get_parser(prog_name) @@ -162,7 +162,7 @@ class SetRegion(command.Command): class ShowRegion(command.ShowOne): - """Display region details""" + _description = _("Display region details") def get_parser(self, prog_name): parser = super(ShowRegion, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/role.py b/openstackclient/identity/v3/role.py index 8b911746..c9d0fbf3 100644 --- a/openstackclient/identity/v3/role.py +++ b/openstackclient/identity/v3/role.py @@ -109,7 +109,8 @@ def _process_identity_and_resource_options(parsed_args, class AddRole(command.Command): - """Adds a role assignment to a user or group on a domain or project""" + _description = _("Adds a role assignment to a user or group on a domain " + "or project") def get_parser(self, prog_name): parser = super(AddRole, self).get_parser(prog_name) @@ -151,7 +152,7 @@ class AddRole(command.Command): class CreateRole(command.ShowOne): - """Create new role""" + _description = _("Create new role") def get_parser(self, prog_name): parser = super(CreateRole, self).get_parser(prog_name) @@ -198,7 +199,7 @@ class CreateRole(command.ShowOne): class DeleteRole(command.Command): - """Delete role(s)""" + _description = _("Delete role(s)") def get_parser(self, prog_name): parser = super(DeleteRole, self).get_parser(prog_name) @@ -233,7 +234,7 @@ class DeleteRole(command.Command): class ListRole(command.Lister): - """List roles""" + _description = _("List roles") def get_parser(self, prog_name): parser = super(ListRole, self).get_parser(prog_name) @@ -371,7 +372,8 @@ class ListRole(command.Lister): class RemoveRole(command.Command): - """Removes a role assignment from domain/project : user/group""" + _description = _("Removes a role assignment from domain/project : " + "user/group") def get_parser(self, prog_name): parser = super(RemoveRole, self).get_parser(prog_name) @@ -415,7 +417,7 @@ class RemoveRole(command.Command): class SetRole(command.Command): - """Set role properties""" + _description = _("Set role properties") def get_parser(self, prog_name): parser = super(SetRole, self).get_parser(prog_name) @@ -452,7 +454,7 @@ class SetRole(command.Command): class ShowRole(command.ShowOne): - """Display role details""" + _description = _("Display role details") def get_parser(self, prog_name): parser = super(ShowRole, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/role_assignment.py b/openstackclient/identity/v3/role_assignment.py index d25cc6ce..9da050de 100644 --- a/openstackclient/identity/v3/role_assignment.py +++ b/openstackclient/identity/v3/role_assignment.py @@ -11,7 +11,7 @@ # under the License. # -"""Identity v3 Assignment action implementations """ +"""Identity v3 Assignment action implementations""" from osc_lib.command import command from osc_lib import utils @@ -21,7 +21,7 @@ from openstackclient.identity import common class ListRoleAssignment(command.Lister): - """List role assignments""" + _description = _("List role assignments") def get_parser(self, prog_name): parser = super(ListRoleAssignment, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/service.py b/openstackclient/identity/v3/service.py index 97e64dc6..7daf8919 100644 --- a/openstackclient/identity/v3/service.py +++ b/openstackclient/identity/v3/service.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateService(command.ShowOne): - """Create new service""" + _description = _("Create new service") def get_parser(self, prog_name): parser = super(CreateService, self).get_parser(prog_name) @@ -81,7 +81,7 @@ class CreateService(command.ShowOne): class DeleteService(command.Command): - """Delete service(s)""" + _description = _("Delete service(s)") def get_parser(self, prog_name): parser = super(DeleteService, self).get_parser(prog_name) @@ -114,7 +114,7 @@ class DeleteService(command.Command): class ListService(command.Lister): - """List services""" + _description = _("List services") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) @@ -140,7 +140,7 @@ class ListService(command.Lister): class SetService(command.Command): - """Set service properties""" + _description = _("Set service properties") def get_parser(self, prog_name): parser = super(SetService, self).get_parser(prog_name) @@ -201,7 +201,7 @@ class SetService(command.Command): class ShowService(command.ShowOne): - """Display service details""" + _description = _("Display service details") def get_parser(self, prog_name): parser = super(ShowService, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/service_provider.py b/openstackclient/identity/v3/service_provider.py index 8548ae1f..459dc00b 100644 --- a/openstackclient/identity/v3/service_provider.py +++ b/openstackclient/identity/v3/service_provider.py @@ -27,7 +27,7 @@ LOG = logging.getLogger(__name__) class CreateServiceProvider(command.ShowOne): - """Create new service provider""" + _description = _("Create new service provider") def get_parser(self, prog_name): parser = super(CreateServiceProvider, self).get_parser(prog_name) @@ -87,7 +87,7 @@ class CreateServiceProvider(command.ShowOne): class DeleteServiceProvider(command.Command): - """Delete service provider(s)""" + _description = _("Delete service provider(s)") def get_parser(self, prog_name): parser = super(DeleteServiceProvider, self).get_parser(prog_name) @@ -119,7 +119,7 @@ class DeleteServiceProvider(command.Command): class ListServiceProvider(command.Lister): - """List service providers""" + _description = _("List service providers") def take_action(self, parsed_args): service_client = self.app.client_manager.identity @@ -134,7 +134,7 @@ class ListServiceProvider(command.Lister): class SetServiceProvider(command.Command): - """Set service provider properties""" + _description = _("Set service provider properties") def get_parser(self, prog_name): parser = super(SetServiceProvider, self).get_parser(prog_name) @@ -192,7 +192,7 @@ class SetServiceProvider(command.Command): class ShowServiceProvider(command.ShowOne): - """Display service provider details""" + _description = _("Display service provider details") def get_parser(self, prog_name): parser = super(ShowServiceProvider, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/token.py b/openstackclient/identity/v3/token.py index 2cd304e6..7a66f23b 100644 --- a/openstackclient/identity/v3/token.py +++ b/openstackclient/identity/v3/token.py @@ -25,7 +25,7 @@ from openstackclient.identity import common class AuthorizeRequestToken(command.ShowOne): - """Authorize a request token""" + _description = _("Authorize a request token") def get_parser(self, prog_name): parser = super(AuthorizeRequestToken, self).get_parser(prog_name) @@ -66,7 +66,7 @@ class AuthorizeRequestToken(command.ShowOne): class CreateAccessToken(command.ShowOne): - """Create an access token""" + _description = _("Create an access token") def get_parser(self, prog_name): parser = super(CreateAccessToken, self).get_parser(prog_name) @@ -112,7 +112,7 @@ class CreateAccessToken(command.ShowOne): class CreateRequestToken(command.ShowOne): - """Create a request token""" + _description = _("Create a request token") def get_parser(self, prog_name): parser = super(CreateRequestToken, self).get_parser(prog_name) @@ -164,7 +164,7 @@ class CreateRequestToken(command.ShowOne): class IssueToken(command.ShowOne): - """Issue new token""" + _description = _("Issue new token") # scoped token is optional required_scope = False @@ -196,7 +196,7 @@ class IssueToken(command.ShowOne): class RevokeToken(command.Command): - """Revoke existing token""" + _description = _("Revoke existing token") def get_parser(self, prog_name): parser = super(RevokeToken, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/trust.py b/openstackclient/identity/v3/trust.py index bbc86adb..62d72ea1 100644 --- a/openstackclient/identity/v3/trust.py +++ b/openstackclient/identity/v3/trust.py @@ -24,7 +24,7 @@ from openstackclient.identity import common class CreateTrust(command.ShowOne): - """Create new trust""" + _description = _("Create new trust") def get_parser(self, prog_name): parser = super(CreateTrust, self).get_parser(prog_name) @@ -131,7 +131,7 @@ class CreateTrust(command.ShowOne): class DeleteTrust(command.Command): - """Delete trust(s)""" + _description = _("Delete trust(s)") def get_parser(self, prog_name): parser = super(DeleteTrust, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class DeleteTrust(command.Command): class ListTrust(command.Lister): - """List trusts""" + _description = _("List trusts") def take_action(self, parsed_args): columns = ('ID', 'Expires At', 'Impersonation', 'Project ID', @@ -165,7 +165,7 @@ class ListTrust(command.Lister): class ShowTrust(command.ShowOne): - """Display trust details""" + _description = _("Display trust details") def get_parser(self, prog_name): parser = super(ShowTrust, self).get_parser(prog_name) diff --git a/openstackclient/identity/v3/unscoped_saml.py b/openstackclient/identity/v3/unscoped_saml.py index f116174b..5940534a 100644 --- a/openstackclient/identity/v3/unscoped_saml.py +++ b/openstackclient/identity/v3/unscoped_saml.py @@ -44,7 +44,7 @@ def auth_with_unscoped_saml(func): class ListAccessibleDomains(command.Lister): - """List accessible domains""" + _description = _("List accessible domains") @auth_with_unscoped_saml def take_action(self, parsed_args): @@ -59,7 +59,7 @@ class ListAccessibleDomains(command.Lister): class ListAccessibleProjects(command.Lister): - """List accessible projects""" + _description = _("List accessible projects") @auth_with_unscoped_saml def take_action(self, parsed_args): diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py index 1e086fb6..796cf28c 100644 --- a/openstackclient/identity/v3/user.py +++ b/openstackclient/identity/v3/user.py @@ -31,7 +31,7 @@ LOG = logging.getLogger(__name__) class CreateUser(command.ShowOne): - """Create new user""" + _description = _("Create new user") def get_parser(self, prog_name): parser = super(CreateUser, self).get_parser(prog_name) @@ -138,7 +138,7 @@ class CreateUser(command.ShowOne): class DeleteUser(command.Command): - """Delete user(s)""" + _description = _("Delete user(s)") def get_parser(self, prog_name): parser = super(DeleteUser, self).get_parser(prog_name) @@ -173,7 +173,7 @@ class DeleteUser(command.Command): class ListUser(command.Lister): - """List users""" + _description = _("List users") def get_parser(self, prog_name): parser = super(ListUser, self).get_parser(prog_name) @@ -273,7 +273,7 @@ class ListUser(command.Lister): class SetUser(command.Command): - """Set user properties""" + _description = _("Set user properties") def get_parser(self, prog_name): parser = super(SetUser, self).get_parser(prog_name) @@ -365,7 +365,7 @@ class SetUser(command.Command): class SetPasswordUser(command.Command): - """Change current user password""" + _description = _("Change current user password") required_scope = False @@ -424,7 +424,7 @@ class SetPasswordUser(command.Command): class ShowUser(command.ShowOne): - """Display user details""" + _description = _("Display user details") def get_parser(self, prog_name): parser = super(ShowUser, self).get_parser(prog_name) diff --git a/openstackclient/image/client.py b/openstackclient/image/client.py index 1be6c765..b67c291f 100644 --- a/openstackclient/image/client.py +++ b/openstackclient/image/client.py @@ -22,7 +22,7 @@ from openstackclient.i18n import _ LOG = logging.getLogger(__name__) -DEFAULT_API_VERSION = '1' +DEFAULT_API_VERSION = '2' API_VERSION_OPTION = 'os_image_api_version' API_NAME = "image" API_VERSIONS = { diff --git a/openstackclient/image/v1/image.py b/openstackclient/image/v1/image.py index 5f669c64..eb79cd2f 100644 --- a/openstackclient/image/v1/image.py +++ b/openstackclient/image/v1/image.py @@ -38,6 +38,8 @@ from openstackclient.i18n import _ DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' +DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx", + "vdi", "iso"] LOG = logging.getLogger(__name__) @@ -59,7 +61,7 @@ def _format_visibility(data): class CreateImage(command.ShowOne): - """Create/upload an image""" + _description = _("Create/upload an image") def get_parser(self, prog_name): parser = super(CreateImage, self).get_parser(prog_name) @@ -89,8 +91,9 @@ class CreateImage(command.ShowOne): "--disk-format", default=DEFAULT_DISK_FORMAT, metavar="<disk-format>", - help=_("Image disk format " - "(default: %s)") % DEFAULT_DISK_FORMAT, + choices=DISK_CHOICES, + help=_("Image disk format. The supported options are: %s. " + "The default format is: raw") % ', '.join(DISK_CHOICES) ) parser.add_argument( "--size", @@ -277,7 +280,7 @@ class CreateImage(command.ShowOne): class DeleteImage(command.Command): - """Delete image(s)""" + _description = _("Delete image(s)") def get_parser(self, prog_name): parser = super(DeleteImage, self).get_parser(prog_name) @@ -300,7 +303,7 @@ class DeleteImage(command.Command): class ListImage(command.Lister): - """List available images""" + _description = _("List available images") def get_parser(self, prog_name): parser = super(ListImage, self).get_parser(prog_name) @@ -350,8 +353,9 @@ class ListImage(command.Lister): parser.add_argument( '--sort', metavar="<key>[:<direction>]", + default='name:asc', help=_("Sort output by selected keys and directions(asc or desc) " - "(default: asc), multiple keys and directions can be " + "(default: name:asc), multiple keys and directions can be " "specified separated by comma"), ) return parser @@ -440,7 +444,7 @@ class ListImage(command.Lister): class SaveImage(command.Command): - """Save an image locally""" + _description = _("Save an image locally") def get_parser(self, prog_name): parser = super(SaveImage, self).get_parser(prog_name) @@ -468,7 +472,7 @@ class SaveImage(command.Command): class SetImage(command.Command): - """Set image properties""" + _description = _("Set image properties") def get_parser(self, prog_name): parser = super(SetImage, self).get_parser(prog_name) @@ -502,14 +506,12 @@ class SetImage(command.Command): container_choices, choices=container_choices ) - disk_choices = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", - "vdi", "iso"] parser.add_argument( "--disk-format", metavar="<disk-format>", - help=_("Disk format of image. Acceptable formats: %s") % - disk_choices, - choices=disk_choices + choices=DISK_CHOICES, + help=_("Image disk format. The supported options are: %s.") % + ', '.join(DISK_CHOICES) ) parser.add_argument( "--size", @@ -702,7 +704,7 @@ class SetImage(command.Command): class ShowImage(command.ShowOne): - """Display image details""" + _description = _("Display image details") def get_parser(self, prog_name): parser = super(ShowImage, self).get_parser(prog_name) diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py index 0712e09c..054d1612 100644 --- a/openstackclient/image/v2/image.py +++ b/openstackclient/image/v2/image.py @@ -32,13 +32,15 @@ from openstackclient.identity import common DEFAULT_CONTAINER_FORMAT = 'bare' DEFAULT_DISK_FORMAT = 'raw' +DISK_CHOICES = ["ami", "ari", "aki", "vhd", "vmdk", "raw", "qcow2", "vhdx", + "vdi", "iso"] LOG = logging.getLogger(__name__) def _format_image(image): - """Format an image to make it more consistent with OSC operations. """ + """Format an image to make it more consistent with OSC operations.""" info = {} properties = {} @@ -69,7 +71,7 @@ def _format_image(image): class AddProjectToImage(command.ShowOne): - """Associate project with image""" + _description = _("Associate project with image") def get_parser(self, prog_name): parser = super(AddProjectToImage, self).get_parser(prog_name) @@ -107,7 +109,7 @@ class AddProjectToImage(command.ShowOne): class CreateImage(command.ShowOne): - """Create/upload an image""" + _description = _("Create/upload an image") deadopts = ('size', 'location', 'copy-from', 'checksum', 'store') @@ -140,9 +142,10 @@ class CreateImage(command.ShowOne): parser.add_argument( "--disk-format", default=DEFAULT_DISK_FORMAT, + choices=DISK_CHOICES, metavar="<disk-format>", - help=_("Image disk format " - "(default: %s)") % DEFAULT_DISK_FORMAT, + help=_("Image disk format. The supported options are: %s. " + "The default format is: raw") % ', '.join(DISK_CHOICES) ) parser.add_argument( "--min-disk", @@ -361,7 +364,7 @@ class CreateImage(command.ShowOne): class DeleteImage(command.Command): - """Delete image(s)""" + _description = _("Delete image(s)") def get_parser(self, prog_name): parser = super(DeleteImage, self).get_parser(prog_name) @@ -398,7 +401,7 @@ class DeleteImage(command.Command): class ListImage(command.Lister): - """List available images""" + _description = _("List available images") def get_parser(self, prog_name): parser = super(ListImage, self).get_parser(prog_name) @@ -447,8 +450,9 @@ class ListImage(command.Lister): parser.add_argument( '--sort', metavar="<key>[:<direction>]", + default='name:asc', help=_("Sort output by selected keys and directions(asc or desc) " - "(default: asc), multiple keys and directions can be " + "(default: name:asc), multiple keys and directions can be " "specified separated by comma"), ) parser.add_argument( @@ -542,7 +546,7 @@ class ListImage(command.Lister): class RemoveProjectImage(command.Command): - """Disassociate project with image""" + _description = _("Disassociate project with image") def get_parser(self, prog_name): parser = super(RemoveProjectImage, self).get_parser(prog_name) @@ -575,7 +579,7 @@ class RemoveProjectImage(command.Command): class SaveImage(command.Command): - """Save an image locally""" + _description = _("Save an image locally") def get_parser(self, prog_name): parser = super(SaveImage, self).get_parser(prog_name) @@ -603,7 +607,7 @@ class SaveImage(command.Command): class SetImage(command.Command): - """Set image properties""" + _description = _("Set image properties") deadopts = ('visibility',) @@ -650,8 +654,9 @@ class SetImage(command.Command): parser.add_argument( "--disk-format", metavar="<disk-format>", - help=_("Image disk format " - "(default: %s)") % DEFAULT_DISK_FORMAT, + choices=DISK_CHOICES, + help=_("Image disk format. The supported options are: %s") % + ', '.join(DISK_CHOICES) ) protected_group = parser.add_mutually_exclusive_group() protected_group.add_argument( @@ -844,7 +849,7 @@ class SetImage(command.Command): class ShowImage(command.ShowOne): - """Display image details""" + _description = _("Display image details") def get_parser(self, prog_name): parser = super(ShowImage, self).get_parser(prog_name) @@ -867,7 +872,7 @@ class ShowImage(command.ShowOne): class UnsetImage(command.Command): - """Unset image tags and properties""" + _description = _("Unset image tags and properties") def get_parser(self, prog_name): parser = super(UnsetImage, self).get_parser(prog_name) diff --git a/openstackclient/network/common.py b/openstackclient/network/common.py index 2b1a5656..a2e700be 100644 --- a/openstackclient/network/common.py +++ b/openstackclient/network/common.py @@ -14,6 +14,7 @@ import abc import logging +import openstack.exceptions from osc_lib.command import command from osc_lib import exceptions import six @@ -181,12 +182,16 @@ class NetworkAndComputeShowOne(command.ShowOne): """ def take_action(self, parsed_args): - if self.app.client_manager.is_network_endpoint_enabled(): - return self.take_action_network(self.app.client_manager.network, - parsed_args) - else: - return self.take_action_compute(self.app.client_manager.compute, - parsed_args) + try: + if self.app.client_manager.is_network_endpoint_enabled(): + return self.take_action_network( + self.app.client_manager.network, parsed_args) + else: + return self.take_action_compute( + self.app.client_manager.compute, parsed_args) + except openstack.exceptions.HttpException as exc: + msg = _("Error while executing command: %s") % exc.message + raise exceptions.CommandError(msg) def get_parser(self, prog_name): LOG.debug('get_parser(%s)', prog_name) diff --git a/openstackclient/network/sdk_utils.py b/openstackclient/network/sdk_utils.py new file mode 100644 index 00000000..7bd54e46 --- /dev/null +++ b/openstackclient/network/sdk_utils.py @@ -0,0 +1,40 @@ +# 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 six + + +# Get the OSC show command display and attribute columns for an SDK resource. +def get_osc_show_columns_for_sdk_resource(sdk_resource, osc_column_map): + if getattr(sdk_resource, 'allow_get', None) is not None: + resource_dict = sdk_resource.to_dict( + body=True, headers=False, ignore_none=False) + else: + resource_dict = sdk_resource + + # Build the OSC column names to display for the SDK resource. + attr_map = {} + display_columns = list(resource_dict.keys()) + for sdk_attr, osc_attr in six.iteritems(osc_column_map): + if sdk_attr in display_columns: + attr_map[osc_attr] = sdk_attr + display_columns.remove(sdk_attr) + if osc_attr not in display_columns: + display_columns.append(osc_attr) + sorted_display_columns = sorted(display_columns) + + # Build the SDK attribute names for the OSC column names. + attr_columns = [] + for column in sorted_display_columns: + new_column = attr_map[column] if column in attr_map else column + attr_columns.append(new_column) + return tuple(sorted_display_columns), tuple(attr_columns) diff --git a/openstackclient/network/v2/address_scope.py b/openstackclient/network/v2/address_scope.py index 6cd13f8c..0d8f80d0 100644 --- a/openstackclient/network/v2/address_scope.py +++ b/openstackclient/network/v2/address_scope.py @@ -21,18 +21,18 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - - return tuple(sorted(columns)) + column_map = { + 'is_shared': 'shared', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): @@ -55,8 +55,10 @@ def _get_attrs(client_manager, parsed_args): return attrs +# TODO(rtheis): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateAddressScope(command.ShowOne): - """Create a new Address Scope""" + _description = _("Create a new Address Scope") def get_parser(self, prog_name): parser = super(CreateAddressScope, self).get_parser(prog_name) @@ -97,14 +99,14 @@ class CreateAddressScope(command.ShowOne): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_address_scope(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) - return (columns, data) + return (display_columns, data) class DeleteAddressScope(command.Command): - """Delete address scope(s)""" + _description = _("Delete address scope(s)") def get_parser(self, prog_name): parser = super(DeleteAddressScope, self).get_parser(prog_name) @@ -139,7 +141,7 @@ class DeleteAddressScope(command.Command): class ListAddressScope(command.Lister): - """List address scopes""" + _description = _("List address scopes") def take_action(self, parsed_args): client = self.app.client_manager.network @@ -147,8 +149,8 @@ class ListAddressScope(command.Lister): 'id', 'name', 'ip_version', - 'shared', - 'tenant_id', + 'is_shared', + 'project_id', ) column_headers = ( 'ID', @@ -165,8 +167,10 @@ class ListAddressScope(command.Lister): ) for s in data)) +# TODO(rtheis): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetAddressScope(command.Command): - """Set address scope properties""" + _description = _("Set address scope properties") def get_parser(self, prog_name): parser = super(SetAddressScope, self).get_parser(prog_name) @@ -210,7 +214,7 @@ class SetAddressScope(command.Command): class ShowAddressScope(command.ShowOne): - """Display address scope details""" + _description = _("Display address scope details") def get_parser(self, prog_name): parser = super(ShowAddressScope, self).get_parser(prog_name) @@ -227,7 +231,7 @@ class ShowAddressScope(command.ShowOne): obj = client.find_address_scope( parsed_args.address_scope, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) - return (columns, data) + return (display_columns, data) diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py index e8057628..c787cd2f 100644 --- a/openstackclient/network/v2/floating_ip.py +++ b/openstackclient/network/v2/floating_ip.py @@ -19,6 +19,14 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.network import common +from openstackclient.network import sdk_utils + + +def _get_network_columns(item): + column_map = { + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_columns(item): @@ -62,7 +70,7 @@ def _get_attrs(client_manager, parsed_args): class CreateFloatingIP(common.NetworkAndComputeShowOne): - """Create floating IP""" + _description = _("Create floating IP") def update_parser_common(self, parser): # In Compute v2 network, floating IPs could be allocated from floating @@ -110,9 +118,9 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_ip(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_network_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): obj = client.floating_ips.create(parsed_args.network) @@ -122,7 +130,7 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne): class CreateIPFloating(CreateFloatingIP): - """Create floating IP""" + _description = _("Create floating IP") # TODO(tangchen): Remove this class and ``ip floating create`` command # two cycles after Mitaka. @@ -146,7 +154,7 @@ class CreateIPFloating(CreateFloatingIP): class DeleteFloatingIP(common.NetworkAndComputeDelete): - """Delete floating IP(s)""" + _description = _("Delete floating IP(s)") # Used by base class to find resources in parsed_args. resource = 'floating_ip' @@ -171,7 +179,7 @@ class DeleteFloatingIP(common.NetworkAndComputeDelete): class DeleteIPFloating(DeleteFloatingIP): - """Delete floating IP(s)""" + _description = _("Delete floating IP(s)") # TODO(tangchen): Remove this class and ``ip floating delete`` command # two cycles after Mitaka. @@ -195,9 +203,33 @@ class DeleteIPFloating(DeleteFloatingIP): class ListFloatingIP(common.NetworkAndComputeLister): - """List floating IP(s)""" + _description = _("List floating IP(s)") + + def update_parser_network(self, parser): + parser.add_argument( + '--network', + metavar='<network>', + help=_("List floating IP(s) according to " + "given network (name or ID)") + ) + parser.add_argument( + '--port', + metavar='<port>', + help=_("List floating IP(s) according to " + "given port (name or ID)") + ) + parser.add_argument( + '--fixed-ip-address', + metavar='<fixed-ip-address>', + help=_("List floating IP(s) according to " + "given fixed IP address") + ) + + return parser def take_action_network(self, client, parsed_args): + network_client = self.app.client_manager.network + columns = ( 'id', 'floating_ip_address', @@ -216,6 +248,18 @@ class ListFloatingIP(common.NetworkAndComputeLister): ) query = {} + + if parsed_args.network is not None: + network = network_client.find_network(parsed_args.network, + ignore_missing=False) + query['floating_network_id'] = network.id + if parsed_args.port is not None: + port = network_client.find_port(parsed_args.port, + ignore_missing=False) + query['port_id'] = port.id + if parsed_args.fixed_ip_address is not None: + query['fixed_ip_address'] = parsed_args.fixed_ip_address + data = client.ips(**query) return (headers, @@ -250,7 +294,7 @@ class ListFloatingIP(common.NetworkAndComputeLister): class ListIPFloating(ListFloatingIP): - """List floating IP(s)""" + _description = _("List floating IP(s)") # TODO(tangchen): Remove this class and ``ip floating list`` command # two cycles after Mitaka. @@ -274,7 +318,7 @@ class ListIPFloating(ListFloatingIP): class ShowFloatingIP(common.NetworkAndComputeShowOne): - """Display floating IP details""" + _description = _("Display floating IP details") def update_parser_common(self, parser): parser.add_argument( @@ -286,9 +330,9 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): obj = client.find_ip(parsed_args.floating_ip, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_network_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): obj = utils.find_resource( @@ -301,7 +345,7 @@ class ShowFloatingIP(common.NetworkAndComputeShowOne): class ShowIPFloating(ShowFloatingIP): - """Display floating IP details""" + _description = _("Display floating IP details") # TODO(tangchen): Remove this class and ``ip floating show`` command # two cycles after Mitaka. diff --git a/openstackclient/network/v2/floating_ip_pool.py b/openstackclient/network/v2/floating_ip_pool.py index c78ca06a..73e94ead 100644 --- a/openstackclient/network/v2/floating_ip_pool.py +++ b/openstackclient/network/v2/floating_ip_pool.py @@ -23,7 +23,7 @@ from openstackclient.network import common class ListFloatingIPPool(common.NetworkAndComputeLister): - """List pools of floating IP addresses""" + _description = _("List pools of floating IP addresses") def take_action_network(self, client, parsed_args): msg = _("Floating ip pool operations are only available for " @@ -43,7 +43,7 @@ class ListFloatingIPPool(common.NetworkAndComputeLister): class ListIPFloatingPool(ListFloatingIPPool): - """List pools of floating IP addresses""" + _description = _("List pools of floating IP addresses") # TODO(tangchen): Remove this class and ``ip floating pool list`` command # two cycles after Mitaka. diff --git a/openstackclient/network/v2/ip_availability.py b/openstackclient/network/v2/ip_availability.py index 1d7b2aed..5960e2fa 100644 --- a/openstackclient/network/v2/ip_availability.py +++ b/openstackclient/network/v2/ip_availability.py @@ -34,7 +34,7 @@ def _get_columns(item): class ListIPAvailability(command.Lister): - """List IP availability for network""" + _description = _("List IP availability for network") def get_parser(self, prog_name): parser = super(ListIPAvailability, self).get_parser(prog_name) @@ -92,7 +92,7 @@ class ListIPAvailability(command.Lister): class ShowIPAvailability(command.ShowOne): - """Show network IP availability details""" + _description = _("Show network IP availability details") def get_parser(self, prog_name): parser = super(ShowIPAvailability, self).get_parser(prog_name) diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py index 40183b73..1c06c462 100644 --- a/openstackclient/network/v2/network.py +++ b/openstackclient/network/v2/network.py @@ -99,6 +99,13 @@ def _get_attrs(client_manager, parsed_args): attrs['provider:physical_network'] = parsed_args.physical_network if parsed_args.segmentation_id: attrs['provider:segmentation_id'] = parsed_args.segmentation_id + if parsed_args.qos_policy is not None: + network_client = client_manager.network + _qos_policy = network_client.find_qos_policy(parsed_args.qos_policy, + ignore_missing=False) + attrs['qos_policy_id'] = _qos_policy.id + if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy: + attrs['qos_policy_id'] = None # Update VLAN Transparency for networks if parsed_args.transparent_vlan: attrs['vlan_transparent'] = True @@ -156,7 +163,7 @@ def _get_attrs_compute(client_manager, parsed_args): class CreateNetwork(common.NetworkAndComputeShowOne): - """Create new network""" + _description = _("Create new network") def update_parser_common(self, parser): parser.add_argument( @@ -249,6 +256,11 @@ class CreateNetwork(common.NetworkAndComputeShowOne): help=_("Do not use the network as the default external network " "(default)") ) + parser.add_argument( + '--qos-policy', + metavar='<qos-policy>', + help=_("QoS policy to attach to this network (name or ID)") + ) _add_additional_network_options(parser) return parser @@ -276,7 +288,7 @@ class CreateNetwork(common.NetworkAndComputeShowOne): class DeleteNetwork(common.NetworkAndComputeDelete): - """Delete network(s)""" + _description = _("Delete network(s)") # Used by base class to find resources in parsed_args. resource = 'network' @@ -302,9 +314,9 @@ class DeleteNetwork(common.NetworkAndComputeDelete): class ListNetwork(common.NetworkAndComputeLister): - """List networks""" + _description = _("List networks") - def update_parser_common(self, parser): + def update_parser_network(self, parser): router_ext_group = parser.add_mutually_exclusive_group() router_ext_group.add_argument( '--external', @@ -361,6 +373,29 @@ class ListNetwork(common.NetworkAndComputeLister): help=_("List networks according to their status " "('ACTIVE', 'BUILD', 'DOWN', 'ERROR')") ) + parser.add_argument( + '--provider-network-type', + metavar='<provider-network-type>', + choices=['flat', 'geneve', 'gre', 'local', + 'vlan', 'vxlan'], + help=_("List networks according to their physical mechanisms. " + "The supported options are: flat, geneve, gre, local, " + "vlan, vxlan.") + ) + parser.add_argument( + '--provider-physical-network', + metavar='<provider-physical-network>', + dest='physical_network', + help=_("List networks according to name of the physical network") + ) + parser.add_argument( + '--provider-segment', + metavar='<provider-segment>', + dest='segmentation_id', + help=_("List networks according to VLAN ID for VLAN networks " + "or Tunnel ID for GENEVE/GRE/VXLAN networks") + ) + return parser def take_action_network(self, client, parsed_args): @@ -433,6 +468,13 @@ class ListNetwork(common.NetworkAndComputeLister): if parsed_args.status: args['status'] = parsed_args.status + if parsed_args.provider_network_type: + args['provider:network_type'] = parsed_args.provider_network_type + if parsed_args.physical_network: + args['provider:physical_network'] = parsed_args.physical_network + if parsed_args.segmentation_id: + args['provider:segmentation_id'] = parsed_args.segmentation_id + data = client.networks(**args) return (column_headers, @@ -463,7 +505,7 @@ class ListNetwork(common.NetworkAndComputeLister): class SetNetwork(command.Command): - """Set network properties""" + _description = _("Set network properties") def get_parser(self, prog_name): parser = super(SetNetwork, self).get_parser(prog_name) @@ -542,6 +584,17 @@ class SetNetwork(command.Command): action='store_true', help=_("Do not use the network as the default external network") ) + qos_group = parser.add_mutually_exclusive_group() + qos_group.add_argument( + '--qos-policy', + metavar='<qos-policy>', + help=_("QoS policy to attach to this network (name or ID)") + ) + qos_group.add_argument( + '--no-qos-policy', + action='store_true', + help=_("Remove the QoS policy attached to this network") + ) _add_additional_network_options(parser) return parser @@ -554,7 +607,7 @@ class SetNetwork(command.Command): class ShowNetwork(common.NetworkAndComputeShowOne): - """Show network details""" + _description = _("Show network details") def update_parser_common(self, parser): parser.add_argument( diff --git a/openstackclient/network/v2/network_agent.py b/openstackclient/network/v2/network_agent.py index fdb34bb7..65708499 100644 --- a/openstackclient/network/v2/network_agent.py +++ b/openstackclient/network/v2/network_agent.py @@ -36,7 +36,7 @@ _formatters = { class DeleteNetworkAgent(command.Command): - """Delete network agent(s)""" + _description = _("Delete network agent(s)") def get_parser(self, prog_name): parser = super(DeleteNetworkAgent, self).get_parser(prog_name) @@ -70,7 +70,7 @@ class DeleteNetworkAgent(command.Command): class ListNetworkAgent(command.Lister): - """List network agents""" + _description = _("List network agents") def take_action(self, parsed_args): client = self.app.client_manager.network @@ -100,7 +100,7 @@ class ListNetworkAgent(command.Lister): class SetNetworkAgent(command.Command): - """Set network agent properties""" + _description = _("Set network agent properties") def get_parser(self, prog_name): parser = super(SetNetworkAgent, self).get_parser(prog_name) @@ -141,7 +141,7 @@ class SetNetworkAgent(command.Command): class ShowNetworkAgent(command.ShowOne): - """Display network agent details""" + _description = _("Display network agent details") def get_parser(self, prog_name): parser = super(ShowNetworkAgent, self).get_parser(prog_name) diff --git a/openstackclient/network/v2/network_qos_policy.py b/openstackclient/network/v2/network_qos_policy.py index a8fcfc59..5ccbe36b 100644 --- a/openstackclient/network/v2/network_qos_policy.py +++ b/openstackclient/network/v2/network_qos_policy.py @@ -21,17 +21,18 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - return tuple(sorted(columns)) + column_map = { + 'is_shared': 'shared', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): @@ -56,8 +57,10 @@ def _get_attrs(client_manager, parsed_args): return attrs +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateNetworkQosPolicy(command.ShowOne): - """Create a QoS policy""" + _description = _("Create a QoS policy") def get_parser(self, prog_name): parser = super(CreateNetworkQosPolicy, self).get_parser(prog_name) @@ -96,13 +99,13 @@ class CreateNetworkQosPolicy(command.ShowOne): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_qos_policy(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters={}) - return columns, data + return (display_columns, data) class DeleteNetworkQosPolicy(command.Command): - """Delete Qos Policy(s)""" + _description = _("Delete Qos Policy(s)") def get_parser(self, prog_name): parser = super(DeleteNetworkQosPolicy, self).get_parser(prog_name) @@ -135,16 +138,18 @@ class DeleteNetworkQosPolicy(command.Command): raise exceptions.CommandError(msg) +# TODO(abhiraut): Use only the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListNetworkQosPolicy(command.Lister): - """List QoS policies""" + _description = _("List QoS policies") def take_action(self, parsed_args): client = self.app.client_manager.network columns = ( 'id', 'name', - 'shared', - 'tenant_id', + 'is_shared', + 'project_id', ) column_headers = ( 'ID', @@ -160,8 +165,10 @@ class ListNetworkQosPolicy(command.Lister): ) for s in data)) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetNetworkQosPolicy(command.Command): - """Set QoS policy properties""" + _description = _("Set QoS policy properties") def get_parser(self, prog_name): parser = super(SetNetworkQosPolicy, self).get_parser(prog_name) @@ -211,7 +218,7 @@ class SetNetworkQosPolicy(command.Command): class ShowNetworkQosPolicy(command.ShowOne): - """Display QoS policy details""" + _description = _("Display QoS policy details") def get_parser(self, prog_name): parser = super(ShowNetworkQosPolicy, self).get_parser(prog_name) @@ -226,6 +233,6 @@ class ShowNetworkQosPolicy(command.ShowOne): client = self.app.client_manager.network obj = client.find_qos_policy(parsed_args.policy, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return columns, data + return (display_columns, data) diff --git a/openstackclient/network/v2/network_rbac.py b/openstackclient/network/v2/network_rbac.py index bb29579f..e837af3a 100644 --- a/openstackclient/network/v2/network_rbac.py +++ b/openstackclient/network/v2/network_rbac.py @@ -21,20 +21,18 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - if 'target_tenant' in columns: - columns.remove('target_tenant') - columns.append('target_project_id') - return tuple(sorted(columns)) + column_map = { + 'target_tenant': 'target_project_id', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _get_attrs(client_manager, parsed_args): @@ -47,9 +45,9 @@ def _get_attrs(client_manager, parsed_args): object_id = network_client.find_network( parsed_args.rbac_object, ignore_missing=False).id if parsed_args.type == 'qos_policy': - # TODO(Huanxuan Ao): Support finding a object ID by obejct name - # after qos policy finding supported in SDK. - object_id = parsed_args.rbac_object + object_id = network_client.find_qos_policy( + parsed_args.rbac_object, + ignore_missing=False).id attrs['object_id'] = object_id identity_client = client_manager.identity @@ -70,16 +68,17 @@ def _get_attrs(client_manager, parsed_args): return attrs +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateNetworkRBAC(command.ShowOne): - """Create network RBAC policy""" + _description = _("Create network RBAC policy") def get_parser(self, prog_name): parser = super(CreateNetworkRBAC, self).get_parser(prog_name) parser.add_argument( 'rbac_object', metavar="<rbac-object>", - help=_("The object to which this RBAC policy affects (name or " - "ID for network objects, ID only for QoS policy objects)") + help=_("The object to which this RBAC policy affects (name or ID)") ) parser.add_argument( '--type', @@ -123,13 +122,13 @@ class CreateNetworkRBAC(command.ShowOne): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_rbac_policy(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return columns, data + return display_columns, data class DeleteNetworkRBAC(command.Command): - """Delete network RBAC policy(s)""" + _description = _("Delete network RBAC policy(s)") def get_parser(self, prog_name): parser = super(DeleteNetworkRBAC, self).get_parser(prog_name) @@ -163,7 +162,7 @@ class DeleteNetworkRBAC(command.Command): class ListNetworkRBAC(command.Lister): - """List network RBAC policies""" + _description = _("List network RBAC policies") def take_action(self, parsed_args): client = self.app.client_manager.network @@ -186,8 +185,10 @@ class ListNetworkRBAC(command.Lister): ) for s in data)) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetNetworkRBAC(command.Command): - """Set network RBAC policy properties""" + _description = _("Set network RBAC policy properties") def get_parser(self, prog_name): parser = super(SetNetworkRBAC, self).get_parser(prog_name) @@ -228,7 +229,7 @@ class SetNetworkRBAC(command.Command): class ShowNetworkRBAC(command.ShowOne): - """Display network RBAC policy details""" + _description = _("Display network RBAC policy details") def get_parser(self, prog_name): parser = super(ShowNetworkRBAC, self).get_parser(prog_name) @@ -243,6 +244,6 @@ class ShowNetworkRBAC(command.ShowOne): client = self.app.client_manager.network obj = client.find_rbac_policy(parsed_args.rbac_policy, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return columns, data + return display_columns, data diff --git a/openstackclient/network/v2/network_segment.py b/openstackclient/network/v2/network_segment.py index 94722f1e..709dc296 100644 --- a/openstackclient/network/v2/network_segment.py +++ b/openstackclient/network/v2/network_segment.py @@ -20,13 +20,18 @@ from osc_lib import exceptions from osc_lib import utils from openstackclient.i18n import _ +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) +def _get_columns(item): + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, {}) + + class CreateNetworkSegment(command.ShowOne): - """Create new network segment""" + _description = _("Create new network segment") def get_parser(self, prog_name): parser = super(CreateNetworkSegment, self).get_parser(prog_name) @@ -84,13 +89,13 @@ class CreateNetworkSegment(command.ShowOne): if parsed_args.segment is not None: attrs['segmentation_id'] = parsed_args.segment obj = client.create_segment(**attrs) - columns = tuple(sorted(obj.keys())) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) class DeleteNetworkSegment(command.Command): - """Delete network segment(s)""" + _description = _("Delete network segment(s)") def get_parser(self, prog_name): parser = super(DeleteNetworkSegment, self).get_parser(prog_name) @@ -125,7 +130,7 @@ class DeleteNetworkSegment(command.Command): class ListNetworkSegment(command.Lister): - """List network segments""" + _description = _("List network segments") def get_parser(self, prog_name): parser = super(ListNetworkSegment, self).get_parser(prog_name) @@ -185,7 +190,7 @@ class ListNetworkSegment(command.Lister): class SetNetworkSegment(command.Command): - """Set network segment properties""" + _description = _("Set network segment properties") def get_parser(self, prog_name): parser = super(SetNetworkSegment, self).get_parser(prog_name) @@ -219,7 +224,7 @@ class SetNetworkSegment(command.Command): class ShowNetworkSegment(command.ShowOne): - """Display network segment details""" + _description = _("Display network segment details") def get_parser(self, prog_name): parser = super(ShowNetworkSegment, self).get_parser(prog_name) @@ -236,6 +241,6 @@ class ShowNetworkSegment(command.ShowOne): parsed_args.network_segment, ignore_missing=False ) - columns = tuple(sorted(obj.keys())) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) diff --git a/openstackclient/network/v2/network_service_provider.py b/openstackclient/network/v2/network_service_provider.py new file mode 100644 index 00000000..3aa33c23 --- /dev/null +++ b/openstackclient/network/v2/network_service_provider.py @@ -0,0 +1,41 @@ +# 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. +# + +"""Network Service Providers Implementation""" + +from osc_lib.command import command +from osc_lib import utils + + +class ListNetworkServiceProvider(command.Lister): + """List Service Providers""" + + def take_action(self, parsed_args): + client = self.app.client_manager.network + + columns = ( + 'service_type', + 'name', + 'is_default', + ) + column_headers = ( + 'Service Type', + 'Name', + 'Default', + ) + + data = client.service_providers() + return(column_headers, + (utils.get_item_properties( + s, columns, + ) for s in data)) diff --git a/openstackclient/network/v2/port.py b/openstackclient/network/v2/port.py index 86174d53..6cae87ee 100644 --- a/openstackclient/network/v2/port.py +++ b/openstackclient/network/v2/port.py @@ -109,6 +109,8 @@ def _get_attrs(client_manager, parsed_args): 'The --host-id option is deprecated, ' 'please use --host instead.' )) + if parsed_args.description is not None: + attrs['description'] = parsed_args.description if parsed_args.fixed_ip is not None: attrs['fixed_ips'] = parsed_args.fixed_ip if parsed_args.device: @@ -146,6 +148,12 @@ def _get_attrs(client_manager, parsed_args): ).id attrs['tenant_id'] = project_id + if parsed_args.disable_port_security: + attrs['port_security_enabled'] = False + + if parsed_args.enable_port_security: + attrs['port_security_enabled'] = True + return attrs @@ -180,50 +188,55 @@ def _prepare_fixed_ips(client_manager, parsed_args): def _add_updatable_args(parser): - # NOTE(dtroyer): --device-id is deprecated in Mar 2016. Do not - # remove before 3.x release or Mar 2017. - device_group = parser.add_mutually_exclusive_group() - device_group.add_argument( - '--device', - metavar='<device-id>', - help=_("Port device ID") - ) - device_group.add_argument( - '--device-id', - metavar='<device-id>', - help=argparse.SUPPRESS, - ) - parser.add_argument( - '--device-owner', - metavar='<device-owner>', - help=_("Device owner of this port. This is the entity that uses " - "the port (for example, network:dhcp).") - ) - parser.add_argument( - '--vnic-type', - metavar='<vnic-type>', - choices=['direct', 'direct-physical', 'macvtap', - 'normal', 'baremetal'], - help=_("VNIC type for this port (direct | direct-physical | " - "macvtap | normal | baremetal, default: normal)") - ) - # NOTE(dtroyer): --host-id is deprecated in Mar 2016. Do not - # remove before 3.x release or Mar 2017. - host_group = parser.add_mutually_exclusive_group() - host_group.add_argument( - '--host', - metavar='<host-id>', - help=_("Allocate port on host <host-id> (ID only)") - ) - host_group.add_argument( - '--host-id', - metavar='<host-id>', - help=argparse.SUPPRESS, - ) + parser.add_argument( + '--description', + metavar='<description>', + help=_("Description of this port") + ) + # NOTE(dtroyer): --device-id is deprecated in Mar 2016. Do not + # remove before 3.x release or Mar 2017. + device_group = parser.add_mutually_exclusive_group() + device_group.add_argument( + '--device', + metavar='<device-id>', + help=_("Port device ID") + ) + device_group.add_argument( + '--device-id', + metavar='<device-id>', + help=argparse.SUPPRESS, + ) + parser.add_argument( + '--device-owner', + metavar='<device-owner>', + help=_("Device owner of this port. This is the entity that uses " + "the port (for example, network:dhcp).") + ) + parser.add_argument( + '--vnic-type', + metavar='<vnic-type>', + choices=['direct', 'direct-physical', 'macvtap', + 'normal', 'baremetal'], + help=_("VNIC type for this port (direct | direct-physical | " + "macvtap | normal | baremetal, default: normal)") + ) + # NOTE(dtroyer): --host-id is deprecated in Mar 2016. Do not + # remove before 3.x release or Mar 2017. + host_group = parser.add_mutually_exclusive_group() + host_group.add_argument( + '--host', + metavar='<host-id>', + help=_("Allocate port on host <host-id> (ID only)") + ) + host_group.add_argument( + '--host-id', + metavar='<host-id>', + help=argparse.SUPPRESS, + ) class CreatePort(command.ShowOne): - """Create a new port""" + _description = _("Create a new port") def get_parser(self, prog_name): parser = super(CreatePort, self).get_parser(prog_name) @@ -297,6 +310,17 @@ class CreatePort(command.ShowOne): action='store_true', help=_("Associate no security groups with this port") ) + port_security = parser.add_mutually_exclusive_group() + port_security.add_argument( + '--enable-port-security', + action='store_true', + help=_("Enable port security for this port (Default)") + ) + port_security.add_argument( + '--disable-port-security', + action='store_true', + help=_("Disable port security for this port") + ) return parser @@ -323,7 +347,7 @@ class CreatePort(command.ShowOne): class DeletePort(command.Command): - """Delete port(s)""" + _description = _("Delete port(s)") def get_parser(self, prog_name): parser = super(DeletePort, self).get_parser(prog_name) @@ -357,7 +381,7 @@ class DeletePort(command.Command): class ListPort(command.Lister): - """List ports""" + _description = _("List ports") def get_parser(self, prog_name): parser = super(ListPort, self).get_parser(prog_name) @@ -385,6 +409,11 @@ class ListPort(command.Lister): help=_("List only ports attached to this server (name or ID)"), ) parser.add_argument( + '--mac-address', + metavar='<mac-address>', + help=_("List only ports with this MAC address") + ) + parser.add_argument( '--long', action='store_true', default=False, @@ -429,6 +458,8 @@ class ListPort(command.Lister): network = network_client.find_network(parsed_args.network, ignore_missing=False) filters['network_id'] = network.id + if parsed_args.mac_address: + filters['mac_address'] = parsed_args.mac_address data = network_client.ports(**filters) @@ -440,7 +471,7 @@ class ListPort(command.Lister): class SetPort(command.Command): - """Set port properties""" + _description = _("Set port properties") def get_parser(self, prog_name): parser = super(SetPort, self).get_parser(prog_name) @@ -512,6 +543,17 @@ class SetPort(command.Command): action='store_true', help=_("Clear existing security groups associated with this port") ) + port_security = parser.add_mutually_exclusive_group() + port_security.add_argument( + '--enable-port-security', + action='store_true', + help=_("Enable port security for this port") + ) + port_security.add_argument( + '--disable-port-security', + action='store_true', + help=_("Disable port security for this port") + ) return parser @@ -556,7 +598,7 @@ class SetPort(command.Command): class ShowPort(command.ShowOne): - """Display port details""" + _description = _("Display port details") def get_parser(self, prog_name): parser = super(ShowPort, self).get_parser(prog_name) @@ -576,7 +618,7 @@ class ShowPort(command.ShowOne): class UnsetPort(command.Command): - """Unset port properties""" + _description = _("Unset port properties") def get_parser(self, prog_name): parser = super(UnsetPort, self).get_parser(prog_name) diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py index d96c314a..cbd412b5 100644 --- a/openstackclient/network/v2/router.py +++ b/openstackclient/network/v2/router.py @@ -100,7 +100,7 @@ def _get_attrs(client_manager, parsed_args): class AddPortToRouter(command.Command): - """Add a port to a router""" + _description = _("Add a port to a router") def get_parser(self, prog_name): parser = super(AddPortToRouter, self).get_parser(prog_name) @@ -124,7 +124,7 @@ class AddPortToRouter(command.Command): class AddSubnetToRouter(command.Command): - """Add a subnet to a router""" + _description = _("Add a subnet to a router") def get_parser(self, prog_name): parser = super(AddSubnetToRouter, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class AddSubnetToRouter(command.Command): class CreateRouter(command.ShowOne): - """Create a new router""" + _description = _("Create a new router") def get_parser(self, prog_name): parser = super(CreateRouter, self).get_parser(prog_name) @@ -222,7 +222,7 @@ class CreateRouter(command.ShowOne): class DeleteRouter(command.Command): - """Delete router(s)""" + _description = _("Delete router(s)") def get_parser(self, prog_name): parser = super(DeleteRouter, self).get_parser(prog_name) @@ -256,7 +256,7 @@ class DeleteRouter(command.Command): class ListRouter(command.Lister): - """List routers""" + _description = _("List routers") def get_parser(self, prog_name): parser = super(ListRouter, self).get_parser(prog_name) @@ -344,7 +344,7 @@ class ListRouter(command.Lister): class RemovePortFromRouter(command.Command): - """Remove a port from a router""" + _description = _("Remove a port from a router") def get_parser(self, prog_name): parser = super(RemovePortFromRouter, self).get_parser(prog_name) @@ -368,7 +368,7 @@ class RemovePortFromRouter(command.Command): class RemoveSubnetFromRouter(command.Command): - """Remove a subnet from a router""" + _description = _("Remove a subnet from a router") def get_parser(self, prog_name): parser = super(RemoveSubnetFromRouter, self).get_parser(prog_name) @@ -395,7 +395,7 @@ class RemoveSubnetFromRouter(command.Command): class SetRouter(command.Command): - """Set router properties""" + _description = _("Set router properties") def get_parser(self, prog_name): parser = super(SetRouter, self).get_parser(prog_name) @@ -509,7 +509,7 @@ class SetRouter(command.Command): class ShowRouter(command.ShowOne): - """Display router details""" + _description = _("Display router details") def get_parser(self, prog_name): parser = super(ShowRouter, self).get_parser(prog_name) @@ -529,7 +529,7 @@ class ShowRouter(command.ShowOne): class UnsetRouter(command.Command): - """Unset router properties""" + _description = _("Unset router properties") def get_parser(self, prog_name): parser = super(UnsetRouter, self).get_parser(prog_name) diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py index f832f721..554dd61d 100644 --- a/openstackclient/network/v2/security_group.py +++ b/openstackclient/network/v2/security_group.py @@ -95,7 +95,7 @@ def _get_columns(item): class CreateSecurityGroup(common.NetworkAndComputeShowOne): - """Create a new security group""" + _description = _("Create a new security group") def update_parser_common(self, parser): parser.add_argument( @@ -165,7 +165,7 @@ class CreateSecurityGroup(common.NetworkAndComputeShowOne): class DeleteSecurityGroup(common.NetworkAndComputeDelete): - """Delete security group(s)""" + _description = _("Delete security group(s)") # Used by base class to find resources in parsed_args. resource = 'group' @@ -190,7 +190,7 @@ class DeleteSecurityGroup(common.NetworkAndComputeDelete): class ListSecurityGroup(common.NetworkAndComputeLister): - """List security groups""" + _description = _("List security groups") def update_parser_network(self, parser): # Maintain and hide the argument for backwards compatibility. @@ -238,7 +238,7 @@ class ListSecurityGroup(common.NetworkAndComputeLister): class SetSecurityGroup(common.NetworkAndComputeCommand): - """Set security group properties""" + _description = _("Set security group properties") def update_parser_common(self, parser): parser.add_argument( @@ -293,7 +293,7 @@ class SetSecurityGroup(common.NetworkAndComputeCommand): class ShowSecurityGroup(common.NetworkAndComputeShowOne): - """Display security group details""" + _description = _("Display security group details") def update_parser_common(self, parser): parser.add_argument( diff --git a/openstackclient/network/v2/security_group_rule.py b/openstackclient/network/v2/security_group_rule.py index e3be44ec..b878d875 100644 --- a/openstackclient/network/v2/security_group_rule.py +++ b/openstackclient/network/v2/security_group_rule.py @@ -14,6 +14,7 @@ """Security Group Rule action implementations""" import argparse +import logging try: from novaclient.v2 import security_group_rules as compute_secgroup_rules @@ -28,9 +29,13 @@ import six from openstackclient.i18n import _ from openstackclient.identity import common as identity_common from openstackclient.network import common +from openstackclient.network import sdk_utils from openstackclient.network import utils as network_utils +LOG = logging.getLogger(__name__) + + def _format_security_group_rule_show(obj): data = network_utils.transform_compute_security_group_rule(obj) return zip(*sorted(six.iteritems(data))) @@ -63,11 +68,10 @@ def _format_network_port_range(rule): def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - return tuple(sorted(columns)) + column_map = { + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def _convert_to_lowercase(string): @@ -85,8 +89,10 @@ def _is_icmp_protocol(protocol): return False +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): - """Create a new security group rule""" + _description = _("Create a new security group rule") def update_parser_common(self, parser): parser.add_argument( @@ -94,22 +100,40 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): metavar='<group>', help=_("Create rule in this security group (name or ID)") ) - source_group = parser.add_mutually_exclusive_group() - source_group.add_argument( + remote_group = parser.add_mutually_exclusive_group() + remote_group.add_argument( + "--remote-ip", + metavar="<ip-address>", + help=_("Remote IP address block (may use CIDR notation; " + "default for IPv4 rule: 0.0.0.0/0)"), + ) + remote_group.add_argument( + "--remote-group", + metavar="<group>", + help=_("Remote security group (name or ID)"), + ) + # Handle deprecated options + # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016. + # Do not remove before 4.x release or Nov 2017. + remote_group.add_argument( "--src-ip", metavar="<ip-address>", - help=_("Source IP address block (may use CIDR notation; " - "default for IPv4 rule: 0.0.0.0/0)") + help=argparse.SUPPRESS, ) - source_group.add_argument( + remote_group.add_argument( "--src-group", metavar="<group>", - help=_("Source security group (name or ID)") + help=argparse.SUPPRESS, ) return parser def update_parser_network(self, parser): parser.add_argument( + '--description', + metavar='<description>', + help=_("Set security group rule description") + ) + parser.add_argument( '--dst-port', metavar='<port-range>', action=parseractions.RangeAction, @@ -235,6 +259,9 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): attrs = {} attrs['protocol'] = self._get_protocol(parsed_args) + if parsed_args.description is not None: + attrs['description'] = parsed_args.description + # NOTE(rtheis): A direction must be specified and ingress # is the default. if parsed_args.ingress or not parsed_args.egress: @@ -277,13 +304,31 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): if parsed_args.icmp_code: attrs['port_range_max'] = parsed_args.icmp_code - if parsed_args.src_group is not None: + # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016. + # Do not remove before 4.x release or Nov 2017. + if not (parsed_args.remote_group is None and + parsed_args.src_group is None): attrs['remote_group_id'] = client.find_security_group( - parsed_args.src_group, + parsed_args.remote_group or parsed_args.src_group, ignore_missing=False ).id - elif parsed_args.src_ip is not None: - attrs['remote_ip_prefix'] = parsed_args.src_ip + if parsed_args.src_group: + LOG.warning( + _("The %(old)s option is deprecated, " + "please use %(new)s instead.") % + {'old': '--src-group', 'new': '--remote-group'}, + ) + elif not (parsed_args.remote_ip is None and + parsed_args.src_ip is None): + attrs['remote_ip_prefix'] = ( + parsed_args.remote_ip or parsed_args.src_ip + ) + if parsed_args.src_ip: + LOG.warning( + _("The %(old)s option is deprecated, " + "please use %(new)s instead.") % + {'old': '--src-ip', 'new': '--remote-ip'}, + ) elif attrs['ethertype'] == 'IPv4': attrs['remote_ip_prefix'] = '0.0.0.0/0' attrs['security_group_id'] = security_group_id @@ -298,9 +343,9 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): # Create and show the security group rule. obj = client.create_security_group_rule(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): group = utils.find_resource( @@ -312,29 +357,46 @@ class CreateSecurityGroupRule(common.NetworkAndComputeShowOne): from_port, to_port = -1, -1 else: from_port, to_port = parsed_args.dst_port - src_ip = None - if parsed_args.src_group is not None: - parsed_args.src_group = utils.find_resource( + + # NOTE(dtroyer): --src-ip and --src-group were deprecated in Nov 2016. + # Do not remove before 4.x release or Nov 2017. + remote_ip = None + if not (parsed_args.remote_group is None and + parsed_args.src_group is None): + parsed_args.remote_group = utils.find_resource( client.security_groups, - parsed_args.src_group, + parsed_args.remote_group or parsed_args.src_group, ).id - if parsed_args.src_ip is not None: - src_ip = parsed_args.src_ip + if parsed_args.src_group: + LOG.warning( + _("The %(old)s option is deprecated, " + "please use %(new)s instead.") % + {'old': '--src-group', 'new': '--remote-group'}, + ) + if not (parsed_args.remote_ip is None and + parsed_args.src_ip is None): + remote_ip = parsed_args.remote_ip or parsed_args.src_ip + if parsed_args.src_ip: + LOG.warning( + _("The %(old)s option is deprecated, " + "please use %(new)s instead.") % + {'old': '--src-ip', 'new': '--remote-ip'}, + ) else: - src_ip = '0.0.0.0/0' + remote_ip = '0.0.0.0/0' obj = client.security_group_rules.create( group.id, protocol, from_port, to_port, - src_ip, - parsed_args.src_group, + remote_ip, + parsed_args.remote_group, ) return _format_security_group_rule_show(obj._info) class DeleteSecurityGroupRule(common.NetworkAndComputeDelete): - """Delete security group rule(s)""" + _description = _("Delete security group rule(s)") # Used by base class to find resources in parsed_args. resource = 'rule' @@ -359,7 +421,7 @@ class DeleteSecurityGroupRule(common.NetworkAndComputeDelete): class ListSecurityGroupRule(common.NetworkAndComputeLister): - """List security group rules""" + _description = _("List security group rules") def update_parser_common(self, parser): parser.add_argument( @@ -380,6 +442,28 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): help=argparse.SUPPRESS ) parser.add_argument( + '--protocol', + metavar='<protocol>', + type=_convert_to_lowercase, + help=_("List rules by the IP protocol (" + "ah, dhcp, egp, esp, gre, icmp, igmp, " + "ipv6-encap, ipv6-frag, ipv6-icmp, ipv6-nonxt, " + "ipv6-opts, ipv6-route, ospf, pgm, rsvp, sctp, tcp, " + "udp, udplite, vrrp and integer representations [0-255])." + ) + ) + direction_group = parser.add_mutually_exclusive_group() + direction_group.add_argument( + '--ingress', + action='store_true', + help=_("List rules applied to incoming network traffic") + ) + direction_group.add_argument( + '--egress', + action='store_true', + help=_("List rules applied to outgoing network traffic") + ) + parser.add_argument( '--long', action='store_true', default=False, @@ -443,6 +527,14 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): query = {'security_group_id': security_group_id} else: columns = columns + ('security_group_id',) + + if parsed_args.ingress: + query['direction'] = 'ingress' + if parsed_args.egress: + query['direction'] = 'egress' + if parsed_args.protocol is not None: + query['protocol'] = parsed_args.protocol + rules = list(client.security_group_rules(**query)) # Reformat the rules to display a port range instead @@ -494,7 +586,7 @@ class ListSecurityGroupRule(common.NetworkAndComputeLister): class ShowSecurityGroupRule(common.NetworkAndComputeShowOne): - """Display security group rule details""" + _description = _("Display security group rule details") def update_parser_common(self, parser): parser.add_argument( @@ -507,9 +599,9 @@ class ShowSecurityGroupRule(common.NetworkAndComputeShowOne): def take_action_network(self, client, parsed_args): obj = client.find_security_group_rule(parsed_args.rule, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns) - return (columns, data) + return (display_columns, data) def take_action_compute(self, client, parsed_args): # NOTE(rtheis): Unfortunately, compute does not have an API diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py index f1ecb5a7..292b7c06 100644 --- a/openstackclient/network/v2/subnet.py +++ b/openstackclient/network/v2/subnet.py @@ -23,6 +23,7 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) @@ -84,6 +85,15 @@ def _get_common_parse_arguments(parser, is_create=True): help=_("DNS server for this subnet " "(repeat option to set multiple DNS servers)") ) + + if not is_create: + parser.add_argument( + '--no-dns-nameservers', + action='store_true', + help=_("Clear existing information of DNS Nameservers. " + "Specify both --dns-nameserver and --no-dns-nameserver " + "to overwrite the current DNS Nameserver information.") + ) parser.add_argument( '--host-route', metavar='destination=<subnet>,gateway=<ip-address>', @@ -117,11 +127,12 @@ def _get_common_parse_arguments(parser, is_create=True): def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - return tuple(sorted(columns)) + column_map = { + 'is_dhcp_enabled': 'enable_dhcp', + 'subnet_pool_id': 'subnetpool_id', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) def convert_entries_to_nexthop(entries): @@ -217,8 +228,10 @@ def _get_attrs(client_manager, parsed_args, is_create=True): return attrs +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateSubnet(command.ShowOne): - """Create a subnet""" + _description = _("Create a subnet") def get_parser(self, prog_name): parser = super(CreateSubnet, self).get_parser(prog_name) @@ -323,13 +336,13 @@ class CreateSubnet(command.ShowOne): client = self.app.client_manager.network attrs = _get_attrs(self.app.client_manager, parsed_args) obj = client.create_subnet(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) class DeleteSubnet(command.Command): - """Delete subnet(s)""" + _description = _("Delete subnet(s)") def get_parser(self, prog_name): parser = super(DeleteSubnet, self).get_parser(prog_name) @@ -362,8 +375,10 @@ class DeleteSubnet(command.Command): raise exceptions.CommandError(msg) +# TODO(abhiraut): Use only the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListSubnet(command.Lister): - """List subnets""" + _description = _("List subnets") def get_parser(self, prog_name): parser = super(ListSubnet, self).get_parser(prog_name) @@ -443,8 +458,10 @@ class ListSubnet(command.Lister): filters['ip_version'] = parsed_args.ip_version if parsed_args.dhcp: filters['enable_dhcp'] = True + filters['is_dhcp_enabled'] = True elif parsed_args.no_dhcp: filters['enable_dhcp'] = False + filters['is_dhcp_enabled'] = False if parsed_args.service_types: filters['service_types'] = parsed_args.service_types if parsed_args.project: @@ -454,6 +471,7 @@ class ListSubnet(command.Lister): parsed_args.project_domain, ).id filters['tenant_id'] = project_id + filters['project_id'] = project_id if parsed_args.network: network_id = network_client.find_network(parsed_args.network, ignore_missing=False).id @@ -472,7 +490,7 @@ class ListSubnet(command.Lister): headers += ('Project', 'DHCP', 'Name Servers', 'Allocation Pools', 'Host Routes', 'IP Version', 'Gateway', 'Service Types') - columns += ('tenant_id', 'enable_dhcp', 'dns_nameservers', + columns += ('project_id', 'is_dhcp_enabled', 'dns_nameservers', 'allocation_pools', 'host_routes', 'ip_version', 'gateway_ip', 'service_types') @@ -483,8 +501,10 @@ class ListSubnet(command.Lister): ) for s in data)) +# TODO(abhiraut): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetSubnet(command.Command): - """Set subnet properties""" + _description = _("Set subnet properties") def get_parser(self, prog_name): parser = super(SetSubnet, self).get_parser(prog_name) @@ -532,7 +552,10 @@ class SetSubnet(command.Command): attrs = _get_attrs(self.app.client_manager, parsed_args, is_create=False) if 'dns_nameservers' in attrs: - attrs['dns_nameservers'] += obj.dns_nameservers + if not parsed_args.no_dns_nameservers: + attrs['dns_nameservers'] += obj.dns_nameservers + elif parsed_args.no_dns_nameservers: + attrs['dns_nameservers'] = [] if 'host_routes' in attrs: if not parsed_args.no_host_route: attrs['host_routes'] += obj.host_routes @@ -550,7 +573,7 @@ class SetSubnet(command.Command): class ShowSubnet(command.ShowOne): - """Display subnet details""" + _description = _("Display subnet details") def get_parser(self, prog_name): parser = super(ShowSubnet, self).get_parser(prog_name) @@ -564,13 +587,13 @@ class ShowSubnet(command.ShowOne): def take_action(self, parsed_args): obj = self.app.client_manager.network.find_subnet(parsed_args.subnet, ignore_missing=False) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) class UnsetSubnet(command.Command): - """Unset subnet properties""" + _description = _("Unset subnet properties") def get_parser(self, prog_name): parser = super(UnsetSubnet, self).get_parser(prog_name) diff --git a/openstackclient/network/v2/subnet_pool.py b/openstackclient/network/v2/subnet_pool.py index a29c4518..a5a24424 100644 --- a/openstackclient/network/v2/subnet_pool.py +++ b/openstackclient/network/v2/subnet_pool.py @@ -12,6 +12,7 @@ # """Subnet pool action implementations""" + import copy import logging @@ -22,17 +23,21 @@ from osc_lib import utils from openstackclient.i18n import _ from openstackclient.identity import common as identity_common +from openstackclient.network import sdk_utils LOG = logging.getLogger(__name__) def _get_columns(item): - columns = list(item.keys()) - if 'tenant_id' in columns: - columns.remove('tenant_id') - columns.append('project_id') - return tuple(sorted(columns)) + column_map = { + 'default_prefix_length': 'default_prefixlen', + 'is_shared': 'shared', + 'maximum_prefix_length': 'max_prefixlen', + 'minimum_prefix_length': 'min_prefixlen', + 'tenant_id': 'project_id', + } + return sdk_utils.get_osc_show_columns_for_sdk_resource(item, column_map) _formatters = { @@ -134,8 +139,10 @@ def _add_default_options(parser): ) +# TODO(rtheis): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class CreateSubnetPool(command.ShowOne): - """Create subnet pool""" + _description = _("Create subnet pool") def get_parser(self, prog_name): parser = super(CreateSubnetPool, self).get_parser(prog_name) @@ -184,13 +191,13 @@ class CreateSubnetPool(command.ShowOne): if "prefixes" not in attrs: attrs['prefixes'] = [] obj = client.create_subnet_pool(**attrs) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) class DeleteSubnetPool(command.Command): - """Delete subnet pool(s)""" + _description = _("Delete subnet pool(s)") def get_parser(self, prog_name): parser = super(DeleteSubnetPool, self).get_parser(prog_name) @@ -223,8 +230,10 @@ class DeleteSubnetPool(command.Command): raise exceptions.CommandError(msg) +# TODO(rtheis): Use only the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class ListSubnetPool(command.Lister): - """List subnet pools""" + _description = _("List subnet pools") def get_parser(self, prog_name): parser = super(ListSubnetPool, self).get_parser(prog_name) @@ -283,8 +292,10 @@ class ListSubnetPool(command.Lister): filters = {} if parsed_args.share: filters['shared'] = True + filters['is_shared'] = True elif parsed_args.no_share: filters['shared'] = False + filters['is_shared'] = False if parsed_args.default: filters['is_default'] = True elif parsed_args.no_default: @@ -296,6 +307,7 @@ class ListSubnetPool(command.Lister): parsed_args.project_domain, ).id filters['tenant_id'] = project_id + filters['project_id'] = project_id if parsed_args.name is not None: filters['name'] = parsed_args.name if parsed_args.address_scope: @@ -310,8 +322,8 @@ class ListSubnetPool(command.Lister): if parsed_args.long: headers += ('Default Prefix Length', 'Address Scope', 'Default Subnet Pool', 'Shared') - columns += ('default_prefixlen', 'address_scope_id', - 'is_default', 'shared') + columns += ('default_prefix_length', 'address_scope_id', + 'is_default', 'is_shared') return (headers, (utils.get_item_properties( @@ -320,8 +332,10 @@ class ListSubnetPool(command.Lister): ) for s in data)) +# TODO(rtheis): Use the SDK resource mapped attribute names once the +# OSC minimum requirements include SDK 1.0. class SetSubnetPool(command.Command): - """Set subnet pool properties""" + _description = _("Set subnet pool properties") def get_parser(self, prog_name): parser = super(SetSubnetPool, self).get_parser(prog_name) @@ -373,7 +387,7 @@ class SetSubnetPool(command.Command): class ShowSubnetPool(command.ShowOne): - """Display subnet pool details""" + _description = _("Display subnet pool details") def get_parser(self, prog_name): parser = super(ShowSubnetPool, self).get_parser(prog_name) @@ -390,13 +404,13 @@ class ShowSubnetPool(command.ShowOne): parsed_args.subnet_pool, ignore_missing=False ) - columns = _get_columns(obj) + display_columns, columns = _get_columns(obj) data = utils.get_item_properties(obj, columns, formatters=_formatters) - return (columns, data) + return (display_columns, data) class UnsetSubnetPool(command.Command): - """Unset subnet pool properties""" + _description = _("Unset subnet pool properties") def get_parser(self, prog_name): parser = super(UnsetSubnetPool, self).get_parser(prog_name) diff --git a/openstackclient/object/v1/account.py b/openstackclient/object/v1/account.py index 2fe00ecb..4847f8bb 100644 --- a/openstackclient/object/v1/account.py +++ b/openstackclient/object/v1/account.py @@ -22,7 +22,7 @@ from openstackclient.i18n import _ class SetAccount(command.Command): - """Set account properties""" + _description = _("Set account properties") def get_parser(self, prog_name): parser = super(SetAccount, self).get_parser(prog_name) @@ -43,7 +43,7 @@ class SetAccount(command.Command): class ShowAccount(command.ShowOne): - """Display account details""" + _description = _("Display account details") def take_action(self, parsed_args): data = self.app.client_manager.object_store.account_show() @@ -53,7 +53,7 @@ class ShowAccount(command.ShowOne): class UnsetAccount(command.Command): - """Unset account properties""" + _description = _("Unset account properties") def get_parser(self, prog_name): parser = super(UnsetAccount, self).get_parser(prog_name) diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py index 01964d0c..88fb8602 100644 --- a/openstackclient/object/v1/container.py +++ b/openstackclient/object/v1/container.py @@ -29,7 +29,7 @@ LOG = logging.getLogger(__name__) class CreateContainer(command.Lister): - """Create new container""" + _description = _("Create new container") def get_parser(self, prog_name): parser = super(CreateContainer, self).get_parser(prog_name) @@ -63,7 +63,7 @@ class CreateContainer(command.Lister): class DeleteContainer(command.Command): - """Delete container""" + _description = _("Delete container") def get_parser(self, prog_name): parser = super(DeleteContainer, self).get_parser(prog_name) @@ -98,7 +98,7 @@ class DeleteContainer(command.Command): class ListContainer(command.Lister): - """List containers""" + _description = _("List containers") def get_parser(self, prog_name): parser = super(ListContainer, self).get_parser(prog_name) @@ -168,7 +168,7 @@ class ListContainer(command.Lister): class SaveContainer(command.Command): - """Save container contents locally""" + _description = _("Save container contents locally") def get_parser(self, prog_name): parser = super(SaveContainer, self).get_parser(prog_name) @@ -186,7 +186,7 @@ class SaveContainer(command.Command): class SetContainer(command.Command): - """Set container properties""" + _description = _("Set container properties") def get_parser(self, prog_name): parser = super(SetContainer, self).get_parser(prog_name) @@ -213,7 +213,7 @@ class SetContainer(command.Command): class ShowContainer(command.ShowOne): - """Display container details""" + _description = _("Display container details") def get_parser(self, prog_name): parser = super(ShowContainer, self).get_parser(prog_name) @@ -236,7 +236,7 @@ class ShowContainer(command.ShowOne): class UnsetContainer(command.Command): - """Unset container properties""" + _description = _("Unset container properties") def get_parser(self, prog_name): parser = super(UnsetContainer, self).get_parser(prog_name) diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py index 3c47ee04..71b6f520 100644 --- a/openstackclient/object/v1/object.py +++ b/openstackclient/object/v1/object.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateObject(command.Lister): - """Upload object to container""" + _description = _("Upload object to container") def get_parser(self, prog_name): parser = super(CreateObject, self).get_parser(prog_name) @@ -81,7 +81,7 @@ class CreateObject(command.Lister): class DeleteObject(command.Command): - """Delete object from container""" + _description = _("Delete object from container") def get_parser(self, prog_name): parser = super(DeleteObject, self).get_parser(prog_name) @@ -108,7 +108,7 @@ class DeleteObject(command.Command): class ListObject(command.Lister): - """List objects""" + _description = _("List objects") def get_parser(self, prog_name): parser = super(ListObject, self).get_parser(prog_name) @@ -197,7 +197,7 @@ class ListObject(command.Lister): class SaveObject(command.Command): - """Save object locally""" + _description = _("Save object locally") def get_parser(self, prog_name): parser = super(SaveObject, self).get_parser(prog_name) @@ -227,7 +227,7 @@ class SaveObject(command.Command): class SetObject(command.Command): - """Set object properties""" + _description = _("Set object properties") def get_parser(self, prog_name): parser = super(SetObject, self).get_parser(prog_name) @@ -260,7 +260,7 @@ class SetObject(command.Command): class ShowObject(command.ShowOne): - """Display object details""" + _description = _("Display object details") def get_parser(self, prog_name): parser = super(ShowObject, self).get_parser(prog_name) @@ -289,7 +289,7 @@ class ShowObject(command.ShowOne): class UnsetObject(command.Command): - """Unset object properties""" + _description = _("Unset object properties") def get_parser(self, prog_name): parser = super(UnsetObject, self).get_parser(prog_name) diff --git a/openstackclient/shell.py b/openstackclient/shell.py index be4b5283..e08eee61 100644 --- a/openstackclient/shell.py +++ b/openstackclient/shell.py @@ -148,10 +148,10 @@ class OpenStackShell(shell.OpenStackShell): 'auth_type': self._auth_type, }, ) - except (IOError, OSError) as e: + except (IOError, OSError): self.log.critical("Could not read clouds.yaml configuration file") self.print_help_if_requested() - raise e + raise if not self.options.debug: self.options.debug = None diff --git a/openstackclient/tests/functional/network/v2/test_network_service_provider.py b/openstackclient/tests/functional/network/v2/test_network_service_provider.py new file mode 100644 index 00000000..379de430 --- /dev/null +++ b/openstackclient/tests/functional/network/v2/test_network_service_provider.py @@ -0,0 +1,26 @@ +# Copyright (c) 2016, Intel Corporation. +# 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. + +from openstackclient.tests.functional import base + + +class TestNetworkServiceProvider(base.TestCase): + """Functional tests for network service provider""" + + SERVICE_TYPE = ['L3_ROUTER_NAT'] + + def test_network_service_provider_list(self): + raw_output = self.openstack('network service provider list') + self.assertIn(self.SERVICE_TYPE, raw_output) diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py index aef74f04..036c8336 100644 --- a/openstackclient/tests/unit/image/v1/test_image.py +++ b/openstackclient/tests/unit/image/v1/test_image.py @@ -116,7 +116,7 @@ class TestImageCreate(TestImage): self.images_mock.configure_mock(**mock_exception) arglist = [ '--container-format', 'ovf', - '--disk-format', 'fs', + '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', '--protected', @@ -126,7 +126,7 @@ class TestImageCreate(TestImage): ] verifylist = [ ('container_format', 'ovf'), - ('disk_format', 'fs'), + ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('protected', True), @@ -147,7 +147,7 @@ class TestImageCreate(TestImage): self.images_mock.create.assert_called_with( name=self.new_image.name, container_format='ovf', - disk_format='fs', + disk_format='ami', min_disk=10, min_ram=4, protected=True, diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py index ebc9c3a7..2f2212e4 100644 --- a/openstackclient/tests/unit/image/v2/test_image.py +++ b/openstackclient/tests/unit/image/v2/test_image.py @@ -130,7 +130,7 @@ class TestImageCreate(TestImage): self.images_mock.configure_mock(**mock_exception) arglist = [ '--container-format', 'ovf', - '--disk-format', 'fs', + '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', ('--protected' @@ -143,7 +143,7 @@ class TestImageCreate(TestImage): ] verifylist = [ ('container_format', 'ovf'), - ('disk_format', 'fs'), + ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('protected', self.new_image.protected), @@ -165,7 +165,7 @@ class TestImageCreate(TestImage): self.images_mock.create.assert_called_with( name=self.new_image.name, container_format='ovf', - disk_format='fs', + disk_format='ami', min_disk=10, min_ram=4, owner=self.project.id, @@ -193,7 +193,7 @@ class TestImageCreate(TestImage): arglist = [ '--container-format', 'ovf', - '--disk-format', 'fs', + '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', '--owner', 'unexist_owner', @@ -203,7 +203,7 @@ class TestImageCreate(TestImage): ] verifylist = [ ('container_format', 'ovf'), - ('disk_format', 'fs'), + ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('owner', 'unexist_owner'), @@ -227,7 +227,7 @@ class TestImageCreate(TestImage): arglist = [ '--container-format', 'ovf', - '--disk-format', 'fs', + '--disk-format', 'ami', '--min-disk', '10', '--min-ram', '4', '--protected', @@ -237,7 +237,7 @@ class TestImageCreate(TestImage): ] verifylist = [ ('container_format', 'ovf'), - ('disk_format', 'fs'), + ('disk_format', 'ami'), ('min_disk', 10), ('min_ram', 4), ('protected', True), diff --git a/openstackclient/tests/unit/network/test_common.py b/openstackclient/tests/unit/network/test_common.py index 325aad2a..4b9a754b 100644 --- a/openstackclient/tests/unit/network/test_common.py +++ b/openstackclient/tests/unit/network/test_common.py @@ -14,6 +14,8 @@ import argparse import mock +import openstack +from openstackclient.common import exceptions from openstackclient.network import common from openstackclient.tests.unit import utils @@ -172,3 +174,15 @@ class TestNetworkAndComputeShowOne(TestNetworkAndCompute): def setUp(self): super(TestNetworkAndComputeShowOne, self).setUp() self.cmd = FakeNetworkAndComputeShowOne(self.app, self.namespace) + + def test_take_action_with_http_exception(self): + with mock.patch.object(self.cmd, 'take_action_network') as m_action: + m_action.side_effect = openstack.exceptions.HttpException("bar") + self.assertRaisesRegex(exceptions.CommandError, "bar", + self.cmd.take_action, mock.Mock()) + + self.app.client_manager.network_endpoint_enabled = False + with mock.patch.object(self.cmd, 'take_action_compute') as m_action: + m_action.side_effect = openstack.exceptions.HttpException("bar") + self.assertRaisesRegex(exceptions.CommandError, "bar", + self.cmd.take_action, mock.Mock()) diff --git a/openstackclient/tests/unit/network/test_sdk_utils.py b/openstackclient/tests/unit/network/test_sdk_utils.py new file mode 100644 index 00000000..d1efa7e4 --- /dev/null +++ b/openstackclient/tests/unit/network/test_sdk_utils.py @@ -0,0 +1,59 @@ +# 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. + +from openstackclient.network import sdk_utils +from openstackclient.tests.unit import utils as tests_utils + + +class TestSDKUtils(tests_utils.TestCase): + + def setUp(self): + super(TestSDKUtils, self).setUp() + + def _test_get_osc_show_columns_for_sdk_resource( + self, sdk_resource, column_map, + expected_display_columns, expected_attr_columns): + display_columns, attr_columns = \ + sdk_utils.get_osc_show_columns_for_sdk_resource( + sdk_resource, column_map) + self.assertEqual(expected_display_columns, display_columns) + self.assertEqual(expected_attr_columns, attr_columns) + + def test_get_osc_show_columns_for_sdk_resource_empty(self): + self._test_get_osc_show_columns_for_sdk_resource( + {}, {}, tuple(), tuple()) + + def test_get_osc_show_columns_for_sdk_resource_empty_map(self): + self._test_get_osc_show_columns_for_sdk_resource( + {'foo': 'foo1'}, {}, + ('foo',), ('foo',)) + + def test_get_osc_show_columns_for_sdk_resource_empty_data(self): + self._test_get_osc_show_columns_for_sdk_resource( + {}, {'foo': 'foo_map'}, + ('foo_map',), ('foo_map',)) + + def test_get_osc_show_columns_for_sdk_resource_map(self): + self._test_get_osc_show_columns_for_sdk_resource( + {'foo': 'foo1'}, {'foo': 'foo_map'}, + ('foo_map',), ('foo',)) + + def test_get_osc_show_columns_for_sdk_resource_map_dup(self): + self._test_get_osc_show_columns_for_sdk_resource( + {'foo': 'foo1', 'foo_map': 'foo1'}, {'foo': 'foo_map'}, + ('foo_map',), ('foo',)) + + def test_get_osc_show_columns_for_sdk_resource_map_full(self): + self._test_get_osc_show_columns_for_sdk_resource( + {'foo': 'foo1', 'bar': 'bar1'}, + {'foo': 'foo_map', 'new': 'bar'}, + ('bar', 'foo_map'), ('bar', 'foo')) diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py index 94727ae3..c18511f7 100644 --- a/openstackclient/tests/unit/network/v2/fakes.py +++ b/openstackclient/tests/unit/network/v2/fakes.py @@ -98,6 +98,7 @@ class FakeAddressScope(object): loaded=True) # Set attributes with special mapping in OpenStack SDK. + address_scope.is_shared = address_scope_attrs['shared'] address_scope.project_id = address_scope_attrs['tenant_id'] return address_scope @@ -291,11 +292,14 @@ class FakeNetwork(object): 'shared': False, 'subnets': ['a', 'b'], 'provider_network_type': 'vlan', + 'provider_physical_network': 'physnet1', + 'provider_segmentation_id': "400", 'router:external': True, 'availability_zones': [], 'availability_zone_hints': [], 'is_default': False, 'port_security_enabled': True, + 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex, } # Overwrite default attributes. @@ -427,6 +431,7 @@ class FakePort(object): 'binding:vif_details': {}, 'binding:vif_type': 'ovs', 'binding:vnic_type': 'normal', + 'description': 'description-' + uuid.uuid4().hex, 'device_id': 'device-id-' + uuid.uuid4().hex, 'device_owner': 'compute:nova', 'dns_assignment': [{}], @@ -741,6 +746,7 @@ class FakeNetworkQosPolicy(object): loaded=True) # Set attributes with special mapping in OpenStack SDK. + qos_policy.is_shared = qos_policy_attrs['shared'] qos_policy.project_id = qos_policy_attrs['tenant_id'] return qos_policy @@ -951,6 +957,8 @@ class FakeSecurityGroupRule(object): # Set default attributes. security_group_rule_attrs = { + 'description': 'security-group-rule-description-' + + uuid.uuid4().hex, 'direction': 'ingress', 'ethertype': 'IPv4', 'id': 'security-group-rule-id-' + uuid.uuid4().hex, @@ -1057,6 +1065,8 @@ class FakeSubnet(object): loaded=True) # Set attributes with special mappings in OpenStack SDK. + subnet.is_dhcp_enabled = subnet_attrs['enable_dhcp'] + subnet.subnet_pool_id = subnet_attrs['subnetpool_id'] subnet.project_id = subnet_attrs['tenant_id'] return subnet @@ -1216,6 +1226,11 @@ class FakeSubnetPool(object): ) # Set attributes with special mapping in OpenStack SDK. + subnet_pool.default_prefix_length = \ + subnet_pool_attrs['default_prefixlen'] + subnet_pool.is_shared = subnet_pool_attrs['shared'] + subnet_pool.maximum_prefix_length = subnet_pool_attrs['max_prefixlen'] + subnet_pool.minimum_prefix_length = subnet_pool_attrs['min_prefixlen'] subnet_pool.project_id = subnet_pool_attrs['tenant_id'] return subnet_pool @@ -1257,3 +1272,38 @@ class FakeSubnetPool(object): if subnet_pools is None: subnet_pools = FakeSubnetPool.create_subnet_pools(count) return mock.Mock(side_effect=subnet_pools) + + +class FakeNetworkServiceProvider(object): + """Fake Network Service Providers""" + + @staticmethod + def create_one_network_service_provider(attrs=None): + """Create service provider""" + attrs = attrs or {} + + service_provider = { + 'name': 'provider-name-' + uuid.uuid4().hex, + 'service_type': 'service-type-' + uuid.uuid4().hex, + 'default': False, + } + + service_provider.update(attrs) + + provider = fakes.FakeResource( + info=copy.deepcopy(service_provider), + loaded=True) + provider.is_default = service_provider['default'] + + return provider + + @staticmethod + def create_network_service_providers(attrs=None, count=2): + """Create multiple service providers""" + + service_providers = [] + for i in range(0, count): + service_providers.append(FakeNetworkServiceProvider. + create_one_network_service_provider( + attrs)) + return service_providers diff --git a/openstackclient/tests/unit/network/v2/test_floating_ip.py b/openstackclient/tests/unit/network/v2/test_floating_ip.py index 578c6154..10f3067d 100644 --- a/openstackclient/tests/unit/network/v2/test_floating_ip.py +++ b/openstackclient/tests/unit/network/v2/test_floating_ip.py @@ -231,6 +231,12 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): # The floating ips to list up floating_ips = network_fakes.FakeFloatingIP.create_floating_ips(count=3) + fake_network = network_fakes.FakeNetwork.create_one_network({ + 'id': 'fake_network_id', + }) + fake_port = network_fakes.FakePort.create_one_port({ + 'id': 'fake_port_id', + }) columns = ( 'ID', @@ -256,6 +262,8 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): super(TestListFloatingIPNetwork, self).setUp() self.network.ips = mock.Mock(return_value=self.floating_ips) + self.network.find_network = mock.Mock(return_value=self.fake_network) + self.network.find_port = mock.Mock(return_value=self.fake_port) # Get the command object to test self.cmd = floating_ip.ListFloatingIP(self.app, self.namespace) @@ -267,7 +275,58 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork): columns, data = self.cmd.take_action(parsed_args) - self.network.ips.assert_called_once_with(**{}) + self.network.ips.assert_called_once_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_floating_ip_list_network(self): + arglist = [ + '--network', 'fake_network_id', + ] + verifylist = [ + ('network', 'fake_network_id'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{ + 'floating_network_id': 'fake_network_id', + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_floating_ip_list_port(self): + arglist = [ + '--port', 'fake_port_id', + ] + verifylist = [ + ('port', 'fake_port_id'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{ + 'port_id': 'fake_port_id', + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_floating_ip_list_fixed_ip_address(self): + arglist = [ + '--fixed-ip-address', self.floating_ips[0].fixed_ip_address, + ] + verifylist = [ + ('fixed_ip_address', self.floating_ips[0].fixed_ip_address), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ips.assert_called_once_with(**{ + 'fixed_ip_address': self.floating_ips[0].fixed_ip_address, + }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -300,7 +359,7 @@ class TestShowFloatingIPNetwork(TestFloatingIPNetwork): floating_ip.floating_network_id, floating_ip.id, floating_ip.port_id, - floating_ip.tenant_id, + floating_ip.project_id, floating_ip.router_id, floating_ip.status, ) diff --git a/openstackclient/tests/unit/network/v2/test_network.py b/openstackclient/tests/unit/network/v2/test_network.py index 828da4a2..96b1b102 100644 --- a/openstackclient/tests/unit/network/v2/test_network.py +++ b/openstackclient/tests/unit/network/v2/test_network.py @@ -53,6 +53,8 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'availability_zone_hints': ["nova"], } ) + qos_policy = (network_fakes.FakeNetworkQosPolicy. + create_one_qos_policy(attrs={'id': _network.qos_policy_id})) columns = ( 'admin_state_up', @@ -65,6 +67,9 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'port_security_enabled', 'project_id', 'provider_network_type', + 'provider_physical_network', + 'provider_segmentation_id', + 'qos_policy_id', 'router:external', 'shared', 'status', @@ -82,6 +87,9 @@ class TestCreateNetworkIdentityV3(TestNetwork): _network.is_port_security_enabled, _network.project_id, _network.provider_network_type, + _network.provider_physical_network, + _network.provider_segmentation_id, + _network.qos_policy_id, network._format_router_external(_network.is_router_external), _network.shared, _network.status, @@ -98,6 +106,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): self.projects_mock.get.return_value = self.project self.domains_mock.get.return_value = self.domain + self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy) def test_create_no_options(self): arglist = [] @@ -140,6 +149,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): "--provider-network-type", "vlan", "--provider-physical-network", "physnet1", "--provider-segment", "400", + "--qos-policy", self.qos_policy.id, "--transparent-vlan", "--enable-port-security", self._network.name, @@ -156,6 +166,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): ('provider_network_type', 'vlan'), ('physical_network', 'physnet1'), ('segmentation_id', '400'), + ('qos_policy', self.qos_policy.id), ('transparent_vlan', True), ('enable_port_security', True), ('name', self._network.name), @@ -176,6 +187,7 @@ class TestCreateNetworkIdentityV3(TestNetwork): 'provider:network_type': 'vlan', 'provider:physical_network': 'physnet1', 'provider:segmentation_id': '400', + 'qos_policy_id': self.qos_policy.id, 'vlan_transparent': True, 'port_security_enabled': True, }) @@ -229,6 +241,9 @@ class TestCreateNetworkIdentityV2(TestNetwork): 'port_security_enabled', 'project_id', 'provider_network_type', + 'provider_physical_network', + 'provider_segmentation_id', + 'qos_policy_id', 'router:external', 'shared', 'status', @@ -246,6 +261,9 @@ class TestCreateNetworkIdentityV2(TestNetwork): _network.is_port_security_enabled, _network.project_id, _network.provider_network_type, + _network.provider_physical_network, + _network.provider_segmentation_id, + _network.qos_policy_id, network._format_router_external(_network.is_router_external), _network.shared, _network.status, @@ -681,11 +699,64 @@ class TestListNetwork(TestNetwork): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_network_list_provider_network_type(self): + network_type = self._network[0].provider_network_type + arglist = [ + '--provider-network-type', network_type, + ] + verifylist = [ + ('provider_network_type', network_type), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with( + **{'provider:network_type': network_type} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_network_list_provider_physical_network(self): + physical_network = self._network[0].provider_physical_network + arglist = [ + '--provider-physical-network', physical_network, + ] + verifylist = [ + ('physical_network', physical_network), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with( + **{'provider:physical_network': physical_network} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_network_list_provider_segment(self): + segmentation_id = self._network[0].provider_segmentation_id + arglist = [ + '--provider-segment', segmentation_id, + ] + verifylist = [ + ('segmentation_id', segmentation_id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + self.network.networks.assert_called_once_with( + **{'provider:segmentation_id': segmentation_id} + ) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + class TestSetNetwork(TestNetwork): # The network to set. _network = network_fakes.FakeNetwork.create_one_network() + qos_policy = (network_fakes.FakeNetworkQosPolicy. + create_one_qos_policy(attrs={'id': _network.qos_policy_id})) def setUp(self): super(TestSetNetwork, self).setUp() @@ -693,6 +764,7 @@ class TestSetNetwork(TestNetwork): self.network.update_network = mock.Mock(return_value=None) self.network.find_network = mock.Mock(return_value=self._network) + self.network.find_qos_policy = mock.Mock(return_value=self.qos_policy) # Get the command object to test self.cmd = network.SetNetwork(self.app, self.namespace) @@ -711,6 +783,7 @@ class TestSetNetwork(TestNetwork): '--provider-segment', '400', '--no-transparent-vlan', '--enable-port-security', + '--qos-policy', self.qos_policy.name, ] verifylist = [ ('network', self._network.name), @@ -725,6 +798,7 @@ class TestSetNetwork(TestNetwork): ('segmentation_id', '400'), ('no_transparent_vlan', True), ('enable_port_security', True), + ('qos_policy', self.qos_policy.name), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -742,6 +816,7 @@ class TestSetNetwork(TestNetwork): 'provider:segmentation_id': '400', 'vlan_transparent': False, 'port_security_enabled': True, + 'qos_policy_id': self.qos_policy.id, } self.network.update_network.assert_called_once_with( self._network, **attrs) @@ -754,6 +829,7 @@ class TestSetNetwork(TestNetwork): '--no-share', '--internal', '--disable-port-security', + '--no-qos-policy', ] verifylist = [ ('network', self._network.name), @@ -761,6 +837,7 @@ class TestSetNetwork(TestNetwork): ('no_share', True), ('internal', True), ('disable_port_security', True), + ('no_qos_policy', True), ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -771,6 +848,7 @@ class TestSetNetwork(TestNetwork): 'shared': False, 'router:external': False, 'port_security_enabled': False, + 'qos_policy_id': None, } self.network.update_network.assert_called_once_with( self._network, **attrs) @@ -805,6 +883,9 @@ class TestShowNetwork(TestNetwork): 'port_security_enabled', 'project_id', 'provider_network_type', + 'provider_physical_network', + 'provider_segmentation_id', + 'qos_policy_id', 'router:external', 'shared', 'status', @@ -822,6 +903,9 @@ class TestShowNetwork(TestNetwork): _network.is_port_security_enabled, _network.project_id, _network.provider_network_type, + _network.provider_physical_network, + _network.provider_segmentation_id, + _network.qos_policy_id, network._format_router_external(_network.is_router_external), _network.shared, _network.status, @@ -1111,10 +1195,7 @@ class TestListNetworkCompute(TestNetworkCompute): def test_network_list_no_options(self): arglist = [] - verifylist = [ - ('external', False), - ('long', False), - ] + verifylist = [] parsed_args = self.check_parser(self.cmd, arglist, verifylist) # In base command class Lister in cliff, abstract method take_action() diff --git a/openstackclient/tests/unit/network/v2/test_network_rbac.py b/openstackclient/tests/unit/network/v2/test_network_rbac.py index c526ae4e..b884dbc0 100644 --- a/openstackclient/tests/unit/network/v2/test_network_rbac.py +++ b/openstackclient/tests/unit/network/v2/test_network_rbac.py @@ -36,6 +36,7 @@ class TestNetworkRBAC(network_fakes.TestNetworkV2): class TestCreateNetworkRBAC(TestNetworkRBAC): network_object = network_fakes.FakeNetwork.create_one_network() + qos_object = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy() project = identity_fakes_v3.FakeProject.create_one_project() rbac_policy = network_fakes.FakeNetworkRBAC.create_one_network_rbac( attrs={'tenant_id': project.id, @@ -71,6 +72,8 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): return_value=self.rbac_policy) self.network.find_network = mock.Mock( return_value=self.network_object) + self.network.find_qos_policy = mock.Mock( + return_value=self.qos_object) self.projects_mock.get.return_value = self.project def test_network_rbac_create_no_type(self): @@ -194,6 +197,43 @@ class TestCreateNetworkRBAC(TestNetworkRBAC): self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) + def test_network_rbac_create_qos_object(self): + self.rbac_policy.object_type = 'qos_policy' + self.rbac_policy.object_id = self.qos_object.id + arglist = [ + '--type', 'qos_policy', + '--action', self.rbac_policy.action, + '--target-project', self.rbac_policy.target_tenant, + self.qos_object.name, + ] + verifylist = [ + ('type', 'qos_policy'), + ('action', self.rbac_policy.action), + ('target_project', self.rbac_policy.target_tenant), + ('rbac_object', self.qos_object.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + # DisplayCommandBase.take_action() returns two tuples + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_rbac_policy.assert_called_with(**{ + 'object_id': self.qos_object.id, + 'object_type': 'qos_policy', + 'action': self.rbac_policy.action, + 'target_tenant': self.rbac_policy.target_tenant, + }) + self.data = [ + self.rbac_policy.action, + self.rbac_policy.id, + self.qos_object.id, + 'qos_policy', + self.rbac_policy.tenant_id, + self.rbac_policy.target_tenant, + ] + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + class TestDeleteNetworkRBAC(TestNetworkRBAC): diff --git a/openstackclient/tests/unit/network/v2/test_network_service_provider.py b/openstackclient/tests/unit/network/v2/test_network_service_provider.py new file mode 100644 index 00000000..5ba85ddb --- /dev/null +++ b/openstackclient/tests/unit/network/v2/test_network_service_provider.py @@ -0,0 +1,71 @@ +# Copyright (c) 2016, Intel Corporation. +# 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. + +import mock + +from openstackclient.network.v2 import network_service_provider \ + as service_provider +from openstackclient.tests.unit.network.v2 import fakes + + +class TestNetworkServiceProvider(fakes.TestNetworkV2): + + def setUp(self): + super(TestNetworkServiceProvider, self).setUp() + self.network = self.app.client_manager.network + + +class TestListNetworkServiceProvider(TestNetworkServiceProvider): + provider_list = \ + fakes.FakeNetworkServiceProvider.create_network_service_providers( + count=2 + ) + + columns = ( + 'Service Type', + 'Name', + 'Default', + ) + + data = [] + + for provider in provider_list: + data.append(( + provider.service_type, + provider.name, + provider.is_default, + )) + + def setUp(self): + super(TestListNetworkServiceProvider, self).setUp() + self.network.service_providers = mock.Mock( + return_value=self.provider_list + ) + + self.cmd = \ + service_provider.ListNetworkServiceProvider(self.app, + self.namespace) + + def test_network_service_provider_list(self): + arglist = [] + verifylist = [] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.service_providers.assert_called_with() + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) diff --git a/openstackclient/tests/unit/network/v2/test_port.py b/openstackclient/tests/unit/network/v2/test_port.py index 4ff278a9..9312a897 100644 --- a/openstackclient/tests/unit/network/v2/test_port.py +++ b/openstackclient/tests/unit/network/v2/test_port.py @@ -41,6 +41,7 @@ class TestPort(network_fakes.TestNetworkV2): 'binding_vif_details', 'binding_vif_type', 'binding_vnic_type', + 'description', 'device_id', 'device_owner', 'dns_assignment', @@ -65,6 +66,7 @@ class TestPort(network_fakes.TestNetworkV2): utils.format_dict(fake_port.binding_vif_details), fake_port.binding_vif_type, fake_port.binding_vnic_type, + fake_port.description, fake_port.device_id, fake_port.device_owner, utils.format_list_of_dicts(fake_port.dns_assignment), @@ -130,6 +132,7 @@ class TestCreatePort(TestPort): '--mac-address', 'aa:aa:aa:aa:aa:aa', '--fixed-ip', 'subnet=%s,ip-address=10.0.0.2' % self.fake_subnet.id, + '--description', self._port.description, '--device', 'deviceid', '--device-owner', 'fakeowner', '--disable', @@ -146,6 +149,7 @@ class TestCreatePort(TestPort): 'fixed_ip', [{'subnet': self.fake_subnet.id, 'ip-address': '10.0.0.2'}] ), + ('description', self._port.description), ('device', 'deviceid'), ('device_owner', 'fakeowner'), ('disable', True), @@ -163,6 +167,7 @@ class TestCreatePort(TestPort): 'mac_address': 'aa:aa:aa:aa:aa:aa', 'fixed_ips': [{'subnet_id': self.fake_subnet.id, 'ip_address': '10.0.0.2'}], + 'description': self._port.description, 'device_id': 'deviceid', 'device_owner': 'fakeowner', 'admin_state_up': False, @@ -315,6 +320,54 @@ class TestCreatePort(TestPort): self.assertEqual(ref_columns, columns) self.assertEqual(ref_data, data) + def test_create_port_security_enabled(self): + arglist = [ + '--network', self._port.network_id, + '--enable-port-security', + 'test-port', + ] + verifylist = [ + ('network', self._port.network_id,), + ('enable', True), + ('enable_port_security', True), + ('name', 'test-port'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.network.create_port.assert_called_once_with(**{ + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'port_security_enabled': True, + 'name': 'test-port', + }) + + def test_create_port_security_disabled(self): + arglist = [ + '--network', self._port.network_id, + '--disable-port-security', + 'test-port', + ] + verifylist = [ + ('network', self._port.network_id,), + ('enable', True), + ('disable_port_security', True), + ('name', 'test-port'), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.network.create_port.assert_called_once_with(**{ + 'admin_state_up': True, + 'network_id': self._port.network_id, + 'port_security_enabled': False, + 'name': 'test-port', + }) + class TestDeletePort(TestPort): @@ -530,12 +583,14 @@ class TestListPort(TestPort): '--device-owner', self._ports[0].device_owner, '--router', 'fake-router-name', '--network', 'fake-network-name', + '--mac-address', self._ports[0].mac_address, ] verifylist = [ ('device_owner', self._ports[0].device_owner), ('router', 'fake-router-name'), - ('network', 'fake-network-name') + ('network', 'fake-network-name'), + ('mac_address', self._ports[0].mac_address) ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) @@ -545,7 +600,27 @@ class TestListPort(TestPort): self.network.ports.assert_called_once_with(**{ 'device_owner': self._ports[0].device_owner, 'device_id': 'fake-router-id', - 'network_id': 'fake-network-id' + 'network_id': 'fake-network-id', + 'mac_address': self._ports[0].mac_address + }) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_port_list_mac_address_opt(self): + arglist = [ + '--mac-address', self._ports[0].mac_address, + ] + + verifylist = [ + ('mac_address', self._ports[0].mac_address) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.ports.assert_called_once_with(**{ + 'mac_address': self._ports[0].mac_address }) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) @@ -693,6 +768,7 @@ class TestSetPort(TestPort): def test_set_that(self): arglist = [ + '--description', 'newDescription', '--enable', '--vnic-type', 'macvtap', '--binding-profile', 'foo=bar', @@ -701,6 +777,7 @@ class TestSetPort(TestPort): self._port.name, ] verifylist = [ + ('description', 'newDescription'), ('enable', True), ('vnic_type', 'macvtap'), ('binding_profile', {'foo': 'bar'}), @@ -717,6 +794,7 @@ class TestSetPort(TestPort): 'binding:vnic_type': 'macvtap', 'binding:profile': {'foo': 'bar'}, 'binding:host_id': 'binding-host-id-xxxx', + 'description': 'newDescription', 'name': 'newName', } self.network.update_port.assert_called_once_with(self._port, **attrs) @@ -868,6 +946,42 @@ class TestSetPort(TestPort): self.network.update_port.assert_called_once_with(_testport, **attrs) self.assertIsNone(result) + def test_port_security_enabled(self): + arglist = [ + '--enable-port-security', + self._port.id, + ] + verifylist = [ + ('enable_port_security', True), + ('port', self._port.id,) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.network.update_port.assert_called_once_with(self._port, **{ + 'port_security_enabled': True, + }) + + def test_port_security_disabled(self): + arglist = [ + '--disable-port-security', + self._port.id, + ] + verifylist = [ + ('disable_port_security', True), + ('port', self._port.id,) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + self.cmd.take_action(parsed_args) + + self.network.update_port.assert_called_once_with(self._port, **{ + 'port_security_enabled': False, + }) + class TestShowPort(TestPort): diff --git a/openstackclient/tests/unit/network/v2/test_security_group_rule.py b/openstackclient/tests/unit/network/v2/test_security_group_rule.py index 96d58e5c..5fe9013e 100644 --- a/openstackclient/tests/unit/network/v2/test_security_group_rule.py +++ b/openstackclient/tests/unit/network/v2/test_security_group_rule.py @@ -60,6 +60,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): network_fakes.FakeSecurityGroup.create_one_security_group() expected_columns = ( + 'description', 'direction', 'ethertype', 'id', @@ -81,6 +82,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.network.create_security_group_rule = mock.Mock( return_value=self._security_group_rule) self.expected_data = ( + self._security_group_rule.description, self._security_group_rule.direction, self._security_group_rule.ethertype, self._security_group_rule.id, @@ -119,6 +121,15 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertRaises(tests_utils.ParserException, self.check_parser, self.cmd, arglist, []) + def test_create_all_remote_options(self): + arglist = [ + '--remote-ip', '10.10.0.0/24', + '--remote-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + def test_create_bad_ethertype(self): arglist = [ '--ethertype', 'foo', @@ -213,7 +224,7 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) - def test_create_source_group(self): + def test_create_remote_group(self): self._setup_security_group_rule({ 'port_range_max': 22, 'port_range_min': 22, @@ -248,6 +259,34 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + def test_create_source_group(self): + self._setup_security_group_rule({ + 'remote_group_id': self._security_group.id, + }) + arglist = [ + '--ingress', + '--src-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('ingress', True), + ('src_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_group_id': self._security_group_rule.remote_group_id, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + def test_create_source_ip(self): self._setup_security_group_rule({ 'protocol': 'icmp', @@ -277,6 +316,35 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + def test_create_remote_ip(self): + self._setup_security_group_rule({ + 'protocol': 'icmp', + 'remote_ip_prefix': '10.0.2.0/24', + }) + arglist = [ + '--protocol', self._security_group_rule.protocol, + '--remote-ip', self._security_group_rule.remote_ip_prefix, + self._security_group.id, + ] + verifylist = [ + ('protocol', self._security_group_rule.protocol), + ('remote_ip', self._security_group_rule.remote_ip_prefix), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + def test_create_network_options(self): self._setup_security_group_rule({ 'direction': 'egress', @@ -452,6 +520,33 @@ class TestCreateSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertEqual(self.expected_columns, columns) self.assertEqual(self.expected_data, data) + def test_create_with_description(self): + self._setup_security_group_rule({ + 'description': 'Setting SGR', + }) + arglist = [ + '--description', self._security_group_rule.description, + self._security_group.id, + ] + verifylist = [ + ('description', self._security_group_rule.description), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = (self.cmd.take_action(parsed_args)) + + self.network.create_security_group_rule.assert_called_once_with(**{ + 'description': self._security_group_rule.description, + 'direction': self._security_group_rule.direction, + 'ethertype': self._security_group_rule.ethertype, + 'protocol': self._security_group_rule.protocol, + 'remote_ip_prefix': self._security_group_rule.remote_ip_prefix, + 'security_group_id': self._security_group.id, + }) + self.assertEqual(self.expected_columns, columns) + self.assertEqual(self.expected_data, data) + class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): @@ -498,6 +593,15 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): self.assertRaises(tests_utils.ParserException, self.check_parser, self.cmd, arglist, []) + def test_create_all_remote_options(self): + arglist = [ + '--remote-ip', '10.10.0.0/24', + '--remote-group', self._security_group.id, + self._security_group.id, + ] + self.assertRaises(tests_utils.ParserException, + self.check_parser, self.cmd, arglist, []) + def test_create_bad_protocol(self): arglist = [ '--protocol', 'foo', @@ -588,6 +692,38 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + def test_create_remote_group(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'from_port': 22, + 'to_port': 22, + 'group': {'name': self._security_group.name}, + }) + arglist = [ + '--dst-port', str(self._security_group_rule.from_port), + '--remote-group', self._security_group.name, + self._security_group.id, + ] + verifylist = [ + ('dst_port', (self._security_group_rule.from_port, + self._security_group_rule.to_port)), + ('remote_group', self._security_group.name), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + def test_create_source_ip(self): expected_columns, expected_data = self._setup_security_group_rule({ 'ip_protocol': 'icmp', @@ -620,6 +756,38 @@ class TestCreateSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): self.assertEqual(expected_columns, columns) self.assertEqual(expected_data, data) + def test_create_remote_ip(self): + expected_columns, expected_data = self._setup_security_group_rule({ + 'ip_protocol': 'icmp', + 'from_port': -1, + 'to_port': -1, + 'ip_range': {'cidr': '10.0.2.0/24'}, + }) + arglist = [ + '--protocol', self._security_group_rule.ip_protocol, + '--remote-ip', self._security_group_rule.ip_range['cidr'], + self._security_group.id, + ] + verifylist = [ + ('protocol', self._security_group_rule.ip_protocol), + ('remote_ip', self._security_group_rule.ip_range['cidr']), + ('group', self._security_group.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.compute.security_group_rules.create.assert_called_once_with( + self._security_group.id, + self._security_group_rule.ip_protocol, + self._security_group_rule.from_port, + self._security_group_rule.to_port, + self._security_group_rule.ip_range['cidr'], + None, + ) + self.assertEqual(expected_columns, columns) + self.assertEqual(expected_data, data) + def test_create_proto_option(self): expected_columns, expected_data = self._setup_security_group_rule({ 'ip_protocol': 'icmp', @@ -942,6 +1110,60 @@ class TestListSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): self.assertEqual(self.expected_columns_no_group, columns) self.assertEqual(self.expected_data_no_group, list(data)) + def test_list_with_protocol(self): + self._security_group_rule_tcp.port_range_min = 80 + arglist = [ + '--protocol', 'tcp', + ] + verifylist = [ + ('protocol', 'tcp'), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{ + 'protocol': 'tcp', + }) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_ingress(self): + self._security_group_rule_tcp.port_range_min = 80 + arglist = [ + '--ingress', + ] + verifylist = [ + ('ingress', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{ + 'direction': 'ingress', + }) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + + def test_list_with_wrong_egress(self): + self._security_group_rule_tcp.port_range_min = 80 + arglist = [ + '--egress', + ] + verifylist = [ + ('egress', True), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.network.security_group_rules.assert_called_once_with(**{ + 'direction': 'egress', + }) + self.assertEqual(self.expected_columns_no_group, columns) + self.assertEqual(self.expected_data_no_group, list(data)) + class TestListSecurityGroupRuleCompute(TestSecurityGroupRuleCompute): @@ -1075,6 +1297,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): network_fakes.FakeSecurityGroupRule.create_one_security_group_rule() columns = ( + 'description', 'direction', 'ethertype', 'id', @@ -1088,6 +1311,7 @@ class TestShowSecurityGroupRuleNetwork(TestSecurityGroupRuleNetwork): ) data = ( + _security_group_rule.description, _security_group_rule.direction, _security_group_rule.ethertype, _security_group_rule.id, diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py index 2d51aa4a..47de5616 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet.py +++ b/openstackclient/tests/unit/network/v2/test_subnet.py @@ -636,7 +636,7 @@ class TestListSubnet(TestSubnet): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'enable_dhcp': True} + filters = {'enable_dhcp': True, 'is_dhcp_enabled': True} self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -652,7 +652,7 @@ class TestListSubnet(TestSubnet): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'enable_dhcp': False} + filters = {'enable_dhcp': False, 'is_dhcp_enabled': False} self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -685,7 +685,7 @@ class TestListSubnet(TestSubnet): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -723,7 +723,7 @@ class TestListSubnet(TestSubnet): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.subnets.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -925,13 +925,16 @@ class TestSetSubnet(TestSubnet): {'host_routes': [{'destination': '10.20.20.0/24', 'nexthop': '10.20.20.1'}], 'allocation_pools': [{'start': '8.8.8.200', - 'end': '8.8.8.250'}], }) + 'end': '8.8.8.250'}], + 'dns_nameservers': ["10.0.0.1"], }) self.network.find_subnet = mock.Mock(return_value=_testsubnet) arglist = [ '--host-route', 'destination=10.30.30.30/24,gateway=10.30.30.1', '--no-host-route', '--allocation-pool', 'start=8.8.8.100,end=8.8.8.150', '--no-allocation-pool', + '--dns-nameserver', '10.1.10.1', + '--no-dns-nameservers', _testsubnet.name, ] verifylist = [ @@ -939,6 +942,8 @@ class TestSetSubnet(TestSubnet): "destination": "10.30.30.30/24", "gateway": "10.30.30.1"}]), ('allocation_pools', [{ 'start': '8.8.8.100', 'end': '8.8.8.150'}]), + ('dns_nameservers', ['10.1.10.1']), + ('no_dns_nameservers', True), ('no_host_route', True), ('no_allocation_pool', True), ] @@ -948,6 +953,7 @@ class TestSetSubnet(TestSubnet): 'host_routes': [{ "destination": "10.30.30.30/24", "nexthop": "10.30.30.1"}], 'allocation_pools': [{'start': '8.8.8.100', 'end': '8.8.8.150'}], + 'dns_nameservers': ["10.1.10.1"], } self.network.update_subnet.assert_called_once_with( _testsubnet, **attrs) diff --git a/openstackclient/tests/unit/network/v2/test_subnet_pool.py b/openstackclient/tests/unit/network/v2/test_subnet_pool.py index fa6ffff3..f12537e7 100644 --- a/openstackclient/tests/unit/network/v2/test_subnet_pool.py +++ b/openstackclient/tests/unit/network/v2/test_subnet_pool.py @@ -435,7 +435,7 @@ class TestListSubnetPool(TestSubnetPool): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'shared': False} + filters = {'shared': False, 'is_shared': False} self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -451,7 +451,7 @@ class TestListSubnetPool(TestSubnetPool): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'shared': True} + filters = {'shared': True, 'is_shared': True} self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -501,7 +501,7 @@ class TestListSubnetPool(TestSubnetPool): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) @@ -521,7 +521,7 @@ class TestListSubnetPool(TestSubnetPool): parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) - filters = {'tenant_id': project.id} + filters = {'tenant_id': project.id, 'project_id': project.id} self.network.subnet_pools.assert_called_once_with(**filters) self.assertEqual(self.columns, columns) diff --git a/openstackclient/tests/unit/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py index a11ea491..78a8227e 100644 --- a/openstackclient/tests/unit/volume/v1/fakes.py +++ b/openstackclient/tests/unit/volume/v1/fakes.py @@ -23,115 +23,6 @@ from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes from openstackclient.tests.unit import utils -volume_id = 'vvvvvvvv-vvvv-vvvv-vvvvvvvv' -volume_name = 'nigel' -volume_description = 'Nigel Tufnel' -volume_status = 'available' -volume_size = 120 -volume_type = 'to-eleven' -volume_zone = 'stonehenge' -volume_metadata = { - 'Alpha': 'a', - 'Beta': 'b', - 'Gamma': 'g', -} -volume_metadata_str = "Alpha='a', Beta='b', Gamma='g'" - -VOLUME = { - 'id': volume_id, - 'display_name': volume_name, - 'display_description': volume_description, - 'size': volume_size, - 'status': volume_status, - 'attach_status': 'detached', - 'availability_zone': volume_zone, - 'volume_type': volume_type, - 'metadata': volume_metadata, -} - -extension_name = 'SchedulerHints' -extension_namespace = 'http://docs.openstack.org/'\ - 'block-service/ext/scheduler-hints/api/v2' -extension_description = 'Pass arbitrary key/value'\ - 'pairs to the scheduler.' -extension_updated = '2014-02-07T12:00:0-00:00' -extension_alias = 'OS-SCH-HNT' -extension_links = '[{"href":'\ - '"https://github.com/openstack/block-api", "type":'\ - ' "text/html", "rel": "describedby"}]' - -EXTENSION = { - 'name': extension_name, - 'namespace': extension_namespace, - 'description': extension_description, - 'updated': extension_updated, - 'alias': extension_alias, - 'links': extension_links, -} - -# NOTE(dtroyer): duplicating here the minimum image info needed to test -# volume create --image until circular references can be -# avoided by refactoring the test fakes. - -image_id = 'im1' -image_name = 'graven' - - -IMAGE = { - 'id': image_id, - 'name': image_name, -} - -type_id = "5520dc9e-6f9b-4378-a719-729911c0f407" -type_name = "fake-lvmdriver-1" - -TYPE = { - 'id': type_id, - 'name': type_name -} - -qos_id = '6f2be1de-997b-4230-b76c-a3633b59e8fb' -qos_consumer = 'front-end' -qos_default_consumer = 'both' -qos_name = "fake-qos-specs" -qos_specs = { - 'foo': 'bar', - 'iops': '9001' -} -qos_association = { - 'association_type': 'volume_type', - 'name': type_name, - 'id': type_id -} - -QOS = { - 'id': qos_id, - 'consumer': qos_consumer, - 'name': qos_name -} - -QOS_DEFAULT_CONSUMER = { - 'id': qos_id, - 'consumer': qos_default_consumer, - 'name': qos_name -} - -QOS_WITH_SPECS = { - 'id': qos_id, - 'consumer': qos_consumer, - 'name': qos_name, - 'specs': qos_specs -} - -QOS_WITH_ASSOCIATIONS = { - 'id': qos_id, - 'consumer': qos_consumer, - 'name': qos_name, - 'specs': qos_specs, - 'associations': [qos_association] -} - - class FakeTransfer(object): """Fake one or more Transfer.""" diff --git a/openstackclient/tests/unit/volume/v1/test_backup.py b/openstackclient/tests/unit/volume/v1/test_backup.py index 32c2fd22..1097d3f1 100644 --- a/openstackclient/tests/unit/volume/v1/test_backup.py +++ b/openstackclient/tests/unit/volume/v1/test_backup.py @@ -249,26 +249,65 @@ class TestBackupList(TestBackup): self.volumes_mock.list.return_value = [self.volume] self.backups_mock.list.return_value = self.backups + self.volumes_mock.get.return_value = self.volume # Get the command to test self.cmd = backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): arglist = [] - verifylist = [("long", False)] + verifylist = [ + ("long", False), + ("name", None), + ("status", None), + ("volume", None), + ('all_projects', False), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + search_opts = { + "name": None, + "status": None, + "volume_id": None, + "all_tenants": False, + } + self.volumes_mock.get.assert_not_called() + self.backups_mock.list.assert_called_with( + search_opts=search_opts, + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) def test_backup_list_with_options(self): - arglist = ["--long"] - verifylist = [("long", True)] + arglist = [ + "--long", + "--name", self.backups[0].name, + "--status", "error", + "--volume", self.volume.id, + "--all-projects" + ] + verifylist = [ + ("long", True), + ("name", self.backups[0].name), + ("status", "error"), + ("volume", self.volume.id), + ('all_projects', True), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + search_opts = { + "name": self.backups[0].name, + "status": "error", + "volume_id": self.volume.id, + "all_tenants": True, + } + self.volumes_mock.get.assert_called_once_with(self.volume.id) + self.backups_mock.list.assert_called_with( + search_opts=search_opts, + ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) diff --git a/openstackclient/tests/unit/volume/v1/test_type.py b/openstackclient/tests/unit/volume/v1/test_type.py index 23a1186d..81ad8301 100644 --- a/openstackclient/tests/unit/volume/v1/test_type.py +++ b/openstackclient/tests/unit/volume/v1/test_type.py @@ -158,11 +158,13 @@ class TestTypeList(TestType): columns = ( "ID", - "Name" + "Name", + "Is Public", ) columns_long = ( "ID", "Name", + "Is Public", "Properties" ) @@ -171,12 +173,14 @@ class TestTypeList(TestType): data.append(( t.id, t.name, + t.is_public, )) data_long = [] for t in volume_types: data_long.append(( t.id, t.name, + t.is_public, utils.format_dict(t.extra_specs), )) diff --git a/openstackclient/tests/unit/volume/v1/test_volume.py b/openstackclient/tests/unit/volume/v1/test_volume.py index 73c00844..7a44dea8 100644 --- a/openstackclient/tests/unit/volume/v1/test_volume.py +++ b/openstackclient/tests/unit/volume/v1/test_volume.py @@ -14,15 +14,14 @@ # import argparse -import copy import mock from mock import call from osc_lib import exceptions from osc_lib import utils -from openstackclient.tests.unit import fakes from openstackclient.tests.unit.identity.v2_0 import fakes as identity_fakes +from openstackclient.tests.unit.image.v1 import fakes as image_fakes from openstackclient.tests.unit import utils as tests_utils from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes from openstackclient.volume.v1 import volume @@ -58,10 +57,6 @@ class TestVolume(volume_fakes.TestVolumev1): return volumes -# TODO(dtroyer): The volume create tests are incomplete, only the minimal -# options and the options that require additional processing -# are implemented at this time. - class TestVolumeCreate(TestVolume): project = identity_fakes.FakeProject.create_one_project() @@ -321,19 +316,16 @@ class TestVolumeCreate(TestVolume): self.assertEqual(self.datalist, data) def test_volume_create_image_id(self): - self.images_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(volume_fakes.IMAGE), - loaded=True, - ) + image = image_fakes.FakeImage.create_one_image() + self.images_mock.get.return_value = image arglist = [ - '--image', volume_fakes.image_id, + '--image', image.id, '--size', str(self.new_volume.size), self.new_volume.display_name, ] verifylist = [ - ('image', volume_fakes.image_id), + ('image', image.id), ('size', self.new_volume.size), ('name', self.new_volume.display_name), ] @@ -360,26 +352,23 @@ class TestVolumeCreate(TestVolume): None, None, None, - volume_fakes.image_id, + image.id, ) self.assertEqual(self.columns, columns) self.assertEqual(self.datalist, data) def test_volume_create_image_name(self): - self.images_mock.get.return_value = fakes.FakeResource( - None, - copy.deepcopy(volume_fakes.IMAGE), - loaded=True, - ) + image = image_fakes.FakeImage.create_one_image() + self.images_mock.get.return_value = image arglist = [ - '--image', volume_fakes.image_name, + '--image', image.name, '--size', str(self.new_volume.size), self.new_volume.display_name, ] verifylist = [ - ('image', volume_fakes.image_name), + ('image', image.name), ('size', self.new_volume.size), ('name', self.new_volume.display_name), ] @@ -406,7 +395,7 @@ class TestVolumeCreate(TestVolume): None, None, None, - volume_fakes.image_id, + image.id, ) self.assertEqual(self.columns, columns) @@ -739,6 +728,68 @@ class TestVolumeList(TestVolume): self.cmd, arglist, verifylist) +class TestVolumeMigrate(TestVolume): + + _volume = volume_fakes.FakeVolume.create_one_volume() + + def setUp(self): + super(TestVolumeMigrate, self).setUp() + + self.volumes_mock.get.return_value = self._volume + self.volumes_mock.migrate_volume.return_value = None + # Get the command object to test + self.cmd = volume.MigrateVolume(self.app, None) + + def test_volume_migrate(self): + arglist = [ + "--host", "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("host", "host@backend-name#pool"), + ("volume", self._volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_once_with(self._volume.id) + self.volumes_mock.migrate_volume.assert_called_once_with( + self._volume.id, "host@backend-name#pool", False) + self.assertIsNone(result) + + def test_volume_migrate_with_option(self): + arglist = [ + "--force-host-copy", + "--host", "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", True), + ("host", "host@backend-name#pool"), + ("volume", self._volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_once_with(self._volume.id) + self.volumes_mock.migrate_volume.assert_called_once_with( + self._volume.id, "host@backend-name#pool", True) + self.assertIsNone(result) + + def test_volume_migrate_without_host(self): + arglist = [ + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("volume", self._volume.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + class TestVolumeSet(TestVolume): _volume = volume_fakes.FakeVolume.create_one_volume() @@ -844,8 +895,7 @@ class TestVolumeSet(TestVolume): ) self.assertIsNone(result) - @mock.patch.object(volume.LOG, 'error') - def test_volume_set_size_smaller(self, mock_log_error): + def test_volume_set_size_smaller(self): self._volume.status = 'available' arglist = [ '--size', '1', @@ -860,15 +910,11 @@ class TestVolumeSet(TestVolume): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - mock_log_error.assert_called_with("New size must be greater " - "than %s GB", - self._volume.size) - self.assertIsNone(result) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) - @mock.patch.object(volume.LOG, 'error') - def test_volume_set_size_not_available(self, mock_log_error): + def test_volume_set_size_not_available(self): self._volume.status = 'error' arglist = [ '--size', '130', @@ -883,12 +929,9 @@ class TestVolumeSet(TestVolume): ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) - result = self.cmd.take_action(parsed_args) - - mock_log_error.assert_called_with("Volume is in %s state, it must be " - "available before size can be " - "extended", 'error') - self.assertIsNone(result) + self.assertRaises(exceptions.CommandError, + self.cmd.take_action, + parsed_args) def test_volume_set_property(self): arglist = [ @@ -896,6 +939,8 @@ class TestVolumeSet(TestVolume): self._volume.display_name, ] verifylist = [ + ('read_only', False), + ('read_write', False), ('name', None), ('description', None), ('size', None), @@ -916,6 +961,7 @@ class TestVolumeSet(TestVolume): self._volume.id, metadata ) + self.volumes_mock.update_readonly_flag.assert_not_called() self.assertIsNone(result) def test_volume_set_bootable(self): @@ -943,6 +989,44 @@ class TestVolumeSet(TestVolume): self.volumes_mock.set_bootable.assert_called_with( self._volume.id, verifylist[index][0][1]) + def test_volume_set_readonly(self): + arglist = [ + '--read-only', + self._volume.id + ] + verifylist = [ + ('read_only', True), + ('read_write', False), + ('volume', self._volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.update_readonly_flag.assert_called_once_with( + self._volume.id, + True) + self.assertIsNone(result) + + def test_volume_set_read_write(self): + arglist = [ + '--read-write', + self._volume.id + ] + verifylist = [ + ('read_only', False), + ('read_write', True), + ('volume', self._volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.update_readonly_flag.assert_called_once_with( + self._volume.id, + False) + self.assertIsNone(result) + class TestVolumeShow(TestVolume): diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py index 5e1d16e1..3137bfb0 100644 --- a/openstackclient/tests/unit/volume/v2/fakes.py +++ b/openstackclient/tests/unit/volume/v2/fakes.py @@ -224,6 +224,8 @@ class FakeVolumeClient(object): self.quota_classes.resource_class = fakes.FakeResource(None, {}) self.consistencygroups = mock.Mock() self.consistencygroups.resource_class = fakes.FakeResource(None, {}) + self.cgsnapshots = mock.Mock() + self.cgsnapshots.resource_class = fakes.FakeResource(None, {}) self.auth_token = kwargs['token'] self.management_url = kwargs['endpoint'] @@ -248,10 +250,7 @@ class TestVolume(utils.TestCommand): class FakeVolume(object): - """Fake one or more volumes. - - TODO(xiexs): Currently, only volume API v2 is supported by this class. - """ + """Fake one or more volumes.""" @staticmethod def create_one_volume(attrs=None): @@ -547,6 +546,106 @@ class FakeConsistencyGroup(object): return consistency_groups + @staticmethod + def get_consistency_groups(consistency_groups=None, count=2): + """Note: + + Get an iterable MagicMock object with a list of faked + consistency_groups. + + If consistency_groups list is provided, then initialize + the Mock object with the list. Otherwise create one. + + :param List consistency_groups: + A list of FakeResource objects faking consistency_groups + :param Integer count: + The number of consistency_groups to be faked + :return + An iterable Mock object with side_effect set to a list of faked + consistency_groups + """ + if consistency_groups is None: + consistency_groups = (FakeConsistencyGroup. + create_consistency_groups(count)) + + return mock.Mock(side_effect=consistency_groups) + + +class FakeConsistencyGroupSnapshot(object): + """Fake one or more consistency group snapshot.""" + + @staticmethod + def create_one_consistency_group_snapshot(attrs=None): + """Create a fake consistency group snapshot. + + :param Dictionary attrs: + A dictionary with all attributes + :return: + A FakeResource object with id, name, description, etc. + """ + attrs = attrs or {} + + # Set default attributes. + consistency_group_snapshot_info = { + "id": 'id-' + uuid.uuid4().hex, + "name": 'backup-name-' + uuid.uuid4().hex, + "description": 'description-' + uuid.uuid4().hex, + "status": "error", + "consistencygroup_id": 'consistency-group-id' + uuid.uuid4().hex, + "created_at": 'time-' + uuid.uuid4().hex, + } + + # Overwrite default attributes. + consistency_group_snapshot_info.update(attrs) + + consistency_group_snapshot = fakes.FakeResource( + info=copy.deepcopy(consistency_group_snapshot_info), + loaded=True) + return consistency_group_snapshot + + @staticmethod + def create_consistency_group_snapshots(attrs=None, count=2): + """Create multiple fake consistency group snapshots. + + :param Dictionary attrs: + A dictionary with all attributes + :param int count: + The number of consistency group snapshots to fake + :return: + A list of FakeResource objects faking the + consistency group snapshots + """ + consistency_group_snapshots = [] + for i in range(0, count): + consistency_group_snapshot = ( + FakeConsistencyGroupSnapshot. + create_one_consistency_group_snapshot(attrs) + ) + consistency_group_snapshots.append(consistency_group_snapshot) + + return consistency_group_snapshots + + @staticmethod + def get_consistency_group_snapshots(snapshots=None, count=2): + """Get an iterable MagicMock object with a list of faked cgsnapshots. + + If consistenct group snapshots list is provided, then initialize + the Mock object with the list. Otherwise create one. + + :param List snapshots: + A list of FakeResource objects faking consistency group snapshots + :param Integer count: + The number of consistency group snapshots to be faked + :return + An iterable Mock object with side_effect set to a list of faked + consistency groups + """ + if snapshots is None: + snapshots = (FakeConsistencyGroupSnapshot. + create_consistency_group_snapshots(count)) + + return mock.Mock(side_effect=snapshots) + class FakeExtension(object): """Fake one or more extension.""" diff --git a/openstackclient/tests/unit/volume/v2/test_backup.py b/openstackclient/tests/unit/volume/v2/test_backup.py index 306c9eb3..10e7aac5 100644 --- a/openstackclient/tests/unit/volume/v2/test_backup.py +++ b/openstackclient/tests/unit/volume/v2/test_backup.py @@ -280,26 +280,78 @@ class TestBackupList(TestBackup): self.volumes_mock.list.return_value = [self.volume] self.backups_mock.list.return_value = self.backups + self.volumes_mock.get.return_value = self.volume + self.backups_mock.get.return_value = self.backups[0] # Get the command to test self.cmd = backup.ListVolumeBackup(self.app, None) def test_backup_list_without_options(self): arglist = [] - verifylist = [("long", False)] + verifylist = [ + ("long", False), + ("name", None), + ("status", None), + ("volume", None), + ("marker", None), + ("limit", None), + ('all_projects', False), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + search_opts = { + "name": None, + "status": None, + "volume_id": None, + 'all_tenants': False, + } + self.volumes_mock.get.assert_not_called() + self.backups_mock.get.assert_not_called() + self.backups_mock.list.assert_called_with( + search_opts=search_opts, + marker=None, + limit=None, + ) self.assertEqual(self.columns, columns) self.assertEqual(self.data, list(data)) def test_backup_list_with_options(self): - arglist = ["--long"] - verifylist = [("long", True)] + arglist = [ + "--long", + "--name", self.backups[0].name, + "--status", "error", + "--volume", self.volume.id, + "--marker", self.backups[0].id, + "--all-projects", + "--limit", "3", + ] + verifylist = [ + ("long", True), + ("name", self.backups[0].name), + ("status", "error"), + ("volume", self.volume.id), + ("marker", self.backups[0].id), + ('all_projects', True), + ("limit", 3), + ] parsed_args = self.check_parser(self.cmd, arglist, verifylist) columns, data = self.cmd.take_action(parsed_args) + search_opts = { + "name": self.backups[0].name, + "status": "error", + "volume_id": self.volume.id, + 'all_tenants': True, + } + self.volumes_mock.get.assert_called_once_with(self.volume.id) + self.backups_mock.get.assert_called_once_with(self.backups[0].id) + self.backups_mock.list.assert_called_with( + search_opts=search_opts, + marker=self.backups[0].id, + limit=3, + ) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py index 00e1b60e..5beb6ef2 100644 --- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py @@ -12,6 +12,10 @@ # under the License. # +import mock +from mock import call + +from osc_lib import exceptions from osc_lib import utils from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes @@ -28,6 +32,235 @@ class TestConsistencyGroup(volume_fakes.TestVolume): self.app.client_manager.volume.consistencygroups) self.consistencygroups_mock.reset_mock() + self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock.reset_mock() + + +class TestConsistencyGroupCreate(TestConsistencyGroup): + + volume_type = volume_fakes.FakeType.create_one_type() + new_consistency_group = ( + volume_fakes.FakeConsistencyGroup.create_one_consistency_group()) + + columns = ( + 'availability_zone', + 'created_at', + 'description', + 'id', + 'name', + 'status', + 'volume_types', + ) + data = ( + new_consistency_group.availability_zone, + new_consistency_group.created_at, + new_consistency_group.description, + new_consistency_group.id, + new_consistency_group.name, + new_consistency_group.status, + new_consistency_group.volume_types, + ) + + def setUp(self): + super(TestConsistencyGroupCreate, self).setUp() + self.consistencygroups_mock.create.return_value = ( + self.new_consistency_group) + self.consistencygroups_mock.create_from_src.return_value = ( + self.new_consistency_group) + self.consistencygroups_mock.get.return_value = ( + self.new_consistency_group) + self.types_mock.get.return_value = self.volume_type + + # Get the command object to test + self.cmd = consistency_group.CreateConsistencyGroup(self.app, None) + + def test_consistency_group_create(self): + arglist = [ + '--volume-type', self.volume_type.id, + '--description', self.new_consistency_group.description, + '--availability-zone', + self.new_consistency_group.availability_zone, + self.new_consistency_group.name, + ] + verifylist = [ + ('volume_type', self.volume_type.id), + ('description', self.new_consistency_group.description), + ('availability_zone', + self.new_consistency_group.availability_zone), + ('name', self.new_consistency_group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.types_mock.get.assert_called_once_with( + self.volume_type.id) + self.consistencygroups_mock.get.assert_not_called() + self.consistencygroups_mock.create.assert_called_once_with( + self.volume_type.id, + name=self.new_consistency_group.name, + description=self.new_consistency_group.description, + availability_zone=self.new_consistency_group.availability_zone, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_consistency_group_create_without_name(self): + arglist = [ + '--volume-type', self.volume_type.id, + '--description', self.new_consistency_group.description, + '--availability-zone', + self.new_consistency_group.availability_zone, + ] + verifylist = [ + ('volume_type', self.volume_type.id), + ('description', self.new_consistency_group.description), + ('availability_zone', + self.new_consistency_group.availability_zone), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.types_mock.get.assert_called_once_with( + self.volume_type.id) + self.consistencygroups_mock.get.assert_not_called() + self.consistencygroups_mock.create.assert_called_once_with( + self.volume_type.id, + name=None, + description=self.new_consistency_group.description, + availability_zone=self.new_consistency_group.availability_zone, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_consistency_group_create_from_source(self): + arglist = [ + '--consistency-group-source', self.new_consistency_group.id, + '--description', self.new_consistency_group.description, + self.new_consistency_group.name, + ] + verifylist = [ + ('consistency_group_source', self.new_consistency_group.id), + ('description', self.new_consistency_group.description), + ('name', self.new_consistency_group.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.types_mock.get.assert_not_called() + self.consistencygroups_mock.get.assert_called_once_with( + self.new_consistency_group.id) + self.consistencygroups_mock.create_from_src.assert_called_with( + None, + self.new_consistency_group.id, + name=self.new_consistency_group.name, + description=self.new_consistency_group.description, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestConsistencyGroupDelete(TestConsistencyGroup): + + consistency_groups =\ + volume_fakes.FakeConsistencyGroup.create_consistency_groups(count=2) + + def setUp(self): + super(TestConsistencyGroupDelete, self).setUp() + + self.consistencygroups_mock.get = volume_fakes.FakeConsistencyGroup.\ + get_consistency_groups(self.consistency_groups) + self.consistencygroups_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = consistency_group.DeleteConsistencyGroup(self.app, None) + + def test_consistency_group_delete(self): + arglist = [ + self.consistency_groups[0].id + ] + verifylist = [ + ("consistency_groups", [self.consistency_groups[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.consistencygroups_mock.delete.assert_called_with( + self.consistency_groups[0].id, False) + self.assertIsNone(result) + + def test_consistency_group_delete_with_force(self): + arglist = [ + '--force', + self.consistency_groups[0].id, + ] + verifylist = [ + ('force', True), + ("consistency_groups", [self.consistency_groups[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.consistencygroups_mock.delete.assert_called_with( + self.consistency_groups[0].id, True) + self.assertIsNone(result) + + def test_delete_multiple_consistency_groups(self): + arglist = [] + for b in self.consistency_groups: + arglist.append(b.id) + verifylist = [ + ('consistency_groups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for b in self.consistency_groups: + calls.append(call(b.id, False)) + self.consistencygroups_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + def test_delete_multiple_consistency_groups_with_exception(self): + arglist = [ + self.consistency_groups[0].id, + 'unexist_consistency_group', + ] + verifylist = [ + ('consistency_groups', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + find_mock_result = [self.consistency_groups[0], + exceptions.CommandError] + with mock.patch.object(utils, 'find_resource', + side_effect=find_mock_result) as find_mock: + try: + self.cmd.take_action(parsed_args) + self.fail('CommandError should be raised.') + except exceptions.CommandError as e: + self.assertEqual('1 of 2 consistency groups failed to delete.', + str(e)) + + find_mock.assert_any_call(self.consistencygroups_mock, + self.consistency_groups[0].id) + find_mock.assert_any_call(self.consistencygroups_mock, + 'unexist_consistency_group') + + self.assertEqual(2, find_mock.call_count) + self.consistencygroups_mock.delete.assert_called_once_with( + self.consistency_groups[0].id, False + ) + class TestConsistencyGroupList(TestConsistencyGroup): @@ -120,3 +353,46 @@ class TestConsistencyGroupList(TestConsistencyGroup): detailed=True, search_opts={'all_tenants': False}) self.assertEqual(self.columns_long, columns) self.assertEqual(self.data_long, list(data)) + + +class TestConsistencyGroupShow(TestConsistencyGroup): + columns = ( + 'availability_zone', + 'created_at', + 'description', + 'id', + 'name', + 'status', + 'volume_types', + ) + + def setUp(self): + super(TestConsistencyGroupShow, self).setUp() + + self.consistency_group = ( + volume_fakes.FakeConsistencyGroup.create_one_consistency_group()) + self.data = ( + self.consistency_group.availability_zone, + self.consistency_group.created_at, + self.consistency_group.description, + self.consistency_group.id, + self.consistency_group.name, + self.consistency_group.status, + self.consistency_group.volume_types, + ) + self.consistencygroups_mock.get.return_value = self.consistency_group + self.cmd = consistency_group.ShowConsistencyGroup(self.app, None) + + def test_consistency_group_show(self): + arglist = [ + self.consistency_group.id + ] + verifylist = [ + ("consistency_group", self.consistency_group.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.consistencygroups_mock.get.assert_called_once_with( + self.consistency_group.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py new file mode 100644 index 00000000..3bfe93df --- /dev/null +++ b/openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py @@ -0,0 +1,351 @@ +# +# 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. +# + +from mock import call + +from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes +from openstackclient.volume.v2 import consistency_group_snapshot + + +class TestConsistencyGroupSnapshot(volume_fakes.TestVolume): + + def setUp(self): + super(TestConsistencyGroupSnapshot, self).setUp() + + # Get a shortcut to the TransferManager Mock + self.cgsnapshots_mock = ( + self.app.client_manager.volume.cgsnapshots) + self.cgsnapshots_mock.reset_mock() + self.consistencygroups_mock = ( + self.app.client_manager.volume.consistencygroups) + self.consistencygroups_mock.reset_mock() + + +class TestConsistencyGroupSnapshotCreate(TestConsistencyGroupSnapshot): + + _consistency_group_snapshot = ( + volume_fakes. + FakeConsistencyGroupSnapshot. + create_one_consistency_group_snapshot() + ) + consistency_group = ( + volume_fakes.FakeConsistencyGroup.create_one_consistency_group()) + + columns = ( + 'consistencygroup_id', + 'created_at', + 'description', + 'id', + 'name', + 'status', + ) + data = ( + _consistency_group_snapshot.consistencygroup_id, + _consistency_group_snapshot.created_at, + _consistency_group_snapshot.description, + _consistency_group_snapshot.id, + _consistency_group_snapshot.name, + _consistency_group_snapshot.status, + ) + + def setUp(self): + super(TestConsistencyGroupSnapshotCreate, self).setUp() + self.cgsnapshots_mock.create.return_value = ( + self._consistency_group_snapshot) + self.consistencygroups_mock.get.return_value = ( + self.consistency_group) + + # Get the command object to test + self.cmd = (consistency_group_snapshot. + CreateConsistencyGroupSnapshot(self.app, None)) + + def test_consistency_group_snapshot_create(self): + arglist = [ + '--consistency-group', self.consistency_group.id, + '--description', self._consistency_group_snapshot.description, + self._consistency_group_snapshot.name, + ] + verifylist = [ + ('consistency_group', self.consistency_group.id), + ('description', self._consistency_group_snapshot.description), + ('snapshot_name', self._consistency_group_snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.consistencygroups_mock.get.assert_called_once_with( + self.consistency_group.id) + self.cgsnapshots_mock.create.assert_called_once_with( + self.consistency_group.id, + name=self._consistency_group_snapshot.name, + description=self._consistency_group_snapshot.description, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + def test_consistency_group_snapshot_create_no_consistency_group(self): + arglist = [ + '--description', self._consistency_group_snapshot.description, + self._consistency_group_snapshot.name, + ] + verifylist = [ + ('description', self._consistency_group_snapshot.description), + ('snapshot_name', self._consistency_group_snapshot.name), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + columns, data = self.cmd.take_action(parsed_args) + + self.consistencygroups_mock.get.assert_called_once_with( + self._consistency_group_snapshot.name) + self.cgsnapshots_mock.create.assert_called_once_with( + self.consistency_group.id, + name=self._consistency_group_snapshot.name, + description=self._consistency_group_snapshot.description, + ) + + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) + + +class TestConsistencyGroupSnapshotDelete(TestConsistencyGroupSnapshot): + + consistency_group_snapshots = ( + volume_fakes.FakeConsistencyGroupSnapshot. + create_consistency_group_snapshots(count=2) + ) + + def setUp(self): + super(TestConsistencyGroupSnapshotDelete, self).setUp() + + self.cgsnapshots_mock.get = ( + volume_fakes.FakeConsistencyGroupSnapshot. + get_consistency_group_snapshots(self.consistency_group_snapshots) + ) + self.cgsnapshots_mock.delete.return_value = None + + # Get the command object to mock + self.cmd = (consistency_group_snapshot. + DeleteConsistencyGroupSnapshot(self.app, None)) + + def test_consistency_group_snapshot_delete(self): + arglist = [ + self.consistency_group_snapshots[0].id + ] + verifylist = [ + ("consistency_group_snapshot", + [self.consistency_group_snapshots[0].id]) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + + self.cgsnapshots_mock.delete.assert_called_once_with( + self.consistency_group_snapshots[0].id) + self.assertIsNone(result) + + def test_multiple_consistency_group_snapshots_delete(self): + arglist = [] + for c in self.consistency_group_snapshots: + arglist.append(c.id) + verifylist = [ + ('consistency_group_snapshot', arglist), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + result = self.cmd.take_action(parsed_args) + + calls = [] + for c in self.consistency_group_snapshots: + calls.append(call(c.id)) + self.cgsnapshots_mock.delete.assert_has_calls(calls) + self.assertIsNone(result) + + +class TestConsistencyGroupSnapshotList(TestConsistencyGroupSnapshot): + + consistency_group_snapshots = ( + volume_fakes.FakeConsistencyGroupSnapshot. + create_consistency_group_snapshots(count=2) + ) + consistency_group = ( + volume_fakes.FakeConsistencyGroup.create_one_consistency_group() + ) + + columns = [ + 'ID', + 'Status', + 'Name', + ] + columns_long = [ + 'ID', + 'Status', + 'ConsistencyGroup ID', + 'Name', + 'Description', + 'Created At', + ] + data = [] + for c in consistency_group_snapshots: + data.append(( + c.id, + c.status, + c.name, + )) + data_long = [] + for c in consistency_group_snapshots: + data_long.append(( + c.id, + c.status, + c.consistencygroup_id, + c.name, + c.description, + c.created_at, + )) + + def setUp(self): + super(TestConsistencyGroupSnapshotList, self).setUp() + + self.cgsnapshots_mock.list.return_value = ( + self.consistency_group_snapshots) + self.consistencygroups_mock.get.return_value = self.consistency_group + # Get the command to test + self.cmd = ( + consistency_group_snapshot. + ListConsistencyGroupSnapshot(self.app, None) + ) + + def test_consistency_group_snapshot_list_without_options(self): + arglist = [] + verifylist = [ + ("all_projects", False), + ("long", False), + ("status", None), + ("consistency_group", None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'status': None, + 'consistencygroup_id': None, + } + self.cgsnapshots_mock.list.assert_called_once_with( + detailed=True, search_opts=search_opts) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + def test_consistency_group_snapshot_list_with_long(self): + arglist = [ + "--long", + ] + verifylist = [ + ("all_projects", False), + ("long", True), + ("status", None), + ("consistency_group", None), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': False, + 'status': None, + 'consistencygroup_id': None, + } + self.cgsnapshots_mock.list.assert_called_once_with( + detailed=True, search_opts=search_opts) + self.assertEqual(self.columns_long, columns) + self.assertEqual(self.data_long, list(data)) + + def test_consistency_group_snapshot_list_with_options(self): + arglist = [ + "--all-project", + "--status", self.consistency_group_snapshots[0].status, + "--consistency-group", self.consistency_group.id, + ] + verifylist = [ + ("all_projects", True), + ("long", False), + ("status", self.consistency_group_snapshots[0].status), + ("consistency_group", self.consistency_group.id), + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + + search_opts = { + 'all_tenants': True, + 'status': self.consistency_group_snapshots[0].status, + 'consistencygroup_id': self.consistency_group.id, + } + self.consistencygroups_mock.get.assert_called_once_with( + self.consistency_group.id) + self.cgsnapshots_mock.list.assert_called_once_with( + detailed=True, search_opts=search_opts) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, list(data)) + + +class TestConsistencyGroupSnapshotShow(TestConsistencyGroupSnapshot): + + _consistency_group_snapshot = ( + volume_fakes. + FakeConsistencyGroupSnapshot. + create_one_consistency_group_snapshot() + ) + + columns = ( + 'consistencygroup_id', + 'created_at', + 'description', + 'id', + 'name', + 'status', + ) + data = ( + _consistency_group_snapshot.consistencygroup_id, + _consistency_group_snapshot.created_at, + _consistency_group_snapshot.description, + _consistency_group_snapshot.id, + _consistency_group_snapshot.name, + _consistency_group_snapshot.status, + ) + + def setUp(self): + super(TestConsistencyGroupSnapshotShow, self).setUp() + + self.cgsnapshots_mock.get.return_value = ( + self._consistency_group_snapshot) + self.cmd = (consistency_group_snapshot. + ShowConsistencyGroupSnapshot(self.app, None)) + + def test_consistency_group_snapshot_show(self): + arglist = [ + self._consistency_group_snapshot.id + ] + verifylist = [ + ("consistency_group_snapshot", self._consistency_group_snapshot.id) + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + columns, data = self.cmd.take_action(parsed_args) + self.cgsnapshots_mock.get.assert_called_once_with( + self._consistency_group_snapshot.id) + self.assertEqual(self.columns, columns) + self.assertEqual(self.data, data) diff --git a/openstackclient/tests/unit/volume/v2/test_type.py b/openstackclient/tests/unit/volume/v2/test_type.py index 84f87e3b..325872d7 100644 --- a/openstackclient/tests/unit/volume/v2/test_type.py +++ b/openstackclient/tests/unit/volume/v2/test_type.py @@ -165,7 +165,8 @@ class TestTypeList(TestType): columns = [ "ID", - "Name" + "Name", + "Is Public", ] columns_long = columns + [ "Description", @@ -177,12 +178,14 @@ class TestTypeList(TestType): data.append(( t.id, t.name, + t.is_public, )) data_long = [] for t in volume_types: data_long.append(( t.id, t.name, + t.is_public, t.description, utils.format_dict(t.extra_specs), )) diff --git a/openstackclient/tests/unit/volume/v2/test_volume.py b/openstackclient/tests/unit/volume/v2/test_volume.py index f4a7c142..41728342 100644 --- a/openstackclient/tests/unit/volume/v2/test_volume.py +++ b/openstackclient/tests/unit/volume/v2/test_volume.py @@ -46,6 +46,9 @@ class TestVolume(volume_fakes.TestVolume): self.snapshots_mock = self.app.client_manager.volume.volume_snapshots self.snapshots_mock.reset_mock() + self.types_mock = self.app.client_manager.volume.volume_types + self.types_mock.reset_mock() + self.consistencygroups_mock = ( self.app.client_manager.volume.consistencygroups) self.consistencygroups_mock.reset_mock() @@ -996,13 +999,106 @@ class TestVolumeList(TestVolume): self.cmd, arglist, verifylist) +class TestVolumeMigrate(TestVolume): + + _volume = volume_fakes.FakeVolume.create_one_volume() + + def setUp(self): + super(TestVolumeMigrate, self).setUp() + + self.volumes_mock.get.return_value = self._volume + self.volumes_mock.migrate_volume.return_value = None + # Get the command object to test + self.cmd = volume.MigrateVolume(self.app, None) + + def test_volume_migrate(self): + arglist = [ + "--host", "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("lock_volume", False), + ("unlock_volume", False), + ("host", "host@backend-name#pool"), + ("volume", self._volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_once_with(self._volume.id) + self.volumes_mock.migrate_volume.assert_called_once_with( + self._volume.id, "host@backend-name#pool", False, False) + self.assertIsNone(result) + + def test_volume_migrate_with_option(self): + arglist = [ + "--force-host-copy", + "--lock-volume", + "--host", "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", True), + ("lock_volume", True), + ("unlock_volume", False), + ("host", "host@backend-name#pool"), + ("volume", self._volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_once_with(self._volume.id) + self.volumes_mock.migrate_volume.assert_called_once_with( + self._volume.id, "host@backend-name#pool", True, True) + self.assertIsNone(result) + + def test_volume_migrate_with_unlock_volume(self): + arglist = [ + "--unlock-volume", + "--host", "host@backend-name#pool", + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("lock_volume", False), + ("unlock_volume", True), + ("host", "host@backend-name#pool"), + ("volume", self._volume.id), + ] + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.get.assert_called_once_with(self._volume.id) + self.volumes_mock.migrate_volume.assert_called_once_with( + self._volume.id, "host@backend-name#pool", False, False) + self.assertIsNone(result) + + def test_volume_migrate_without_host(self): + arglist = [ + self._volume.id, + ] + verifylist = [ + ("force_host_copy", False), + ("lock_volume", False), + ("unlock_volume", False), + ("volume", self._volume.id), + ] + + self.assertRaises(tests_utils.ParserException, self.check_parser, + self.cmd, arglist, verifylist) + + class TestVolumeSet(TestVolume): + volume_type = volume_fakes.FakeType.create_one_type() + def setUp(self): super(TestVolumeSet, self).setUp() self.new_volume = volume_fakes.FakeVolume.create_one_volume() self.volumes_mock.get.return_value = self.new_volume + self.types_mock.get.return_value = self.volume_type # Get the command object to test self.cmd = volume.SetVolume(self.app, None) @@ -1033,6 +1129,8 @@ class TestVolumeSet(TestVolume): self.new_volume.id ] verifylist = [ + ('read_only', False), + ('read_write', False), ('state', 'error'), ('volume', self.new_volume.id) ] @@ -1042,6 +1140,7 @@ class TestVolumeSet(TestVolume): result = self.cmd.take_action(parsed_args) self.volumes_mock.reset_state.assert_called_with( self.new_volume.id, 'error') + self.volumes_mock.update_readonly_flag.assert_not_called() self.assertIsNone(result) def test_volume_set_state_failed(self): @@ -1090,6 +1189,104 @@ class TestVolumeSet(TestVolume): self.volumes_mock.set_bootable.assert_called_with( self.new_volume.id, verifylist[index][0][1]) + def test_volume_set_readonly(self): + arglist = [ + '--read-only', + self.new_volume.id + ] + verifylist = [ + ('read_only', True), + ('read_write', False), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.update_readonly_flag.assert_called_once_with( + self.new_volume.id, + True) + self.assertIsNone(result) + + def test_volume_set_read_write(self): + arglist = [ + '--read-write', + self.new_volume.id + ] + verifylist = [ + ('read_only', False), + ('read_write', True), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.update_readonly_flag.assert_called_once_with( + self.new_volume.id, + False) + self.assertIsNone(result) + + def test_volume_set_type(self): + arglist = [ + '--type', self.volume_type.id, + self.new_volume.id + ] + verifylist = [ + ('retype_policy', None), + ('type', self.volume_type.id), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.retype.assert_called_once_with( + self.new_volume.id, + self.volume_type.id, + 'never') + self.assertIsNone(result) + + def test_volume_set_type_with_policy(self): + arglist = [ + '--retype-policy', 'on-demand', + '--type', self.volume_type.id, + self.new_volume.id + ] + verifylist = [ + ('retype_policy', 'on-demand'), + ('type', self.volume_type.id), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.retype.assert_called_once_with( + self.new_volume.id, + self.volume_type.id, + 'on-demand') + self.assertIsNone(result) + + @mock.patch.object(volume.LOG, 'warning') + def test_volume_set_with_only_retype_policy(self, mock_warning): + arglist = [ + '--retype-policy', 'on-demand', + self.new_volume.id + ] + verifylist = [ + ('retype_policy', 'on-demand'), + ('volume', self.new_volume.id) + ] + + parsed_args = self.check_parser(self.cmd, arglist, verifylist) + + result = self.cmd.take_action(parsed_args) + self.volumes_mock.retype.assert_not_called() + mock_warning.assert_called_with("'--retype-policy' option will " + "not work without '--type' option") + self.assertIsNone(result) + class TestVolumeShow(TestVolume): diff --git a/openstackclient/volume/v1/backup.py b/openstackclient/volume/v1/backup.py index c9d0ca0d..9ac1302a 100644 --- a/openstackclient/volume/v1/backup.py +++ b/openstackclient/volume/v1/backup.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateVolumeBackup(command.ShowOne): - """Create new volume backup""" + _description = _("Create new volume backup") def get_parser(self, prog_name): parser = super(CreateVolumeBackup, self).get_parser(prog_name) @@ -73,7 +73,7 @@ class CreateVolumeBackup(command.ShowOne): class CreateBackup(CreateVolumeBackup): - """Create new backup""" + _description = _("Create new backup") # TODO(Huanxuan Ao): Remove this class and ``backup create`` command # two cycles after Newton. @@ -90,7 +90,7 @@ class CreateBackup(CreateVolumeBackup): class DeleteVolumeBackup(command.Command): - """Delete volume backup(s)""" + _description = _("Delete volume backup(s)") def get_parser(self, prog_name): parser = super(DeleteVolumeBackup, self).get_parser(prog_name) @@ -125,7 +125,7 @@ class DeleteVolumeBackup(command.Command): class DeleteBackup(DeleteVolumeBackup): - """Delete backup(s)""" + _description = _("Delete backup(s)") # TODO(Huanxuan Ao): Remove this class and ``backup delete`` command # two cycles after Newton. @@ -142,7 +142,7 @@ class DeleteBackup(DeleteVolumeBackup): class ListVolumeBackup(command.Lister): - """List volume backups""" + _description = _("List volume backups") def get_parser(self, prog_name): parser = super(ListVolumeBackup, self).get_parser(prog_name) @@ -152,9 +152,36 @@ class ListVolumeBackup(command.Lister): default=False, help=_('List additional fields in output'), ) + parser.add_argument( + "--name", + metavar="<name>", + help=_("Filters results by the backup name") + ) + parser.add_argument( + "--status", + metavar="<status>", + choices=['creating', 'available', 'deleting', + 'error', 'restoring', 'error_restoring'], + help=_("Filters results by the backup status " + "('creating', 'available', 'deleting', " + "'error', 'restoring' or 'error_restoring')") + ) + parser.add_argument( + "--volume", + metavar="<volume>", + help=_("Filters results by the volume which they " + "backup (name or ID)") + ) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help=_('Include all projects (admin only)'), + ) return parser def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume def _format_volume_id(volume_id): """Return a volume name if available @@ -180,13 +207,25 @@ class ListVolumeBackup(command.Lister): # Cache the volume list volume_cache = {} try: - for s in self.app.client_manager.volume.volumes.list(): + for s in volume_client.volumes.list(): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble pass - data = self.app.client_manager.volume.backups.list() + filter_volume_id = None + if parsed_args.volume: + filter_volume_id = utils.find_resource(volume_client.volumes, + parsed_args.volume).id + search_opts = { + 'name': parsed_args.name, + 'status': parsed_args.status, + 'volume_id': filter_volume_id, + 'all_tenants': parsed_args.all_projects, + } + data = volume_client.backups.list( + search_opts=search_opts, + ) return (column_headers, (utils.get_item_properties( @@ -196,7 +235,7 @@ class ListVolumeBackup(command.Lister): class ListBackup(ListVolumeBackup): - """List backups""" + _description = _("List backups") # TODO(Huanxuan Ao): Remove this class and ``backup list`` command # two cycles after Newton. @@ -213,7 +252,7 @@ class ListBackup(ListVolumeBackup): class RestoreVolumeBackup(command.Command): - """Restore volume backup""" + _description = _("Restore volume backup") def get_parser(self, prog_name): parser = super(RestoreVolumeBackup, self).get_parser(prog_name) @@ -240,7 +279,7 @@ class RestoreVolumeBackup(command.Command): class RestoreBackup(RestoreVolumeBackup): - """Restore backup""" + _description = _("Restore backup") # TODO(Huanxuan Ao): Remove this class and ``backup restore`` command # two cycles after Newton. @@ -257,7 +296,7 @@ class RestoreBackup(RestoreVolumeBackup): class ShowVolumeBackup(command.ShowOne): - """Display volume backup details""" + _description = _("Display volume backup details") def get_parser(self, prog_name): parser = super(ShowVolumeBackup, self).get_parser(prog_name) @@ -277,7 +316,7 @@ class ShowVolumeBackup(command.ShowOne): class ShowBackup(ShowVolumeBackup): - """Display backup details""" + _description = _("Display backup details") # TODO(Huanxuan Ao): Remove this class and ``backup show`` command # two cycles after Newton. diff --git a/openstackclient/volume/v1/qos_specs.py b/openstackclient/volume/v1/qos_specs.py index 93c24a21..b824b351 100644 --- a/openstackclient/volume/v1/qos_specs.py +++ b/openstackclient/volume/v1/qos_specs.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class AssociateQos(command.Command): - """Associate a QoS specification to a volume type""" + _description = _("Associate a QoS specification to a volume type") def get_parser(self, prog_name): parser = super(AssociateQos, self).get_parser(prog_name) @@ -57,7 +57,7 @@ class AssociateQos(command.Command): class CreateQos(command.ShowOne): - """Create new QoS specification""" + _description = _("Create new QoS specification") def get_parser(self, prog_name): parser = super(CreateQos, self).get_parser(prog_name) @@ -99,7 +99,7 @@ class CreateQos(command.ShowOne): class DeleteQos(command.Command): - """Delete QoS specification""" + _description = _("Delete QoS specification") def get_parser(self, prog_name): parser = super(DeleteQos, self).get_parser(prog_name) @@ -139,7 +139,7 @@ class DeleteQos(command.Command): class DisassociateQos(command.Command): - """Disassociate a QoS specification from a volume type""" + _description = _("Disassociate a QoS specification from a volume type") def get_parser(self, prog_name): parser = super(DisassociateQos, self).get_parser(prog_name) @@ -177,7 +177,7 @@ class DisassociateQos(command.Command): class ListQos(command.Lister): - """List QoS specifications""" + _description = _("List QoS specifications") def take_action(self, parsed_args): volume_client = self.app.client_manager.volume @@ -202,7 +202,7 @@ class ListQos(command.Lister): class SetQos(command.Command): - """Set QoS specification properties""" + _description = _("Set QoS specification properties") def get_parser(self, prog_name): parser = super(SetQos, self).get_parser(prog_name) @@ -231,7 +231,7 @@ class SetQos(command.Command): class ShowQos(command.ShowOne): - """Display QoS specification details""" + _description = _("Display QoS specification details") def get_parser(self, prog_name): parser = super(ShowQos, self).get_parser(prog_name) @@ -260,7 +260,7 @@ class ShowQos(command.ShowOne): class UnsetQos(command.Command): - """Unset QoS specification properties""" + _description = _("Unset QoS specification properties") def get_parser(self, prog_name): parser = super(UnsetQos, self).get_parser(prog_name) diff --git a/openstackclient/volume/v1/service.py b/openstackclient/volume/v1/service.py index 867c4b9c..d468c6ff 100644 --- a/openstackclient/volume/v1/service.py +++ b/openstackclient/volume/v1/service.py @@ -22,7 +22,7 @@ from openstackclient.i18n import _ class ListService(command.Lister): - """List service command""" + _description = _("List service command") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) @@ -76,7 +76,7 @@ class ListService(command.Lister): class SetService(command.Command): - """Set volume service properties""" + _description = _("Set volume service properties") def get_parser(self, prog_name): parser = super(SetService, self).get_parser(prog_name) diff --git a/openstackclient/volume/v1/snapshot.py b/openstackclient/volume/v1/snapshot.py index 1c0c0bc7..e9e3894b 100644 --- a/openstackclient/volume/v1/snapshot.py +++ b/openstackclient/volume/v1/snapshot.py @@ -37,7 +37,7 @@ LOG = logging.getLogger(__name__) class CreateSnapshot(command.ShowOne): - """Create new snapshot""" + _description = _("Create new snapshot") def get_parser(self, prog_name): parser = super(CreateSnapshot, self).get_parser(prog_name) @@ -87,7 +87,7 @@ class CreateSnapshot(command.ShowOne): class DeleteSnapshot(command.Command): - """Delete snapshot(s)""" + _description = _("Delete snapshot(s)") def get_parser(self, prog_name): parser = super(DeleteSnapshot, self).get_parser(prog_name) @@ -124,7 +124,7 @@ class DeleteSnapshot(command.Command): class ListSnapshot(command.Lister): - """List snapshots""" + _description = _("List snapshots") def get_parser(self, prog_name): parser = super(ListSnapshot, self).get_parser(prog_name) @@ -197,7 +197,7 @@ class ListSnapshot(command.Lister): class SetSnapshot(command.Command): - """Set snapshot properties""" + _description = _("Set snapshot properties") def get_parser(self, prog_name): parser = super(SetSnapshot, self).get_parser(prog_name) @@ -260,7 +260,7 @@ class SetSnapshot(command.Command): class ShowSnapshot(command.ShowOne): - """Display snapshot details""" + _description = _("Display snapshot details") def get_parser(self, prog_name): parser = super(ShowSnapshot, self).get_parser(prog_name) @@ -286,7 +286,7 @@ class ShowSnapshot(command.ShowOne): class UnsetSnapshot(command.Command): - """Unset snapshot properties""" + _description = _("Unset snapshot properties") def get_parser(self, prog_name): parser = super(UnsetSnapshot, self).get_parser(prog_name) diff --git a/openstackclient/volume/v1/volume.py b/openstackclient/volume/v1/volume.py index cafe8ce6..0087bad4 100644 --- a/openstackclient/volume/v1/volume.py +++ b/openstackclient/volume/v1/volume.py @@ -45,7 +45,7 @@ def _check_size_arg(args): class CreateVolume(command.ShowOne): - """Create new volume""" + _description = _("Create new volume") def get_parser(self, prog_name): parser = super(CreateVolume, self).get_parser(prog_name) @@ -178,7 +178,7 @@ class CreateVolume(command.ShowOne): class DeleteVolume(command.Command): - """Delete volume(s)""" + _description = _("Delete volume(s)") def get_parser(self, prog_name): parser = super(DeleteVolume, self).get_parser(prog_name) @@ -223,7 +223,7 @@ class DeleteVolume(command.Command): class ListVolume(command.Lister): - """List volumes""" + _description = _("List volumes") def get_parser(self, prog_name): parser = super(ListVolume, self).get_parser(prog_name) @@ -344,8 +344,39 @@ class ListVolume(command.Lister): ) for s in data)) +class MigrateVolume(command.Command): + _description = _("Migrate volume to a new host") + + def get_parser(self, prog_name): + parser = super(MigrateVolume, self).get_parser(prog_name) + parser.add_argument( + 'volume', + metavar="<volume>", + help=_("Volume to migrate (name or ID)") + ) + parser.add_argument( + '--host', + metavar="<host>", + required=True, + help=_("Destination host (takes the form: host@backend-name#pool)") + ) + parser.add_argument( + '--force-host-copy', + action="store_true", + help=_("Enable generic host-based force-migration, " + "which bypasses driver optimizations") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + volume = utils.find_resource(volume_client.volumes, parsed_args.volume) + volume_client.volumes.migrate_volume(volume.id, parsed_args.host, + parsed_args.force_host_copy,) + + class SetVolume(command.Command): - """Set volume properties""" + _description = _("Set volume properties") def get_parser(self, prog_name): parser = super(SetVolume, self).get_parser(prog_name) @@ -388,42 +419,82 @@ class SetVolume(command.Command): action="store_true", help=_("Mark volume as non-bootable") ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + help=_("Set volume to read-only access mode") + ) + readonly_group.add_argument( + "--read-write", + action="store_true", + help=_("Set volume to read-write access mode") + ) return parser def take_action(self, parsed_args): volume_client = self.app.client_manager.volume volume = utils.find_resource(volume_client.volumes, parsed_args.volume) + result = 0 if parsed_args.size: - if volume.status != 'available': - LOG.error(_("Volume is in %s state, it must be available " - "before size can be extended"), volume.status) - return - if parsed_args.size <= volume.size: - LOG.error(_("New size must be greater than %s GB"), - volume.size) - return - volume_client.volumes.extend(volume.id, parsed_args.size) - + try: + if volume.status != 'available': + msg = (_("Volume is in %s state, it must be available " + "before size can be extended") % volume.status) + raise exceptions.CommandError(msg) + if parsed_args.size <= volume.size: + msg = (_("New size must be greater than %s GB") + % volume.size) + raise exceptions.CommandError(msg) + volume_client.volumes.extend(volume.id, parsed_args.size) + except Exception as e: + LOG.error(_("Failed to set volume size: %s"), e) + result += 1 if parsed_args.property: - volume_client.volumes.set_metadata(volume.id, parsed_args.property) + try: + volume_client.volumes.set_metadata( + volume.id, + parsed_args.property) + except Exception as e: + LOG.error(_("Failed to set volume property: %s"), e) + result += 1 if parsed_args.bootable or parsed_args.non_bootable: try: volume_client.volumes.set_bootable( volume.id, parsed_args.bootable) except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) + result += 1 + if parsed_args.read_only or parsed_args.read_write: + try: + volume_client.volumes.update_readonly_flag( + volume.id, + parsed_args.read_only) + except Exception as e: + LOG.error(_("Failed to set volume read-only access " + "mode flag: %s"), e) + result += 1 kwargs = {} if parsed_args.name: kwargs['display_name'] = parsed_args.name if parsed_args.description: kwargs['display_description'] = parsed_args.description if kwargs: - volume_client.volumes.update(volume.id, **kwargs) + try: + volume_client.volumes.update(volume.id, **kwargs) + except Exception as e: + LOG.error(_("Failed to update volume display name " + "or display description: %s"), e) + result += 1 + + if result > 0: + raise exceptions.CommandError(_("One or more of the " + "set operations failed")) class ShowVolume(command.ShowOne): - """Show volume details""" + _description = _("Show volume details") def get_parser(self, prog_name): parser = super(ShowVolume, self).get_parser(prog_name) @@ -453,7 +524,7 @@ class ShowVolume(command.ShowOne): class UnsetVolume(command.Command): - """Unset volume properties""" + _description = _("Unset volume properties") def get_parser(self, prog_name): parser = super(UnsetVolume, self).get_parser(prog_name) diff --git a/openstackclient/volume/v1/volume_transfer_request.py b/openstackclient/volume/v1/volume_transfer_request.py index 4d6f2161..f24d5a56 100644 --- a/openstackclient/volume/v1/volume_transfer_request.py +++ b/openstackclient/volume/v1/volume_transfer_request.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) class AcceptTransferRequest(command.ShowOne): - """Accept volume transfer request.""" + _description = _("Accept volume transfer request.") def get_parser(self, prog_name): parser = super(AcceptTransferRequest, self).get_parser(prog_name) @@ -56,7 +56,7 @@ class AcceptTransferRequest(command.ShowOne): class CreateTransferRequest(command.ShowOne): - """Create volume transfer request.""" + _description = _("Create volume transfer request.") def get_parser(self, prog_name): parser = super(CreateTransferRequest, self).get_parser(prog_name) @@ -85,7 +85,7 @@ class CreateTransferRequest(command.ShowOne): class DeleteTransferRequest(command.Command): - """Delete volume transfer request(s).""" + _description = _("Delete volume transfer request(s).") def get_parser(self, prog_name): parser = super(DeleteTransferRequest, self).get_parser(prog_name) @@ -120,7 +120,7 @@ class DeleteTransferRequest(command.Command): class ListTransferRequest(command.Lister): - """Lists all volume transfer requests.""" + _description = _("Lists all volume transfer requests.") def get_parser(self, prog_name): parser = super(ListTransferRequest, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class ListTransferRequest(command.Lister): class ShowTransferRequest(command.ShowOne): - """Show volume transfer request details.""" + _description = _("Show volume transfer request details.") def get_parser(self, prog_name): parser = super(ShowTransferRequest, self).get_parser(prog_name) diff --git a/openstackclient/volume/v1/volume_type.py b/openstackclient/volume/v1/volume_type.py index 61e9f7fc..8adce322 100644 --- a/openstackclient/volume/v1/volume_type.py +++ b/openstackclient/volume/v1/volume_type.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateVolumeType(command.ShowOne): - """Create new volume type""" + _description = _("Create new volume type") def get_parser(self, prog_name): parser = super(CreateVolumeType, self).get_parser(prog_name) @@ -61,7 +61,7 @@ class CreateVolumeType(command.ShowOne): class DeleteVolumeType(command.Command): - """Delete volume type(s)""" + _description = _("Delete volume type(s)") def get_parser(self, prog_name): parser = super(DeleteVolumeType, self).get_parser(prog_name) @@ -97,7 +97,7 @@ class DeleteVolumeType(command.Command): class ListVolumeType(command.Lister): - """List volume types""" + _description = _("List volume types") def get_parser(self, prog_name): parser = super(ListVolumeType, self).get_parser(prog_name) @@ -111,10 +111,10 @@ class ListVolumeType(command.Lister): def take_action(self, parsed_args): if parsed_args.long: - columns = ('ID', 'Name', 'Extra Specs') - column_headers = ('ID', 'Name', 'Properties') + columns = ('ID', 'Name', 'Is Public', 'Extra Specs') + column_headers = ('ID', 'Name', 'Is Public', 'Properties') else: - columns = ('ID', 'Name') + columns = ('ID', 'Name', 'Is Public') column_headers = columns data = self.app.client_manager.volume.volume_types.list() return (column_headers, @@ -125,7 +125,7 @@ class ListVolumeType(command.Lister): class SetVolumeType(command.Command): - """Set volume type properties""" + _description = _("Set volume type properties") def get_parser(self, prog_name): parser = super(SetVolumeType, self).get_parser(prog_name) @@ -153,7 +153,7 @@ class SetVolumeType(command.Command): class ShowVolumeType(command.ShowOne): - """Display volume type details""" + _description = _("Display volume type details") def get_parser(self, prog_name): parser = super(ShowVolumeType, self).get_parser(prog_name) @@ -175,7 +175,7 @@ class ShowVolumeType(command.ShowOne): class UnsetVolumeType(command.Command): - """Unset volume type properties""" + _description = _("Unset volume type properties") def get_parser(self, prog_name): parser = super(UnsetVolumeType, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/backup.py b/openstackclient/volume/v2/backup.py index 2ca35b24..00389fcb 100644 --- a/openstackclient/volume/v2/backup.py +++ b/openstackclient/volume/v2/backup.py @@ -17,6 +17,7 @@ import copy import logging +from osc_lib.cli import parseractions from osc_lib.command import command from osc_lib import exceptions from osc_lib import utils @@ -29,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateVolumeBackup(command.ShowOne): - """Create new volume backup""" + _description = _("Create new volume backup") def get_parser(self, prog_name): parser = super(CreateVolumeBackup, self).get_parser(prog_name) @@ -94,7 +95,7 @@ class CreateVolumeBackup(command.ShowOne): class CreateBackup(CreateVolumeBackup): - """Create new backup""" + _description = _("Create new backup") # TODO(Huanxuan Ao): Remove this class and ``backup create`` command # two cycles after Newton. @@ -111,7 +112,7 @@ class CreateBackup(CreateVolumeBackup): class DeleteVolumeBackup(command.Command): - """Delete volume backup(s)""" + _description = _("Delete volume backup(s)") def get_parser(self, prog_name): parser = super(DeleteVolumeBackup, self).get_parser(prog_name) @@ -152,7 +153,7 @@ class DeleteVolumeBackup(command.Command): class DeleteBackup(DeleteVolumeBackup): - """Delete backup(s)""" + _description = _("Delete backup(s)") # TODO(Huanxuan Ao): Remove this class and ``backup delete`` command # two cycles after Newton. @@ -169,7 +170,7 @@ class DeleteBackup(DeleteVolumeBackup): class ListVolumeBackup(command.Lister): - """List volume backups""" + _description = _("List volume backups") def get_parser(self, prog_name): parser = super(ListVolumeBackup, self).get_parser(prog_name) @@ -179,9 +180,48 @@ class ListVolumeBackup(command.Lister): default=False, help=_("List additional fields in output") ) + parser.add_argument( + "--name", + metavar="<name>", + help=_("Filters results by the backup name") + ) + parser.add_argument( + "--status", + metavar="<status>", + choices=['creating', 'available', 'deleting', + 'error', 'restoring', 'error_restoring'], + help=_("Filters results by the backup status " + "('creating', 'available', 'deleting', " + "'error', 'restoring' or 'error_restoring')") + ) + parser.add_argument( + "--volume", + metavar="<volume>", + help=_("Filters results by the volume which they " + "backup (name or ID)") + ) + parser.add_argument( + '--marker', + metavar='<marker>', + help=_('The last backup of the previous page (name or ID)'), + ) + parser.add_argument( + '--limit', + type=int, + action=parseractions.NonNegativeAction, + metavar='<limit>', + help=_('Maximum number of backups to display'), + ) + parser.add_argument( + '--all-projects', + action='store_true', + default=False, + help=_('Include all projects (admin only)'), + ) return parser def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume def _format_volume_id(volume_id): """Return a volume name if available @@ -207,13 +247,31 @@ class ListVolumeBackup(command.Lister): # Cache the volume list volume_cache = {} try: - for s in self.app.client_manager.volume.volumes.list(): + for s in volume_client.volumes.list(): volume_cache[s.id] = s except Exception: # Just forget it if there's any trouble pass - data = self.app.client_manager.volume.backups.list() + filter_volume_id = None + if parsed_args.volume: + filter_volume_id = utils.find_resource(volume_client.volumes, + parsed_args.volume).id + marker_backup_id = None + if parsed_args.marker: + marker_backup_id = utils.find_resource(volume_client.backups, + parsed_args.marker).id + search_opts = { + 'name': parsed_args.name, + 'status': parsed_args.status, + 'volume_id': filter_volume_id, + 'all_tenants': parsed_args.all_projects, + } + data = volume_client.backups.list( + search_opts=search_opts, + marker=marker_backup_id, + limit=parsed_args.limit, + ) return (column_headers, (utils.get_item_properties( @@ -223,7 +281,7 @@ class ListVolumeBackup(command.Lister): class ListBackup(ListVolumeBackup): - """List backups""" + _description = _("List backups") # TODO(Huanxuan Ao): Remove this class and ``backup list`` command # two cycles after Newton. @@ -240,7 +298,7 @@ class ListBackup(ListVolumeBackup): class RestoreVolumeBackup(command.ShowOne): - """Restore volume backup""" + _description = _("Restore volume backup") def get_parser(self, prog_name): parser = super(RestoreVolumeBackup, self).get_parser(prog_name) @@ -265,7 +323,7 @@ class RestoreVolumeBackup(command.ShowOne): class RestoreBackup(RestoreVolumeBackup): - """Restore backup""" + _description = _("Restore backup") # TODO(Huanxuan Ao): Remove this class and ``backup restore`` command # two cycles after Newton. @@ -282,7 +340,7 @@ class RestoreBackup(RestoreVolumeBackup): class SetVolumeBackup(command.Command): - """Set volume backup properties""" + _description = _("Set volume backup properties") def get_parser(self, prog_name): parser = super(SetVolumeBackup, self).get_parser(prog_name) @@ -344,7 +402,7 @@ class SetVolumeBackup(command.Command): class ShowVolumeBackup(command.ShowOne): - """Display volume backup details""" + _description = _("Display volume backup details") def get_parser(self, prog_name): parser = super(ShowVolumeBackup, self).get_parser(prog_name) @@ -364,7 +422,7 @@ class ShowVolumeBackup(command.ShowOne): class ShowBackup(ShowVolumeBackup): - """Display backup details""" + _description = _("Display backup details") # TODO(Huanxuan Ao): Remove this class and ``backup show`` command # two cycles after Newton. diff --git a/openstackclient/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py index 39f2d577..661bcbe5 100644 --- a/openstackclient/volume/v2/consistency_group.py +++ b/openstackclient/volume/v2/consistency_group.py @@ -14,14 +14,137 @@ """Volume v2 consistency group action implementations""" +import logging + from osc_lib.command import command +from osc_lib import exceptions from osc_lib import utils +import six from openstackclient.i18n import _ +LOG = logging.getLogger(__name__) + + +class DeleteConsistencyGroup(command.Command): + _description = _("Delete consistency group(s).") + + def get_parser(self, prog_name): + parser = super(DeleteConsistencyGroup, self).get_parser(prog_name) + parser.add_argument( + 'consistency_groups', + metavar='<consistency-group>', + nargs="+", + help=_('Consistency group(s) to delete (name or ID)'), + ) + parser.add_argument( + '--force', + action='store_true', + default=False, + help=_("Allow delete in state other than error or available"), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + result = 0 + + for i in parsed_args.consistency_groups: + try: + consistency_group_id = utils.find_resource( + volume_client.consistencygroups, i).id + volume_client.consistencygroups.delete( + consistency_group_id, parsed_args.force) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consistency group with " + "name or ID '%(consistency_group)s':%(e)s") + % {'consistency_group': i, 'e': e}) + + if result > 0: + total = len(parsed_args.consistency_groups) + msg = (_("%(result)s of %(total)s consistency groups failed " + "to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +LOG = logging.getLogger(__name__) + + +class CreateConsistencyGroup(command.ShowOne): + _description = _("Create new consistency group.") + + def get_parser(self, prog_name): + parser = super(CreateConsistencyGroup, self).get_parser(prog_name) + parser.add_argument( + "name", + metavar="<name>", + nargs="?", + help=_("Name of new consistency group (default to None)") + ) + exclusive_group = parser.add_mutually_exclusive_group(required=True) + exclusive_group.add_argument( + "--volume-type", + metavar="<volume-type>", + help=_("Volume type of this consistency group (name or ID)") + ) + exclusive_group.add_argument( + "--consistency-group-source", + metavar="<consistency-group>", + help=_("Existing consistency group (name or ID)") + ) + parser.add_argument( + "--description", + metavar="<description>", + help=_("Description of this consistency group") + ) + parser.add_argument( + "--availability-zone", + metavar="<availability-zone>", + help=_("Availability zone for this consistency group " + "(not available if creating consistency group " + "from source)"), + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + if parsed_args.volume_type: + volume_type_id = utils.find_resource( + volume_client.volume_types, + parsed_args.volume_type).id + consistency_group = volume_client.consistencygroups.create( + volume_type_id, + name=parsed_args.name, + description=parsed_args.description, + availability_zone=parsed_args.availability_zone + ) + elif parsed_args.consistency_group_source: + if parsed_args.availability_zone: + msg = _("'--availability-zone' option will not work " + "if creating consistency group from source") + LOG.warning(msg) + consistency_group_id = utils.find_resource( + volume_client.consistencygroups, + parsed_args.consistency_group_source).id + consistency_group_snapshot = None + # TODO(Huanxuan Ao): Support for creating from consistency group + # snapshot after adding "consistency_group_snapshot" resource + consistency_group = ( + volume_client.consistencygroups.create_from_src( + consistency_group_snapshot, + consistency_group_id, + name=parsed_args.name, + description=parsed_args.description + ) + ) + + return zip(*sorted(six.iteritems(consistency_group._info))) + + class ListConsistencyGroup(command.Lister): - """List consistency groups.""" + _description = _("List consistency groups.") def get_parser(self, prog_name): parser = super(ListConsistencyGroup, self).get_parser(prog_name) @@ -55,3 +178,23 @@ class ListConsistencyGroup(command.Lister): s, columns, formatters={'Volume Types': utils.format_list}) for s in consistency_groups)) + + +class ShowConsistencyGroup(command.ShowOne): + _description = _("Display consistency group details.") + + def get_parser(self, prog_name): + parser = super(ShowConsistencyGroup, self).get_parser(prog_name) + parser.add_argument( + "consistency_group", + metavar="<consistency-group>", + help=_("Consistency group to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + consistency_group = utils.find_resource( + volume_client.consistencygroups, + parsed_args.consistency_group) + return zip(*sorted(six.iteritems(consistency_group._info))) diff --git a/openstackclient/volume/v2/consistency_group_snapshot.py b/openstackclient/volume/v2/consistency_group_snapshot.py new file mode 100644 index 00000000..540deb01 --- /dev/null +++ b/openstackclient/volume/v2/consistency_group_snapshot.py @@ -0,0 +1,190 @@ +# +# 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 v2 consistency group snapshot action implementations""" + +import logging + +from osc_lib.command import command +from osc_lib import exceptions +from osc_lib import utils +import six + +from openstackclient.i18n import _ + + +LOG = logging.getLogger(__name__) + + +class CreateConsistencyGroupSnapshot(command.ShowOne): + _description = _("Create new consistency group snapshot.") + + def get_parser(self, prog_name): + parser = super( + CreateConsistencyGroupSnapshot, self).get_parser(prog_name) + parser.add_argument( + "snapshot_name", + metavar="<snapshot-name>", + nargs="?", + help=_("Name of new consistency group snapshot (default to None)") + ) + parser.add_argument( + "--consistency-group", + metavar="<consistency-group>", + help=_("Consistency group to snapshot (name or ID) " + "(default to be the same as <snapshot-name>)") + ) + parser.add_argument( + "--description", + metavar="<description>", + help=_("Description of this consistency group snapshot") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + consistency_group = parsed_args.consistency_group + if not parsed_args.consistency_group: + # If "--consistency-group" not specified, then consistency_group + # will be the same as the new consistency group snapshot name + consistency_group = parsed_args.snapshot_name + consistency_group_id = utils.find_resource( + volume_client.consistencygroups, + consistency_group).id + consistency_group_snapshot = volume_client.cgsnapshots.create( + consistency_group_id, + name=parsed_args.snapshot_name, + description=parsed_args.description, + ) + + return zip(*sorted(six.iteritems(consistency_group_snapshot._info))) + + +class DeleteConsistencyGroupSnapshot(command.Command): + _description = _("Delete consistency group snapshot(s).") + + def get_parser(self, prog_name): + parser = super( + DeleteConsistencyGroupSnapshot, self).get_parser(prog_name) + parser.add_argument( + "consistency_group_snapshot", + metavar="<consistency-group-snapshot>", + nargs="+", + help=_("Consistency group snapshot(s) to delete (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + result = 0 + + for snapshot in parsed_args.consistency_group_snapshot: + try: + snapshot_id = utils.find_resource(volume_client.cgsnapshots, + snapshot).id + + volume_client.cgsnapshots.delete(snapshot_id) + except Exception as e: + result += 1 + LOG.error(_("Failed to delete consistency group snapshot " + "with name or ID '%(snapshot)s': %(e)s") + % {'snapshot': snapshot, 'e': e}) + + if result > 0: + total = len(parsed_args.consistency_group_snapshot) + msg = (_("%(result)s of %(total)s consistency group snapshots " + "failed to delete.") % {'result': result, 'total': total}) + raise exceptions.CommandError(msg) + + +class ListConsistencyGroupSnapshot(command.Lister): + _description = _("List consistency group snapshots.") + + def get_parser(self, prog_name): + parser = super( + ListConsistencyGroupSnapshot, self).get_parser(prog_name) + parser.add_argument( + '--all-projects', + action="store_true", + help=_('Show detail for all projects (admin only) ' + '(defaults to False)') + ) + parser.add_argument( + '--long', + action="store_true", + help=_('List additional fields in output') + ) + parser.add_argument( + '--status', + metavar="<status>", + choices=['available', 'error', 'creating', 'deleting', + 'error-deleting'], + help=_('Filters results by a status ("available", "error", ' + '"creating", "deleting" or "error_deleting")') + ) + parser.add_argument( + '--consistency-group', + metavar="<consistency-group>", + help=_('Filters results by a consistency group (name or ID)') + ) + return parser + + def take_action(self, parsed_args): + if parsed_args.long: + columns = ['ID', 'Status', 'ConsistencyGroup ID', + 'Name', 'Description', 'Created At'] + else: + columns = ['ID', 'Status', 'Name'] + volume_client = self.app.client_manager.volume + consistency_group_id = None + if parsed_args.consistency_group: + consistency_group_id = utils.find_resource( + volume_client.consistencygroups, + parsed_args.consistency_group, + ).id + search_opts = { + 'all_tenants': parsed_args.all_projects, + 'status': parsed_args.status, + 'consistencygroup_id': consistency_group_id, + } + consistency_group_snapshots = volume_client.cgsnapshots.list( + detailed=True, + search_opts=search_opts, + ) + + return (columns, ( + utils.get_item_properties( + s, columns) + for s in consistency_group_snapshots)) + + +class ShowConsistencyGroupSnapshot(command.ShowOne): + _description = _("Display consistency group snapshot details") + + def get_parser(self, prog_name): + parser = super( + ShowConsistencyGroupSnapshot, self).get_parser(prog_name) + parser.add_argument( + "consistency_group_snapshot", + metavar="<consistency-group-snapshot>", + help=_("Consistency group snapshot to display (name or ID)") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + consistency_group_snapshot = utils.find_resource( + volume_client.cgsnapshots, + parsed_args.consistency_group_snapshot) + return zip(*sorted(six.iteritems(consistency_group_snapshot._info))) diff --git a/openstackclient/volume/v2/qos_specs.py b/openstackclient/volume/v2/qos_specs.py index 9797f1a6..b7f49eca 100644 --- a/openstackclient/volume/v2/qos_specs.py +++ b/openstackclient/volume/v2/qos_specs.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class AssociateQos(command.Command): - """Associate a QoS specification to a volume type""" + _description = _("Associate a QoS specification to a volume type") def get_parser(self, prog_name): parser = super(AssociateQos, self).get_parser(prog_name) @@ -57,7 +57,7 @@ class AssociateQos(command.Command): class CreateQos(command.ShowOne): - """Create new QoS specification""" + _description = _("Create new QoS specification") def get_parser(self, prog_name): parser = super(CreateQos, self).get_parser(prog_name) @@ -99,7 +99,7 @@ class CreateQos(command.ShowOne): class DeleteQos(command.Command): - """Delete QoS specification""" + _description = _("Delete QoS specification") def get_parser(self, prog_name): parser = super(DeleteQos, self).get_parser(prog_name) @@ -139,7 +139,7 @@ class DeleteQos(command.Command): class DisassociateQos(command.Command): - """Disassociate a QoS specification from a volume type""" + _description = _("Disassociate a QoS specification from a volume type") def get_parser(self, prog_name): parser = super(DisassociateQos, self).get_parser(prog_name) @@ -177,7 +177,7 @@ class DisassociateQos(command.Command): class ListQos(command.Lister): - """List QoS specifications""" + _description = _("List QoS specifications") def take_action(self, parsed_args): volume_client = self.app.client_manager.volume @@ -202,7 +202,7 @@ class ListQos(command.Lister): class SetQos(command.Command): - """Set QoS specification properties""" + _description = _("Set QoS specification properties") def get_parser(self, prog_name): parser = super(SetQos, self).get_parser(prog_name) @@ -231,7 +231,7 @@ class SetQos(command.Command): class ShowQos(command.ShowOne): - """Display QoS specification details""" + _description = _("Display QoS specification details") def get_parser(self, prog_name): parser = super(ShowQos, self).get_parser(prog_name) @@ -260,7 +260,7 @@ class ShowQos(command.ShowOne): class UnsetQos(command.Command): - """Unset QoS specification properties""" + _description = _("Unset QoS specification properties") def get_parser(self, prog_name): parser = super(UnsetQos, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/service.py b/openstackclient/volume/v2/service.py index 867c4b9c..d468c6ff 100644 --- a/openstackclient/volume/v2/service.py +++ b/openstackclient/volume/v2/service.py @@ -22,7 +22,7 @@ from openstackclient.i18n import _ class ListService(command.Lister): - """List service command""" + _description = _("List service command") def get_parser(self, prog_name): parser = super(ListService, self).get_parser(prog_name) @@ -76,7 +76,7 @@ class ListService(command.Lister): class SetService(command.Command): - """Set volume service properties""" + _description = _("Set volume service properties") def get_parser(self, prog_name): parser = super(SetService, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/snapshot.py b/openstackclient/volume/v2/snapshot.py index 2f3de211..a18887e3 100644 --- a/openstackclient/volume/v2/snapshot.py +++ b/openstackclient/volume/v2/snapshot.py @@ -36,7 +36,7 @@ LOG = logging.getLogger(__name__) class CreateSnapshot(command.ShowOne): - """Create new snapshot""" + _description = _("Create new snapshot") def get_parser(self, prog_name): parser = super(CreateSnapshot, self).get_parser(prog_name) @@ -91,7 +91,7 @@ class CreateSnapshot(command.ShowOne): class DeleteSnapshot(command.Command): - """Delete volume snapshot(s)""" + _description = _("Delete volume snapshot(s)") def get_parser(self, prog_name): parser = super(DeleteSnapshot, self).get_parser(prog_name) @@ -128,7 +128,7 @@ class DeleteSnapshot(command.Command): class ListSnapshot(command.Lister): - """List snapshots""" + _description = _("List snapshots") def get_parser(self, prog_name): parser = super(ListSnapshot, self).get_parser(prog_name) @@ -211,7 +211,7 @@ class ListSnapshot(command.Lister): class SetSnapshot(command.Command): - """Set snapshot properties""" + _description = _("Set snapshot properties") def get_parser(self, prog_name): parser = super(SetSnapshot, self).get_parser(prog_name) @@ -294,7 +294,7 @@ class SetSnapshot(command.Command): class ShowSnapshot(command.ShowOne): - """Display snapshot details""" + _description = _("Display snapshot details") def get_parser(self, prog_name): parser = super(ShowSnapshot, self).get_parser(prog_name) @@ -318,7 +318,7 @@ class ShowSnapshot(command.ShowOne): class UnsetSnapshot(command.Command): - """Unset snapshot properties""" + _description = _("Unset snapshot properties") def get_parser(self, prog_name): parser = super(UnsetSnapshot, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py index cb409711..80abfb55 100644 --- a/openstackclient/volume/v2/volume.py +++ b/openstackclient/volume/v2/volume.py @@ -45,7 +45,7 @@ def _check_size_arg(args): class CreateVolume(command.ShowOne): - """Create new volume""" + _description = _("Create new volume") def get_parser(self, prog_name): parser = super(CreateVolume, self).get_parser(prog_name) @@ -211,7 +211,7 @@ class CreateVolume(command.ShowOne): class DeleteVolume(command.Command): - """Delete volume(s)""" + _description = _("Delete volume(s)") def get_parser(self, prog_name): parser = super(DeleteVolume, self).get_parser(prog_name) @@ -263,7 +263,7 @@ class DeleteVolume(command.Command): class ListVolume(command.Lister): - """List volumes""" + _description = _("List volumes") def get_parser(self, prog_name): parser = super(ListVolume, self).get_parser(prog_name) @@ -409,8 +409,55 @@ class ListVolume(command.Lister): ) for s in data)) +class MigrateVolume(command.Command): + _description = _("Migrate volume to a new host") + + def get_parser(self, prog_name): + parser = super(MigrateVolume, self).get_parser(prog_name) + parser.add_argument( + 'volume', + metavar="<volume>", + help=_("Volume to migrate (name or ID)") + ) + parser.add_argument( + '--host', + metavar="<host>", + required=True, + help=_("Destination host (takes the form: host@backend-name#pool)") + ) + parser.add_argument( + '--force-host-copy', + action="store_true", + help=_("Enable generic host-based force-migration, " + "which bypasses driver optimizations") + ) + lock_group = parser.add_mutually_exclusive_group() + lock_group.add_argument( + '--lock-volume', + action="store_true", + help=_("If specified, the volume state will be locked " + "and will not allow a migration to be aborted " + "(possibly by another operation)") + ) + lock_group.add_argument( + '--unlock-volume', + action="store_true", + help=_("If specified, the volume state will not be " + "locked and the a migration can be aborted " + "(default) (possibly by another operation)") + ) + return parser + + def take_action(self, parsed_args): + volume_client = self.app.client_manager.volume + volume = utils.find_resource(volume_client.volumes, parsed_args.volume) + volume_client.volumes.migrate_volume(volume.id, parsed_args.host, + parsed_args.force_host_copy, + parsed_args.lock_volume,) + + class SetVolume(command.Command): - """Set volume properties""" + _description = _("Set volume properties") def get_parser(self, prog_name): parser = super(SetVolume, self).get_parser(prog_name) @@ -462,6 +509,19 @@ class SetVolume(command.Command): 'in the database with no regard to actual status, ' 'exercise caution when using)'), ) + parser.add_argument( + '--type', + metavar='<volume-type>', + help=_('New volume type (name or ID)'), + ) + parser.add_argument( + '--retype-policy', + metavar='<retype-policy>', + choices=['never', 'on-demand'], + help=_('Migration policy while re-typing volume ' + '("never" or "on-demand", default is "never" ) ' + '(available only when "--type" option is specified)'), + ) bootable_group = parser.add_mutually_exclusive_group() bootable_group.add_argument( "--bootable", @@ -473,6 +533,17 @@ class SetVolume(command.Command): action="store_true", help=_("Mark volume as non-bootable") ) + readonly_group = parser.add_mutually_exclusive_group() + readonly_group.add_argument( + "--read-only", + action="store_true", + help=_("Set volume to read-only access mode") + ) + readonly_group.add_argument( + "--read-write", + action="store_true", + help=_("Set volume to read-write access mode") + ) return parser def take_action(self, parsed_args): @@ -523,6 +594,37 @@ class SetVolume(command.Command): except Exception as e: LOG.error(_("Failed to set volume bootable property: %s"), e) result += 1 + if parsed_args.read_only or parsed_args.read_write: + try: + volume_client.volumes.update_readonly_flag( + volume.id, + parsed_args.read_only) + except Exception as e: + LOG.error(_("Failed to set volume read-only access " + "mode flag: %s"), e) + result += 1 + if parsed_args.type: + # get the migration policy + migration_policy = 'never' + if parsed_args.retype_policy: + migration_policy = parsed_args.retype_policy + try: + # find the volume type + volume_type = utils.find_resource( + volume_client.volume_types, + parsed_args.type) + # reset to the new volume type + volume_client.volumes.retype( + volume.id, + volume_type.id, + migration_policy) + except Exception as e: + LOG.error(_("Failed to set volume type: %s"), e) + result += 1 + elif parsed_args.retype_policy: + # If the "--retype-policy" is specified without "--type" + LOG.warning(_("'--retype-policy' option will not work " + "without '--type' option")) kwargs = {} if parsed_args.name: @@ -543,7 +645,7 @@ class SetVolume(command.Command): class ShowVolume(command.ShowOne): - """Display volume details""" + _description = _("Display volume details") def get_parser(self, prog_name): parser = super(ShowVolume, self).get_parser(prog_name) @@ -574,7 +676,7 @@ class ShowVolume(command.ShowOne): class UnsetVolume(command.Command): - """Unset volume properties""" + _description = _("Unset volume properties") def get_parser(self, prog_name): parser = super(UnsetVolume, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/volume_transfer_request.py b/openstackclient/volume/v2/volume_transfer_request.py index 9008fe3c..aefe594a 100644 --- a/openstackclient/volume/v2/volume_transfer_request.py +++ b/openstackclient/volume/v2/volume_transfer_request.py @@ -28,7 +28,7 @@ LOG = logging.getLogger(__name__) class AcceptTransferRequest(command.ShowOne): - """Accept volume transfer request.""" + _description = _("Accept volume transfer request.") def get_parser(self, prog_name): parser = super(AcceptTransferRequest, self).get_parser(prog_name) @@ -56,7 +56,7 @@ class AcceptTransferRequest(command.ShowOne): class CreateTransferRequest(command.ShowOne): - """Create volume transfer request.""" + _description = _("Create volume transfer request.") def get_parser(self, prog_name): parser = super(CreateTransferRequest, self).get_parser(prog_name) @@ -85,7 +85,7 @@ class CreateTransferRequest(command.ShowOne): class DeleteTransferRequest(command.Command): - """Delete volume transfer request(s).""" + _description = _("Delete volume transfer request(s).") def get_parser(self, prog_name): parser = super(DeleteTransferRequest, self).get_parser(prog_name) @@ -120,7 +120,7 @@ class DeleteTransferRequest(command.Command): class ListTransferRequest(command.Lister): - """Lists all volume transfer requests.""" + _description = _("Lists all volume transfer requests.") def get_parser(self, prog_name): parser = super(ListTransferRequest, self).get_parser(prog_name) @@ -151,7 +151,7 @@ class ListTransferRequest(command.Lister): class ShowTransferRequest(command.ShowOne): - """Show volume transfer request details.""" + _description = _("Show volume transfer request details.") def get_parser(self, prog_name): parser = super(ShowTransferRequest, self).get_parser(prog_name) diff --git a/openstackclient/volume/v2/volume_type.py b/openstackclient/volume/v2/volume_type.py index 80a1f21b..42ebb53e 100644 --- a/openstackclient/volume/v2/volume_type.py +++ b/openstackclient/volume/v2/volume_type.py @@ -30,7 +30,7 @@ LOG = logging.getLogger(__name__) class CreateVolumeType(command.ShowOne): - """Create new volume type""" + _description = _("Create new volume type") def get_parser(self, prog_name): parser = super(CreateVolumeType, self).get_parser(prog_name) @@ -116,7 +116,7 @@ class CreateVolumeType(command.ShowOne): class DeleteVolumeType(command.Command): - """Delete volume type(s)""" + _description = _("Delete volume type(s)") def get_parser(self, prog_name): parser = super(DeleteVolumeType, self).get_parser(prog_name) @@ -152,7 +152,7 @@ class DeleteVolumeType(command.Command): class ListVolumeType(command.Lister): - """List volume types""" + _description = _("List volume types") def get_parser(self, prog_name): parser = super(ListVolumeType, self).get_parser(prog_name) @@ -176,10 +176,11 @@ class ListVolumeType(command.Lister): def take_action(self, parsed_args): if parsed_args.long: - columns = ['ID', 'Name', 'Description', 'Extra Specs'] - column_headers = ['ID', 'Name', 'Description', 'Properties'] + columns = ['ID', 'Name', 'Is Public', 'Description', 'Extra Specs'] + column_headers = [ + 'ID', 'Name', 'Is Public', 'Description', 'Properties'] else: - columns = ['ID', 'Name'] + columns = ['ID', 'Name', 'Is Public'] column_headers = columns is_public = None @@ -197,7 +198,7 @@ class ListVolumeType(command.Lister): class SetVolumeType(command.Command): - """Set volume type properties""" + _description = _("Set volume type properties") def get_parser(self, prog_name): parser = super(SetVolumeType, self).get_parser(prog_name) @@ -213,7 +214,7 @@ class SetVolumeType(command.Command): ) parser.add_argument( '--description', - metavar='<name>', + metavar='<description>', help=_('Set volume type description'), ) parser.add_argument( @@ -286,7 +287,7 @@ class SetVolumeType(command.Command): class ShowVolumeType(command.ShowOne): - """Display volume type details""" + _description = _("Display volume type details") def get_parser(self, prog_name): parser = super(ShowVolumeType, self).get_parser(prog_name) @@ -324,7 +325,7 @@ class ShowVolumeType(command.ShowOne): class UnsetVolumeType(command.Command): - """Unset volume type properties""" + _description = _("Unset volume type properties") def get_parser(self, prog_name): parser = super(UnsetVolumeType, self).get_parser(prog_name) |
