Merge "Define 'delete_volume' method as a static method"
diff --git a/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml b/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
new file mode 100644
index 0000000..9a4e6b1
--- /dev/null
+++ b/releasenotes/notes/add-snapshot-manage-client-as-library-a76ffdba9d8d01cb.yaml
@@ -0,0 +1,8 @@
+---
+features:
+ - |
+ Define v2 snapshot_manage_client client for the volume service as
+ library interfaces, allowing other projects to use this module as
+ stable libraries without maintenance changes.
+
+ * snapshot_manage_client(v2)
diff --git a/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml b/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
new file mode 100644
index 0000000..c0a06d1
--- /dev/null
+++ b/releasenotes/notes/deprecate-identity-feature-enabled.reseller-84800a8232fe217f.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - The default value for the ``reseller`` option in the
+ ``identity-feature-enabled`` section has been changed from ``False``
+ to ``True``.
+deprecations:
+ - The ``reseller`` option in the ``identity-feature-enabled`` section is now
+ deprecated.
diff --git a/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml b/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
new file mode 100644
index 0000000..c80f159
--- /dev/null
+++ b/releasenotes/notes/deprecate-volume_feature_enabled.volume_services-dbe024ea067d5ab2.yaml
@@ -0,0 +1,8 @@
+---
+upgrade:
+ - The default value for the ``volume_services`` option in the
+ ``volume_feature_enabled`` section has been changed from ``False``
+ to ``True``.
+deprecations:
+ - The ``volume_services`` option in the ``volume_feature_enabled`` section
+ is now deprecated.
diff --git a/tempest/api/compute/admin/test_volumes_negative.py b/tempest/api/compute/admin/test_volumes_negative.py
index b9dac6f..26b8742 100644
--- a/tempest/api/compute/admin/test_volumes_negative.py
+++ b/tempest/api/compute/admin/test_volumes_negative.py
@@ -49,6 +49,7 @@
self.server['id'], nonexistent_volume,
volumeId=volume['id'])
+ @test.related_bug('1629110', status_code=400)
@test.idempotent_id('7dcac15a-b107-46d3-a5f6-cb863f4e454a')
def test_update_attached_volume_with_nonexistent_volume_in_body(self):
volume = self.create_volume()
diff --git a/tempest/api/compute/security_groups/test_security_group_rules.py b/tempest/api/compute/security_groups/test_security_group_rules.py
index 38c294b..60caa19 100644
--- a/tempest/api/compute/security_groups/test_security_group_rules.py
+++ b/tempest/api/compute/security_groups/test_security_group_rules.py
@@ -43,7 +43,6 @@
group = {}
ip_range = {}
cls.expected = {
- 'id': None,
'parent_group_id': None,
'ip_protocol': cls.ip_protocol,
'from_port': from_port,
@@ -54,8 +53,6 @@
def _check_expected_response(self, actual_rule):
for key in self.expected:
- if key == 'id':
- continue
self.assertEqual(self.expected[key], actual_rule[key],
"Miss-matched key is %s" % key)
diff --git a/tempest/api/compute/servers/test_servers_negative.py b/tempest/api/compute/servers/test_servers_negative.py
index 1b1b339..f66bc72 100644
--- a/tempest/api/compute/servers/test_servers_negative.py
+++ b/tempest/api/compute/servers/test_servers_negative.py
@@ -217,6 +217,26 @@
name=server_name)
@test.attr(type=['negative'])
+ @test.related_bug('1651064', status_code=500)
+ @test.idempotent_id('12146ac1-d7df-4928-ad25-b1f99e5286cd')
+ def test_create_server_invalid_bdm_in_2nd_dict(self):
+ volume = self.create_volume()
+ bdm_1st = {"source_type": "image",
+ "delete_on_termination": True,
+ "boot_index": 0,
+ "uuid": self.image_ref,
+ "destination_type": "local"}
+ bdm_2nd = {"source_type": "volume",
+ "uuid": volume["id"],
+ "destination_type": "invalid"}
+ bdm = [bdm_1st, bdm_2nd]
+
+ self.assertRaises(lib_exc.BadRequest,
+ self.create_test_server,
+ image_id=self.image_ref,
+ block_device_mapping_v2=bdm)
+
+ @test.attr(type=['negative'])
@test.idempotent_id('4e72dc2d-44c5-4336-9667-f7972e95c402')
def test_create_with_invalid_network_uuid(self):
# Pass invalid network uuid while creating a server
diff --git a/tempest/api/image/v1/test_images_negative.py b/tempest/api/image/v1/test_images_negative.py
index d8f103a..3493cc2 100644
--- a/tempest/api/image/v1/test_images_negative.py
+++ b/tempest/api/image/v1/test_images_negative.py
@@ -15,6 +15,7 @@
from tempest.api.image import base
+from tempest.common.utils import data_utils
from tempest.lib import exceptions as lib_exc
from tempest import test
@@ -44,7 +45,7 @@
def test_delete_non_existent_image(self):
# Return an error while trying to delete a non-existent image
- non_existent_image_id = '11a22b9-12a9-5555-cc11-00ab112223fa'
+ non_existent_image_id = data_utils.rand_uuid()
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
non_existent_image_id)
@@ -58,9 +59,9 @@
@test.idempotent_id('950e5054-a3c7-4dee-ada5-e576f1087abd')
def test_delete_image_non_hex_string_id(self):
# Return an error while trying to delete an image with non hex id
- image_id = '11a22b9-120q-5555-cc11-00ab112223gj'
+ invalid_image_id = data_utils.rand_uuid()[:-1] + "j"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- image_id)
+ invalid_image_id)
@test.attr(type=['negative'])
@test.idempotent_id('4ed757cd-450c-44b1-9fd1-c819748c650d')
@@ -70,7 +71,8 @@
@test.attr(type=['negative'])
@test.idempotent_id('a4a448ab-3db2-4d2d-b9b2-6a1271241dfe')
- def test_delete_image_id_is_over_35_character_limit(self):
+ def test_delete_image_id_over_character_limit(self):
# Return an error while trying to delete image with id over limit
+ overlimit_image_id = data_utils.rand_uuid() + "1"
self.assertRaises(lib_exc.NotFound, self.client.delete_image,
- '11a22b9-12a9-5555-cc11-00ab112223fa-3fac')
+ overlimit_image_id)
diff --git a/tempest/api/volume/admin/v2/test_snapshot_manage.py b/tempest/api/volume/admin/v2/test_snapshot_manage.py
new file mode 100644
index 0000000..98f6d32
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_snapshot_manage.py
@@ -0,0 +1,74 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 testtools
+
+from tempest.api.volume import base
+from tempest.common import waiters
+from tempest import config
+from tempest import test
+
+CONF = config.CONF
+
+
+class SnapshotManageAdminV2Test(base.BaseVolumeAdminTest):
+ """Unmanage & manage snapshots
+
+ This feature provides the ability to import/export volume snapshot
+ from one Cinder to another and to import snapshots that have not been
+ managed by Cinder from a storage back end to Cinder
+ """
+
+ @test.idempotent_id('0132f42d-0147-4b45-8501-cc504bbf7810')
+ @testtools.skipIf(CONF.volume.storage_protocol == 'ceph',
+ 'Currently, Ceph does not support '
+ 'unmanage/manage snapshot')
+ def test_unmanage_manage_snapshot(self):
+ # Create a volume
+ volume = self.create_volume()
+
+ # Create a snapshot
+ snapshot = self.create_snapshot(volume_id=volume['id'])
+
+ # Unmanage the snapshot
+ # Unmanage snapshot function works almost the same as delete snapshot,
+ # but it does not delete the snapshot data
+ self.admin_snapshots_client.unmanage_snapshot(snapshot['id'])
+ self.admin_snapshots_client.wait_for_resource_deletion(snapshot['id'])
+
+ # Fetch snapshot ids
+ snapshot_list = [
+ snap['id'] for snap in
+ self.snapshots_client.list_snapshots()['snapshots']
+ ]
+
+ # Verify snapshot does not exist in snapshot list
+ self.assertNotIn(snapshot['id'], snapshot_list)
+
+ # Manage the snapshot
+ snapshot_ref = '_snapshot-%s' % snapshot['id']
+ new_snapshot = self.admin_snapshot_manage_client.manage_snapshot(
+ volume_id=volume['id'],
+ ref={'source-name': snapshot_ref})['snapshot']
+ self.addCleanup(self.delete_snapshot,
+ self.admin_snapshots_client, new_snapshot['id'])
+
+ # Wait for the snapshot to be available after manage operation
+ waiters.wait_for_snapshot_status(self.admin_snapshots_client,
+ new_snapshot['id'],
+ 'available')
+
+ # Verify the managed snapshot has the expected parent volume
+ self.assertEqual(new_snapshot['volume_id'], volume['id'])
diff --git a/tempest/api/volume/base.py b/tempest/api/volume/base.py
index c9060af..8a861a1 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -171,6 +171,11 @@
client.delete_volume(volume_id)
client.wait_for_resource_deletion(volume_id)
+ def delete_snapshot(self, client, snapshot_id):
+ """Delete snapshot by the given client"""
+ client.delete_snapshot(snapshot_id)
+ client.wait_for_resource_deletion(snapshot_id)
+
def attach_volume(self, server_id, volume_id):
"""Attachs a volume to a server"""
self.servers_client.attach_volume(
@@ -257,6 +262,8 @@
cls.admin_volume_types_client = cls.os_adm.volume_types_v2_client
cls.admin_volume_client = cls.os_adm.volumes_v2_client
cls.admin_hosts_client = cls.os_adm.volume_hosts_v2_client
+ cls.admin_snapshot_manage_client = \
+ cls.os_adm.snapshot_manage_v2_client
cls.admin_snapshots_client = cls.os_adm.snapshots_v2_client
cls.admin_backups_client = cls.os_adm.backups_v2_client
cls.admin_encryption_types_client = \
diff --git a/tempest/clients.py b/tempest/clients.py
index f99060a..8093a72 100644
--- a/tempest/clients.py
+++ b/tempest/clients.py
@@ -261,6 +261,7 @@
self.encryption_types_client = self.volume_v1.EncryptionTypesClient()
self.encryption_types_v2_client = \
self.volume_v2.EncryptionTypesClient()
+ self.snapshot_manage_v2_client = self.volume_v2.SnapshotManageClient()
self.snapshots_client = self.volume_v1.SnapshotsClient()
self.snapshots_v2_client = self.volume_v2.SnapshotsClient()
self.volumes_client = self.volume_v1.VolumesClient()
diff --git a/tempest/cmd/run.py b/tempest/cmd/run.py
index 5fa8b74..a3105e0 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -88,6 +88,7 @@
from cliff import command
from os_testr import regex_builder
from os_testr import subunit_trace
+import six
from testrepository.commands import run_argv
from tempest.cmd import init
@@ -109,6 +110,12 @@
return
else:
os.environ["TESTR_PDB"] = ""
+ # NOTE(dims): most of our .testr.conf try to test for PYTHON
+ # environment variable and fall back to "python", under python3
+ # if it does not exist. we should set it to the python3 executable
+ # to deal with this situation better for now.
+ if six.PY3 and 'PYTHON' not in os.environ:
+ os.environ['PYTHON'] = sys.executable
def _create_testrepository(self):
if not os.path.isdir('.testrepository'):
diff --git a/tempest/config.py b/tempest/config.py
index 0e45f2e..ea7811d 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -220,8 +220,11 @@
# TODO(rodrigods): Remove the reseller flag when Kilo and Liberty is end
# of life.
cfg.BoolOpt('reseller',
- default=False,
- help='Does the environment support reseller?'),
+ default=True,
+ help='Does the environment support reseller?',
+ deprecated_for_removal=True,
+ deprecated_reason="All supported version of OpenStack now "
+ "supports the 'reseller' feature"),
cfg.BoolOpt('security_compliance',
default=False,
help='Does the environment have the security compliance '
@@ -817,8 +820,11 @@
help="Is the v3 volume API enabled"),
# TODO(ynesenenko): Remove volume_services once liberty-eol happens.
cfg.BoolOpt('volume_services',
- default=False,
- help='Extract correct host info from host@backend')
+ default=True,
+ help='Extract correct host info from host@backend',
+ deprecated_for_removal=True,
+ deprecated_reason='This config switch was added for Liberty '
+ 'which is not supported anymore.')
]
diff --git a/tempest/lib/services/volume/v2/__init__.py b/tempest/lib/services/volume/v2/__init__.py
index 837b4f6..8acad0f 100644
--- a/tempest/lib/services/volume/v2/__init__.py
+++ b/tempest/lib/services/volume/v2/__init__.py
@@ -27,6 +27,8 @@
from tempest.lib.services.volume.v2.scheduler_stats_client import \
SchedulerStatsClient
from tempest.lib.services.volume.v2.services_client import ServicesClient
+from tempest.lib.services.volume.v2.snapshot_manage_client import \
+ SnapshotManageClient
from tempest.lib.services.volume.v2.snapshots_client import SnapshotsClient
from tempest.lib.services.volume.v2.types_client import TypesClient
from tempest.lib.services.volume.v2.volumes_client import VolumesClient
@@ -34,4 +36,5 @@
__all__ = ['AvailabilityZoneClient', 'BackupsClient', 'EncryptionTypesClient',
'ExtensionsClient', 'HostsClient', 'QosSpecsClient', 'QuotasClient',
'ServicesClient', 'SnapshotsClient', 'TypesClient', 'VolumesClient',
- 'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient']
+ 'LimitsClient', 'CapabilitiesClient', 'SchedulerStatsClient',
+ 'SnapshotManageClient']
diff --git a/tempest/lib/services/volume/v2/snapshot_manage_client.py b/tempest/lib/services/volume/v2/snapshot_manage_client.py
new file mode 100644
index 0000000..aecd30b
--- /dev/null
+++ b/tempest/lib/services/volume/v2/snapshot_manage_client.py
@@ -0,0 +1,33 @@
+# Copyright 2016 Red Hat, Inc.
+# 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 oslo_serialization import jsonutils as json
+
+from tempest.lib.common import rest_client
+
+
+class SnapshotManageClient(rest_client.RestClient):
+ """Snapshot manage V2 client."""
+
+ api_version = "v2"
+
+ def manage_snapshot(self, **kwargs):
+ """Manage a snapshot."""
+ post_body = json.dumps({'snapshot': kwargs})
+ url = 'os-snapshot-manage'
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ body = json.loads(body)
+ return rest_client.ResponseBody(resp, body)
diff --git a/tempest/lib/services/volume/v2/snapshots_client.py b/tempest/lib/services/volume/v2/snapshots_client.py
index 6f51b51..2bdf1b1 100644
--- a/tempest/lib/services/volume/v2/snapshots_client.py
+++ b/tempest/lib/services/volume/v2/snapshots_client.py
@@ -184,3 +184,11 @@
resp, body = self.post('snapshots/%s/action' % snapshot_id, post_body)
self.expected_success(202, resp.status)
return rest_client.ResponseBody(resp, body)
+
+ def unmanage_snapshot(self, snapshot_id):
+ """Unmanage a snapshot."""
+ post_body = json.dumps({'os-unmanage': {}})
+ url = 'snapshots/%s/action' % (snapshot_id)
+ resp, body = self.post(url, post_body)
+ self.expected_success(202, resp.status)
+ return rest_client.ResponseBody(resp, body)