summaryrefslogtreecommitdiff
path: root/openstackclient
diff options
context:
space:
mode:
Diffstat (limited to 'openstackclient')
-rw-r--r--openstackclient/image/client.py2
-rw-r--r--openstackclient/image/v1/image.py15
-rw-r--r--openstackclient/image/v2/image.py12
-rw-r--r--openstackclient/network/v2/network.py23
-rw-r--r--openstackclient/network/v2/network_service_provider.py41
-rw-r--r--openstackclient/network/v2/port.py28
-rw-r--r--openstackclient/network/v2/subnet.py14
-rw-r--r--openstackclient/shell.py4
-rw-r--r--openstackclient/tests/functional/network/v2/test_network_service_provider.py26
-rw-r--r--openstackclient/tests/unit/image/v1/test_image.py6
-rw-r--r--openstackclient/tests/unit/image/v2/test_image.py14
-rw-r--r--openstackclient/tests/unit/network/v2/fakes.py36
-rw-r--r--openstackclient/tests/unit/network/v2/test_network.py21
-rw-r--r--openstackclient/tests/unit/network/v2/test_network_service_provider.py71
-rw-r--r--openstackclient/tests/unit/network/v2/test_port.py84
-rw-r--r--openstackclient/tests/unit/network/v2/test_subnet.py8
-rw-r--r--openstackclient/tests/unit/volume/v1/fakes.py96
-rw-r--r--openstackclient/tests/unit/volume/v2/fakes.py78
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group.py132
-rw-r--r--openstackclient/tests/unit/volume/v2/test_consistency_group_snapshot.py351
-rw-r--r--openstackclient/volume/v2/consistency_group.py77
-rw-r--r--openstackclient/volume/v2/consistency_group_snapshot.py190
-rw-r--r--openstackclient/volume/v2/volume_type.py2
23 files changed, 1208 insertions, 123 deletions
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 d9ac8c08..0d54e339 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__)
@@ -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",
@@ -502,14 +505,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",
diff --git a/openstackclient/image/v2/image.py b/openstackclient/image/v2/image.py
index 657277c0..4031952b 100644
--- a/openstackclient/image/v2/image.py
+++ b/openstackclient/image/v2/image.py
@@ -32,6 +32,8 @@ 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__)
@@ -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",
@@ -650,8 +653,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(
diff --git a/openstackclient/network/v2/network.py b/openstackclient/network/v2/network.py
index 37775e6c..1c06c462 100644
--- a/openstackclient/network/v2/network.py
+++ b/openstackclient/network/v2/network.py
@@ -99,6 +99,13 @@ def _get_attrs(client_manager, parsed_args):
attrs['provider:physical_network'] = parsed_args.physical_network
if parsed_args.segmentation_id:
attrs['provider:segmentation_id'] = parsed_args.segmentation_id
+ if parsed_args.qos_policy is not None:
+ network_client = client_manager.network
+ _qos_policy = network_client.find_qos_policy(parsed_args.qos_policy,
+ ignore_missing=False)
+ attrs['qos_policy_id'] = _qos_policy.id
+ if 'no_qos_policy' in parsed_args and parsed_args.no_qos_policy:
+ attrs['qos_policy_id'] = None
# Update VLAN Transparency for networks
if parsed_args.transparent_vlan:
attrs['vlan_transparent'] = True
@@ -249,6 +256,11 @@ class CreateNetwork(common.NetworkAndComputeShowOne):
help=_("Do not use the network as the default external network "
"(default)")
)
+ parser.add_argument(
+ '--qos-policy',
+ metavar='<qos-policy>',
+ help=_("QoS policy to attach to this network (name or ID)")
+ )
_add_additional_network_options(parser)
return parser
@@ -572,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
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 64b30c7c..6cae87ee 100644
--- a/openstackclient/network/v2/port.py
+++ b/openstackclient/network/v2/port.py
@@ -148,6 +148,12 @@ def _get_attrs(client_manager, parsed_args):
).id
attrs['tenant_id'] = project_id
+ if parsed_args.disable_port_security:
+ attrs['port_security_enabled'] = False
+
+ if parsed_args.enable_port_security:
+ attrs['port_security_enabled'] = True
+
return attrs
@@ -304,6 +310,17 @@ class CreatePort(command.ShowOne):
action='store_true',
help=_("Associate no security groups with this port")
)
+ port_security = parser.add_mutually_exclusive_group()
+ port_security.add_argument(
+ '--enable-port-security',
+ action='store_true',
+ help=_("Enable port security for this port (Default)")
+ )
+ port_security.add_argument(
+ '--disable-port-security',
+ action='store_true',
+ help=_("Disable port security for this port")
+ )
return parser
@@ -526,6 +543,17 @@ class SetPort(command.Command):
action='store_true',
help=_("Clear existing security groups associated with this port")
)
+ port_security = parser.add_mutually_exclusive_group()
+ port_security.add_argument(
+ '--enable-port-security',
+ action='store_true',
+ help=_("Enable port security for this port")
+ )
+ port_security.add_argument(
+ '--disable-port-security',
+ action='store_true',
+ help=_("Disable port security for this port")
+ )
return parser
diff --git a/openstackclient/network/v2/subnet.py b/openstackclient/network/v2/subnet.py
index eda92948..5366dff6 100644
--- a/openstackclient/network/v2/subnet.py
+++ b/openstackclient/network/v2/subnet.py
@@ -84,6 +84,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>',
@@ -532,7 +541,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
diff --git a/openstackclient/shell.py b/openstackclient/shell.py
index be4b5283..e08eee61 100644
--- a/openstackclient/shell.py
+++ b/openstackclient/shell.py
@@ -148,10 +148,10 @@ class OpenStackShell(shell.OpenStackShell):
'auth_type': self._auth_type,
},
)
- except (IOError, OSError) as e:
+ except (IOError, OSError):
self.log.critical("Could not read clouds.yaml configuration file")
self.print_help_if_requested()
- raise e
+ raise
if not self.options.debug:
self.options.debug = None
diff --git a/openstackclient/tests/functional/network/v2/test_network_service_provider.py b/openstackclient/tests/functional/network/v2/test_network_service_provider.py
new file mode 100644
index 00000000..379de430
--- /dev/null
+++ b/openstackclient/tests/functional/network/v2/test_network_service_provider.py
@@ -0,0 +1,26 @@
+# Copyright (c) 2016, Intel Corporation.
+# All Rights Reserved.
+#
+# Licensed under the Apache License, Version 2.0 (the "License"); you may
+# not use this file except in compliance with the License. You may obtain
+# a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
+# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
+# License for the specific language governing permissions and limitations
+# under the License.
+
+from openstackclient.tests.functional import base
+
+
+class TestNetworkServiceProvider(base.TestCase):
+ """Functional tests for network service provider"""
+
+ SERVICE_TYPE = ['L3_ROUTER_NAT']
+
+ def test_network_service_provider_list(self):
+ raw_output = self.openstack('network service provider list')
+ self.assertIn(self.SERVICE_TYPE, raw_output)
diff --git a/openstackclient/tests/unit/image/v1/test_image.py b/openstackclient/tests/unit/image/v1/test_image.py
index aef74f04..036c8336 100644
--- a/openstackclient/tests/unit/image/v1/test_image.py
+++ b/openstackclient/tests/unit/image/v1/test_image.py
@@ -116,7 +116,7 @@ class TestImageCreate(TestImage):
self.images_mock.configure_mock(**mock_exception)
arglist = [
'--container-format', 'ovf',
- '--disk-format', 'fs',
+ '--disk-format', 'ami',
'--min-disk', '10',
'--min-ram', '4',
'--protected',
@@ -126,7 +126,7 @@ class TestImageCreate(TestImage):
]
verifylist = [
('container_format', 'ovf'),
- ('disk_format', 'fs'),
+ ('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
('protected', True),
@@ -147,7 +147,7 @@ class TestImageCreate(TestImage):
self.images_mock.create.assert_called_with(
name=self.new_image.name,
container_format='ovf',
- disk_format='fs',
+ disk_format='ami',
min_disk=10,
min_ram=4,
protected=True,
diff --git a/openstackclient/tests/unit/image/v2/test_image.py b/openstackclient/tests/unit/image/v2/test_image.py
index ebc9c3a7..2f2212e4 100644
--- a/openstackclient/tests/unit/image/v2/test_image.py
+++ b/openstackclient/tests/unit/image/v2/test_image.py
@@ -130,7 +130,7 @@ class TestImageCreate(TestImage):
self.images_mock.configure_mock(**mock_exception)
arglist = [
'--container-format', 'ovf',
- '--disk-format', 'fs',
+ '--disk-format', 'ami',
'--min-disk', '10',
'--min-ram', '4',
('--protected'
@@ -143,7 +143,7 @@ class TestImageCreate(TestImage):
]
verifylist = [
('container_format', 'ovf'),
- ('disk_format', 'fs'),
+ ('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
('protected', self.new_image.protected),
@@ -165,7 +165,7 @@ class TestImageCreate(TestImage):
self.images_mock.create.assert_called_with(
name=self.new_image.name,
container_format='ovf',
- disk_format='fs',
+ disk_format='ami',
min_disk=10,
min_ram=4,
owner=self.project.id,
@@ -193,7 +193,7 @@ class TestImageCreate(TestImage):
arglist = [
'--container-format', 'ovf',
- '--disk-format', 'fs',
+ '--disk-format', 'ami',
'--min-disk', '10',
'--min-ram', '4',
'--owner', 'unexist_owner',
@@ -203,7 +203,7 @@ class TestImageCreate(TestImage):
]
verifylist = [
('container_format', 'ovf'),
- ('disk_format', 'fs'),
+ ('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
('owner', 'unexist_owner'),
@@ -227,7 +227,7 @@ class TestImageCreate(TestImage):
arglist = [
'--container-format', 'ovf',
- '--disk-format', 'fs',
+ '--disk-format', 'ami',
'--min-disk', '10',
'--min-ram', '4',
'--protected',
@@ -237,7 +237,7 @@ class TestImageCreate(TestImage):
]
verifylist = [
('container_format', 'ovf'),
- ('disk_format', 'fs'),
+ ('disk_format', 'ami'),
('min_disk', 10),
('min_ram', 4),
('protected', True),
diff --git a/openstackclient/tests/unit/network/v2/fakes.py b/openstackclient/tests/unit/network/v2/fakes.py
index e96abc09..37f10147 100644
--- a/openstackclient/tests/unit/network/v2/fakes.py
+++ b/openstackclient/tests/unit/network/v2/fakes.py
@@ -299,6 +299,7 @@ class FakeNetwork(object):
'availability_zone_hints': [],
'is_default': False,
'port_security_enabled': True,
+ 'qos_policy_id': 'qos-policy-id-' + uuid.uuid4().hex,
}
# Overwrite default attributes.
@@ -1268,3 +1269,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_network.py b/openstackclient/tests/unit/network/v2/test_network.py
index c0de8640..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',
@@ -67,6 +69,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
'provider_network_type',
'provider_physical_network',
'provider_segmentation_id',
+ 'qos_policy_id',
'router:external',
'shared',
'status',
@@ -86,6 +89,7 @@ class TestCreateNetworkIdentityV3(TestNetwork):
_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,
@@ -102,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 = []
@@ -144,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,
@@ -160,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),
@@ -180,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,
})
@@ -235,6 +243,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
'provider_network_type',
'provider_physical_network',
'provider_segmentation_id',
+ 'qos_policy_id',
'router:external',
'shared',
'status',
@@ -254,6 +263,7 @@ class TestCreateNetworkIdentityV2(TestNetwork):
_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,
@@ -745,6 +755,8 @@ 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()
@@ -752,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)
@@ -770,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),
@@ -784,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)
@@ -801,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)
@@ -813,6 +829,7 @@ class TestSetNetwork(TestNetwork):
'--no-share',
'--internal',
'--disable-port-security',
+ '--no-qos-policy',
]
verifylist = [
('network', self._network.name),
@@ -820,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)
@@ -830,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)
@@ -866,6 +885,7 @@ class TestShowNetwork(TestNetwork):
'provider_network_type',
'provider_physical_network',
'provider_segmentation_id',
+ 'qos_policy_id',
'router:external',
'shared',
'status',
@@ -885,6 +905,7 @@ class TestShowNetwork(TestNetwork):
_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,
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 a7a4bf46..9312a897 100644
--- a/openstackclient/tests/unit/network/v2/test_port.py
+++ b/openstackclient/tests/unit/network/v2/test_port.py
@@ -320,6 +320,54 @@ class TestCreatePort(TestPort):
self.assertEqual(ref_columns, columns)
self.assertEqual(ref_data, data)
+ def test_create_port_security_enabled(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--enable-port-security',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('enable_port_security', True),
+ ('name', 'test-port'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'port_security_enabled': True,
+ 'name': 'test-port',
+ })
+
+ def test_create_port_security_disabled(self):
+ arglist = [
+ '--network', self._port.network_id,
+ '--disable-port-security',
+ 'test-port',
+ ]
+ verifylist = [
+ ('network', self._port.network_id,),
+ ('enable', True),
+ ('disable_port_security', True),
+ ('name', 'test-port'),
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.network.create_port.assert_called_once_with(**{
+ 'admin_state_up': True,
+ 'network_id': self._port.network_id,
+ 'port_security_enabled': False,
+ 'name': 'test-port',
+ })
+
class TestDeletePort(TestPort):
@@ -898,6 +946,42 @@ class TestSetPort(TestPort):
self.network.update_port.assert_called_once_with(_testport, **attrs)
self.assertIsNone(result)
+ def test_port_security_enabled(self):
+ arglist = [
+ '--enable-port-security',
+ self._port.id,
+ ]
+ verifylist = [
+ ('enable_port_security', True),
+ ('port', self._port.id,)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.network.update_port.assert_called_once_with(self._port, **{
+ 'port_security_enabled': True,
+ })
+
+ def test_port_security_disabled(self):
+ arglist = [
+ '--disable-port-security',
+ self._port.id,
+ ]
+ verifylist = [
+ ('disable_port_security', True),
+ ('port', self._port.id,)
+ ]
+
+ parsed_args = self.check_parser(self.cmd, arglist, verifylist)
+
+ self.cmd.take_action(parsed_args)
+
+ self.network.update_port.assert_called_once_with(self._port, **{
+ 'port_security_enabled': False,
+ })
+
class TestShowPort(TestPort):
diff --git a/openstackclient/tests/unit/network/v2/test_subnet.py b/openstackclient/tests/unit/network/v2/test_subnet.py
index 2d51aa4a..e7406575 100644
--- a/openstackclient/tests/unit/network/v2/test_subnet.py
+++ b/openstackclient/tests/unit/network/v2/test_subnet.py
@@ -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/volume/v1/fakes.py b/openstackclient/tests/unit/volume/v1/fakes.py
index 434e637a..78a8227e 100644
--- a/openstackclient/tests/unit/volume/v1/fakes.py
+++ b/openstackclient/tests/unit/volume/v1/fakes.py
@@ -23,102 +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,
-}
-
-type_id = "5520dc9e-6f9b-4378-a719-729911c0f407"
-type_name = "fake-lvmdriver-1"
-
-TYPE = {
- 'id': type_id,
- 'name': type_name
-}
-
-qos_id = '6f2be1de-997b-4230-b76c-a3633b59e8fb'
-qos_consumer = 'front-end'
-qos_default_consumer = 'both'
-qos_name = "fake-qos-specs"
-qos_specs = {
- 'foo': 'bar',
- 'iops': '9001'
-}
-qos_association = {
- 'association_type': 'volume_type',
- 'name': type_name,
- 'id': type_id
-}
-
-QOS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name
-}
-
-QOS_DEFAULT_CONSUMER = {
- 'id': qos_id,
- 'consumer': qos_default_consumer,
- 'name': qos_name
-}
-
-QOS_WITH_SPECS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name,
- 'specs': qos_specs
-}
-
-QOS_WITH_ASSOCIATIONS = {
- 'id': qos_id,
- 'consumer': qos_consumer,
- 'name': qos_name,
- 'specs': qos_specs,
- 'associations': [qos_association]
-}
-
-
class FakeTransfer(object):
"""Fake one or more Transfer."""
diff --git a/openstackclient/tests/unit/volume/v2/fakes.py b/openstackclient/tests/unit/volume/v2/fakes.py
index bd7e5973..41d8e794 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']
@@ -545,6 +547,82 @@ class FakeConsistencyGroup(object):
return 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_consistency_group.py b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
index 00e1b60e..e0056421 100644
--- a/openstackclient/tests/unit/volume/v2/test_consistency_group.py
+++ b/openstackclient/tests/unit/volume/v2/test_consistency_group.py
@@ -28,6 +28,138 @@ 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 TestConsistencyGroupList(TestConsistencyGroup):
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/volume/v2/consistency_group.py b/openstackclient/volume/v2/consistency_group.py
index 0754fdc7..9316f287 100644
--- a/openstackclient/volume/v2/consistency_group.py
+++ b/openstackclient/volume/v2/consistency_group.py
@@ -14,12 +14,89 @@
"""Volume v2 consistency group action implementations"""
+import logging
+
from osc_lib.command import command
from osc_lib import utils
+import six
from openstackclient.i18n import _
+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):
_description = _("List consistency groups.")
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/volume_type.py b/openstackclient/volume/v2/volume_type.py
index 0fcfaf7e..d0df677d 100644
--- a/openstackclient/volume/v2/volume_type.py
+++ b/openstackclient/volume/v2/volume_type.py
@@ -213,7 +213,7 @@ class SetVolumeType(command.Command):
)
parser.add_argument(
'--description',
- metavar='<name>',
+ metavar='<description>',
help=_('Set volume type description'),
)
parser.add_argument(