summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/common/availability_zone.py2
-rw-r--r--openstackclient/common/client_config.py7
-rw-r--r--openstackclient/common/configuration.py2
-rw-r--r--openstackclient/common/extension.py2
-rw-r--r--openstackclient/common/limits.py2
-rw-r--r--openstackclient/common/module.py4
-rw-r--r--openstackclient/common/quota.py4
-rw-r--r--openstackclient/compute/v2/agent.py8
-rw-r--r--openstackclient/compute/v2/aggregate.py16
-rw-r--r--openstackclient/compute/v2/console.py4
-rw-r--r--openstackclient/compute/v2/fixedip.py4
-rw-r--r--openstackclient/compute/v2/flavor.py12
-rw-r--r--openstackclient/compute/v2/floatingip.py4
-rw-r--r--openstackclient/compute/v2/host.py7
-rw-r--r--openstackclient/compute/v2/hypervisor.py16
-rw-r--r--openstackclient/compute/v2/hypervisor_stats.py4
-rw-r--r--openstackclient/compute/v2/keypair.py23
-rw-r--r--openstackclient/compute/v2/server.py81
-rw-r--r--openstackclient/compute/v2/server_backup.py2
-rw-r--r--openstackclient/compute/v2/server_group.py8
-rw-r--r--openstackclient/compute/v2/server_image.py2
-rw-r--r--openstackclient/compute/v2/service.py6
-rw-r--r--openstackclient/compute/v2/usage.py4
-rw-r--r--openstackclient/identity/v2_0/catalog.py4
-rw-r--r--openstackclient/identity/v2_0/ec2creds.py8
-rw-r--r--openstackclient/identity/v2_0/endpoint.py8
-rw-r--r--openstackclient/identity/v2_0/project.py12
-rw-r--r--openstackclient/identity/v2_0/role.py14
-rw-r--r--openstackclient/identity/v2_0/role_assignment.py2
-rw-r--r--openstackclient/identity/v2_0/service.py8
-rw-r--r--openstackclient/identity/v2_0/token.py4
-rw-r--r--openstackclient/identity/v2_0/user.py18
-rw-r--r--openstackclient/identity/v3/catalog.py4
-rw-r--r--openstackclient/identity/v3/consumer.py10
-rw-r--r--openstackclient/identity/v3/credential.py10
-rw-r--r--openstackclient/identity/v3/domain.py10
-rw-r--r--openstackclient/identity/v3/ec2creds.py8
-rw-r--r--openstackclient/identity/v3/endpoint.py10
-rw-r--r--openstackclient/identity/v3/federation_protocol.py10
-rw-r--r--openstackclient/identity/v3/group.py16
-rw-r--r--openstackclient/identity/v3/identity_provider.py10
-rw-r--r--openstackclient/identity/v3/mapping.py12
-rw-r--r--openstackclient/identity/v3/policy.py10
-rw-r--r--openstackclient/identity/v3/project.py10
-rw-r--r--openstackclient/identity/v3/region.py10
-rw-r--r--openstackclient/identity/v3/role.py16
-rw-r--r--openstackclient/identity/v3/role_assignment.py4
-rw-r--r--openstackclient/identity/v3/service.py10
-rw-r--r--openstackclient/identity/v3/service_provider.py10
-rw-r--r--openstackclient/identity/v3/token.py10
-rw-r--r--openstackclient/identity/v3/trust.py8
-rw-r--r--openstackclient/identity/v3/unscoped_saml.py26
-rw-r--r--openstackclient/identity/v3/user.py24
-rw-r--r--openstackclient/image/client.py2
-rw-r--r--openstackclient/image/v1/image.py30
-rw-r--r--openstackclient/image/v2/image.py50
-rw-r--r--openstackclient/network/common.py17
-rw-r--r--openstackclient/network/v2/address_scope.py10
-rw-r--r--openstackclient/network/v2/floating_ip.py88
-rw-r--r--openstackclient/network/v2/floating_ip_pool.py4
-rw-r--r--openstackclient/network/v2/ip_availability.py4
-rw-r--r--openstackclient/network/v2/network.py66
-rw-r--r--openstackclient/network/v2/network_agent.py8
-rw-r--r--openstackclient/network/v2/network_qos_policy.py238
-rw-r--r--openstackclient/network/v2/network_rbac.py45
-rw-r--r--openstackclient/network/v2/network_segment.py23
-rw-r--r--openstackclient/network/v2/network_service_provider.py41
-rw-r--r--openstackclient/network/v2/port.py229
-rw-r--r--openstackclient/network/v2/router.py108
-rw-r--r--openstackclient/network/v2/security_group.py31
-rw-r--r--openstackclient/network/v2/security_group_rule.py156
-rw-r--r--openstackclient/network/v2/subnet.py59
-rw-r--r--openstackclient/network/v2/subnet_pool.py66
-rw-r--r--openstackclient/object/v1/account.py16
-rw-r--r--openstackclient/object/v1/container.py48
-rw-r--r--openstackclient/object/v1/object.py68
-rw-r--r--openstackclient/shell.py12
-rw-r--r--openstackclient/tests/functional/base.py4
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_qos_policy.py55
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_service_provider.py26
-rw-r--r--openstackclient/tests/functional/volume/v1/test_snapshot.py32
-rw-r--r--openstackclient/tests/functional/volume/v2/test_snapshot.py32
-rw-r--r--openstackclient/tests/functional/volume/v2/test_volume.py10
-rw-r--r--openstackclient/tests/unit/compute/v2/test_hypervisor.py67
-rw-r--r--openstackclient/tests/unit/compute/v2/test_keypair.py3
-rw-r--r--openstackclient/tests/unit/identity/v3/test_unscoped_saml.py26
-rw-r--r--openstackclient/tests/unit/image/v1/fakes.py44
-rw-r--r--openstackclient/tests/unit/image/v1/test_image.py242
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py35
-rw-r--r--openstackclient/tests/unit/network/test_common.py14
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py199
-rw-r--r--openstackclient/tests/unit/network/v2/test_floating_ip.py117
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py93
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_qos_policy.py380
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_rbac.py40
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_service_provider.py71
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py429
-rw-r--r--openstackclient/tests/unit/network/v2/test_router.py103
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group.py38
-rw-r--r--openstackclient/tests/unit/network/v2/test_security_group_rule.py226
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py16
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet_pool.py8
-rw-r--r--openstackclient/tests/unit/volume/v1/fakes.py135
-rw-r--r--openstackclient/tests/unit/volume/v1/test_backup.py45
-rw-r--r--openstackclient/tests/unit/volume/v1/test_qos_specs.py257
-rw-r--r--openstackclient/tests/unit/volume/v1/test_snapshot.py50
-rw-r--r--openstackclient/tests/unit/volume/v1/test_type.py6
-rw-r--r--openstackclient/tests/unit/volume/v1/test_volume.py160
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py107
-rw-r--r--openstackclient/tests/unit/volume/v2/test_backup.py58
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group.py276
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py351
-rw-r--r--openstackclient/tests/unit/volume/v2/test_snapshot.py51
-rw-r--r--openstackclient/tests/unit/volume/v2/test_type.py5
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume.py197
-rw-r--r--openstackclient/tests/unit/volume/v2/test_volume_host.py86
-rw-r--r--openstackclient/volume/v1/backup.py63
-rw-r--r--openstackclient/volume/v1/qos_specs.py16
-rw-r--r--openstackclient/volume/v1/service.py4
-rw-r--r--openstackclient/volume/v1/snapshot.py30
-rw-r--r--openstackclient/volume/v1/volume.py107
-rw-r--r--openstackclient/volume/v1/volume_snapshot.py305
-rw-r--r--openstackclient/volume/v1/volume_transfer_request.py10
-rw-r--r--openstackclient/volume/v1/volume_type.py18
-rw-r--r--openstackclient/volume/v2/backup.py84
-rw-r--r--openstackclient/volume/v2/consistency_group.py145
-rw-r--r--openstackclient/volume/v2/consistency_group_snapshot.py190
-rw-r--r--openstackclient/volume/v2/qos_specs.py16
-rw-r--r--openstackclient/volume/v2/service.py4
-rw-r--r--openstackclient/volume/v2/snapshot.py30
-rw-r--r--openstackclient/volume/v2/volume.py119
-rw-r--r--openstackclient/volume/v2/volume_host.py50
-rw-r--r--openstackclient/volume/v2/volume_snapshot.py338
-rw-r--r--openstackclient/volume/v2/volume_transfer_request.py10
-rw-r--r--openstackclient/volume/v2/volume_type.py21
135 files changed, 6374 insertions, 1239 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 30286df8..1ac53f7f 100644
--- a/openstackclient/common/client_config.py
+++ b/openstackclient/common/client_config.py
@@ -17,6 +17,8 @@ import logging
from os_client_config import config
from os_client_config import exceptions as occ_exceptions
+from oslo_utils import strutils
+import six
LOG = logging.getLogger(__name__)
@@ -149,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')
):
@@ -180,7 +183,9 @@ class OSC_Config(config.OpenStackConfig):
config = self._auth_v2_ignore_v3(config)
config = self._auth_default_domain(config)
- LOG.debug("auth_config_hook(): %s" % config)
+ if LOG.isEnabledFor(logging.DEBUG):
+ LOG.debug("auth_config_hook(): %s",
+ strutils.mask_password(six.text_type(config)))
return config
def load_auth_plugin(self, config):
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 0222e899..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)
@@ -35,14 +35,24 @@ class ListHypervisor(command.Lister):
metavar="<hostname>",
help=_("Filter hypervisors using <hostname> substring")
)
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ help=_("List additional fields in output")
+ )
return parser
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
columns = (
"ID",
- "Hypervisor Hostname"
+ "Hypervisor Hostname",
+ "Hypervisor Type",
+ "Host IP",
+ "State"
)
+ if parsed_args.long:
+ columns += ("vCPUs Used", "vCPUs", "Memory MB Used", "Memory MB")
if parsed_args.matching:
data = compute_client.hypervisors.search(parsed_args.matching)
@@ -56,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 d30fd429..a63cbfec 100644
--- a/openstackclient/compute/v2/keypair.py
+++ b/openstackclient/compute/v2/keypair.py
@@ -32,19 +32,20 @@ LOG = logging.getLogger(__name__)
class CreateKeypair(command.ShowOne):
- """Create new public key"""
+ _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)
parser.add_argument(
'name',
metavar='<name>',
- help=_("New public key name")
+ help=_("New public or private key name")
)
parser.add_argument(
'--public-key',
metavar='<file>',
- help=_("Filename for public key to add")
+ help=_("Filename for public key to add. If not used, "
+ "creates a private key.")
)
return parser
@@ -82,7 +83,7 @@ class CreateKeypair(command.ShowOne):
class DeleteKeypair(command.Command):
- """Delete public key(s)"""
+ _description = _("Delete public or private key(s)")
def get_parser(self, prog_name):
parser = super(DeleteKeypair, self).get_parser(prog_name)
@@ -90,7 +91,7 @@ class DeleteKeypair(command.Command):
'name',
metavar='<key>',
nargs='+',
- help=_("Public key(s) to delete (name only)")
+ help=_("Name of key(s) to delete (name only)")
)
return parser
@@ -104,19 +105,19 @@ class DeleteKeypair(command.Command):
compute_client.keypairs.delete(data.name)
except Exception as e:
result += 1
- LOG.error(_("Failed to delete public key with name "
+ LOG.error(_("Failed to delete key with name "
"'%(name)s': %(e)s")
% {'name': n, 'e': e})
if result > 0:
total = len(parsed_args.name)
- msg = (_("%(result)s of %(total)s public keys failed "
+ msg = (_("%(result)s of %(total)s keys failed "
"to delete.") % {'result': result, 'total': total})
raise exceptions.CommandError(msg)
class ListKeypair(command.Lister):
- """List public key fingerprints"""
+ _description = _("List key fingerprints")
def take_action(self, parsed_args):
compute_client = self.app.client_manager.compute
@@ -133,20 +134,20 @@ class ListKeypair(command.Lister):
class ShowKeypair(command.ShowOne):
- """Display public key details"""
+ _description = _("Display key details")
def get_parser(self, prog_name):
parser = super(ShowKeypair, self).get_parser(prog_name)
parser.add_argument(
'name',
metavar='<key>',
- help=_("Public key to display (name only)")
+ help=_("Public or private key to display (name only)")
)
parser.add_argument(
'--public-key',
action='store_true',
default=False,
- help=_("Show only bare public key (name only)")
+ help=_("Show only bare public key paired with the generated key")
)
return parser
diff --git a/openstackclient/compute/v2/server.py b/openstackclient/compute/v2/server.py
index 1ca31497..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)
@@ -973,15 +974,15 @@ class MigrateServer(command.Command):
migration_group = parser.add_mutually_exclusive_group()
migration_group.add_argument(
'--shared-migration',
- dest='shared_migration',
- action='store_true',
- default=True,
+ dest='block_migration',
+ action='store_false',
+ default=False,
help=_('Perform a shared live migration (default)'),
)
migration_group.add_argument(
'--block-migration',
- dest='shared_migration',
- action='store_false',
+ dest='block_migration',
+ action='store_true',
help=_('Perform a block live migration'),
)
disk_group = parser.add_mutually_exclusive_group()
@@ -1016,9 +1017,9 @@ class MigrateServer(command.Command):
)
if parsed_args.live:
server.live_migrate(
- parsed_args.live,
- parsed_args.shared_migration,
- parsed_args.disk_overcommit,
+ host=parsed_args.live,
+ block_migration=parsed_args.block_migration,
+ disk_over_commit=parsed_args.disk_overcommit,
)
else:
server.migrate()
@@ -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 d2075150..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)
@@ -94,6 +94,10 @@ class CreateUser(command.ShowOne):
if parsed_args.password_prompt:
parsed_args.password = utils.get_password(self.app.stdin)
+ if not parsed_args.password:
+ LOG.warning(_("No password was supplied, authentication will fail "
+ "when a user does not have a password."))
+
try:
user = identity_client.users.create(
parsed_args.name,
@@ -126,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)
@@ -150,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)
@@ -238,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)
@@ -292,6 +296,10 @@ class SetUser(command.Command):
if parsed_args.password_prompt:
parsed_args.password = utils.get_password(self.app.stdin)
+ if '' == parsed_args.password:
+ LOG.warning(_("No password was supplied, authentication will fail "
+ "when a user does not have a password."))
+
user = utils.find_resource(
identity_client.users,
parsed_args.user,
@@ -328,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..f7598f17 100644
--- a/openstackclient/identity/v3/unscoped_saml.py
+++ b/openstackclient/identity/v3/unscoped_saml.py
@@ -18,35 +18,14 @@ the user can list domains and projects they are allowed to access, and request
a scoped token."""
from osc_lib.command import command
-from osc_lib import exceptions
from osc_lib import utils
from openstackclient.i18n import _
-UNSCOPED_AUTH_PLUGINS = ['v3unscopedsaml', 'v3unscopedadfs', 'v3oidc']
-
-
-def auth_with_unscoped_saml(func):
- """Check the unscoped federated context"""
-
- def _decorated(self, parsed_args):
- auth_plugin_name = self.app.client_manager.auth_plugin_name
- if auth_plugin_name in UNSCOPED_AUTH_PLUGINS:
- return func(self, parsed_args)
- else:
- msg = (_('This command requires the use of an unscoped SAML '
- 'authentication plugin. Please use argument '
- '--os-auth-type with one of the following '
- 'plugins: %s') % ', '.join(UNSCOPED_AUTH_PLUGINS))
- raise exceptions.CommandError(msg)
- return _decorated
-
-
class ListAccessibleDomains(command.Lister):
- """List accessible domains"""
+ _description = _("List accessible domains")
- @auth_with_unscoped_saml
def take_action(self, parsed_args):
columns = ('ID', 'Enabled', 'Name', 'Description')
identity_client = self.app.client_manager.identity
@@ -59,9 +38,8 @@ 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):
columns = ('ID', 'Domain ID', 'Enabled', 'Name')
identity_client = self.app.client_manager.identity
diff --git a/openstackclient/identity/v3/user.py b/openstackclient/identity/v3/user.py
index dc47ef8d..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)
@@ -110,6 +110,10 @@ class CreateUser(command.ShowOne):
if parsed_args.password_prompt:
parsed_args.password = utils.get_password(self.app.stdin)
+ if not parsed_args.password:
+ LOG.warning(_("No password was supplied, authentication will fail "
+ "when a user does not have a password."))
+
try:
user = identity_client.users.create(
name=parsed_args.name,
@@ -134,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)
@@ -169,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)
@@ -269,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)
@@ -329,6 +333,10 @@ class SetUser(command.Command):
if parsed_args.password_prompt:
parsed_args.password = utils.get_password(self.app.stdin)
+ if '' == parsed_args.password:
+ LOG.warning(_("No password was supplied, authentication will fail "
+ "when a user does not have a password."))
+
user = utils.find_resource(
identity_client.users,
parsed_args.user,
@@ -357,7 +365,7 @@ class SetUser(command.Command):
class SetPasswordUser(command.Command):
- """Change current user password"""
+ _description = _("Change current user password")
required_scope = False
@@ -408,11 +416,15 @@ class SetPasswordUser(command.Command):
password = utils.get_password(
self.app.stdin, prompt="New Password:")
+ if '' == password:
+ LOG.warning(_("No password was supplied, authentication will fail "
+ "when a user does not have a password."))
+
identity_client.users.update_password(current_password, password)
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..1d167605 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(
@@ -482,7 +486,6 @@ class ListImage(command.Lister):
if parsed_args.marker:
kwargs['marker'] = utils.find_resource(image_client.images,
parsed_args.marker).id
-
if parsed_args.long:
columns = (
'ID',
@@ -515,7 +518,19 @@ class ListImage(command.Lister):
column_headers = columns
# List of image data received
- data = image_client.api.image_list(**kwargs)
+ data = []
+ if 'marker' in kwargs:
+ data = image_client.api.image_list(**kwargs)
+ else:
+ # No pages received yet, so start the page marker at None.
+ marker = None
+ while True:
+ page = image_client.api.image_list(marker=marker, **kwargs)
+ if not page:
+ break
+ data.extend(page)
+ # Set the marker to the id of the last item we received
+ marker = page[-1]['id']
if parsed_args.property:
# NOTE(dtroyer): coerce to a list to subscript it in py3
@@ -542,7 +557,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 +590,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 +618,7 @@ class SaveImage(command.Command):
class SetImage(command.Command):
- """Set image properties"""
+ _description = _("Set image properties")
deadopts = ('visibility',)
@@ -650,8 +665,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 +860,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 +883,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/v2/address_scope.py b/openstackclient/network/v2/address_scope.py
index 95f6c947..0d8f80d0 100644
--- a/openstackclient/network/v2/address_scope.py
+++ b/openstackclient/network/v2/address_scope.py
@@ -58,7 +58,7 @@ def _get_attrs(client_manager, parsed_args):
# 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)
@@ -106,7 +106,7 @@ class CreateAddressScope(command.ShowOne):
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)
@@ -141,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
@@ -170,7 +170,7 @@ class ListAddressScope(command.Lister):
# 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)
@@ -214,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)
diff --git a/openstackclient/network/v2/floating_ip.py b/openstackclient/network/v2/floating_ip.py
index bb75540c..7b8374e2 100644
--- a/openstackclient/network/v2/floating_ip.py
+++ b/openstackclient/network/v2/floating_ip.py
@@ -18,7 +18,16 @@ import logging
from osc_lib import utils
from openstackclient.i18n import _
+from openstackclient.identity import common as identity_common
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):
@@ -58,11 +67,20 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.description is not None:
attrs['description'] = parsed_args.description
+ if parsed_args.project:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
return attrs
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
@@ -105,14 +123,20 @@ class CreateFloatingIP(common.NetworkAndComputeShowOne):
metavar='<description>',
help=_('Set floating IP description')
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
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 +146,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 +170,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 +195,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,23 +219,63 @@ 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',
'fixed_ip_address',
'port_id',
+ 'floating_network_id',
+ 'project_id',
)
headers = (
'ID',
'Floating IP Address',
'Fixed IP Address',
'Port',
+ 'Floating Network',
+ 'Project',
)
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,
@@ -246,7 +310,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.
@@ -270,7 +334,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(
@@ -282,9 +346,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(
@@ -297,7 +361,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 839e1d00..a80fe1c4 100644
--- a/openstackclient/network/v2/ip_availability.py
+++ b/openstackclient/network/v2/ip_availability.py
@@ -35,7 +35,7 @@ def _get_columns(item):
# TODO(ankur-gupta-f): Use the SDK resource mapped attribute names once
# the OSC minimum requirements include SDK 1.0.
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)
@@ -94,7 +94,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 dbf1b601..1c06c462 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -42,6 +42,7 @@ def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
+ if 'project_id' not in columns:
columns.append('project_id')
return tuple(sorted(columns))
@@ -98,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
@@ -155,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(
@@ -248,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
@@ -275,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'
@@ -301,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',
@@ -360,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):
@@ -432,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,
@@ -462,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)
@@ -541,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
@@ -553,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
new file mode 100644
index 00000000..5ccbe36b
--- /dev/null
+++ b/openstackclient/network/v2/network_qos_policy.py
@@ -0,0 +1,238 @@
+# 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 logging
+
+from osc_lib.command import command
+from osc_lib import exceptions
+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):
+ 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):
+ attrs = {}
+ if parsed_args.name is not None:
+ attrs['name'] = str(parsed_args.name)
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+ if parsed_args.share:
+ attrs['shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
+ if parsed_args.project is not None:
+ identity_client = client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ attrs['tenant_id'] = project_id
+
+ return attrs
+
+
+# TODO(abhiraut): Use the SDK resource mapped attribute names once the
+# OSC minimum requirements include SDK 1.0.
+class CreateNetworkQosPolicy(command.ShowOne):
+ _description = _("Create a QoS policy")
+
+ def get_parser(self, prog_name):
+ parser = super(CreateNetworkQosPolicy, self).get_parser(prog_name)
+ parser.add_argument(
+ 'name',
+ metavar='<name>',
+ help=_("Name of QoS policy to create")
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("Description of the QoS policy")
+ )
+ share_group = parser.add_mutually_exclusive_group()
+ share_group.add_argument(
+ '--share',
+ action='store_true',
+ default=None,
+ help=_("Make the QoS policy accessible by other projects")
+ )
+ share_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_("Make the QoS policy not accessible by other projects "
+ "(default)")
+ )
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("Owner's project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ attrs = _get_attrs(self.app.client_manager, parsed_args)
+ obj = client.create_qos_policy(**attrs)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns, formatters={})
+ return (display_columns, data)
+
+
+class DeleteNetworkQosPolicy(command.Command):
+ _description = _("Delete Qos Policy(s)")
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteNetworkQosPolicy, self).get_parser(prog_name)
+ parser.add_argument(
+ 'policy',
+ metavar="<qos-policy>",
+ nargs="+",
+ help=_("QoS policy(s) to delete (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ result = 0
+
+ for policy in parsed_args.policy:
+ try:
+ obj = client.find_qos_policy(policy, ignore_missing=False)
+ client.delete_qos_policy(obj)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete QoS policy "
+ "name or ID '%(qos_policy)s': %(e)s"),
+ {'qos_policy': policy, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.policy)
+ msg = (_("%(result)s of %(total)s QoS policies failed "
+ "to delete.") % {'result': result, 'total': total})
+ 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):
+ _description = _("List QoS policies")
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ columns = (
+ 'id',
+ 'name',
+ 'is_shared',
+ 'project_id',
+ )
+ column_headers = (
+ 'ID',
+ 'Name',
+ 'Shared',
+ 'Project',
+ )
+ data = client.qos_policies()
+
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns, formatters={},
+ ) 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):
+ _description = _("Set QoS policy properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetNetworkQosPolicy, self).get_parser(prog_name)
+ parser.add_argument(
+ 'policy',
+ metavar="<qos-policy>",
+ help=_("QoS policy to modify (name or ID)")
+ )
+ parser.add_argument(
+ '--name',
+ metavar="<name>",
+ help=_('Set QoS policy name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_("Description of the QoS policy")
+ )
+ enable_group = parser.add_mutually_exclusive_group()
+ enable_group.add_argument(
+ '--share',
+ action='store_true',
+ help=_('Make the QoS policy accessible by other projects'),
+ )
+ enable_group.add_argument(
+ '--no-share',
+ action='store_true',
+ help=_('Make the QoS policy not accessible by other projects'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_qos_policy(
+ parsed_args.policy,
+ ignore_missing=False)
+ attrs = {}
+ if parsed_args.name is not None:
+ attrs['name'] = parsed_args.name
+ if parsed_args.share:
+ attrs['shared'] = True
+ if parsed_args.no_share:
+ attrs['shared'] = False
+ if parsed_args.description is not None:
+ attrs['description'] = parsed_args.description
+ client.update_qos_policy(obj, **attrs)
+
+
+class ShowNetworkQosPolicy(command.ShowOne):
+ _description = _("Display QoS policy details")
+
+ def get_parser(self, prog_name):
+ parser = super(ShowNetworkQosPolicy, self).get_parser(prog_name)
+ parser.add_argument(
+ 'policy',
+ metavar="<qos-policy>",
+ help=_("QoS policy to display (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ client = self.app.client_manager.network
+ obj = client.find_qos_policy(parsed_args.policy,
+ ignore_missing=False)
+ display_columns, columns = _get_columns(obj)
+ data = utils.get_item_properties(obj, columns)
+ 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 92b286a9..bce3e2d3 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -50,7 +50,8 @@ def _get_columns(item):
columns = list(item.keys())
if 'tenant_id' in columns:
columns.remove('tenant_id')
- columns.append('project_id')
+ if 'project_id' not in columns:
+ columns.append('project_id')
binding_columns = [
'binding:host_id',
'binding:profile',
@@ -109,6 +110,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:
@@ -126,6 +129,8 @@ def _get_attrs(client_manager, parsed_args):
if parsed_args.host:
attrs['binding:host_id'] = parsed_args.host
+ if parsed_args.dns_name is not None:
+ attrs['dns_name'] = parsed_args.dns_name
# It is possible that name is not updated during 'port set'
if parsed_args.name is not None:
attrs['name'] = str(parsed_args.name)
@@ -146,6 +151,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 +191,61 @@ 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,
+ )
+ parser.add_argument(
+ '--dns-name',
+ metavar='dns-name',
+ help=_("Set DNS name to this port "
+ "(requires DNS integration extension)")
+ )
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)
@@ -281,7 +303,34 @@ class CreatePort(command.ShowOne):
help=_("Name of this port")
)
# TODO(singhj): Add support for extended options:
- # qos,security groups,dhcp, address pairs
+ # qos,dhcp, address pairs
+ secgroups = parser.add_mutually_exclusive_group()
+ secgroups.add_argument(
+ '--security-group',
+ metavar='<security-group>',
+ action='append',
+ dest='security_groups',
+ help=_("Security group to associate with this port (name or ID) "
+ "(repeat option to set multiple security groups)")
+ )
+ secgroups.add_argument(
+ '--no-security-group',
+ dest='no_security_group',
+ 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
def take_action(self, parsed_args):
@@ -291,6 +340,14 @@ class CreatePort(command.ShowOne):
parsed_args.network = _network.id
_prepare_fixed_ips(self.app.client_manager, parsed_args)
attrs = _get_attrs(self.app.client_manager, parsed_args)
+
+ if parsed_args.security_groups:
+ attrs['security_groups'] = [client.find_security_group(
+ sg, ignore_missing=False).id
+ for sg in parsed_args.security_groups]
+ if parsed_args.no_security_group:
+ attrs['security_groups'] = []
+
obj = client.create_port(**attrs)
columns = _get_columns(obj)
data = utils.get_item_properties(obj, columns, formatters=_formatters)
@@ -299,7 +356,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)
@@ -333,7 +390,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)
@@ -360,6 +417,17 @@ class ListPort(command.Lister):
metavar='<server>',
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,
+ help=_("List additional fields in output")
+ )
return parser
def take_action(self, parsed_args):
@@ -371,15 +439,20 @@ class ListPort(command.Lister):
'name',
'mac_address',
'fixed_ips',
+ 'status',
)
column_headers = (
'ID',
'Name',
'MAC Address',
'Fixed IP Addresses',
+ 'Status',
)
filters = {}
+ if parsed_args.long:
+ columns += ('security_groups', 'device_owner',)
+ column_headers += ('Security Groups', 'Device Owner',)
if parsed_args.device_owner is not None:
filters['device_owner'] = parsed_args.device_owner
if parsed_args.router:
@@ -394,6 +467,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)
@@ -405,7 +480,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)
@@ -463,6 +538,32 @@ class SetPort(command.Command):
metavar="<port>",
help=_("Port to modify (name or ID)")
)
+ parser.add_argument(
+ '--security-group',
+ metavar='<security-group>',
+ action='append',
+ dest='security_groups',
+ help=_("Security group to associate with this port (name or ID) "
+ "(repeat option to set multiple security groups)")
+ )
+ parser.add_argument(
+ '--no-security-group',
+ dest='no_security_group',
+ 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
def take_action(self, parsed_args):
@@ -490,12 +591,23 @@ class SetPort(command.Command):
attrs['fixed_ips'] += [ip for ip in obj.fixed_ips if ip]
elif parsed_args.no_fixed_ip:
attrs['fixed_ips'] = []
+ if parsed_args.security_groups and parsed_args.no_security_group:
+ attrs['security_groups'] = [client.find_security_group(sg,
+ ignore_missing=False).id
+ for sg in parsed_args.security_groups]
+ elif parsed_args.security_groups:
+ attrs['security_groups'] = obj.security_groups
+ for sg in parsed_args.security_groups:
+ sg_id = client.find_security_group(sg, ignore_missing=False).id
+ attrs['security_groups'].append(sg_id)
+ elif parsed_args.no_security_group:
+ attrs['security_groups'] = []
client.update_port(obj, **attrs)
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)
@@ -515,7 +627,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)
@@ -536,6 +648,15 @@ class UnsetPort(command.Command):
help=_("Desired key which should be removed from binding:profile"
"(repeat option to unset multiple binding:profile data)"))
parser.add_argument(
+ '--security-group',
+ metavar='<security-group>',
+ action='append',
+ dest='security_groups',
+ help=_("Security group which should be removed this port (name "
+ "or ID) (repeat option to unset multiple security groups)")
+ )
+
+ parser.add_argument(
'port',
metavar="<port>",
help=_("Port to modify (name or ID)")
@@ -550,6 +671,7 @@ class UnsetPort(command.Command):
# Unset* classes
tmp_fixed_ips = copy.deepcopy(obj.fixed_ips)
tmp_binding_profile = copy.deepcopy(obj.binding_profile)
+ tmp_secgroups = copy.deepcopy(obj.security_groups)
_prepare_fixed_ips(self.app.client_manager, parsed_args)
attrs = {}
if parsed_args.fixed_ip:
@@ -568,5 +690,16 @@ class UnsetPort(command.Command):
msg = _("Port does not contain binding-profile %s") % key
raise exceptions.CommandError(msg)
attrs['binding:profile'] = tmp_binding_profile
+ if parsed_args.security_groups:
+ try:
+ for sg in parsed_args.security_groups:
+ sg_id = client.find_security_group(
+ sg, ignore_missing=False).id
+ tmp_secgroups.remove(sg_id)
+ except ValueError:
+ msg = _("Port does not contain security group %s") % sg
+ raise exceptions.CommandError(msg)
+ attrs['security_groups'] = tmp_secgroups
+
if attrs:
client.update_port(obj, **attrs)
diff --git a/openstackclient/network/v2/router.py b/openstackclient/network/v2/router.py
index 48a3a92c..cdd634d0 100644
--- a/openstackclient/network/v2/router.py
+++ b/openstackclient/network/v2/router.py
@@ -25,6 +25,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__)
@@ -59,11 +60,10 @@ _formatters = {
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 _get_attrs(client_manager, parsed_args):
@@ -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)
@@ -215,14 +215,14 @@ class CreateRouter(command.ShowOne):
attrs['ha'] = parsed_args.ha
obj = client.create_router(**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 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,21 +256,43 @@ 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)
parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_("List routers according to their name")
+ )
+ admin_state_group = parser.add_mutually_exclusive_group()
+ admin_state_group.add_argument(
+ '--enable',
+ action='store_true',
+ help=_("List enabled routers")
+ )
+ admin_state_group.add_argument(
+ '--disable',
+ action='store_true',
+ help=_("List disabled routers")
+ )
+ parser.add_argument(
'--long',
action='store_true',
default=False,
help=_("List additional fields in output")
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("List routers according to their project (name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def take_action(self, parsed_args):
+ identity_client = self.app.client_manager.identity
client = self.app.client_manager.network
-
columns = (
'id',
'name',
@@ -289,6 +311,24 @@ class ListRouter(command.Lister):
'HA',
'Project',
)
+
+ args = {}
+
+ if parsed_args.name is not None:
+ args['name'] = parsed_args.name
+
+ if parsed_args.enable:
+ args['admin_state_up'] = True
+ elif parsed_args.disable:
+ args['admin_state_up'] = False
+
+ if parsed_args.project:
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ args['tenant_id'] = project_id
if parsed_args.long:
columns = columns + (
'routes',
@@ -308,7 +348,7 @@ class ListRouter(command.Lister):
'Availability zones',
)
- data = client.routers()
+ data = client.routers(**args)
return (column_headers,
(utils.get_item_properties(
s, columns,
@@ -317,7 +357,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)
@@ -341,7 +381,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)
@@ -368,7 +408,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)
@@ -433,11 +473,19 @@ class SetRouter(command.Command):
action='store_true',
help=argparse.SUPPRESS,
)
-
- # TODO(tangchen): Support setting 'ha' property in 'router set'
- # command. It appears that changing the ha state is supported by
- # neutron under certain conditions.
-
+ routes_ha = parser.add_mutually_exclusive_group()
+ routes_ha.add_argument(
+ '--ha',
+ action='store_true',
+ help=_("Set the router as highly available "
+ "(disabled router only)")
+ )
+ routes_ha.add_argument(
+ '--no-ha',
+ action='store_true',
+ help=_("Clear high availablability attribute of the router "
+ "(disabled router only)")
+ )
# TODO(tangchen): Support setting 'external_gateway_info' property in
# 'router set' command.
@@ -451,6 +499,10 @@ class SetRouter(command.Command):
attrs = _get_attrs(self.app.client_manager, parsed_args)
# Get the route attributes.
+ if parsed_args.ha:
+ attrs['ha'] = True
+ elif parsed_args.no_ha:
+ attrs['ha'] = False
if parsed_args.no_route:
attrs['routes'] = []
elif parsed_args.clear_routes:
@@ -470,7 +522,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)
@@ -484,13 +536,14 @@ class ShowRouter(command.ShowOne):
def take_action(self, parsed_args):
client = self.app.client_manager.network
obj = client.find_router(parsed_args.router, 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 UnsetRouter(command.Command):
- """Unset router properties"""
+ _description = _("Unset router properties")
def get_parser(self, prog_name):
parser = super(UnsetRouter, self).get_parser(prog_name)
@@ -520,12 +573,11 @@ class UnsetRouter(command.Command):
if parsed_args.routes:
try:
for route in parsed_args.routes:
+ route['nexthop'] = route.pop('gateway')
tmp_routes.remove(route)
except ValueError:
msg = (_("Router does not contain route %s") % route)
raise exceptions.CommandError(msg)
- for route in tmp_routes:
- route['nexthop'] = route.pop('gateway')
attrs['routes'] = tmp_routes
if attrs:
client.update_router(obj, **attrs)
diff --git a/openstackclient/network/v2/security_group.py b/openstackclient/network/v2/security_group.py
index f832f721..5420bc8b 100644
--- a/openstackclient/network/v2/security_group.py
+++ b/openstackclient/network/v2/security_group.py
@@ -81,8 +81,9 @@ def _get_columns(item):
columns.remove('security_group_rules')
property_column_mappings.append(('rules', 'security_group_rules'))
if 'tenant_id' in columns:
- columns.append('project_id')
columns.remove('tenant_id')
+ if 'project_id' not in columns:
+ columns.append('project_id')
property_column_mappings.append(('project_id', 'tenant_id'))
display_columns = sorted(columns)
@@ -95,7 +96,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 +166,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 +191,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.
@@ -201,6 +202,13 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
default=False,
help=argparse.SUPPRESS,
)
+ parser.add_argument(
+ '--project',
+ metavar='<project>',
+ help=_("List security groups according to the project "
+ "(name or ID)")
+ )
+ identity_common.add_project_domain_option_to_parser(parser)
return parser
def update_parser_compute(self, parser):
@@ -228,7 +236,16 @@ class ListSecurityGroup(common.NetworkAndComputeLister):
) for s in data))
def take_action_network(self, client, parsed_args):
- return self._get_return_data(client.security_groups())
+ filters = {}
+ if parsed_args.project:
+ identity_client = self.app.client_manager.identity
+ project_id = identity_common.find_project(
+ identity_client,
+ parsed_args.project,
+ parsed_args.project_domain,
+ ).id
+ filters['tenant_id'] = project_id
+ return self._get_return_data(client.security_groups(**filters))
def take_action_compute(self, client, parsed_args):
search = {'all_tenants': parsed_args.all_projects}
@@ -238,7 +255,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 +310,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 1b778c91..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
@@ -542,7 +565,7 @@ class SetSubnet(command.Command):
if not parsed_args.no_allocation_pool:
attrs['allocation_pools'] += obj.allocation_pools
elif parsed_args.no_allocation_pool:
- attrs['allocation_pools'] = ''
+ attrs['allocation_pools'] = []
if 'service_types' in attrs:
attrs['service_types'] += obj.service_types
client.update_subnet(obj, **attrs)
@@ -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 a01d2f7b..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)
@@ -238,40 +247,42 @@ class ListSubnetPool(command.Lister):
shared_group.add_argument(
'--share',
action='store_true',
- help=_("List subnets shared between projects"),
+ help=_("List subnet pools shared between projects"),
)
shared_group.add_argument(
'--no-share',
action='store_true',
- help=_("List subnets not shared between projects"),
+ help=_("List subnet pools not shared between projects"),
)
default_group = parser.add_mutually_exclusive_group()
default_group.add_argument(
'--default',
action='store_true',
- help=_("List subnets used as the default external subnet pool"),
+ help=_("List subnet pools used as the default external "
+ "subnet pool"),
)
default_group.add_argument(
'--no-default',
action='store_true',
- help=_("List subnets not used as the default external subnet pool")
+ help=_("List subnet pools not used as the default external "
+ "subnet pool")
)
parser.add_argument(
'--project',
metavar='<project>',
- help=_("List subnets according to their project (name or ID)")
+ help=_("List subnet pools according to their project (name or ID)")
)
identity_common.add_project_domain_option_to_parser(parser)
parser.add_argument(
'--name',
metavar='<name>',
- help=_("List only subnets of given name in output")
+ help=_("List only subnet pools of given name in output")
)
parser.add_argument(
'--address-scope',
metavar='<address-scope>',
- help=_("List only subnets of given address scope (name or ID) "
- "in output")
+ help=_("List only subnet pools of given address scope "
+ "(name or ID) in output")
)
return parser
@@ -281,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:
@@ -294,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:
@@ -308,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(
@@ -318,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)
@@ -371,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)
@@ -388,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 801fe450..4847f8bb 100644
--- a/openstackclient/object/v1/account.py
+++ b/openstackclient/object/v1/account.py
@@ -18,9 +18,11 @@ from osc_lib.command import command
from osc_lib import utils
import six
+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)
@@ -29,8 +31,8 @@ class SetAccount(command.Command):
metavar="<key=value>",
required=True,
action=parseractions.KeyValueAction,
- help="Set a property on this account "
- "(repeat option to set multiple properties)"
+ help=_("Set a property on this account "
+ "(repeat option to set multiple properties)")
)
return parser
@@ -41,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()
@@ -51,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)
@@ -61,8 +63,8 @@ class UnsetAccount(command.Command):
required=True,
action='append',
default=[],
- help='Property to remove from account '
- '(repeat option to remove multiple properties)',
+ help=_('Property to remove from account '
+ '(repeat option to remove multiple properties)'),
)
return parser
diff --git a/openstackclient/object/v1/container.py b/openstackclient/object/v1/container.py
index 2f0d4ac2..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)
@@ -37,7 +37,7 @@ class CreateContainer(command.Lister):
'containers',
metavar='<container-name>',
nargs="+",
- help='New container name(s)',
+ help=_('New container name(s)'),
)
return parser
@@ -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)
@@ -71,13 +71,13 @@ class DeleteContainer(command.Command):
'--recursive', '-r',
action='store_true',
default=False,
- help='Recursively delete objects and container',
+ help=_('Recursively delete objects and container'),
)
parser.add_argument(
'containers',
metavar='<container>',
nargs="+",
- help='Container(s) to delete',
+ help=_('Container(s) to delete'),
)
return parser
@@ -98,42 +98,42 @@ 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)
parser.add_argument(
"--prefix",
metavar="<prefix>",
- help="Filter list using <prefix>",
+ help=_("Filter list using <prefix>"),
)
parser.add_argument(
"--marker",
metavar="<marker>",
- help="Anchor for paging",
+ help=_("Anchor for paging"),
)
parser.add_argument(
"--end-marker",
metavar="<end-marker>",
- help="End anchor for paging",
+ help=_("End anchor for paging"),
)
parser.add_argument(
"--limit",
metavar="<limit>",
type=int,
- help="Limit the number of containers returned",
+ help=_("Limit the number of containers returned"),
)
parser.add_argument(
'--long',
action='store_true',
default=False,
- help='List additional fields in output',
+ help=_('List additional fields in output'),
)
parser.add_argument(
'--all',
action='store_true',
default=False,
- help='List all containers (default is 10000)',
+ help=_('List all containers (default is 10000)'),
)
return parser
@@ -168,14 +168,14 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Container to save',
+ help=_('Container to save'),
)
return parser
@@ -186,22 +186,22 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Container to modify',
+ help=_('Container to modify'),
)
parser.add_argument(
"--property",
metavar="<key=value>",
required=True,
action=parseractions.KeyValueAction,
- help="Set a property on this container "
- "(repeat option to set multiple properties)"
+ help=_("Set a property on this container "
+ "(repeat option to set multiple properties)")
)
return parser
@@ -213,14 +213,14 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Container to display',
+ help=_('Container to display'),
)
return parser
@@ -236,14 +236,14 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Container to modify',
+ help=_('Container to modify'),
)
parser.add_argument(
'--property',
@@ -251,8 +251,8 @@ class UnsetContainer(command.Command):
required=True,
action='append',
default=[],
- help='Property to remove from container '
- '(repeat option to remove multiple properties)',
+ help=_('Property to remove from container '
+ '(repeat option to remove multiple properties)'),
)
return parser
diff --git a/openstackclient/object/v1/object.py b/openstackclient/object/v1/object.py
index 88f6815e..71b6f520 100644
--- a/openstackclient/object/v1/object.py
+++ b/openstackclient/object/v1/object.py
@@ -30,26 +30,26 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Container for new object',
+ help=_('Container for new object'),
)
parser.add_argument(
'objects',
metavar='<filename>',
nargs="+",
- help='Local filename(s) to upload',
+ help=_('Local filename(s) to upload'),
)
parser.add_argument(
'--name',
metavar='<name>',
- help='Upload a file and rename it. '
- 'Can only be used when uploading a single object'
+ help=_('Upload a file and rename it. '
+ 'Can only be used when uploading a single object')
)
return parser
@@ -81,20 +81,20 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Delete object(s) from <container>',
+ help=_('Delete object(s) from <container>'),
)
parser.add_argument(
'objects',
metavar='<object>',
nargs="+",
- help='Object(s) to delete',
+ help=_('Object(s) to delete'),
)
return parser
@@ -108,52 +108,52 @@ 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)
parser.add_argument(
"container",
metavar="<container>",
- help="Container to list",
+ help=_("Container to list"),
)
parser.add_argument(
"--prefix",
metavar="<prefix>",
- help="Filter list using <prefix>",
+ help=_("Filter list using <prefix>"),
)
parser.add_argument(
"--delimiter",
metavar="<delimiter>",
- help="Roll up items with <delimiter>",
+ help=_("Roll up items with <delimiter>"),
)
parser.add_argument(
"--marker",
metavar="<marker>",
- help="Anchor for paging",
+ help=_("Anchor for paging"),
)
parser.add_argument(
"--end-marker",
metavar="<end-marker>",
- help="End anchor for paging",
+ help=_("End anchor for paging"),
)
parser.add_argument(
"--limit",
metavar="<limit>",
type=int,
- help="Limit the number of objects returned",
+ help=_("Limit the number of objects returned"),
)
parser.add_argument(
'--long',
action='store_true',
default=False,
- help='List additional fields in output',
+ help=_('List additional fields in output'),
)
parser.add_argument(
'--all',
action='store_true',
default=False,
- help='List all objects in container (default is 10000)',
+ help=_('List all objects in container (default is 10000)'),
)
return parser
@@ -197,24 +197,24 @@ 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)
parser.add_argument(
"--file",
metavar="<filename>",
- help="Destination filename (defaults to object name)",
+ help=_("Destination filename (defaults to object name)"),
)
parser.add_argument(
'container',
metavar='<container>',
- help='Download <object> from <container>',
+ help=_('Download <object> from <container>'),
)
parser.add_argument(
"object",
metavar="<object>",
- help="Object to save",
+ help=_("Object to save"),
)
return parser
@@ -227,27 +227,27 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Modify <object> from <container>',
+ help=_('Modify <object> from <container>'),
)
parser.add_argument(
'object',
metavar='<object>',
- help='Object to modify',
+ help=_('Object to modify'),
)
parser.add_argument(
"--property",
metavar="<key=value>",
required=True,
action=parseractions.KeyValueAction,
- help="Set a property on this object "
- "(repeat option to set multiple properties)"
+ help=_("Set a property on this object "
+ "(repeat option to set multiple properties)")
)
return parser
@@ -260,19 +260,19 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Display <object> from <container>',
+ help=_('Display <object> from <container>'),
)
parser.add_argument(
'object',
metavar='<object>',
- help='Object to display',
+ help=_('Object to display'),
)
return parser
@@ -289,19 +289,19 @@ 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)
parser.add_argument(
'container',
metavar='<container>',
- help='Modify <object> from <container>',
+ help=_('Modify <object> from <container>'),
)
parser.add_argument(
'object',
metavar='<object>',
- help='Object to modify',
+ help=_('Object to modify'),
)
parser.add_argument(
'--property',
@@ -309,8 +309,8 @@ class UnsetObject(command.Command):
required=True,
action='append',
default=[],
- help='Property to remove from object '
- '(repeat option to remove multiple properties)',
+ help=_('Property to remove from object '
+ '(repeat option to remove multiple properties)'),
)
return parser
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index 3971b6ef..e08eee61 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -93,10 +93,12 @@ class OpenStackShell(shell.OpenStackShell):
mod_versions = getattr(mod, 'API_VERSIONS', None)
if not skip_old_check and mod_versions:
if version_opt not in mod_versions:
+ sorted_versions = sorted(
+ mod.API_VERSIONS.keys(),
+ key=lambda s: list(map(int, s.split('.'))))
self.log.warning(
- "%s version %s is not in supported versions %s"
- % (api, version_opt,
- ', '.join(list(mod.API_VERSIONS.keys()))))
+ "%s version %s is not in supported versions: %s"
+ % (api, version_opt, ', '.join(sorted_versions)))
# Command groups deal only with major versions
version = '.v' + version_opt.replace('.', '_').split('_')[0]
@@ -146,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/base.py b/openstackclient/tests/functional/base.py
index 298b2454..885abc02 100644
--- a/openstackclient/tests/functional/base.py
+++ b/openstackclient/tests/functional/base.py
@@ -63,8 +63,8 @@ class TestCase(testtools.TestCase):
return cls.openstack('extension list ' + opts)
@classmethod
- def get_opts(cls, fields, format='value'):
- return ' -f {0} {1}'.format(format,
+ def get_opts(cls, fields, output_format='value'):
+ return ' -f {0} {1}'.format(output_format,
' '.join(['-c ' + it for it in fields]))
@classmethod
diff --git a/openstackclient/tests/functional/network/v2/test_network_qos_policy.py b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py
new file mode 100644
index 00000000..07dea31b
--- /dev/null
+++ b/openstackclient/tests/functional/network/v2/test_network_qos_policy.py
@@ -0,0 +1,55 @@
+# 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 uuid
+
+from openstackclient.tests.functional import base
+
+
+class QosPolicyTests(base.TestCase):
+ """Functional tests for QoS policy. """
+ NAME = uuid.uuid4().hex
+ HEADERS = ['Name']
+ FIELDS = ['name']
+
+ @classmethod
+ def setUpClass(cls):
+ opts = cls.get_opts(cls.FIELDS)
+ raw_output = cls.openstack('network qos policy create ' + cls.NAME +
+ opts)
+ cls.assertOutput(cls.NAME + "\n", raw_output)
+
+ @classmethod
+ def tearDownClass(cls):
+ raw_output = cls.openstack('network qos policy delete ' + cls.NAME)
+ cls.assertOutput('', raw_output)
+
+ def test_qos_policy_list(self):
+ opts = self.get_opts(self.HEADERS)
+ raw_output = self.openstack('network qos policy list' + opts)
+ self.assertIn(self.NAME, raw_output)
+
+ def test_qos_policy_show(self):
+ opts = self.get_opts(self.FIELDS)
+ raw_output = self.openstack('network qos policy show ' + self.NAME +
+ opts)
+ self.assertEqual(self.NAME + "\n", raw_output)
+
+ def test_qos_policy_set(self):
+ self.openstack('network qos policy set --share ' + self.NAME)
+ opts = self.get_opts(['shared'])
+ raw_output = self.openstack('network qos policy show ' + self.NAME +
+ opts)
+ self.assertEqual("True\n", raw_output)
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/functional/volume/v1/test_snapshot.py b/openstackclient/tests/functional/volume/v1/test_snapshot.py
index c6d65ccc..1e1c6b21 100644
--- a/openstackclient/tests/functional/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v1/test_snapshot.py
@@ -16,8 +16,8 @@ import uuid
from openstackclient.tests.functional.volume.v1 import common
-class SnapshotTests(common.BaseVolumeTests):
- """Functional tests for snapshot. """
+class VolumeSnapshotTests(common.BaseVolumeTests):
+ """Functional tests for volume snapshot. """
VOLLY = uuid.uuid4().hex
NAME = uuid.uuid4().hex
@@ -36,24 +36,25 @@ class SnapshotTests(common.BaseVolumeTests):
@classmethod
def setUpClass(cls):
- super(SnapshotTests, cls).setUpClass()
+ super(VolumeSnapshotTests, cls).setUpClass()
cls.openstack('volume create --size 1 ' + cls.VOLLY)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 3)
opts = cls.get_opts(['status'])
- raw_output = cls.openstack('snapshot create --name ' + cls.NAME +
- ' ' + cls.VOLLY + opts)
+ raw_output = cls.openstack('volume snapshot create --volume ' +
+ cls.VOLLY + ' ' + cls.NAME + opts)
cls.assertOutput('creating\n', raw_output)
- cls.wait_for_status('snapshot show ' + cls.NAME, 'available\n', 3)
+ cls.wait_for_status(
+ 'volume snapshot show ' + cls.NAME, 'available\n', 3)
@classmethod
def tearDownClass(cls):
# Rename test
raw_output = cls.openstack(
- 'snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
+ 'volume snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
cls.assertOutput('', raw_output)
# Delete test
raw_output_snapshot = cls.openstack(
- 'snapshot delete ' + cls.OTHER_NAME)
+ 'volume snapshot delete ' + cls.OTHER_NAME)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 6)
raw_output_volume = cls.openstack('volume delete --force ' + cls.VOLLY)
cls.assertOutput('', raw_output_snapshot)
@@ -61,26 +62,27 @@ class SnapshotTests(common.BaseVolumeTests):
def test_snapshot_list(self):
opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('snapshot list' + opts)
+ raw_output = self.openstack('volume snapshot list' + opts)
self.assertIn(self.NAME, raw_output)
def test_snapshot_set_unset_properties(self):
raw_output = self.openstack(
- 'snapshot set --property a=b --property c=d ' + self.NAME)
+ 'volume snapshot set --property a=b --property c=d ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["properties"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("a='b', c='d'\n", raw_output)
- raw_output = self.openstack('snapshot unset --property a ' + self.NAME)
+ raw_output = self.openstack(
+ 'volume snapshot unset --property a ' + self.NAME)
self.assertEqual("", raw_output)
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("c='d'\n", raw_output)
def test_snapshot_set_description(self):
raw_output = self.openstack(
- 'snapshot set --description backup ' + self.NAME)
+ 'volume snapshot set --description backup ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["display_description", "display_name"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("backup\n" + self.NAME + "\n", raw_output)
diff --git a/openstackclient/tests/functional/volume/v2/test_snapshot.py b/openstackclient/tests/functional/volume/v2/test_snapshot.py
index fcbc31cb..4eb69e9d 100644
--- a/openstackclient/tests/functional/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/functional/volume/v2/test_snapshot.py
@@ -16,8 +16,8 @@ import uuid
from openstackclient.tests.functional.volume.v2 import common
-class SnapshotTests(common.BaseVolumeTests):
- """Functional tests for snapshot. """
+class VolumeSnapshotTests(common.BaseVolumeTests):
+ """Functional tests for volume snapshot. """
VOLLY = uuid.uuid4().hex
NAME = uuid.uuid4().hex
@@ -36,24 +36,25 @@ class SnapshotTests(common.BaseVolumeTests):
@classmethod
def setUpClass(cls):
- super(SnapshotTests, cls).setUpClass()
+ super(VolumeSnapshotTests, cls).setUpClass()
cls.openstack('volume create --size 1 ' + cls.VOLLY)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 3)
opts = cls.get_opts(['status'])
- raw_output = cls.openstack('snapshot create --name ' + cls.NAME +
- ' ' + cls.VOLLY + opts)
+ raw_output = cls.openstack('volume snapshot create --volume ' +
+ cls.VOLLY + ' ' + cls.NAME + opts)
cls.assertOutput('creating\n', raw_output)
- cls.wait_for_status('snapshot show ' + cls.NAME, 'available\n', 3)
+ cls.wait_for_status(
+ 'volume snapshot show ' + cls.NAME, 'available\n', 3)
@classmethod
def tearDownClass(cls):
# Rename test
raw_output = cls.openstack(
- 'snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
+ 'volume snapshot set --name ' + cls.OTHER_NAME + ' ' + cls.NAME)
cls.assertOutput('', raw_output)
# Delete test
raw_output_snapshot = cls.openstack(
- 'snapshot delete ' + cls.OTHER_NAME)
+ 'volume snapshot delete ' + cls.OTHER_NAME)
cls.wait_for_status('volume show ' + cls.VOLLY, 'available\n', 6)
raw_output_volume = cls.openstack('volume delete --force ' + cls.VOLLY)
cls.assertOutput('', raw_output_snapshot)
@@ -61,26 +62,27 @@ class SnapshotTests(common.BaseVolumeTests):
def test_snapshot_list(self):
opts = self.get_opts(self.HEADERS)
- raw_output = self.openstack('snapshot list' + opts)
+ raw_output = self.openstack('volume snapshot list' + opts)
self.assertIn(self.NAME, raw_output)
def test_snapshot_properties(self):
raw_output = self.openstack(
- 'snapshot set --property a=b --property c=d ' + self.NAME)
+ 'volume snapshot set --property a=b --property c=d ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["properties"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("a='b', c='d'\n", raw_output)
- raw_output = self.openstack('snapshot unset --property a ' + self.NAME)
+ raw_output = self.openstack(
+ 'volume snapshot unset --property a ' + self.NAME)
self.assertEqual("", raw_output)
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("c='d'\n", raw_output)
def test_snapshot_set(self):
raw_output = self.openstack(
- 'snapshot set --description backup ' + self.NAME)
+ 'volume snapshot set --description backup ' + self.NAME)
self.assertEqual("", raw_output)
opts = self.get_opts(["description", "name"])
- raw_output = self.openstack('snapshot show ' + self.NAME + opts)
+ raw_output = self.openstack('volume snapshot show ' + self.NAME + opts)
self.assertEqual("backup\n" + self.NAME + "\n", raw_output)
diff --git a/openstackclient/tests/functional/volume/v2/test_volume.py b/openstackclient/tests/functional/volume/v2/test_volume.py
index fb880578..ea891cba 100644
--- a/openstackclient/tests/functional/volume/v2/test_volume.py
+++ b/openstackclient/tests/functional/volume/v2/test_volume.py
@@ -106,11 +106,12 @@ class VolumeTests(common.BaseVolumeTests):
opts = self.get_opts(self.FIELDS)
# Create snapshot from test volume
- raw_output = self.openstack('snapshot create ' + self.NAME +
- ' --name ' + self.SNAPSHOT_NAME + opts)
+ raw_output = self.openstack('volume snapshot create ' +
+ self.SNAPSHOT_NAME +
+ ' --volume ' + self.NAME + opts)
expected = self.SNAPSHOT_NAME + '\n'
self.assertOutput(expected, raw_output)
- self.wait_for("snapshot", self.SNAPSHOT_NAME, "available")
+ self.wait_for("volume snapshot", self.SNAPSHOT_NAME, "available")
# Create volume from snapshot
raw_output = self.openstack('volume create --size 2 --snapshot ' +
@@ -126,7 +127,8 @@ class VolumeTests(common.BaseVolumeTests):
self.assertOutput('', raw_output)
# Delete test snapshot
- raw_output = self.openstack('snapshot delete ' + self.SNAPSHOT_NAME)
+ raw_output = self.openstack(
+ 'volume snapshot delete ' + self.SNAPSHOT_NAME)
self.assertOutput('', raw_output)
self.wait_for("volume", self.NAME, "available")
diff --git a/openstackclient/tests/unit/compute/v2/test_hypervisor.py b/openstackclient/tests/unit/compute/v2/test_hypervisor.py
index e39570af..7200d04e 100644
--- a/openstackclient/tests/unit/compute/v2/test_hypervisor.py
+++ b/openstackclient/tests/unit/compute/v2/test_hypervisor.py
@@ -48,19 +48,63 @@ class TestHypervisorList(TestHypervisor):
self.columns = (
"ID",
- "Hypervisor Hostname"
+ "Hypervisor Hostname",
+ "Hypervisor Type",
+ "Host IP",
+ "State"
+ )
+ self.columns_long = (
+ "ID",
+ "Hypervisor Hostname",
+ "Hypervisor Type",
+ "Host IP",
+ "State",
+ "vCPUs Used",
+ "vCPUs",
+ "Memory MB Used",
+ "Memory MB"
)
self.data = (
(
self.hypervisors[0].id,
self.hypervisors[0].hypervisor_hostname,
+ self.hypervisors[0].hypervisor_type,
+ self.hypervisors[0].host_ip,
+ self.hypervisors[0].state
),
(
self.hypervisors[1].id,
self.hypervisors[1].hypervisor_hostname,
+ self.hypervisors[1].hypervisor_type,
+ self.hypervisors[1].host_ip,
+ self.hypervisors[1].state
),
)
+ self.data_long = (
+ (
+ self.hypervisors[0].id,
+ self.hypervisors[0].hypervisor_hostname,
+ self.hypervisors[0].hypervisor_type,
+ self.hypervisors[0].host_ip,
+ self.hypervisors[0].state,
+ self.hypervisors[0].vcpus_used,
+ self.hypervisors[0].vcpus,
+ self.hypervisors[0].memory_mb_used,
+ self.hypervisors[0].memory_mb
+ ),
+ (
+ self.hypervisors[1].id,
+ self.hypervisors[1].hypervisor_hostname,
+ self.hypervisors[1].hypervisor_type,
+ self.hypervisors[1].host_ip,
+ self.hypervisors[1].state,
+ self.hypervisors[1].vcpus_used,
+ self.hypervisors[1].vcpus,
+ self.hypervisors[1].memory_mb_used,
+ self.hypervisors[1].memory_mb
+ ),
+ )
# Get the command object to test
self.cmd = hypervisor.ListHypervisor(self.app, None)
@@ -93,6 +137,9 @@ class TestHypervisorList(TestHypervisor):
(
self.hypervisors[0].id,
self.hypervisors[0].hypervisor_hostname,
+ self.hypervisors[1].hypervisor_type,
+ self.hypervisors[1].host_ip,
+ self.hypervisors[1].state,
),
)
@@ -123,6 +170,24 @@ class TestHypervisorList(TestHypervisor):
self.cmd.take_action,
parsed_args)
+ def test_hypervisor_list_long_option(self):
+ arglist = [
+ '--long',
+ ]
+ verifylist = [
+ ('long', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ # In base command class Lister in cliff, abstract method take_action()
+ # returns a tuple containing the column names and an iterable
+ # containing the data to be listed.
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.hypervisors_mock.list.assert_called_with()
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(self.data_long, tuple(data))
+
class TestHypervisorShow(TestHypervisor):
diff --git a/openstackclient/tests/unit/compute/v2/test_keypair.py b/openstackclient/tests/unit/compute/v2/test_keypair.py
index cb008545..efc5463c 100644
--- a/openstackclient/tests/unit/compute/v2/test_keypair.py
+++ b/openstackclient/tests/unit/compute/v2/test_keypair.py
@@ -179,8 +179,7 @@ class TestKeypairDelete(TestKeypair):
self.cmd.take_action(parsed_args)
self.fail('CommandError should be raised.')
except exceptions.CommandError as e:
- self.assertEqual('1 of 2 public keys failed to delete.',
- str(e))
+ self.assertEqual('1 of 2 keys failed to delete.', str(e))
find_mock.assert_any_call(
self.keypairs_mock, self.keypairs[0].name)
diff --git a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
index 9e4e1876..34655263 100644
--- a/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
+++ b/openstackclient/tests/unit/identity/v3/test_unscoped_saml.py
@@ -12,8 +12,6 @@
import copy
-from osc_lib import exceptions
-
from openstackclient.identity.v3 import unscoped_saml
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes
@@ -48,7 +46,6 @@ class TestDomainList(TestUnscopedSAML):
self.cmd = unscoped_saml.ListAccessibleDomains(self.app, None)
def test_accessible_domains_list(self):
- self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -70,17 +67,6 @@ class TestDomainList(TestUnscopedSAML):
), )
self.assertEqual(datalist, tuple(data))
- def test_accessible_domains_list_wrong_auth(self):
- auth = identity_fakes.FakeAuth("wrong auth")
- self.app.client_manager.identity.session.auth = auth
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
-
class TestProjectList(TestUnscopedSAML):
@@ -99,7 +85,6 @@ class TestProjectList(TestUnscopedSAML):
self.cmd = unscoped_saml.ListAccessibleProjects(self.app, None)
def test_accessible_projects_list(self):
- self.app.client_manager.auth_plugin_name = 'v3unscopedsaml'
arglist = []
verifylist = []
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -120,14 +105,3 @@ class TestProjectList(TestUnscopedSAML):
identity_fakes.project_name,
), )
self.assertEqual(datalist, tuple(data))
-
- def test_accessible_projects_list_wrong_auth(self):
- auth = identity_fakes.FakeAuth("wrong auth")
- self.app.client_manager.identity.session.auth = auth
- arglist = []
- verifylist = []
- parsed_args = self.check_parser(self.cmd, arglist, verifylist)
-
- self.assertRaises(exceptions.CommandError,
- self.cmd.take_action,
- parsed_args)
diff --git a/openstackclient/tests/unit/image/v1/fakes.py b/openstackclient/tests/unit/image/v1/fakes.py
index a8e52fa3..080356ee 100644
--- a/openstackclient/tests/unit/image/v1/fakes.py
+++ b/openstackclient/tests/unit/image/v1/fakes.py
@@ -13,7 +13,9 @@
# under the License.
#
+import copy
import mock
+import uuid
from openstackclient.tests.unit import fakes
from openstackclient.tests.unit import utils
@@ -74,3 +76,45 @@ class TestImagev1(utils.TestCommand):
endpoint=fakes.AUTH_URL,
token=fakes.AUTH_TOKEN,
)
+
+
+class FakeImage(object):
+ """Fake one or more images."""
+
+ @staticmethod
+ def create_one_image(attrs=None):
+ """Create a fake image.
+
+ :param Dictionary attrs:
+ A dictionary with all attrbutes of image
+ :return:
+ A FakeResource object with id, name, owner, protected,
+ visibility and tags attrs
+ """
+ attrs = attrs or {}
+
+ # Set default attribute
+ image_info = {
+ 'id': str(uuid.uuid4()),
+ 'name': 'image-name' + uuid.uuid4().hex,
+ 'owner': 'image-owner' + uuid.uuid4().hex,
+ 'container_format': '',
+ 'disk_format': '',
+ 'min_disk': 0,
+ 'min_ram': 0,
+ 'is_public': True,
+ 'protected': False,
+ 'properties': {
+ 'Alpha': 'a',
+ 'Beta': 'b',
+ 'Gamma': 'g'},
+ }
+
+ # Overwrite default attributes if there are some attributes set
+ image_info.update(attrs)
+
+ image = fakes.FakeResource(
+ info=copy.deepcopy(image_info),
+ loaded=True)
+
+ return image
diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py
index a6bc80a0..036c8336 100644
--- a/openstackclient/tests/unit/image/v1/test_image.py
+++ b/openstackclient/tests/unit/image/v1/test_image.py
@@ -17,6 +17,7 @@ import copy
import mock
from osc_lib import exceptions
+from osc_lib import utils
from openstackclient.image.v1 import image
from openstackclient.tests.unit import fakes
@@ -35,25 +36,39 @@ class TestImage(image_fakes.TestImagev1):
class TestImageCreate(TestImage):
+ new_image = image_fakes.FakeImage.create_one_image()
+ columns = (
+ 'container_format',
+ 'disk_format',
+ 'id',
+ 'is_public',
+ 'min_disk',
+ 'min_ram',
+ 'name',
+ 'owner',
+ 'properties',
+ 'protected',
+ )
+ data = (
+ new_image.container_format,
+ new_image.disk_format,
+ new_image.id,
+ new_image.is_public,
+ new_image.min_disk,
+ new_image.min_ram,
+ new_image.name,
+ new_image.owner,
+ utils.format_dict(new_image.properties),
+ new_image.protected,
+ )
+
def setUp(self):
super(TestImageCreate, self).setUp()
- self.images_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
+ self.images_mock.create.return_value = self.new_image
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
- self.images_mock.update.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
+ self.images_mock.get.return_value = self.new_image
+ self.images_mock.update.return_value = self.new_image
# Get the command object to test
self.cmd = image.CreateImage(self.app, None)
@@ -65,12 +80,12 @@ class TestImageCreate(TestImage):
}
self.images_mock.configure_mock(**mock_exception)
arglist = [
- image_fakes.image_name,
+ self.new_image.name,
]
verifylist = [
('container_format', image.DEFAULT_CONTAINER_FORMAT),
('disk_format', image.DEFAULT_DISK_FORMAT),
- ('name', image_fakes.image_name),
+ ('name', self.new_image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -81,7 +96,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.images_mock.create.assert_called_with(
- name=image_fakes.image_name,
+ name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
data=mock.ANY,
@@ -90,8 +105,8 @@ class TestImageCreate(TestImage):
# Verify update() was not called, if it was show the args
self.assertEqual(self.images_mock.update.call_args_list, [])
- self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertEqual(image_fakes.IMAGE_data, data)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
def test_image_reserve_options(self):
mock_exception = {
@@ -101,17 +116,17 @@ 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',
'--private',
'--project', 'q',
- image_fakes.image_name,
+ self.new_image.name,
]
verifylist = [
('container_format', 'ovf'),
- ('disk_format', 'fs'),
+ ('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
('protected', True),
@@ -119,7 +134,7 @@ class TestImageCreate(TestImage):
('public', False),
('private', True),
('project', 'q'),
- ('name', image_fakes.image_name),
+ ('name', self.new_image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -130,9 +145,9 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.images_mock.create.assert_called_with(
- name=image_fakes.image_name,
+ name=self.new_image.name,
container_format='ovf',
- disk_format='fs',
+ disk_format='ami',
min_disk=10,
min_ram=4,
protected=True,
@@ -144,14 +159,14 @@ class TestImageCreate(TestImage):
# Verify update() was not called, if it was show the args
self.assertEqual(self.images_mock.update.call_args_list, [])
- self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertEqual(image_fakes.IMAGE_data, data)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
@mock.patch('openstackclient.image.v1.image.io.open', name='Open')
def test_image_create_file(self, mock_open):
mock_file = mock.Mock(name='File')
mock_open.return_value = mock_file
- mock_open.read.return_value = image_fakes.image_data
+ mock_open.read.return_value = self.data
mock_exception = {
'find.side_effect': exceptions.CommandError('x'),
'get.side_effect': exceptions.CommandError('x'),
@@ -164,7 +179,7 @@ class TestImageCreate(TestImage):
'--public',
'--property', 'Alpha=1',
'--property', 'Beta=2',
- image_fakes.image_name,
+ self.new_image.name,
]
verifylist = [
('file', 'filer'),
@@ -173,7 +188,7 @@ class TestImageCreate(TestImage):
('public', True),
('private', False),
('properties', {'Alpha': '1', 'Beta': '2'}),
- ('name', image_fakes.image_name),
+ ('name', self.new_image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -193,7 +208,7 @@ class TestImageCreate(TestImage):
# ImageManager.create(name=, **)
self.images_mock.create.assert_called_with(
- name=image_fakes.image_name,
+ name=self.new_image.name,
container_format=image.DEFAULT_CONTAINER_FORMAT,
disk_format=image.DEFAULT_DISK_FORMAT,
protected=False,
@@ -208,21 +223,19 @@ class TestImageCreate(TestImage):
# Verify update() was not called, if it was show the args
self.assertEqual(self.images_mock.update.call_args_list, [])
- self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertEqual(image_fakes.IMAGE_data, data)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
class TestImageDelete(TestImage):
+ _image = image_fakes.FakeImage.create_one_image()
+
def setUp(self):
super(TestImageDelete, self).setUp()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
+ self.images_mock.get.return_value = self._image
self.images_mock.delete.return_value = None
# Get the command object to test
@@ -230,21 +243,23 @@ class TestImageDelete(TestImage):
def test_image_delete_no_options(self):
arglist = [
- image_fakes.image_id,
+ self._image.id,
]
verifylist = [
- ('images', [image_fakes.image_id]),
+ ('images', [self._image.id]),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.images_mock.delete.assert_called_with(image_fakes.image_id)
+ self.images_mock.delete.assert_called_with(self._image.id)
self.assertIsNone(result)
class TestImageList(TestImage):
+ _image = image_fakes.FakeImage.create_one_image()
+
columns = (
'ID',
'Name',
@@ -252,18 +267,33 @@ class TestImageList(TestImage):
)
datalist = (
(
- image_fakes.image_id,
- image_fakes.image_name,
+ _image.id,
+ _image.name,
'',
),
)
+ # create a image_info as the side_effect of the fake image_list()
+ info = {
+ 'id': _image.id,
+ 'name': _image.name,
+ 'owner': _image.owner,
+ 'container_format': _image.container_format,
+ 'disk_format': _image.disk_format,
+ 'min_disk': _image.min_disk,
+ 'min_ram': _image.min_ram,
+ 'is_public': _image.is_public,
+ 'protected': _image.protected,
+ 'properties': _image.properties,
+ }
+ image_info = copy.deepcopy(info)
+
def setUp(self):
super(TestImageList, self).setUp()
self.api_mock = mock.Mock()
self.api_mock.image_list.side_effect = [
- [copy.deepcopy(image_fakes.IMAGE)], [],
+ [self.image_info], [],
]
self.app.client_manager.image.api = self.api_mock
@@ -285,7 +315,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
detailed=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -309,7 +339,7 @@ class TestImageList(TestImage):
self.api_mock.image_list.assert_called_with(
detailed=True,
public=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -333,7 +363,7 @@ class TestImageList(TestImage):
self.api_mock.image_list.assert_called_with(
detailed=True,
private=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -354,7 +384,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
detailed=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
collist = (
@@ -373,8 +403,8 @@ class TestImageList(TestImage):
self.assertEqual(collist, columns)
datalist = ((
- image_fakes.image_id,
- image_fakes.image_name,
+ self._image.id,
+ self._image.name,
'',
'',
'',
@@ -382,7 +412,7 @@ class TestImageList(TestImage):
'',
'public',
False,
- image_fakes.image_owner,
+ self._image.owner,
"Alpha='a', Beta='b', Gamma='g'",
), )
self.assertEqual(datalist, tuple(data))
@@ -390,7 +420,7 @@ class TestImageList(TestImage):
@mock.patch('openstackclient.api.utils.simple_filter')
def test_image_list_property_option(self, sf_mock):
sf_mock.side_effect = [
- [copy.deepcopy(image_fakes.IMAGE)], [],
+ [self.image_info], [],
]
arglist = [
@@ -407,10 +437,10 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
detailed=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
sf_mock.assert_called_with(
- [image_fakes.IMAGE],
+ [self.image_info],
attr='a',
value='1',
property_field='properties',
@@ -422,7 +452,7 @@ class TestImageList(TestImage):
@mock.patch('osc_lib.utils.sort_items')
def test_image_list_sort_option(self, si_mock):
si_mock.side_effect = [
- [copy.deepcopy(image_fakes.IMAGE)], [],
+ [self.image_info], [],
]
arglist = ['--sort', 'name:asc']
@@ -435,10 +465,10 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
detailed=True,
- marker=image_fakes.image_id,
+ marker=self._image.id,
)
si_mock.assert_called_with(
- [image_fakes.IMAGE],
+ [self.image_info],
'name:asc'
)
@@ -448,36 +478,30 @@ class TestImageList(TestImage):
class TestImageSet(TestImage):
+ _image = image_fakes.FakeImage.create_one_image()
+
def setUp(self):
super(TestImageSet, self).setUp()
# This is the return value for utils.find_resource()
- self.images_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
- self.images_mock.update.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
+ self.images_mock.get.return_value = self._image
+ self.images_mock.update.return_value = self._image
# Get the command object to test
self.cmd = image.SetImage(self.app, None)
def test_image_set_no_options(self):
arglist = [
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.images_mock.update.assert_called_with(image_fakes.image_id,
+ self.images_mock.update.assert_called_with(self._image.id,
**{})
self.assertIsNone(result)
@@ -490,7 +514,7 @@ class TestImageSet(TestImage):
'--disk-format', 'vmdk',
'--size', '35165824',
'--project', 'new-owner',
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
('name', 'new-name'),
@@ -500,7 +524,7 @@ class TestImageSet(TestImage):
('disk_format', 'vmdk'),
('size', 35165824),
('project', 'new-owner'),
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -517,7 +541,7 @@ class TestImageSet(TestImage):
}
# ImageManager.update(image, **kwargs)
self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -526,14 +550,14 @@ class TestImageSet(TestImage):
arglist = [
'--protected',
'--private',
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
('protected', True),
('unprotected', False),
('public', False),
('private', True),
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -545,7 +569,7 @@ class TestImageSet(TestImage):
}
# ImageManager.update(image, **kwargs)
self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -554,14 +578,14 @@ class TestImageSet(TestImage):
arglist = [
'--unprotected',
'--public',
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
('protected', False),
('unprotected', True),
('public', True),
('private', False),
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -573,7 +597,7 @@ class TestImageSet(TestImage):
}
# ImageManager.update(image, **kwargs)
self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -582,11 +606,11 @@ class TestImageSet(TestImage):
arglist = [
'--property', 'Alpha=1',
'--property', 'Beta=2',
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
('properties', {'Alpha': '1', 'Beta': '2'}),
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -601,7 +625,7 @@ class TestImageSet(TestImage):
}
# ImageManager.update(image, **kwargs)
self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
**kwargs
)
self.assertIsNone(result)
@@ -624,7 +648,7 @@ class TestImageSet(TestImage):
"volume_type": 'volume_type',
"container_format": image.DEFAULT_CONTAINER_FORMAT,
"disk_format": image.DEFAULT_DISK_FORMAT,
- "image": image_fakes.image_name,
+ "image": self._image.name,
}
full_response = {"os-volume_upload_image": response}
volumes_mock.upload_to_image.return_value = (201, full_response)
@@ -632,7 +656,7 @@ class TestImageSet(TestImage):
arglist = [
'--volume', 'volly',
'--name', 'updated_image',
- image_fakes.image_name,
+ self._image.name,
]
verifylist = [
('private', False),
@@ -642,7 +666,7 @@ class TestImageSet(TestImage):
('volume', 'volly'),
('force', False),
('name', 'updated_image'),
- ('image', image_fakes.image_name),
+ ('image', self._image.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -653,13 +677,13 @@ class TestImageSet(TestImage):
volumes_mock.upload_to_image.assert_called_with(
'vol1',
False,
- image_fakes.image_name,
+ self._image.name,
'',
'',
)
# ImageManager.update(image_id, remove_props=, **)
self.images_mock.update.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
name='updated_image',
volume='volly',
)
@@ -668,24 +692,46 @@ class TestImageSet(TestImage):
class TestImageShow(TestImage):
+ _image = image_fakes.FakeImage.create_one_image()
+ columns = (
+ 'container_format',
+ 'disk_format',
+ 'id',
+ 'is_public',
+ 'min_disk',
+ 'min_ram',
+ 'name',
+ 'owner',
+ 'properties',
+ 'protected',
+ )
+ data = (
+ _image.container_format,
+ _image.disk_format,
+ _image.id,
+ _image.is_public,
+ _image.min_disk,
+ _image.min_ram,
+ _image.name,
+ _image.owner,
+ utils.format_dict(_image.properties),
+ _image.protected,
+ )
+
def setUp(self):
super(TestImageShow, self).setUp()
- self.images_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(image_fakes.IMAGE),
- loaded=True,
- )
+ self.images_mock.get.return_value = self._image
# Get the command object to test
self.cmd = image.ShowImage(self.app, None)
def test_image_show(self):
arglist = [
- image_fakes.image_id,
+ self._image.id,
]
verifylist = [
- ('image', image_fakes.image_id),
+ ('image', self._image.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -694,8 +740,8 @@ class TestImageShow(TestImage):
# data to be shown.
columns, data = self.cmd.take_action(parsed_args)
self.images_mock.get.assert_called_with(
- image_fakes.image_id,
+ self._image.id,
)
- self.assertEqual(image_fakes.IMAGE_columns, columns)
- self.assertEqual(image_fakes.IMAGE_data, data)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index ebc9c3a7..a054e513 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),
@@ -535,7 +535,9 @@ class TestImageList(TestImage):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, tuple(data))
@@ -558,6 +560,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
public=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -581,6 +584,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
private=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -604,6 +608,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
shared=True,
+ marker=self._image.id,
)
self.assertEqual(self.columns, columns)
@@ -622,7 +627,9 @@ class TestImageList(TestImage):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
collist = (
'ID',
@@ -670,7 +677,9 @@ class TestImageList(TestImage):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
sf_mock.assert_called_with(
[self._image],
attr='a',
@@ -693,7 +702,9 @@ class TestImageList(TestImage):
# returns a tuple containing the column names and an iterable
# containing the data to be listed.
columns, data = self.cmd.take_action(parsed_args)
- self.api_mock.image_list.assert_called_with()
+ self.api_mock.image_list.assert_called_with(
+ marker=self._image.id,
+ )
si_mock.assert_called_with(
[self._image],
'name:asc'
@@ -712,7 +723,7 @@ class TestImageList(TestImage):
columns, data = self.cmd.take_action(parsed_args)
self.api_mock.image_list.assert_called_with(
- limit=1,
+ limit=1, marker=self._image.id
)
self.assertEqual(self.columns, columns)
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/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index 527649b8..97d07076 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -298,11 +298,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.
@@ -434,6 +437,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': [{}],
@@ -640,6 +644,157 @@ class FakeNetworkRBAC(object):
return mock.Mock(side_effect=rbac_policies)
+class FakeNetworkQosBandwidthLimitRule(object):
+ """Fake one or more QoS bandwidth limit rules."""
+
+ @staticmethod
+ def create_one_qos_bandwidth_limit_rule(attrs=None):
+ """Create a fake QoS bandwidth limit rule.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with id, qos_policy_id, max_kbps and
+ max_burst_kbps attributes.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ qos_bandwidth_limit_rule_attrs = {
+ 'id': 'qos-bandwidth-limit-rule-id-' + uuid.uuid4().hex,
+ 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
+ 'max_kbps': 1500,
+ 'max_burst_kbps': 1200,
+ }
+
+ # Overwrite default attributes.
+ qos_bandwidth_limit_rule_attrs.update(attrs)
+
+ qos_bandwidth_limit_rule = fakes.FakeResource(
+ info=copy.deepcopy(qos_bandwidth_limit_rule_attrs),
+ loaded=True)
+
+ return qos_bandwidth_limit_rule
+
+ @staticmethod
+ def create_qos_bandwidth_limit_rules(attrs=None, count=2):
+ """Create multiple fake QoS bandwidth limit rules.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of QoS bandwidth limit rules to fake
+ :return:
+ A list of FakeResource objects faking the QoS bandwidth limit rules
+ """
+ qos_policies = []
+ for i in range(0, count):
+ qos_policies.append(FakeNetworkQosBandwidthLimitRule.
+ create_one_qos_bandwidth_limit_rule(attrs))
+
+ return qos_policies
+
+ @staticmethod
+ def get_qos_bandwidth_limit_rules(qos_rules=None, count=2):
+ """Get a list of faked QoS bandwidth limit rules.
+
+ If QoS bandwidth limit rules list is provided, then initialize the
+ Mock object with the list. Otherwise create one.
+
+ :param List address scopes:
+ A list of FakeResource objects faking QoS bandwidth limit rules
+ :param int count:
+ The number of QoS bandwidth limit rules to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ qos bandwidth limit rules
+ """
+ if qos_rules is None:
+ qos_rules = (FakeNetworkQosBandwidthLimitRule.
+ create_qos_bandwidth_limit_rules(count))
+ return mock.Mock(side_effect=qos_rules)
+
+
+class FakeNetworkQosPolicy(object):
+ """Fake one or more QoS policies."""
+
+ @staticmethod
+ def create_one_qos_policy(attrs=None):
+ """Create a fake QoS policy.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with name, id, etc.
+ """
+ attrs = attrs or {}
+ qos_id = attrs.get('id') or 'qos-policy-id-' + uuid.uuid4().hex
+ rule_attrs = {'qos_policy_id': qos_id}
+ rules = [
+ FakeNetworkQosBandwidthLimitRule.
+ create_one_qos_bandwidth_limit_rule(rule_attrs)]
+
+ # Set default attributes.
+ qos_policy_attrs = {
+ 'name': 'qos-policy-name-' + uuid.uuid4().hex,
+ 'id': qos_id,
+ 'tenant_id': 'project-id-' + uuid.uuid4().hex,
+ 'shared': False,
+ 'description': 'qos-policy-description-' + uuid.uuid4().hex,
+ 'rules': rules,
+ }
+
+ # Overwrite default attributes.
+ qos_policy_attrs.update(attrs)
+
+ qos_policy = fakes.FakeResource(
+ info=copy.deepcopy(qos_policy_attrs),
+ 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
+
+ @staticmethod
+ def create_qos_policies(attrs=None, count=2):
+ """Create multiple fake QoS policies.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :param int count:
+ The number of QoS policies to fake
+ :return:
+ A list of FakeResource objects faking the QoS policies
+ """
+ qos_policies = []
+ for i in range(0, count):
+ qos_policies.append(
+ FakeNetworkQosPolicy.create_one_qos_policy(attrs))
+
+ return qos_policies
+
+ @staticmethod
+ def get_qos_policies(qos_policies=None, count=2):
+ """Get an iterable MagicMock object with a list of faked QoS policies.
+
+ If qos policies list is provided, then initialize the Mock object
+ with the list. Otherwise create one.
+
+ :param List address scopes:
+ A list of FakeResource objects faking qos policies
+ :param int count:
+ The number of QoS policies to fake
+ :return:
+ An iterable Mock object with side_effect set to a list of faked
+ QoS policies
+ """
+ if qos_policies is None:
+ qos_policies = FakeNetworkQosPolicy.create_qos_policies(count)
+ return mock.Mock(side_effect=qos_policies)
+
+
class FakeRouter(object):
"""Fake one or more routers."""
@@ -808,6 +963,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,
@@ -914,6 +1071,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
@@ -1073,6 +1232,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
@@ -1114,3 +1278,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 1f30f2e9..b3d211ba 100644
--- a/openstackclient/tests/unit/network/v2/test_floating_ip.py
+++ b/openstackclient/tests/unit/network/v2/test_floating_ip.py
@@ -18,6 +18,7 @@ from osc_lib import exceptions
from openstackclient.network.v2 import floating_ip
from openstackclient.tests.unit.compute.v2 import fakes as compute_fakes
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -31,6 +32,7 @@ class TestFloatingIPNetwork(network_fakes.TestNetworkV2):
# Get a shortcut to the network client
self.network = self.app.client_manager.network
+ self.projects_mock = self.app.client_manager.identity.projects
class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
@@ -145,6 +147,54 @@ class TestCreateFloatingIPNetwork(TestFloatingIPNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_floating_ip_create_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_ip.assert_called_once_with(**{
+ 'floating_network_id': self.floating_ip.floating_network_id,
+ 'tenant_id': project.id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_floating_ip_create_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ domain = identity_fakes_v3.FakeDomain.create_one_domain()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ "--project", project.name,
+ "--project-domain", domain.name,
+ self.floating_ip.floating_network_id,
+ ]
+ verifylist = [
+ ('network', self.floating_ip.floating_network_id),
+ ('project', project.name),
+ ('project_domain', domain.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_ip.assert_called_once_with(**{
+ 'floating_network_id': self.floating_ip.floating_network_id,
+ 'tenant_id': project.id,
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestDeleteFloatingIPNetwork(TestFloatingIPNetwork):
@@ -231,12 +281,20 @@ 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',
'Floating IP Address',
'Fixed IP Address',
'Port',
+ 'Floating Network',
+ 'Project',
)
data = []
@@ -246,12 +304,16 @@ class TestListFloatingIPNetwork(TestFloatingIPNetwork):
ip.floating_ip_address,
ip.fixed_ip_address,
ip.port_id,
+ ip.floating_network_id,
+ ip.tenant_id,
))
def setUp(self):
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)
@@ -263,7 +325,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))
@@ -296,7 +409,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 50a60c2d..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,
@@ -609,7 +627,7 @@ class TestListNetwork(TestNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
- def test_networ_list_project_domain(self):
+ def test_network_list_project_domain(self):
project = identity_fakes_v3.FakeProject.create_one_project()
self.projects_mock.get.return_value = project
arglist = [
@@ -625,6 +643,8 @@ class TestListNetwork(TestNetwork):
filters = {'tenant_id': project.id}
self.network.networks.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
def test_network_list_share(self):
arglist = [
@@ -679,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()
@@ -691,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)
@@ -709,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),
@@ -723,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)
@@ -740,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)
@@ -752,6 +829,7 @@ class TestSetNetwork(TestNetwork):
'--no-share',
'--internal',
'--disable-port-security',
+ '--no-qos-policy',
]
verifylist = [
('network', self._network.name),
@@ -759,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)
@@ -769,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)
@@ -803,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',
@@ -820,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,
@@ -1109,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_qos_policy.py b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py
new file mode 100644
index 00000000..bd30579a
--- /dev/null
+++ b/openstackclient/tests/unit/network/v2/test_network_qos_policy.py
@@ -0,0 +1,380 @@
+# 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 mock import call
+
+from osc_lib import exceptions
+
+from openstackclient.network.v2 import network_qos_policy
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
+from openstackclient.tests.unit.network.v2 import fakes as network_fakes
+from openstackclient.tests.unit import utils as tests_utils
+
+
+class TestQosPolicy(network_fakes.TestNetworkV2):
+
+ def setUp(self):
+ super(TestQosPolicy, self).setUp()
+ # Get a shortcut to the network client
+ self.network = self.app.client_manager.network
+ # Get a shortcut to the ProjectManager Mock
+ self.projects_mock = self.app.client_manager.identity.projects
+
+
+class TestCreateNetworkQosPolicy(TestQosPolicy):
+
+ project = identity_fakes_v3.FakeProject.create_one_project()
+
+ # The new qos policy created.
+ new_qos_policy = (
+ network_fakes.FakeNetworkQosPolicy.create_one_qos_policy(
+ attrs={
+ 'tenant_id': project.id,
+ }
+ ))
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ 'shared',
+ )
+
+ data = (
+ new_qos_policy.description,
+ new_qos_policy.id,
+ new_qos_policy.name,
+ new_qos_policy.project_id,
+ new_qos_policy.rules,
+ new_qos_policy.shared,
+ )
+
+ def setUp(self):
+ super(TestCreateNetworkQosPolicy, self).setUp()
+ self.network.create_qos_policy = mock.Mock(
+ return_value=self.new_qos_policy)
+
+ # Get the command object to test
+ self.cmd = network_qos_policy.CreateNetworkQosPolicy(
+ self.app, self.namespace)
+
+ self.projects_mock.get.return_value = self.project
+
+ def test_create_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_create_default_options(self):
+ arglist = [
+ self.new_qos_policy.name,
+ ]
+ verifylist = [
+ ('project', None),
+ ('name', self.new_qos_policy.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_qos_policy.assert_called_once_with(**{
+ 'name': self.new_qos_policy.name
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+ def test_create_all_options(self):
+ arglist = [
+ '--share',
+ '--project', self.project.name,
+ self.new_qos_policy.name,
+ '--description', 'QoS policy description',
+ ]
+ verifylist = [
+ ('share', True),
+ ('project', self.project.name),
+ ('name', self.new_qos_policy.name),
+ ('description', 'QoS policy description'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.create_qos_policy.assert_called_once_with(**{
+ 'shared': True,
+ 'tenant_id': self.project.id,
+ 'name': self.new_qos_policy.name,
+ 'description': 'QoS policy description',
+ })
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
+
+class TestDeleteNetworkQosPolicy(TestQosPolicy):
+
+ # The address scope to delete.
+ _qos_policies = (
+ network_fakes.FakeNetworkQosPolicy.create_qos_policies(count=2))
+
+ def setUp(self):
+ super(TestDeleteNetworkQosPolicy, self).setUp()
+ self.network.delete_qos_policy = mock.Mock(return_value=None)
+ self.network.find_qos_policy = (
+ network_fakes.FakeNetworkQosPolicy.get_qos_policies(
+ qos_policies=self._qos_policies)
+ )
+
+ # Get the command object to test
+ self.cmd = network_qos_policy.DeleteNetworkQosPolicy(
+ self.app, self.namespace)
+
+ def test_qos_policy_delete(self):
+ arglist = [
+ self._qos_policies[0].name,
+ ]
+ verifylist = [
+ ('policy', [self._qos_policies[0].name]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+ self.network.find_qos_policy.assert_called_once_with(
+ self._qos_policies[0].name, ignore_missing=False)
+ self.network.delete_qos_policy.assert_called_once_with(
+ self._qos_policies[0])
+ self.assertIsNone(result)
+
+ def test_multi_qos_policies_delete(self):
+ arglist = []
+
+ for a in self._qos_policies:
+ arglist.append(a.name)
+ verifylist = [
+ ('policy', arglist),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ calls = []
+ for a in self._qos_policies:
+ calls.append(call(a))
+ self.network.delete_qos_policy.assert_has_calls(calls)
+ self.assertIsNone(result)
+
+ def test_multi_qos_policies_delete_with_exception(self):
+ arglist = [
+ self._qos_policies[0].name,
+ 'unexist_qos_policy',
+ ]
+ verifylist = [
+ ('policy',
+ [self._qos_policies[0].name, 'unexist_qos_policy']),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ find_mock_result = [self._qos_policies[0], exceptions.CommandError]
+ self.network.find_qos_policy = (
+ mock.MagicMock(side_effect=find_mock_result)
+ )
+
+ try:
+ self.cmd.take_action(parsed_args)
+ self.fail('CommandError should be raised.')
+ except exceptions.CommandError as e:
+ self.assertEqual('1 of 2 QoS policies failed to delete.', str(e))
+
+ self.network.find_qos_policy.assert_any_call(
+ self._qos_policies[0].name, ignore_missing=False)
+ self.network.find_qos_policy.assert_any_call(
+ 'unexist_qos_policy', ignore_missing=False)
+ self.network.delete_qos_policy.assert_called_once_with(
+ self._qos_policies[0]
+ )
+
+
+class TestListNetworkQosPolicy(TestQosPolicy):
+
+ # The QoS policies to list up.
+ qos_policies = (
+ network_fakes.FakeNetworkQosPolicy.create_qos_policies(count=3))
+ columns = (
+ 'ID',
+ 'Name',
+ 'Shared',
+ 'Project',
+ )
+ data = []
+ for qos_policy in qos_policies:
+ data.append((
+ qos_policy.id,
+ qos_policy.name,
+ qos_policy.shared,
+ qos_policy.project_id,
+ ))
+
+ def setUp(self):
+ super(TestListNetworkQosPolicy, self).setUp()
+ self.network.qos_policies = mock.Mock(return_value=self.qos_policies)
+
+ # Get the command object to test
+ self.cmd = network_qos_policy.ListNetworkQosPolicy(self.app,
+ self.namespace)
+
+ def test_qos_policy_list(self):
+ arglist = []
+ verifylist = []
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.qos_policies.assert_called_once_with(**{})
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+
+class TestSetNetworkQosPolicy(TestQosPolicy):
+
+ # The QoS policy to set.
+ _qos_policy = network_fakes.FakeNetworkQosPolicy.create_one_qos_policy()
+
+ def setUp(self):
+ super(TestSetNetworkQosPolicy, self).setUp()
+ self.network.update_qos_policy = mock.Mock(return_value=None)
+ self.network.find_qos_policy = mock.Mock(
+ return_value=self._qos_policy)
+
+ # Get the command object to test
+ self.cmd = network_qos_policy.SetNetworkQosPolicy(self.app,
+ self.namespace)
+
+ def test_set_nothing(self):
+ arglist = [self._qos_policy.name, ]
+ verifylist = [
+ ('policy', self._qos_policy.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {}
+ self.network.update_qos_policy.assert_called_with(
+ self._qos_policy, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_name_share_description(self):
+ arglist = [
+ '--name', 'new_qos_policy',
+ '--share',
+ '--description', 'QoS policy description',
+ self._qos_policy.name,
+ ]
+ verifylist = [
+ ('name', 'new_qos_policy'),
+ ('share', True),
+ ('description', 'QoS policy description'),
+ ('policy', self._qos_policy.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'name': 'new_qos_policy',
+ 'description': 'QoS policy description',
+ 'shared': True,
+ }
+ self.network.update_qos_policy.assert_called_with(
+ self._qos_policy, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_share(self):
+ arglist = [
+ '--no-share',
+ self._qos_policy.name,
+ ]
+ verifylist = [
+ ('no_share', True),
+ ('policy', self._qos_policy.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'shared': False
+ }
+ self.network.update_qos_policy.assert_called_with(
+ self._qos_policy, **attrs)
+ self.assertIsNone(result)
+
+
+class TestShowNetworkQosPolicy(TestQosPolicy):
+
+ # The QoS policy to show.
+ _qos_policy = (
+ network_fakes.FakeNetworkQosPolicy.create_one_qos_policy())
+ columns = (
+ 'description',
+ 'id',
+ 'name',
+ 'project_id',
+ 'rules',
+ 'shared',
+ )
+ data = (
+ _qos_policy.description,
+ _qos_policy.id,
+ _qos_policy.name,
+ _qos_policy.project_id,
+ _qos_policy.rules,
+ _qos_policy.shared,
+ )
+
+ def setUp(self):
+ super(TestShowNetworkQosPolicy, self).setUp()
+ self.network.find_qos_policy = mock.Mock(return_value=self._qos_policy)
+
+ # Get the command object to test
+ self.cmd = network_qos_policy.ShowNetworkQosPolicy(self.app,
+ self.namespace)
+
+ def test_show_no_options(self):
+ arglist = []
+ verifylist = []
+
+ # Missing required args should bail here
+ self.assertRaises(tests_utils.ParserException, self.check_parser,
+ self.cmd, arglist, verifylist)
+
+ def test_show_all_options(self):
+ arglist = [
+ self._qos_policy.name,
+ ]
+ verifylist = [
+ ('policy', self._qos_policy.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.find_qos_policy.assert_called_once_with(
+ self._qos_policy.name, ignore_missing=False)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(list(self.data), list(data))
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 a2aceab1..aeb9884a 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',
@@ -137,6 +140,7 @@ class TestCreatePort(TestPort):
'--binding-profile', 'foo=bar',
'--binding-profile', 'foo2=bar2',
'--network', self._port.network_id,
+ '--dns-name', '8.8.8.8',
'test-port',
]
@@ -146,12 +150,14 @@ 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),
('vnic_type', 'macvtap'),
('binding_profile', {'foo': 'bar', 'foo2': 'bar2'}),
('network', self._port.network_id),
+ ('dns_name', '8.8.8.8'),
('name', 'test-port'),
]
@@ -163,12 +169,14 @@ 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,
'binding:vnic_type': 'macvtap',
'binding:profile': {'foo': 'bar', 'foo2': 'bar2'},
'network_id': self._port.network_id,
+ 'dns_name': '8.8.8.8',
'name': 'test-port',
})
@@ -228,6 +236,169 @@ class TestCreatePort(TestPort):
self.assertEqual(ref_columns, columns)
self.assertEqual(ref_data, data)
+ def test_create_with_security_group(self):
+ secgroup = network_fakes.FakeSecurityGroup.create_one_security_group()
+ self.network.find_security_group = mock.Mock(return_value=secgroup)
+ arglist = [
+ '--network', self._port.network_id,
+ '--security-group', secgroup.id,
+ 'test-port',
+ ]
+
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('security_groups', [secgroup.id]),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'security_groups': [secgroup.id],
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
+ def test_create_port_with_dns_name(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--dns-name', '8.8.8.8',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('dns_name', '8.8.8.8'),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'dns_name': '8.8.8.8',
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
+ def test_create_with_security_groups(self):
+ sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ self.network.find_security_group = mock.Mock(side_effect=[sg_1, sg_2])
+ arglist = [
+ '--network', self._port.network_id,
+ '--security-group', sg_1.id,
+ '--security-group', sg_2.id,
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('security_groups', [sg_1.id, sg_2.id]),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'security_groups': [sg_1.id, sg_2.id],
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ self.assertEqual(ref_columns, columns)
+ self.assertEqual(ref_data, data)
+
+ def test_create_with_no_secuirty_groups(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--no-security-group',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id),
+ ('enable', True),
+ ('no_security_group', True),
+ ('name', 'test-port'),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = (self.cmd.take_action(parsed_args))
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'security_groups': [],
+ 'name': 'test-port',
+ })
+
+ ref_columns, ref_data = self._get_common_cols_data(self._port)
+ 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):
@@ -317,6 +488,17 @@ class TestListPort(TestPort):
'Name',
'MAC Address',
'Fixed IP Addresses',
+ 'Status',
+ )
+
+ columns_long = (
+ 'ID',
+ 'Name',
+ 'MAC Address',
+ 'Fixed IP Addresses',
+ 'Status',
+ 'Security Groups',
+ 'Device Owner',
)
data = []
@@ -326,6 +508,19 @@ class TestListPort(TestPort):
prt.name,
prt.mac_address,
utils.format_list_of_dicts(prt.fixed_ips),
+ prt.status,
+ ))
+
+ data_long = []
+ for prt in _ports:
+ data_long.append((
+ prt.id,
+ prt.name,
+ prt.mac_address,
+ utils.format_list_of_dicts(prt.fixed_ips),
+ prt.status,
+ utils.format_list(prt.security_groups),
+ prt.device_owner,
))
def setUp(self):
@@ -419,12 +614,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)
@@ -434,11 +631,48 @@ 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))
+
+ def test_list_port_with_long(self):
+ arglist = [
+ '--long',
+ ]
+
+ verifylist = [
+ ('long', True),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.ports.assert_called_once_with()
+ self.assertEqual(self.columns_long, columns)
+ self.assertEqual(self.data_long, list(data))
+
class TestSetPort(TestPort):
@@ -473,6 +707,25 @@ class TestSetPort(TestPort):
self.network.update_port.assert_called_once_with(self._port, **attrs)
self.assertIsNone(result)
+ def test_set_dns_name(self):
+ arglist = [
+ '--dns-name', '8.8.8.8',
+ self._port.name,
+ ]
+ verifylist = [
+ ('dns_name', '8.8.8.8'),
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'dns_name': '8.8.8.8',
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
def test_append_fixed_ip(self):
_testport = network_fakes.FakePort.create_one_port(
{'fixed_ips': [{'ip_address': '0.0.0.1'}]})
@@ -565,6 +818,7 @@ class TestSetPort(TestPort):
def test_set_that(self):
arglist = [
+ '--description', 'newDescription',
'--enable',
'--vnic-type', 'macvtap',
'--binding-profile', 'foo=bar',
@@ -573,6 +827,7 @@ class TestSetPort(TestPort):
self._port.name,
]
verifylist = [
+ ('description', 'newDescription'),
('enable', True),
('vnic_type', 'macvtap'),
('binding_profile', {'foo': 'bar'}),
@@ -589,6 +844,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)
@@ -651,6 +907,131 @@ class TestSetPort(TestPort):
self.network.update_port.assert_called_once_with(self._port, **attrs)
self.assertIsNone(result)
+ def test_set_security_group(self):
+ sg = network_fakes.FakeSecurityGroup.create_one_security_group()
+ self.network.find_security_group = mock.Mock(return_value=sg)
+ arglist = [
+ '--security-group', sg.id,
+ self._port.name,
+ ]
+ verifylist = [
+ ('security_groups', [sg.id]),
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'security_groups': [sg.id],
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
+ def test_append_security_group(self):
+ sg_1 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ sg_2 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ sg_3 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ self.network.find_security_group = mock.Mock(side_effect=[sg_2, sg_3])
+ _testport = network_fakes.FakePort.create_one_port(
+ {'security_groups': [sg_1.id]})
+ self.network.find_port = mock.Mock(return_value=_testport)
+ arglist = [
+ '--security-group', sg_2.id,
+ '--security-group', sg_3.id,
+ _testport.name,
+ ]
+ verifylist = [
+ ('security_groups', [sg_2.id, sg_3.id]),
+ ('port', _testport.name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'security_groups': [sg_1.id, sg_2.id, sg_3.id],
+ }
+ self.network.update_port.assert_called_once_with(_testport, **attrs)
+ self.assertIsNone(result)
+
+ def test_set_no_security_groups(self):
+ arglist = [
+ '--no-security-group',
+ self._port.name,
+ ]
+ verifylist = [
+ ('no_security_group', True),
+ ('port', self._port.name),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'security_groups': [],
+ }
+ self.network.update_port.assert_called_once_with(self._port, **attrs)
+ self.assertIsNone(result)
+
+ def test_overwrite_security_group(self):
+ sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ _testport = network_fakes.FakePort.create_one_port(
+ {'security_groups': [sg1.id]})
+ self.network.find_port = mock.Mock(return_value=_testport)
+ self.network.find_security_group = mock.Mock(return_value=sg2)
+ arglist = [
+ '--security-group', sg2.id,
+ '--no-security-group',
+ _testport.name,
+ ]
+ verifylist = [
+ ('security_groups', [sg2.id]),
+ ('no_security_group', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+ attrs = {
+ 'security_groups': [sg2.id],
+ }
+ 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):
@@ -767,3 +1148,47 @@ class TestUnsetPort(TestPort):
self.assertRaises(exceptions.CommandError,
self.cmd.take_action,
parsed_args)
+
+ def test_unset_security_group(self):
+ _fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ _fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ _fake_port = network_fakes.FakePort.create_one_port(
+ {'security_groups': [_fake_sg1.id, _fake_sg2.id]})
+ self.network.find_port = mock.Mock(return_value=_fake_port)
+ self.network.find_security_group = mock.Mock(return_value=_fake_sg2)
+ arglist = [
+ '--security-group', _fake_sg2.id,
+ _fake_port.name,
+ ]
+ verifylist = [
+ ('security_groups', [_fake_sg2.id]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ attrs = {
+ 'security_groups': [_fake_sg1.id]
+ }
+ self.network.update_port.assert_called_once_with(
+ _fake_port, **attrs)
+ self.assertIsNone(result)
+
+ def test_unset_port_security_group_not_existent(self):
+ _fake_sg1 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ _fake_sg2 = network_fakes.FakeSecurityGroup.create_one_security_group()
+ _fake_port = network_fakes.FakePort.create_one_port(
+ {'security_groups': [_fake_sg1.id]})
+ self.network.find_security_group = mock.Mock(return_value=_fake_sg2)
+ arglist = [
+ '--security-group', _fake_sg2.id,
+ _fake_port.name,
+ ]
+ verifylist = [
+ ('security_groups', [_fake_sg2.id]),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ self.assertRaises(exceptions.CommandError,
+ self.cmd.take_action,
+ parsed_args)
diff --git a/openstackclient/tests/unit/network/v2/test_router.py b/openstackclient/tests/unit/network/v2/test_router.py
index 6a445862..b0409447 100644
--- a/openstackclient/tests/unit/network/v2/test_router.py
+++ b/openstackclient/tests/unit/network/v2/test_router.py
@@ -18,6 +18,7 @@ from osc_lib import exceptions
from osc_lib import utils as osc_utils
from openstackclient.network.v2 import router
+from openstackclient.tests.unit.identity.v3 import fakes as identity_fakes_v3
from openstackclient.tests.unit.network.v2 import fakes as network_fakes
from openstackclient.tests.unit import utils as tests_utils
@@ -29,6 +30,7 @@ class TestRouter(network_fakes.TestNetworkV2):
# Get a shortcut to the network client
self.network = self.app.client_manager.network
+ self.projects_mock = self.app.client_manager.identity.projects
class TestAddPortToRouter(TestRouter):
@@ -427,6 +429,97 @@ class TestListRouter(TestRouter):
self.assertEqual(self.columns_long_no_az, columns)
self.assertEqual(self.data_long_no_az, list(data))
+ def test_list_name(self):
+ test_name = "fakename"
+ arglist = [
+ '--name', test_name,
+ ]
+ verifylist = [
+ ('long', False),
+ ('name', test_name),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.routers.assert_called_once_with(
+ **{'name': test_name}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_enable(self):
+ arglist = [
+ '--enable',
+ ]
+ verifylist = [
+ ('long', False),
+ ('enable', True),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.routers.assert_called_once_with(
+ **{'admin_state_up': True}
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_disable(self):
+ arglist = [
+ '--disable',
+ ]
+ verifylist = [
+ ('long', False),
+ ('disable', True)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.network.routers.assert_called_once_with(
+ **{'admin_state_up': False}
+ )
+
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_project(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.routers.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_router_list_project_domain(self):
+ project = identity_fakes_v3.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ('project_domain', project.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.routers.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestRemovePortFromRouter(TestRouter):
'''Remove port from a Router '''
@@ -529,6 +622,7 @@ class TestSetRouter(TestRouter):
'--enable',
'--distributed',
'--name', 'noob',
+ '--no-ha',
'--description', 'router',
]
verifylist = [
@@ -537,6 +631,7 @@ class TestSetRouter(TestRouter):
('distributed', True),
('name', 'noob'),
('description', 'router'),
+ ('no_ha', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -546,6 +641,7 @@ class TestSetRouter(TestRouter):
'admin_state_up': True,
'distributed': True,
'name': 'noob',
+ 'ha': False,
'description': 'router',
}
self.network.update_router.assert_called_once_with(
@@ -557,11 +653,13 @@ class TestSetRouter(TestRouter):
self._router.name,
'--disable',
'--centralized',
+ '--ha',
]
verifylist = [
('router', self._router.name),
('disable', True),
('centralized', True),
+ ('ha', True),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -570,6 +668,7 @@ class TestSetRouter(TestRouter):
attrs = {
'admin_state_up': False,
'distributed': False,
+ 'ha': True,
}
self.network.update_router.assert_called_once_with(
self._router, **attrs)
@@ -773,9 +872,9 @@ class TestUnsetRouter(TestRouter):
super(TestUnsetRouter, self).setUp()
self._testrouter = network_fakes.FakeRouter.create_one_router(
{'routes': [{"destination": "192.168.101.1/24",
- "gateway": "172.24.4.3"},
+ "nexthop": "172.24.4.3"},
{"destination": "192.168.101.2/24",
- "gateway": "172.24.4.3"}], })
+ "nexthop": "172.24.4.3"}], })
self.fake_subnet = network_fakes.FakeSubnet.create_one_subnet()
self.network.find_router = mock.Mock(return_value=self._testrouter)
self.network.update_router = mock.Mock(return_value=None)
diff --git a/openstackclient/tests/unit/network/v2/test_security_group.py b/openstackclient/tests/unit/network/v2/test_security_group.py
index 2615b77a..43aa07cc 100644
--- a/openstackclient/tests/unit/network/v2/test_security_group.py
+++ b/openstackclient/tests/unit/network/v2/test_security_group.py
@@ -444,6 +444,44 @@ class TestListSecurityGroupNetwork(TestSecurityGroupNetwork):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, list(data))
+ def test_security_group_list_project(self):
+ project = identity_fakes.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.security_groups.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
+ def test_security_group_list_project_domain(self):
+ project = identity_fakes.FakeProject.create_one_project()
+ self.projects_mock.get.return_value = project
+ arglist = [
+ '--project', project.id,
+ '--project-domain', project.domain_id,
+ ]
+ verifylist = [
+ ('project', project.id),
+ ('project_domain', project.domain_id),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+ filters = {'tenant_id': project.id}
+
+ self.network.security_groups.assert_called_once_with(**filters)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, list(data))
+
class TestListSecurityGroupCompute(TestSecurityGroupCompute):
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 3999543c..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."""
@@ -306,6 +197,32 @@ class FakeQos(object):
return qos
@staticmethod
+ def create_one_qos_association(attrs=None):
+ """Create a fake Qos specification association.
+
+ :param Dictionary attrs:
+ A dictionary with all attributes
+ :return:
+ A FakeResource object with id, name, association_type, etc.
+ """
+ attrs = attrs or {}
+
+ # Set default attributes.
+ qos_association_info = {
+ "id": 'type-id-' + uuid.uuid4().hex,
+ "name": 'type-name-' + uuid.uuid4().hex,
+ "association_type": 'volume_type',
+ }
+
+ # Overwrite default attributes.
+ qos_association_info.update(attrs)
+
+ qos_association = fakes.FakeResource(
+ info=copy.deepcopy(qos_association_info),
+ loaded=True)
+ return qos_association
+
+ @staticmethod
def create_qoses(attrs=None, count=2):
"""Create multiple fake Qos specifications.
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_qos_specs.py b/openstackclient/tests/unit/volume/v1/test_qos_specs.py
index 1982980a..464038e7 100644
--- a/openstackclient/tests/unit/volume/v1/test_qos_specs.py
+++ b/openstackclient/tests/unit/volume/v1/test_qos_specs.py
@@ -13,14 +13,12 @@
# under the License.
#
-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.volume.v1 import fakes as volume_fakes
from openstackclient.volume.v1 import qos_specs
@@ -39,154 +37,124 @@ class TestQos(volume_fakes.TestVolumev1):
class TestQosAssociate(TestQos):
+ volume_type = volume_fakes.FakeType.create_one_type()
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+
def setUp(self):
super(TestQosAssociate, self).setUp()
+ self.qos_mock.get.return_value = self.qos_spec
+ self.types_mock.get.return_value = self.volume_type
# Get the command object to test
self.cmd = qos_specs.AssociateQos(self.app, None)
def test_qos_associate(self):
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS),
- loaded=True
- )
- self.types_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.TYPE),
- loaded=True
- )
arglist = [
- volume_fakes.qos_id,
- volume_fakes.type_id
+ self.qos_spec.id,
+ self.volume_type.id
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id),
- ('volume_type', volume_fakes.type_id)
+ ('qos_spec', self.qos_spec.id),
+ ('volume_type', self.volume_type.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.qos_mock.associate.assert_called_with(
- volume_fakes.qos_id,
- volume_fakes.type_id
+ self.qos_spec.id,
+ self.volume_type.id
)
self.assertIsNone(result)
class TestQosCreate(TestQos):
+ new_qos_spec = volume_fakes.FakeQos.create_one_qos()
columns = (
'consumer',
'id',
'name',
+ 'specs'
)
datalist = (
- volume_fakes.qos_consumer,
- volume_fakes.qos_id,
- volume_fakes.qos_name
+ new_qos_spec.consumer,
+ new_qos_spec.id,
+ new_qos_spec.name,
+ new_qos_spec.specs
)
def setUp(self):
super(TestQosCreate, self).setUp()
+ self.qos_mock.create.return_value = self.new_qos_spec
# Get the command object to test
self.cmd = qos_specs.CreateQos(self.app, None)
def test_qos_create_without_properties(self):
- self.qos_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS_DEFAULT_CONSUMER),
- loaded=True
- )
-
arglist = [
- volume_fakes.qos_name,
+ self.new_qos_spec.name,
]
verifylist = [
- ('name', volume_fakes.qos_name),
+ ('name', self.new_qos_spec.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.qos_mock.create.assert_called_with(
- volume_fakes.qos_name,
- {'consumer': volume_fakes.qos_default_consumer}
+ self.new_qos_spec.name,
+ {'consumer': 'both'}
)
self.assertEqual(self.columns, columns)
- datalist = (
- volume_fakes.qos_default_consumer,
- volume_fakes.qos_id,
- volume_fakes.qos_name
- )
- self.assertEqual(datalist, data)
+ self.assertEqual(self.datalist, data)
def test_qos_create_with_consumer(self):
- self.qos_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS),
- loaded=True
- )
-
arglist = [
- volume_fakes.qos_name,
- '--consumer', volume_fakes.qos_consumer
+ '--consumer', self.new_qos_spec.consumer,
+ self.new_qos_spec.name,
]
verifylist = [
- ('name', volume_fakes.qos_name),
- ('consumer', volume_fakes.qos_consumer)
+ ('consumer', self.new_qos_spec.consumer),
+ ('name', self.new_qos_spec.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.qos_mock.create.assert_called_with(
- volume_fakes.qos_name,
- {'consumer': volume_fakes.qos_consumer}
+ self.new_qos_spec.name,
+ {'consumer': self.new_qos_spec.consumer}
)
self.assertEqual(self.columns, columns)
self.assertEqual(self.datalist, data)
def test_qos_create_with_properties(self):
- self.qos_mock.create.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS_WITH_SPECS),
- loaded=True
- )
-
arglist = [
- volume_fakes.qos_name,
- '--consumer', volume_fakes.qos_consumer,
+ '--consumer', self.new_qos_spec.consumer,
'--property', 'foo=bar',
- '--property', 'iops=9001'
+ '--property', 'iops=9001',
+ self.new_qos_spec.name,
]
verifylist = [
- ('name', volume_fakes.qos_name),
- ('consumer', volume_fakes.qos_consumer),
- ('property', volume_fakes.qos_specs)
+ ('consumer', self.new_qos_spec.consumer),
+ ('property', self.new_qos_spec.specs),
+ ('name', self.new_qos_spec.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
- specs = volume_fakes.qos_specs.copy()
- specs.update({'consumer': volume_fakes.qos_consumer})
+ self.new_qos_spec.specs.update(
+ {'consumer': self.new_qos_spec.consumer})
self.qos_mock.create.assert_called_with(
- volume_fakes.qos_name,
- specs
+ self.new_qos_spec.name,
+ self.new_qos_spec.specs
)
- columns = self.columns + (
- 'specs',
- )
- self.assertEqual(columns, columns)
- datalist = self.datalist + (
- volume_fakes.qos_specs,
- )
- self.assertEqual(datalist, data)
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.datalist, data)
class TestQosDelete(TestQos):
@@ -294,79 +262,62 @@ class TestQosDelete(TestQos):
class TestQosDisassociate(TestQos):
+ volume_type = volume_fakes.FakeType.create_one_type()
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+
def setUp(self):
super(TestQosDisassociate, self).setUp()
+ self.qos_mock.get.return_value = self.qos_spec
+ self.types_mock.get.return_value = self.volume_type
# Get the command object to test
self.cmd = qos_specs.DisassociateQos(self.app, None)
def test_qos_disassociate_with_volume_type(self):
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS),
- loaded=True
- )
- self.types_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.TYPE),
- loaded=True
- )
arglist = [
- volume_fakes.qos_id,
- '--volume-type', volume_fakes.type_id
+ '--volume-type', self.volume_type.id,
+ self.qos_spec.id,
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id),
- ('volume_type', volume_fakes.type_id)
+ ('volume_type', self.volume_type.id),
+ ('qos_spec', self.qos_spec.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.qos_mock.disassociate.assert_called_with(
- volume_fakes.qos_id,
- volume_fakes.type_id
+ self.qos_spec.id,
+ self.volume_type.id
)
self.assertIsNone(result)
def test_qos_disassociate_with_all_volume_types(self):
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS),
- loaded=True
- )
-
arglist = [
- volume_fakes.qos_id,
- '--all'
+ '--all',
+ self.qos_spec.id,
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id)
+ ('qos_spec', self.qos_spec.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
- self.qos_mock.disassociate_all.assert_called_with(volume_fakes.qos_id)
+ self.qos_mock.disassociate_all.assert_called_with(self.qos_spec.id)
self.assertIsNone(result)
class TestQosList(TestQos):
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+ qos_association = volume_fakes.FakeQos.create_one_qos_association()
+
def setUp(self):
super(TestQosList, self).setUp()
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS_WITH_ASSOCIATIONS),
- loaded=True,
- )
- self.qos_mock.list.return_value = [self.qos_mock.get.return_value]
- self.qos_mock.get_associations.return_value = [fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.qos_association),
- loaded=True,
- )]
+ self.qos_mock.list.return_value = [self.qos_spec]
+ self.qos_mock.get_associations.return_value = [self.qos_association]
# Get the command object to test
self.cmd = qos_specs.ListQos(self.app, None)
@@ -389,81 +340,72 @@ class TestQosList(TestQos):
)
self.assertEqual(collist, columns)
datalist = ((
- volume_fakes.qos_id,
- volume_fakes.qos_name,
- volume_fakes.qos_consumer,
- volume_fakes.type_name,
- utils.format_dict(volume_fakes.qos_specs),
+ self.qos_spec.id,
+ self.qos_spec.name,
+ self.qos_spec.consumer,
+ self.qos_association.name,
+ utils.format_dict(self.qos_spec.specs),
), )
self.assertEqual(datalist, tuple(data))
class TestQosSet(TestQos):
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+
def setUp(self):
super(TestQosSet, self).setUp()
+ self.qos_mock.get.return_value = self.qos_spec
# Get the command object to test
self.cmd = qos_specs.SetQos(self.app, None)
def test_qos_set_with_properties_with_id(self):
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS_WITH_SPECS),
- loaded=True
- )
arglist = [
- volume_fakes.qos_id,
'--property', 'foo=bar',
- '--property', 'iops=9001'
+ '--property', 'iops=9001',
+ self.qos_spec.id,
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id),
- ('property', volume_fakes.qos_specs)
+ ('property', self.qos_spec.specs),
+ ('qos_spec', self.qos_spec.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.qos_mock.set_keys.assert_called_with(
- volume_fakes.qos_id,
- volume_fakes.qos_specs
+ self.qos_spec.id,
+ self.qos_spec.specs
)
self.assertIsNone(result)
class TestQosShow(TestQos):
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+ qos_association = volume_fakes.FakeQos.create_one_qos_association()
+
def setUp(self):
super(TestQosShow, self).setUp()
-
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS_WITH_ASSOCIATIONS),
- loaded=True,
- )
- self.qos_mock.get_associations.return_value = [fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.qos_association),
- loaded=True,
- )]
-
+ self.qos_mock.get.return_value = self.qos_spec
+ self.qos_mock.get_associations.return_value = [self.qos_association]
# Get the command object to test
self.cmd = qos_specs.ShowQos(self.app, None)
def test_qos_show(self):
arglist = [
- volume_fakes.qos_id
+ self.qos_spec.id
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id)
+ ('qos_spec', self.qos_spec.id)
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
columns, data = self.cmd.take_action(parsed_args)
self.qos_mock.get.assert_called_with(
- volume_fakes.qos_id
+ self.qos_spec.id
)
collist = (
@@ -475,56 +417,53 @@ class TestQosShow(TestQos):
)
self.assertEqual(collist, columns)
datalist = (
- volume_fakes.type_name,
- volume_fakes.qos_consumer,
- volume_fakes.qos_id,
- volume_fakes.qos_name,
- utils.format_dict(volume_fakes.qos_specs),
+ self.qos_association.name,
+ self.qos_spec.consumer,
+ self.qos_spec.id,
+ self.qos_spec.name,
+ utils.format_dict(self.qos_spec.specs),
)
self.assertEqual(datalist, tuple(data))
class TestQosUnset(TestQos):
+ qos_spec = volume_fakes.FakeQos.create_one_qos()
+
def setUp(self):
super(TestQosUnset, self).setUp()
+ self.qos_mock.get.return_value = self.qos_spec
# Get the command object to test
self.cmd = qos_specs.UnsetQos(self.app, None)
def test_qos_unset_with_properties(self):
- self.qos_mock.get.return_value = fakes.FakeResource(
- None,
- copy.deepcopy(volume_fakes.QOS),
- loaded=True
- )
arglist = [
- volume_fakes.qos_id,
'--property', 'iops',
- '--property', 'foo'
+ '--property', 'foo',
+ self.qos_spec.id,
]
-
verifylist = [
- ('qos_spec', volume_fakes.qos_id),
- ('property', ['iops', 'foo'])
+ ('property', ['iops', 'foo']),
+ ('qos_spec', self.qos_spec.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
result = self.cmd.take_action(parsed_args)
self.qos_mock.unset_keys.assert_called_with(
- volume_fakes.qos_id,
+ self.qos_spec.id,
['iops', 'foo']
)
self.assertIsNone(result)
def test_qos_unset_nothing(self):
arglist = [
- volume_fakes.qos_id,
+ self.qos_spec.id,
]
verifylist = [
- ('qos_spec', volume_fakes.qos_id),
+ ('qos_spec', self.qos_spec.id),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
diff --git a/openstackclient/tests/unit/volume/v1/test_snapshot.py b/openstackclient/tests/unit/volume/v1/test_snapshot.py
index edfbdc19..8e30d6a9 100644
--- a/openstackclient/tests/unit/volume/v1/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v1/test_snapshot.py
@@ -19,7 +19,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v1 import fakes as volume_fakes
-from openstackclient.volume.v1 import snapshot
+from openstackclient.volume.v1 import volume_snapshot
class TestSnapshot(volume_fakes.TestVolumev1):
@@ -67,20 +67,20 @@ class TestSnapshotCreate(TestSnapshot):
self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.create.return_value = self.new_snapshot
# Get the command object to test
- self.cmd = snapshot.CreateSnapshot(self.app, None)
+ self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
def test_snapshot_create(self):
arglist = [
- "--name", self.new_snapshot.display_name,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.display_description,
"--force",
- self.new_snapshot.volume_id,
+ self.new_snapshot.display_name,
]
verifylist = [
- ("name", self.new_snapshot.display_name),
+ ("volume", self.new_snapshot.volume_id),
("description", self.new_snapshot.display_description),
("force", True),
- ("volume", self.new_snapshot.volume_id),
+ ("snapshot_name", self.new_snapshot.display_name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -97,7 +97,7 @@ class TestSnapshotCreate(TestSnapshot):
def test_snapshot_create_without_name(self):
arglist = [
- self.new_snapshot.volume_id,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.display_description,
"--force"
]
@@ -119,6 +119,32 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_snapshot_create_without_volume(self):
+ arglist = [
+ "--description", self.new_snapshot.display_description,
+ "--force",
+ self.new_snapshot.display_name
+ ]
+ verifylist = [
+ ("description", self.new_snapshot.display_description),
+ ("force", True),
+ ("snapshot_name", self.new_snapshot.display_name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.get.assert_called_once_with(
+ self.new_snapshot.display_name)
+ self.snapshots_mock.create.assert_called_once_with(
+ self.new_snapshot.volume_id,
+ True,
+ self.new_snapshot.display_name,
+ self.new_snapshot.display_description,
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestSnapshotDelete(TestSnapshot):
@@ -132,7 +158,7 @@ class TestSnapshotDelete(TestSnapshot):
self.snapshots_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = snapshot.DeleteSnapshot(self.app, None)
+ self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
def test_snapshot_delete(self):
arglist = [
@@ -244,7 +270,7 @@ class TestSnapshotList(TestSnapshot):
self.volumes_mock.list.return_value = [self.volume]
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
- self.cmd = snapshot.ListSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
def test_snapshot_list_without_options(self):
arglist = []
@@ -307,7 +333,7 @@ class TestSnapshotSet(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.set_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.SetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
def test_snapshot_set_all(self):
arglist = [
@@ -404,7 +430,7 @@ class TestSnapshotShow(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
# Get the command object to test
- self.cmd = snapshot.ShowSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
def test_snapshot_show(self):
arglist = [
@@ -432,7 +458,7 @@ class TestSnapshotUnset(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.delete_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.UnsetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
def test_snapshot_unset(self):
arglist = [
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_snapshot.py b/openstackclient/tests/unit/volume/v2/test_snapshot.py
index d355662d..b67dd6eb 100644
--- a/openstackclient/tests/unit/volume/v2/test_snapshot.py
+++ b/openstackclient/tests/unit/volume/v2/test_snapshot.py
@@ -20,7 +20,7 @@ from osc_lib import exceptions
from osc_lib import utils
from openstackclient.tests.unit.volume.v2 import fakes as volume_fakes
-from openstackclient.volume.v2 import snapshot
+from openstackclient.volume.v2 import volume_snapshot
class TestSnapshot(volume_fakes.TestVolume):
@@ -68,23 +68,23 @@ class TestSnapshotCreate(TestSnapshot):
self.volumes_mock.get.return_value = self.volume
self.snapshots_mock.create.return_value = self.new_snapshot
# Get the command object to test
- self.cmd = snapshot.CreateSnapshot(self.app, None)
+ self.cmd = volume_snapshot.CreateVolumeSnapshot(self.app, None)
def test_snapshot_create(self):
arglist = [
- "--name", self.new_snapshot.name,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.description,
"--force",
'--property', 'Alpha=a',
'--property', 'Beta=b',
- self.new_snapshot.volume_id,
+ self.new_snapshot.name,
]
verifylist = [
- ("name", self.new_snapshot.name),
+ ("volume", self.new_snapshot.volume_id),
("description", self.new_snapshot.description),
("force", True),
('property', {'Alpha': 'a', 'Beta': 'b'}),
- ("volume", self.new_snapshot.volume_id),
+ ("snapshot_name", self.new_snapshot.name),
]
parsed_args = self.check_parser(self.cmd, arglist, verifylist)
@@ -102,7 +102,7 @@ class TestSnapshotCreate(TestSnapshot):
def test_snapshot_create_without_name(self):
arglist = [
- self.new_snapshot.volume_id,
+ "--volume", self.new_snapshot.volume_id,
"--description", self.new_snapshot.description,
"--force"
]
@@ -125,6 +125,33 @@ class TestSnapshotCreate(TestSnapshot):
self.assertEqual(self.columns, columns)
self.assertEqual(self.data, data)
+ def test_snapshot_create_without_volume(self):
+ arglist = [
+ "--description", self.new_snapshot.description,
+ "--force",
+ self.new_snapshot.name
+ ]
+ verifylist = [
+ ("description", self.new_snapshot.description),
+ ("force", True),
+ ("snapshot_name", self.new_snapshot.name)
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ columns, data = self.cmd.take_action(parsed_args)
+
+ self.volumes_mock.get.assert_called_once_with(
+ self.new_snapshot.name)
+ self.snapshots_mock.create.assert_called_once_with(
+ self.new_snapshot.volume_id,
+ force=True,
+ name=self.new_snapshot.name,
+ description=self.new_snapshot.description,
+ metadata=None,
+ )
+ self.assertEqual(self.columns, columns)
+ self.assertEqual(self.data, data)
+
class TestSnapshotDelete(TestSnapshot):
@@ -138,7 +165,7 @@ class TestSnapshotDelete(TestSnapshot):
self.snapshots_mock.delete.return_value = None
# Get the command object to mock
- self.cmd = snapshot.DeleteSnapshot(self.app, None)
+ self.cmd = volume_snapshot.DeleteVolumeSnapshot(self.app, None)
def test_snapshot_delete(self):
arglist = [
@@ -250,7 +277,7 @@ class TestSnapshotList(TestSnapshot):
self.volumes_mock.list.return_value = [self.volume]
self.snapshots_mock.list.return_value = self.snapshots
# Get the command to test
- self.cmd = snapshot.ListSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ListVolumeSnapshot(self.app, None)
def test_snapshot_list_without_options(self):
arglist = []
@@ -330,7 +357,7 @@ class TestSnapshotSet(TestSnapshot):
self.snapshots_mock.set_metadata.return_value = None
self.snapshots_mock.update.return_value = None
# Get the command object to mock
- self.cmd = snapshot.SetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.SetVolumeSnapshot(self.app, None)
def test_snapshot_set(self):
arglist = [
@@ -457,7 +484,7 @@ class TestSnapshotShow(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
# Get the command object to test
- self.cmd = snapshot.ShowSnapshot(self.app, None)
+ self.cmd = volume_snapshot.ShowVolumeSnapshot(self.app, None)
def test_snapshot_show(self):
arglist = [
@@ -485,7 +512,7 @@ class TestSnapshotUnset(TestSnapshot):
self.snapshots_mock.get.return_value = self.snapshot
self.snapshots_mock.delete_metadata.return_value = None
# Get the command object to mock
- self.cmd = snapshot.UnsetSnapshot(self.app, None)
+ self.cmd = volume_snapshot.UnsetVolumeSnapshot(self.app, None)
def test_snapshot_unset(self):
arglist = [
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/tests/unit/volume/v2/test_volume_host.py b/openstackclient/tests/unit/volume/v2/test_volume_host.py
new file mode 100644
index 00000000..aad7bb0b
--- /dev/null
+++ b/openstackclient/tests/unit/volume/v2/test_volume_host.py
@@ -0,0 +1,86 @@
+#
+# 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.unit.volume.v2 import fakes as host_fakes
+from openstackclient.volume.v2 import volume_host
+
+
+class TestVolumeHost(host_fakes.TestVolume):
+
+ def setUp(self):
+ super(TestVolumeHost, self).setUp()
+
+ self.host_mock = self.app.client_manager.volume.services
+ self.host_mock.reset_mock()
+
+
+class TestVolumeHostSet(TestVolumeHost):
+
+ service = host_fakes.FakeService.create_one_service()
+
+ def setUp(self):
+ super(TestVolumeHostSet, self).setUp()
+
+ self.host_mock.freeze_host.return_value = None
+ self.host_mock.thaw_host.return_value = None
+
+ self.cmd = volume_host.SetVolumeHost(self.app, None)
+
+ def test_volume_host_set_nothing(self):
+ arglist = [
+ self.service.host,
+ ]
+ verifylist = [
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.freeze_host.assert_not_called()
+ self.host_mock.thaw_host.assert_not_called()
+ self.assertIsNone(result)
+
+ def test_volume_host_set_enable(self):
+ arglist = [
+ '--enable',
+ self.service.host,
+ ]
+ verifylist = [
+ ('enable', True),
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.thaw_host.assert_called_with(self.service.host)
+ self.host_mock.freeze_host.assert_not_called()
+ self.assertIsNone(result)
+
+ def test_volume_host_set_disable(self):
+ arglist = [
+ '--disable',
+ self.service.host,
+ ]
+ verifylist = [
+ ('disable', True),
+ ('host', self.service.host),
+ ]
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ result = self.cmd.take_action(parsed_args)
+
+ self.host_mock.freeze_host.assert_called_with(self.service.host)
+ self.host_mock.thaw_host.assert_not_called()
+ self.assertIsNone(result)
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 bc92c0f5..e9e3894b 100644
--- a/openstackclient/volume/v1/snapshot.py
+++ b/openstackclient/volume/v1/snapshot.py
@@ -13,6 +13,10 @@
# under the License.
#
+# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
+# "snapshot set", "snapshot show" and "snapshot unset"
+# commands two cycles after Ocata.
+
"""Volume v1 Snapshot action implementations"""
import copy
@@ -27,11 +31,13 @@ import six
from openstackclient.i18n import _
+deprecated = True
+LOG_DEP = logging.getLogger('deprecated')
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)
@@ -61,6 +67,8 @@ class CreateSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot create" instead.'))
volume_client = self.app.client_manager.volume
volume_id = utils.find_resource(volume_client.volumes,
parsed_args.volume).id
@@ -79,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)
@@ -92,6 +100,8 @@ class DeleteSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot delete" instead.'))
volume_client = self.app.client_manager.volume
result = 0
@@ -114,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)
@@ -133,6 +143,8 @@ class ListSnapshot(command.Lister):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot list" instead.'))
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -185,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)
@@ -214,6 +226,8 @@ class SetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot set" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -246,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)
@@ -258,6 +272,8 @@ class ShowSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot show" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -270,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)
@@ -289,6 +305,8 @@ class UnsetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot unset" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
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_snapshot.py b/openstackclient/volume/v1/volume_snapshot.py
new file mode 100644
index 00000000..c2ecf75b
--- /dev/null
+++ b/openstackclient/volume/v1/volume_snapshot.py
@@ -0,0 +1,305 @@
+# Copyright 2012-2013 OpenStack Foundation
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+#
+
+"""Volume v1 Snapshot action implementations"""
+
+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
+import six
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateVolumeSnapshot(command.ShowOne):
+ """Create new volume snapshot"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot_name',
+ metavar='<snapshot-name>',
+ nargs="?",
+ help=_('Name of the snapshot (default to None)'),
+ )
+ parser.add_argument(
+ '--volume',
+ metavar='<volume>',
+ help=_('Volume to snapshot (name or ID) '
+ '(default is <snapshot-name>)'),
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('Description of the snapshot'),
+ )
+ parser.add_argument(
+ '--force',
+ dest='force',
+ action='store_true',
+ default=False,
+ help=_('Create a snapshot attached to an instance. '
+ 'Default is False'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = parsed_args.volume
+ if not parsed_args.volume:
+ volume = parsed_args.snapshot_name
+ volume_id = utils.find_resource(volume_client.volumes,
+ volume).id
+ snapshot = volume_client.volume_snapshots.create(
+ volume_id,
+ parsed_args.force,
+ parsed_args.snapshot_name,
+ parsed_args.description
+ )
+
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class DeleteVolumeSnapshot(command.Command):
+ """Delete volume snapshot(s)"""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshots',
+ metavar='<snapshot>',
+ nargs="+",
+ help=_('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 i in parsed_args.snapshots:
+ try:
+ snapshot_id = utils.find_resource(
+ volume_client.volume_snapshots, i).id
+ volume_client.volume_snapshots.delete(snapshot_id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete snapshot with "
+ "name or ID '%(snapshot)s': %(e)s"),
+ {'snapshot': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.snapshots)
+ msg = (_("%(result)s of %(total)s snapshots failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListVolumeSnapshot(command.Lister):
+ """List volume snapshots"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_('Include all projects (admin only)'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_('List additional fields in output'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ def _format_volume_id(volume_id):
+ """Return a volume name if available
+
+ :param volume_id: a volume ID
+ :rtype: either the volume ID or name
+ """
+
+ volume = volume_id
+ if volume_id in volume_cache.keys():
+ volume = volume_cache[volume_id].display_name
+ return volume
+
+ if parsed_args.long:
+ columns = ['ID', 'Display Name', 'Display Description', 'Status',
+ 'Size', 'Created At', 'Volume ID', 'Metadata']
+ column_headers = copy.deepcopy(columns)
+ column_headers[6] = 'Volume'
+ column_headers[7] = 'Properties'
+ else:
+ columns = ['ID', 'Display Name', 'Display Description', 'Status',
+ 'Size']
+ column_headers = copy.deepcopy(columns)
+
+ # Always update Name and Description
+ column_headers[1] = 'Name'
+ column_headers[2] = 'Description'
+
+ # Cache the volume list
+ volume_cache = {}
+ try:
+ for s in self.app.client_manager.volume.volumes.list():
+ volume_cache[s.id] = s
+ except Exception:
+ # Just forget it if there's any trouble
+ pass
+
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts)
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={'Metadata': utils.format_dict,
+ 'Volume ID': _format_volume_id},
+ ) for s in data))
+
+
+class SetVolumeSnapshot(command.Command):
+ """Set volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)')
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_('New snapshot name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New snapshot description')
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help=_('Property to add/change for this snapshot '
+ '(repeat option to set multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ result = 0
+ if parsed_args.property:
+ try:
+ volume_client.volume_snapshots.set_metadata(
+ snapshot.id, parsed_args.property)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot property: %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:
+ try:
+ snapshot.update(**kwargs)
+ except Exception as e:
+ LOG.error(_("Failed to update snapshot display name "
+ "or display description: %s"), e)
+ result += 1
+
+ if result > 0:
+ raise exceptions.CommandError(_("One or more of the "
+ "set operations failed"))
+
+
+class ShowVolumeSnapshot(command.ShowOne):
+ """Display volume snapshot details"""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to display (name or ID)')
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class UnsetVolumeSnapshot(command.Command):
+ """Unset volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)'),
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key>',
+ action='append',
+ help=_('Property to remove from snapshot '
+ '(repeat option to remove multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+
+ if parsed_args.property:
+ volume_client.volume_snapshots.delete_metadata(
+ snapshot.id,
+ parsed_args.property,
+ )
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 4994e0da..a18887e3 100644
--- a/openstackclient/volume/v2/snapshot.py
+++ b/openstackclient/volume/v2/snapshot.py
@@ -12,6 +12,10 @@
# under the License.
#
+# TODO(Huanxuan Ao): Remove this file and "snapshot create", "snapshot delete",
+# "snapshot set", "snapshot show" and "snapshot unset"
+# commands two cycles after Ocata.
+
"""Volume v2 snapshot action implementations"""
import copy
@@ -26,11 +30,13 @@ import six
from openstackclient.i18n import _
+deprecated = True
+LOG_DEP = logging.getLogger('deprecated')
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)
@@ -66,6 +72,8 @@ class CreateSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot create" instead.'))
volume_client = self.app.client_manager.volume
volume_id = utils.find_resource(
volume_client.volumes, parsed_args.volume).id
@@ -83,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)
@@ -96,6 +104,8 @@ class DeleteSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot delete" instead.'))
volume_client = self.app.client_manager.volume
result = 0
@@ -118,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)
@@ -149,6 +159,8 @@ class ListSnapshot(command.Lister):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot list" instead.'))
def _format_volume_id(volume_id):
"""Return a volume name if available
@@ -199,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)
@@ -239,6 +251,8 @@ class SetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot set" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(volume_client.volume_snapshots,
parsed_args.snapshot)
@@ -280,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)
@@ -292,6 +306,8 @@ class ShowSnapshot(command.ShowOne):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot show" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
@@ -302,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)
@@ -322,6 +338,8 @@ class UnsetSnapshot(command.Command):
return parser
def take_action(self, parsed_args):
+ LOG_DEP.warning(_('This command has been deprecated. '
+ 'Please use "volume snapshot unset" instead.'))
volume_client = self.app.client_manager.volume
snapshot = utils.find_resource(
volume_client.volume_snapshots, parsed_args.snapshot)
diff --git a/openstackclient/volume/v2/volume.py b/openstackclient/volume/v2/volume.py
index e7405114..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):
@@ -484,10 +555,11 @@ class SetVolume(command.Command):
try:
if volume.status != 'available':
msg = (_("Volume is in %s state, it must be available "
- "before size can be extended"), volume.status)
+ "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
+ 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:
@@ -522,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:
@@ -542,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)
@@ -573,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_host.py b/openstackclient/volume/v2/volume_host.py
new file mode 100644
index 00000000..376e5024
--- /dev/null
+++ b/openstackclient/volume/v2/volume_host.py
@@ -0,0 +1,50 @@
+#
+# 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 host action implementations"""
+
+from osc_lib.command import command
+
+from openstackclient.i18n import _
+
+
+class SetVolumeHost(command.Command):
+ _description = _("Set volume host properties")
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeHost, self).get_parser(prog_name)
+ parser.add_argument(
+ "host",
+ metavar="<host-name>",
+ help=_("Name of volume host")
+ )
+ enabled_group = parser.add_mutually_exclusive_group()
+ enabled_group.add_argument(
+ "--disable",
+ action="store_true",
+ help=_("Freeze and disable the specified volume host.")
+ )
+ enabled_group.add_argument(
+ "--enable",
+ action="store_true",
+ help=_("Thaw and enable the specified volume host.")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ service_client = self.app.client_manager.volume
+ if parsed_args.enable:
+ service_client.services.thaw_host(parsed_args.host)
+ if parsed_args.disable:
+ service_client.services.freeze_host(parsed_args.host)
diff --git a/openstackclient/volume/v2/volume_snapshot.py b/openstackclient/volume/v2/volume_snapshot.py
new file mode 100644
index 00000000..43f30326
--- /dev/null
+++ b/openstackclient/volume/v2/volume_snapshot.py
@@ -0,0 +1,338 @@
+#
+# 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 snapshot action implementations"""
+
+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
+import six
+
+from openstackclient.i18n import _
+
+
+LOG = logging.getLogger(__name__)
+
+
+class CreateVolumeSnapshot(command.ShowOne):
+ """Create new volume snapshot"""
+
+ def get_parser(self, prog_name):
+ parser = super(CreateVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshot_name",
+ metavar="<snapshot-name>",
+ nargs="?",
+ help=_("Name of the new snapshot (default to None)")
+ )
+ parser.add_argument(
+ "--volume",
+ metavar="<volume>",
+ help=_("Volume to snapshot (name or ID) "
+ "(default is <snapshot-name>)")
+ )
+ parser.add_argument(
+ "--description",
+ metavar="<description>",
+ help=_("Description of the snapshot")
+ )
+ parser.add_argument(
+ "--force",
+ action="store_true",
+ default=False,
+ help=_("Create a snapshot attached to an instance. "
+ "Default is False")
+ )
+ parser.add_argument(
+ "--property",
+ metavar="<key=value>",
+ action=parseractions.KeyValueAction,
+ help=_("Set a property to this snapshot "
+ "(repeat option to set multiple properties)"),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ volume = parsed_args.volume
+ if not parsed_args.volume:
+ volume = parsed_args.snapshot_name
+ volume_id = utils.find_resource(
+ volume_client.volumes, volume).id
+ snapshot = volume_client.volume_snapshots.create(
+ volume_id,
+ force=parsed_args.force,
+ name=parsed_args.snapshot_name,
+ description=parsed_args.description,
+ metadata=parsed_args.property,
+ )
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class DeleteVolumeSnapshot(command.Command):
+ """Delete volume snapshot(s)"""
+
+ def get_parser(self, prog_name):
+ parser = super(DeleteVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshots",
+ metavar="<snapshot>",
+ nargs="+",
+ help=_("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 i in parsed_args.snapshots:
+ try:
+ snapshot_id = utils.find_resource(
+ volume_client.volume_snapshots, i).id
+ volume_client.volume_snapshots.delete(snapshot_id)
+ except Exception as e:
+ result += 1
+ LOG.error(_("Failed to delete snapshot with "
+ "name or ID '%(snapshot)s': %(e)s")
+ % {'snapshot': i, 'e': e})
+
+ if result > 0:
+ total = len(parsed_args.snapshots)
+ msg = (_("%(result)s of %(total)s snapshots failed "
+ "to delete.") % {'result': result, 'total': total})
+ raise exceptions.CommandError(msg)
+
+
+class ListVolumeSnapshot(command.Lister):
+ """List volume snapshots"""
+
+ def get_parser(self, prog_name):
+ parser = super(ListVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ '--all-projects',
+ action='store_true',
+ default=False,
+ help=_('Include all projects (admin only)'),
+ )
+ parser.add_argument(
+ '--long',
+ action='store_true',
+ default=False,
+ help=_('List additional fields in output'),
+ )
+ parser.add_argument(
+ '--marker',
+ metavar='<marker>',
+ help=_('The last snapshot ID of the previous page'),
+ )
+ parser.add_argument(
+ '--limit',
+ type=int,
+ action=parseractions.NonNegativeAction,
+ metavar='<limit>',
+ help=_('Maximum number of snapshots to display'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+
+ def _format_volume_id(volume_id):
+ """Return a volume name if available
+
+ :param volume_id: a volume ID
+ :rtype: either the volume ID or name
+ """
+
+ volume = volume_id
+ if volume_id in volume_cache.keys():
+ volume = volume_cache[volume_id].name
+ return volume
+
+ if parsed_args.long:
+ columns = ['ID', 'Name', 'Description', 'Status',
+ 'Size', 'Created At', 'Volume ID', 'Metadata']
+ column_headers = copy.deepcopy(columns)
+ column_headers[6] = 'Volume'
+ column_headers[7] = 'Properties'
+ else:
+ columns = ['ID', 'Name', 'Description', 'Status', 'Size']
+ column_headers = copy.deepcopy(columns)
+
+ # Cache the volume list
+ volume_cache = {}
+ try:
+ for s in self.app.client_manager.volume.volumes.list():
+ volume_cache[s.id] = s
+ except Exception:
+ # Just forget it if there's any trouble
+ pass
+
+ search_opts = {
+ 'all_tenants': parsed_args.all_projects,
+ }
+
+ data = self.app.client_manager.volume.volume_snapshots.list(
+ search_opts=search_opts,
+ marker=parsed_args.marker,
+ limit=parsed_args.limit,
+ )
+ return (column_headers,
+ (utils.get_item_properties(
+ s, columns,
+ formatters={'Metadata': utils.format_dict,
+ 'Volume ID': _format_volume_id},
+ ) for s in data))
+
+
+class SetVolumeSnapshot(command.Command):
+ """Set volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(SetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)')
+ )
+ parser.add_argument(
+ '--name',
+ metavar='<name>',
+ help=_('New snapshot name')
+ )
+ parser.add_argument(
+ '--description',
+ metavar='<description>',
+ help=_('New snapshot description')
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key=value>',
+ action=parseractions.KeyValueAction,
+ help=_('Property to add/change for this snapshot '
+ '(repeat option to set multiple properties)'),
+ )
+ parser.add_argument(
+ '--state',
+ metavar='<state>',
+ choices=['available', 'error', 'creating', 'deleting',
+ 'error-deleting'],
+ help=_('New snapshot state. ("available", "error", "creating", '
+ '"deleting", or "error_deleting") (admin only) '
+ '(This option simply changes the state of the snapshot '
+ 'in the database with no regard to actual status, '
+ 'exercise caution when using)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(volume_client.volume_snapshots,
+ parsed_args.snapshot)
+
+ result = 0
+ if parsed_args.property:
+ try:
+ volume_client.volume_snapshots.set_metadata(
+ snapshot.id, parsed_args.property)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot property: %s"), e)
+ result += 1
+
+ if parsed_args.state:
+ try:
+ volume_client.volume_snapshots.reset_state(
+ snapshot.id, parsed_args.state)
+ except Exception as e:
+ LOG.error(_("Failed to set snapshot state: %s"), e)
+ result += 1
+
+ kwargs = {}
+ if parsed_args.name:
+ kwargs['name'] = parsed_args.name
+ if parsed_args.description:
+ kwargs['description'] = parsed_args.description
+ if kwargs:
+ try:
+ volume_client.volume_snapshots.update(
+ snapshot.id, **kwargs)
+ except Exception as e:
+ LOG.error(_("Failed to update snapshot name "
+ "or description: %s"), e)
+ result += 1
+
+ if result > 0:
+ raise exceptions.CommandError(_("One or more of the "
+ "set operations failed"))
+
+
+class ShowVolumeSnapshot(command.ShowOne):
+ """Display volume snapshot details"""
+
+ def get_parser(self, prog_name):
+ parser = super(ShowVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ "snapshot",
+ metavar="<snapshot>",
+ help=_("Snapshot to display (name or ID)")
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+ snapshot._info.update(
+ {'properties': utils.format_dict(snapshot._info.pop('metadata'))}
+ )
+ return zip(*sorted(six.iteritems(snapshot._info)))
+
+
+class UnsetVolumeSnapshot(command.Command):
+ """Unset volume snapshot properties"""
+
+ def get_parser(self, prog_name):
+ parser = super(UnsetVolumeSnapshot, self).get_parser(prog_name)
+ parser.add_argument(
+ 'snapshot',
+ metavar='<snapshot>',
+ help=_('Snapshot to modify (name or ID)'),
+ )
+ parser.add_argument(
+ '--property',
+ metavar='<key>',
+ action='append',
+ default=[],
+ help=_('Property to remove from snapshot '
+ '(repeat option to remove multiple properties)'),
+ )
+ return parser
+
+ def take_action(self, parsed_args):
+ volume_client = self.app.client_manager.volume
+ snapshot = utils.find_resource(
+ volume_client.volume_snapshots, parsed_args.snapshot)
+
+ if parsed_args.property:
+ volume_client.volume_snapshots.delete_metadata(
+ snapshot.id,
+ parsed_args.property,
+ )
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)