Codebase list cinder-tempest-plugin / 9b434d1
Add tempest tests for Consistency Groups These tempest tests add coverage for all the API's related to consistency groups in Cinder. They were originally proposed for upstream tempest in https://review.openstack.org/#/c/252213/ but were not a good fit since they aren't supported by the reference driver. They have been modified to work as part of the in-tree tempest plugins for Cinder now. The tests are behind a new config option for tempest, which in turn is part of a new config group called 'cinder'. This was added to avoid any collisions with the 'volume-features-enabled' or 'volume' groups already in the upstream tempest tests. To enable them set the following in tempest.conf [cinder] consistency_group = True Then make sure to run tempest with the 'all-plugin' tox environment. Don't forget to update policy.json to allow for CG API's to be called.. Change-Id: I772ea13ca156e71620d722eee476f222a8653831 Co-Authored-By: Xing Yang <xing.yang@emc.com> Patrick East 9 years ago
5 changed file(s) with 534 addition(s) and 1 deletion(s). Raw diff Collapse all Expand all
0 # Copyright (C) 2015 EMC Corporation.
1 # Copyright (C) 2016 Pure Storage, Inc.
2 # All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 from oslo_log import log as logging
17 from tempest.api.volume import base
18 from tempest.common import waiters
19 from tempest import config
20 from tempest.lib.common.utils import data_utils
21 from tempest import test
22
23 from cinder.tests.tempest import cinder_clients
24
25 CONF = config.CONF
26 LOG = logging.getLogger(__name__)
27
28
29 class ConsistencyGroupsV2Test(base.BaseVolumeAdminTest):
30
31 @classmethod
32 def setup_clients(cls):
33 cls._api_version = 2
34 super(ConsistencyGroupsV2Test, cls).setup_clients()
35
36 manager = cinder_clients.Manager(cls.os_adm)
37 cls.consistencygroups_adm_client = manager.consistencygroups_adm_client
38
39 @classmethod
40 def skip_checks(cls):
41 super(ConsistencyGroupsV2Test, cls).skip_checks()
42 if not CONF.cinder.consistency_group:
43 raise cls.skipException("Cinder consistency group "
44 "feature disabled")
45
46 def _delete_consistencygroup(self, cg_id):
47 self.consistencygroups_adm_client.delete_consistencygroup(cg_id)
48 vols = self.admin_volume_client.list_volumes(detail=True)['volumes']
49 for vol in vols:
50 if vol['consistencygroup_id'] == cg_id:
51 self.admin_volume_client.wait_for_resource_deletion(vol['id'])
52 self.consistencygroups_adm_client.wait_for_consistencygroup_deletion(
53 cg_id)
54
55 def _delete_cgsnapshot(self, cgsnapshot_id, cg_id):
56 self.consistencygroups_adm_client.delete_cgsnapshot(cgsnapshot_id)
57 vols = self.admin_volume_client.list_volumes(detail=True)['volumes']
58 snapshots = self.admin_snapshots_client.list_snapshots(
59 detail=True)['snapshots']
60 for vol in vols:
61 for snap in snapshots:
62 if (vol['consistencygroup_id'] == cg_id and
63 vol['id'] == snap['volume_id']):
64 self.snapshots_client.wait_for_resource_deletion(
65 snap['id'])
66 self.consistencygroups_adm_client.wait_for_cgsnapshot_deletion(
67 cgsnapshot_id)
68
69 @test.idempotent_id('3fe776ba-ec1f-4e6c-8d78-4b14c3a7fc44')
70 def test_consistencygroup_create_delete(self):
71 # Create volume type
72 name = data_utils.rand_name("volume-type")
73 volume_type = self.admin_volume_types_client.create_volume_type(
74 name=name)['volume_type']
75
76 # Create CG
77 cg_name = data_utils.rand_name('CG')
78 create_consistencygroup = (
79 self.consistencygroups_adm_client.create_consistencygroup)
80 cg = create_consistencygroup(volume_type['id'],
81 name=cg_name)['consistencygroup']
82 vol_name = data_utils.rand_name("volume")
83 self.name_field = self.special_fields['name_field']
84 params = {self.name_field: vol_name,
85 'volume_type': volume_type['id'],
86 'consistencygroup_id': cg['id']}
87
88 # Create volume
89 volume = self.admin_volume_client.create_volume(**params)['volume']
90
91 waiters.wait_for_volume_status(self.admin_volume_client,
92 volume['id'], 'available')
93 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
94 cg['id'], 'available')
95 self.assertEqual(cg_name, cg['name'])
96
97 # Get a given CG
98 cg = self.consistencygroups_adm_client.show_consistencygroup(
99 cg['id'])['consistencygroup']
100 self.assertEqual(cg_name, cg['name'])
101
102 # Get all CGs with detail
103 cgs = self.consistencygroups_adm_client.list_consistencygroups(
104 detail=True)['consistencygroups']
105 self.assertIn((cg['name'], cg['id']),
106 [(m['name'], m['id']) for m in cgs])
107
108 # Clean up
109 self._delete_consistencygroup(cg['id'])
110 self.admin_volume_types_client.delete_volume_type(volume_type['id'])
111
112 @test.idempotent_id('2134dd52-f333-4456-bb05-6cb0f009a44f')
113 def test_consistencygroup_cgsnapshot_create_delete(self):
114 # Create volume type
115 name = data_utils.rand_name("volume-type")
116 volume_type = self.admin_volume_types_client.create_volume_type(
117 name=name)['volume_type']
118
119 # Create CG
120 cg_name = data_utils.rand_name('CG')
121 create_consistencygroup = (
122 self.consistencygroups_adm_client.create_consistencygroup)
123 cg = create_consistencygroup(volume_type['id'],
124 name=cg_name)['consistencygroup']
125 vol_name = data_utils.rand_name("volume")
126 self.name_field = self.special_fields['name_field']
127 params = {self.name_field: vol_name,
128 'volume_type': volume_type['id'],
129 'consistencygroup_id': cg['id']}
130
131 # Create volume
132 volume = self.admin_volume_client.create_volume(**params)['volume']
133 waiters.wait_for_volume_status(self.admin_volume_client,
134 volume['id'], 'available')
135 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
136 cg['id'], 'available')
137 self.assertEqual(cg_name, cg['name'])
138
139 # Create cgsnapshot
140 cgsnapshot_name = data_utils.rand_name('cgsnapshot')
141 create_cgsnapshot = (
142 self.consistencygroups_adm_client.create_cgsnapshot)
143 cgsnapshot = create_cgsnapshot(cg['id'],
144 name=cgsnapshot_name)['cgsnapshot']
145 snapshots = self.admin_snapshots_client.list_snapshots(
146 detail=True)['snapshots']
147 for snap in snapshots:
148 if volume['id'] == snap['volume_id']:
149 waiters.wait_for_snapshot_status(self.admin_snapshots_client,
150 snap['id'], 'available')
151 self.consistencygroups_adm_client.wait_for_cgsnapshot_status(
152 cgsnapshot['id'], 'available')
153 self.assertEqual(cgsnapshot_name, cgsnapshot['name'])
154
155 # Get a given CG snapshot
156 cgsnapshot = self.consistencygroups_adm_client.show_cgsnapshot(
157 cgsnapshot['id'])['cgsnapshot']
158 self.assertEqual(cgsnapshot_name, cgsnapshot['name'])
159
160 # Get all CG snapshots with detail
161 cgsnapshots = self.consistencygroups_adm_client.list_cgsnapshots(
162 detail=True)['cgsnapshots']
163 self.assertIn((cgsnapshot['name'], cgsnapshot['id']),
164 [(m['name'], m['id']) for m in cgsnapshots])
165
166 # Clean up
167 self._delete_cgsnapshot(cgsnapshot['id'], cg['id'])
168 self._delete_consistencygroup(cg['id'])
169 self.admin_volume_types_client.delete_volume_type(volume_type['id'])
170
171 @test.idempotent_id('3a6a5525-25ca-4a6c-aac4-cac6fa8f5b43')
172 def test_create_consistencygroup_from_cgsnapshot(self):
173 # Create volume type
174 name = data_utils.rand_name("volume-type")
175 volume_type = self.admin_volume_types_client.create_volume_type(
176 name=name)['volume_type']
177
178 # Create CG
179 cg_name = data_utils.rand_name('CG')
180 create_consistencygroup = (
181 self.consistencygroups_adm_client.create_consistencygroup)
182 cg = create_consistencygroup(volume_type['id'],
183 name=cg_name)['consistencygroup']
184 vol_name = data_utils.rand_name("volume")
185 self.name_field = self.special_fields['name_field']
186 params = {self.name_field: vol_name,
187 'volume_type': volume_type['id'],
188 'consistencygroup_id': cg['id']}
189
190 # Create volume
191 volume = self.admin_volume_client.create_volume(**params)['volume']
192 waiters.wait_for_volume_status(self.admin_volume_client,
193 volume['id'], 'available')
194 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
195 cg['id'], 'available')
196 self.assertEqual(cg_name, cg['name'])
197
198 # Create cgsnapshot
199 cgsnapshot_name = data_utils.rand_name('cgsnapshot')
200 create_cgsnapshot = (
201 self.consistencygroups_adm_client.create_cgsnapshot)
202 cgsnapshot = create_cgsnapshot(cg['id'],
203 name=cgsnapshot_name)['cgsnapshot']
204 snapshots = self.snapshots_client.list_snapshots(
205 detail=True)['snapshots']
206 for snap in snapshots:
207 if volume['id'] == snap['volume_id']:
208 waiters.wait_for_snapshot_status(self.admin_snapshots_client,
209 snap['id'], 'available')
210 self.consistencygroups_adm_client.wait_for_cgsnapshot_status(
211 cgsnapshot['id'], 'available')
212 self.assertEqual(cgsnapshot_name, cgsnapshot['name'])
213
214 # Create CG from CG snapshot
215 cg_name2 = data_utils.rand_name('CG_from_snap')
216 create_consistencygroup2 = (
217 self.consistencygroups_adm_client.create_consistencygroup_from_src)
218 cg2 = create_consistencygroup2(cgsnapshot_id=cgsnapshot['id'],
219 name=cg_name2)['consistencygroup']
220 vols = self.admin_volume_client.list_volumes(
221 detail=True)['volumes']
222 for vol in vols:
223 if vol['consistencygroup_id'] == cg2['id']:
224 waiters.wait_for_volume_status(self.admin_volume_client,
225 vol['id'], 'available')
226 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
227 cg2['id'], 'available')
228 self.assertEqual(cg_name2, cg2['name'])
229
230 # Clean up
231 self._delete_consistencygroup(cg2['id'])
232 self._delete_cgsnapshot(cgsnapshot['id'], cg['id'])
233 self._delete_consistencygroup(cg['id'])
234 self.admin_volume_types_client.delete_volume_type(volume_type['id'])
235
236 @test.idempotent_id('556121ae-de9c-4342-9897-e54260447a19')
237 def test_create_consistencygroup_from_consistencygroup(self):
238 # Create volume type
239 name = data_utils.rand_name("volume-type")
240 volume_type = self.admin_volume_types_client.create_volume_type(
241 name=name)['volume_type']
242
243 # Create CG
244 cg_name = data_utils.rand_name('CG')
245 create_consistencygroup = (
246 self.consistencygroups_adm_client.create_consistencygroup)
247 cg = create_consistencygroup(volume_type['id'],
248 name=cg_name)['consistencygroup']
249 vol_name = data_utils.rand_name("volume")
250 self.name_field = self.special_fields['name_field']
251 params = {self.name_field: vol_name,
252 'volume_type': volume_type['id'],
253 'consistencygroup_id': cg['id']}
254
255 # Create volume
256 volume = self.admin_volume_client.create_volume(**params)['volume']
257 waiters.wait_for_volume_status(self.admin_volume_client,
258 volume['id'], 'available')
259 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
260 cg['id'], 'available')
261 self.assertEqual(cg_name, cg['name'])
262
263 # Create CG from CG
264 cg_name2 = data_utils.rand_name('CG_from_cg')
265 create_consistencygroup2 = (
266 self.consistencygroups_adm_client.create_consistencygroup_from_src)
267 cg2 = create_consistencygroup2(source_cgid=cg['id'],
268 name=cg_name2)['consistencygroup']
269 vols = self.admin_volume_client.list_volumes(
270 detail=True)['volumes']
271 for vol in vols:
272 if vol['consistencygroup_id'] == cg2['id']:
273 waiters.wait_for_volume_status(self.admin_volume_client,
274 vol['id'], 'available')
275 self.consistencygroups_adm_client.wait_for_consistencygroup_status(
276 cg2['id'], 'available')
277 self.assertEqual(cg_name2, cg2['name'])
278
279 # Clean up
280 self._delete_consistencygroup(cg2['id'])
281 self._delete_consistencygroup(cg['id'])
282 self.admin_volume_types_client.delete_volume_type(volume_type['id'])
0 # Copyright (c) 2016 Pure Storage, Inc.
1 # All Rights Reserved.
2 #
3 # Licensed under the Apache License, Version 2.0 (the "License"); you may
4 # not use this file except in compliance with the License. You may obtain
5 # a copy of the License at
6 #
7 # http://www.apache.org/licenses/LICENSE-2.0
8 #
9 # Unless required by applicable law or agreed to in writing, software
10 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
11 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
12 # License for the specific language governing permissions and limitations
13 # under the License.
14
15 from tempest import config
16
17 from cinder.tests.tempest.services import consistencygroups_client
18
19 CONF = config.CONF
20
21
22 class Manager(object):
23 def __init__(self, base_manager):
24 params = {
25 'service': CONF.volume.catalog_type,
26 'region': CONF.volume.region or CONF.identity.region,
27 'endpoint_type': CONF.volume.endpoint_type,
28 'build_interval': CONF.volume.build_interval,
29 'build_timeout': CONF.volume.build_timeout
30 }
31 params.update(base_manager.default_params)
32 auth_provider = base_manager.auth_provider
33
34 self.consistencygroups_adm_client = (
35 consistencygroups_client.ConsistencyGroupsClient(auth_provider,
36 **params))
2323 default=True,
2424 help="Whether or not cinder is expected to be available"),
2525 ]
26
27 # Use a new config group specific to the cinder in-tree tests to avoid
28 # any naming confusion with the upstream tempest config options.
29 cinder_group = cfg.OptGroup(name='cinder',
30 title='Cinder Tempest Config Options')
31
32 CinderGroup = [
33 cfg.BoolOpt('consistency_group',
34 default=False,
35 help='Enable to run Cinder volume consistency group tests'),
36 ]
1616 import os
1717
1818 from cinder.tests.tempest import config as project_config
19
1920 from tempest import config
2021 from tempest.test_discover import plugins
2122
3233 config.register_opt_group(
3334 conf, project_config.service_available_group,
3435 project_config.ServiceAvailableGroup)
36 config.register_opt_group(
37 conf, project_config.cinder_group,
38 project_config.CinderGroup
39 )
3540
3641 def get_opt_lists(self):
37 pass
42 return [
43 (project_config.service_available_group.name,
44 project_config.ServiceAvailableGroup),
45 (project_config.cinder_group.name,
46 project_config.CinderGroup),
47 ]
0 # Copyright (C) 2015 EMC Corporation.
1 # Copyright (C) 2016 Pure Storage, Inc.
2 # All Rights Reserved.
3 #
4 # Licensed under the Apache License, Version 2.0 (the "License"); you may
5 # not use this file except in compliance with the License. You may obtain
6 # a copy of the License at
7 #
8 # http://www.apache.org/licenses/LICENSE-2.0
9 #
10 # Unless required by applicable law or agreed to in writing, software
11 # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 # License for the specific language governing permissions and limitations
14 # under the License.
15
16 import time
17
18 from oslo_serialization import jsonutils as json
19 from tempest import exceptions
20 from tempest.lib.common import rest_client
21 from tempest.lib import exceptions as lib_exc
22
23
24 class ConsistencyGroupsClient(rest_client.RestClient):
25 """Client class to send CRUD Volume ConsistencyGroup API requests"""
26
27 def __init__(self, auth_provider, service, region, **kwargs):
28 super(ConsistencyGroupsClient, self).__init__(
29 auth_provider, service, region, **kwargs)
30
31 def create_consistencygroup(self, volume_types, **kwargs):
32 """Creates a consistency group."""
33 post_body = {'volume_types': volume_types}
34 if kwargs.get('availability_zone'):
35 post_body['availability_zone'] = kwargs.get('availability_zone')
36 if kwargs.get('name'):
37 post_body['name'] = kwargs.get('name')
38 if kwargs.get('description'):
39 post_body['description'] = kwargs.get('description')
40 post_body = json.dumps({'consistencygroup': post_body})
41 resp, body = self.post('consistencygroups', post_body)
42 body = json.loads(body)
43 self.expected_success(202, resp.status)
44 return rest_client.ResponseBody(resp, body)
45
46 def create_consistencygroup_from_src(self, **kwargs):
47 """Creates a consistency group from source."""
48 post_body = {}
49 if kwargs.get('cgsnapshot_id'):
50 post_body['cgsnapshot_id'] = kwargs.get('cgsnapshot_id')
51 if kwargs.get('source_cgid'):
52 post_body['source_cgid'] = kwargs.get('source_cgid')
53 if kwargs.get('name'):
54 post_body['name'] = kwargs.get('name')
55 if kwargs.get('description'):
56 post_body['description'] = kwargs.get('description')
57 post_body = json.dumps({'consistencygroup-from-src': post_body})
58 resp, body = self.post('consistencygroups/create_from_src', post_body)
59 body = json.loads(body)
60 self.expected_success(202, resp.status)
61 return rest_client.ResponseBody(resp, body)
62
63 def delete_consistencygroup(self, cg_id):
64 """Delete a consistency group."""
65 post_body = {'force': True}
66 post_body = json.dumps({'consistencygroup': post_body})
67 resp, body = self.post('consistencygroups/%s/delete' % cg_id,
68 post_body)
69 self.expected_success(202, resp.status)
70 return rest_client.ResponseBody(resp, body)
71
72 def show_consistencygroup(self, cg_id):
73 """Returns the details of a single consistency group."""
74 url = "consistencygroups/%s" % str(cg_id)
75 resp, body = self.get(url)
76 body = json.loads(body)
77 self.expected_success(200, resp.status)
78 return rest_client.ResponseBody(resp, body)
79
80 def list_consistencygroups(self, detail=False):
81 """Information for all the tenant's consistency groups."""
82 url = "consistencygroups"
83 if detail:
84 url += "/detail"
85 resp, body = self.get(url)
86 body = json.loads(body)
87 self.expected_success(200, resp.status)
88 return rest_client.ResponseBody(resp, body)
89
90 def create_cgsnapshot(self, consistencygroup_id, **kwargs):
91 """Creates a consistency group snapshot."""
92 post_body = {'consistencygroup_id': consistencygroup_id}
93 if kwargs.get('name'):
94 post_body['name'] = kwargs.get('name')
95 if kwargs.get('description'):
96 post_body['description'] = kwargs.get('description')
97 post_body = json.dumps({'cgsnapshot': post_body})
98 resp, body = self.post('cgsnapshots', post_body)
99 body = json.loads(body)
100 self.expected_success(202, resp.status)
101 return rest_client.ResponseBody(resp, body)
102
103 def delete_cgsnapshot(self, cgsnapshot_id):
104 """Delete a consistency group snapshot."""
105 resp, body = self.delete('cgsnapshots/%s' % (str(cgsnapshot_id)))
106 self.expected_success(202, resp.status)
107 return rest_client.ResponseBody(resp, body)
108
109 def show_cgsnapshot(self, cgsnapshot_id):
110 """Returns the details of a single consistency group snapshot."""
111 url = "cgsnapshots/%s" % str(cgsnapshot_id)
112 resp, body = self.get(url)
113 body = json.loads(body)
114 self.expected_success(200, resp.status)
115 return rest_client.ResponseBody(resp, body)
116
117 def list_cgsnapshots(self, detail=False):
118 """Information for all the tenant's consistency group snapshotss."""
119 url = "cgsnapshots"
120 if detail:
121 url += "/detail"
122 resp, body = self.get(url)
123 body = json.loads(body)
124 self.expected_success(200, resp.status)
125 return rest_client.ResponseBody(resp, body)
126
127 def wait_for_consistencygroup_status(self, cg_id, status):
128 """Waits for a consistency group to reach a given status."""
129 body = self.show_consistencygroup(cg_id)['consistencygroup']
130 cg_status = body['status']
131 start = int(time.time())
132
133 while cg_status != status:
134 time.sleep(self.build_interval)
135 body = self.show_consistencygroup(cg_id)['consistencygroup']
136 cg_status = body['status']
137 if cg_status == 'error':
138 raise exceptions.ConsistencyGroupException(cg_id=cg_id)
139
140 if int(time.time()) - start >= self.build_timeout:
141 message = ('Consistency group %s failed to reach %s status '
142 '(current %s) within the required time (%s s).' %
143 (cg_id, status, cg_status,
144 self.build_timeout))
145 raise exceptions.TimeoutException(message)
146
147 def wait_for_consistencygroup_deletion(self, cg_id):
148 """Waits for consistency group deletion"""
149 start_time = int(time.time())
150 while True:
151 try:
152 self.show_consistencygroup(cg_id)
153 except lib_exc.NotFound:
154 return
155 if int(time.time()) - start_time >= self.build_timeout:
156 raise exceptions.TimeoutException
157 time.sleep(self.build_interval)
158
159 def wait_for_cgsnapshot_status(self, cgsnapshot_id, status):
160 """Waits for a consistency group snapshot to reach a given status."""
161 body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot']
162 cgsnapshot_status = body['status']
163 start = int(time.time())
164
165 while cgsnapshot_status != status:
166 time.sleep(self.build_interval)
167 body = self.show_cgsnapshot(cgsnapshot_id)['cgsnapshot']
168 cgsnapshot_status = body['status']
169 if cgsnapshot_status == 'error':
170 raise exceptions.ConsistencyGroupSnapshotException(
171 cgsnapshot_id=cgsnapshot_id)
172
173 if int(time.time()) - start >= self.build_timeout:
174 message = ('Consistency group snapshot %s failed to reach '
175 '%s status (current %s) within the required time '
176 '(%s s).' %
177 (cgsnapshot_id, status, cgsnapshot_status,
178 self.build_timeout))
179 raise exceptions.TimeoutException(message)
180
181 def wait_for_cgsnapshot_deletion(self, cgsnapshot_id):
182 """Waits for consistency group snapshot deletion"""
183 start_time = int(time.time())
184 while True:
185 try:
186 self.show_cgsnapshot(cgsnapshot_id)
187 except lib_exc.NotFound:
188 return
189 if int(time.time()) - start_time >= self.build_timeout:
190 raise exceptions.TimeoutException
191 time.sleep(self.build_interval)