Merge "Remove unnecessary name definitions"
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/source/conf.py b/releasenotes/source/conf.py
index 140263c..eec42cd 100644
--- a/releasenotes/source/conf.py
+++ b/releasenotes/source/conf.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# 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
diff --git a/tempest/api/image/v1/test_images.py b/tempest/api/image/v1/test_images.py
index 0caaa67..b22ceed 100644
--- a/tempest/api/image/v1/test_images.py
+++ b/tempest/api/image/v1/test_images.py
@@ -30,13 +30,23 @@
a_formats = ['ami', 'ari', 'aki']
container_format = CONF.image.container_formats[0]
- disk_format = CONF.image.disk_formats[0]
- if container_format in a_formats and container_format != disk_format:
- msg = ("The container format and the disk format don't match. "
- "Container format: %(container)s, Disk format: %(disk)s." %
- {'container': container_format, 'disk': disk_format})
- raise exceptions.InvalidConfiguration(msg)
+ # In v1, If container_format is one of ['ami', 'ari', 'aki'], then
+ # disk_format must be same with container_format.
+ # If they are of different item sequence in tempest.conf, such as:
+ # container_formats = ami,ari,aki,bare
+ # disk_formats = ari,ami,aki,vhd
+ # we can select one in disk_format list that is same with container_format.
+ if container_format in a_formats:
+ if container_format in CONF.image.disk_formats:
+ disk_format = container_format
+ else:
+ msg = ("The container format and the disk format don't match. "
+ "Container format: %(container)s, Disk format: %(disk)s." %
+ {'container': container_format, 'disk': disk_format})
+ raise exceptions.InvalidConfiguration(msg)
+ else:
+ disk_format = CONF.image.disk_formats[0]
return container_format, disk_format
diff --git a/tempest/api/orchestration/stacks/test_swift_resources.py b/tempest/api/orchestration/stacks/test_swift_resources.py
index c0f1c4b..3672526 100644
--- a/tempest/api/orchestration/stacks/test_swift_resources.py
+++ b/tempest/api/orchestration/stacks/test_swift_resources.py
@@ -1,4 +1,3 @@
-# -*- coding: utf-8 -*-
# Copyright (C) 2013 eNovance SAS <licensing@enovance.com>
#
# Licensed under the Apache License, Version 2.0 (the "License"); you may
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..6a3f9ee
--- /dev/null
+++ b/tempest/api/volume/admin/v2/test_snapshot_manage.py
@@ -0,0 +1,73 @@
+# 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.skipUnless(CONF.volume_feature_enabled.manage_snapshot,
+ "Manage snapshot tests are disabled")
+ 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 90dc7f4..a4ed7a5 100644
--- a/tempest/api/volume/base.py
+++ b/tempest/api/volume/base.py
@@ -165,14 +165,19 @@
# NOTE(afazekas): these create_* and clean_* could be defined
# only in a single location in the source, and could be more general.
- @classmethod
- def delete_volume(cls, client, volume_id):
+ @staticmethod
+ def delete_volume(client, volume_id):
"""Delete volume by the given client"""
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"""
+ """Attach a volume to a server"""
self.servers_client.attach_volume(
server_id, volumeId=volume_id,
device='/dev/%s' % CONF.compute.volume_device_name)
@@ -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/api/volume/test_volumes_backup.py b/tempest/api/volume/test_volumes_backup.py
index 70b3c58..6dcde08 100644
--- a/tempest/api/volume/test_volumes_backup.py
+++ b/tempest/api/volume/test_volumes_backup.py
@@ -89,14 +89,7 @@
volume['id'])
server = self.create_server(wait_until='ACTIVE')
# Attach volume to instance
- self.servers_client.attach_volume(server['id'],
- volumeId=volume['id'])
- waiters.wait_for_volume_status(self.volumes_client,
- volume['id'], 'in-use')
- self.addCleanup(waiters.wait_for_volume_status, self.volumes_client,
- volume['id'], 'available')
- self.addCleanup(self.servers_client.detach_volume, server['id'],
- volume['id'])
+ self.attach_volume(server['id'], volume['id'])
# Create backup using force flag
backup_name = data_utils.rand_name(
self.__class__.__name__ + '-Backup')
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 a3105e0..54b844a 100644
--- a/tempest/cmd/run.py
+++ b/tempest/cmd/run.py
@@ -21,7 +21,7 @@
* **--regex/-r**: This is a selection regex like what testr uses. It will run
any tests that match on re.match() with the regex
- * **--smoke**: Run all the tests tagged as smoke
+ * **--smoke/-s**: Run all the tests tagged as smoke
There are also the **--blacklist-file** and **--whitelist-file** options that
let you pass a filepath to tempest run with the file format being a line
@@ -52,7 +52,7 @@
There are several options to control how the tests are executed. By default
tempest will run in parallel with a worker for each CPU present on the machine.
If you want to adjust the number of workers use the **--concurrency** option
-and if you want to run tests serially use **--serial**
+and if you want to run tests serially use **--serial/-t**
Running with Workspaces
-----------------------
@@ -198,7 +198,7 @@
help='Configuration file to run tempest with')
# test selection args
regex = parser.add_mutually_exclusive_group()
- regex.add_argument('--smoke', action='store_true',
+ regex.add_argument('--smoke', '-s', action='store_true',
help="Run the smoke tests only")
regex.add_argument('--regex', '-r', default='',
help='A normal testr selection regex used to '
@@ -225,7 +225,7 @@
action='store_true',
help='Run tests in parallel (this is the'
' default)')
- parallel.add_argument('--serial', dest='parallel',
+ parallel.add_argument('--serial', '-t', dest='parallel',
action='store_false',
help='Run tests serially')
# output args
diff --git a/tempest/config.py b/tempest/config.py
index ea7811d..4162718 100644
--- a/tempest/config.py
+++ b/tempest/config.py
@@ -804,6 +804,9 @@
cfg.BoolOpt('clone',
default=True,
help='Runs Cinder volume clone test'),
+ cfg.BoolOpt('manage_snapshot',
+ default=False,
+ help='Runs Cinder manage snapshot tests'),
cfg.ListOpt('api_extensions',
default=['all'],
help='A list of enabled volume extensions with a special '
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)
diff --git a/tempest/tests/lib/test_tempest_lib.py b/tempest/tests/lib/test_tempest_lib.py
index d70e53d..4d9f099 100644
--- a/tempest/tests/lib/test_tempest_lib.py
+++ b/tempest/tests/lib/test_tempest_lib.py
@@ -1,5 +1,3 @@
-# -*- coding: utf-8 -*-
-
# 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