Coverage for manila/tests/volume/test_cinder.py: 100%
206 statements
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
« prev ^ index » next coverage.py v7.11.0, created at 2026-02-18 22:19 +0000
1# Copyright 2014 Mirantis, Inc.
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.
15from unittest import mock
17from cinderclient import exceptions as cinder_exception
18import ddt
20from manila.common import constants as const
21from manila import context
22from manila import exception
23from manila import test
24from manila.tests import fake_volume
25from manila.tests import utils as test_utils
26from manila.volume import cinder
29class FakeCinderClient(object):
30 class Volumes(object):
31 def get(self, volume_id):
32 return {'id': volume_id}
34 def list(self, detailed, search_opts={}):
35 return [{'id': 'id1'}, {'id': 'id2'}]
37 def create(self, *args, **kwargs):
38 return {'id': 'created_id'}
40 def __getattr__(self, item):
41 return None
43 def __init__(self):
44 self.volumes = self.Volumes()
45 self.volume_snapshots = self.volumes
48def get_fake_auth_obj():
49 return type('FakeAuthObj', (object, ), {'get_client': mock.Mock()})
52class CinderclientTestCase(test.TestCase):
54 @mock.patch('manila.volume.cinder.AUTH_OBJ', None)
55 def test_no_auth_obj(self):
56 mock_client_loader = self.mock_object(
57 cinder.client_auth, 'AuthClientLoader')
58 fake_context = 'fake_context'
59 data = {
60 'cinder': {
61 'http_retries': 3,
62 'endpoint_type': 'internalURL',
63 'region_name': 'foo_region_name',
64 }
65 }
67 with test_utils.create_temp_config_with_opts(data):
68 cinder.cinderclient(fake_context)
70 mock_client_loader.assert_called_once_with(
71 client_class=cinder.cinder_client.Client,
72 cfg_group=cinder.CINDER_GROUP
73 )
74 mock_client_loader.return_value.get_client.assert_called_once_with(
75 fake_context,
76 retries=data['cinder']['http_retries'],
77 endpoint_type=data['cinder']['endpoint_type'],
78 region_name=data['cinder']['region_name'],
79 )
81 @mock.patch('manila.volume.cinder.AUTH_OBJ', get_fake_auth_obj())
82 def test_with_auth_obj(self):
83 fake_context = 'fake_context'
84 data = {
85 'cinder': {
86 'http_retries': 3,
87 'endpoint_type': 'internalURL',
88 'region_name': 'foo_region_name',
89 }
90 }
92 with test_utils.create_temp_config_with_opts(data):
93 cinder.cinderclient(fake_context)
95 cinder.AUTH_OBJ.get_client.assert_called_once_with(
96 fake_context,
97 retries=data['cinder']['http_retries'],
98 endpoint_type=data['cinder']['endpoint_type'],
99 region_name=data['cinder']['region_name'],
100 )
103@ddt.ddt
104class CinderApiTestCase(test.TestCase):
105 def setUp(self):
106 super(CinderApiTestCase, self).setUp()
108 self.api = cinder.API()
109 self.cinderclient = FakeCinderClient()
110 self.ctx = context.get_admin_context()
111 self.mock_object(cinder, 'cinderclient',
112 mock.Mock(return_value=self.cinderclient))
113 self.mock_object(cinder, '_untranslate_volume_summary_view',
114 lambda ctx, vol: vol)
115 self.mock_object(cinder, '_untranslate_snapshot_summary_view',
116 lambda ctx, snap: snap)
118 def test_get(self):
119 volume_id = 'volume_id1'
120 result = self.api.get(self.ctx, volume_id)
121 self.assertEqual(volume_id, result['id'])
123 @ddt.data(
124 {'cinder_e': cinder_exception.NotFound(404),
125 'manila_e': exception.VolumeNotFound},
126 {'cinder_e': cinder_exception.BadRequest(400),
127 'manila_e': exception.InvalidInput},
128 )
129 @ddt.unpack
130 def test_get_failed(self, cinder_e, manila_e):
131 cinder.cinderclient.side_effect = cinder_e
132 volume_id = 'volume_id'
133 self.assertRaises(manila_e, self.api.get, self.ctx, volume_id)
135 def test_create(self):
136 result = self.api.create(self.ctx, 1, '', '')
137 self.assertEqual('created_id', result['id'])
139 def test_create_failed(self):
140 cinder.cinderclient.side_effect = cinder_exception.BadRequest(400)
141 self.assertRaises(exception.InvalidInput,
142 self.api.create, self.ctx, 1, '', '')
144 def test_create_not_found_error(self):
145 cinder.cinderclient.side_effect = cinder_exception.NotFound(404)
146 self.assertRaises(exception.NotFound,
147 self.api.create, self.ctx, 1, '', '')
149 def test_create_failed_exception(self):
150 cinder.cinderclient.side_effect = Exception("error msg")
151 self.assertRaises(exception.ManilaException,
152 self.api.create, self.ctx, 1, '', '')
154 def test_get_all(self):
155 cinder._untranslate_volume_summary_view.return_value = ['id1', 'id2']
156 self.assertEqual([{'id': 'id1'}, {'id': 'id2'}],
157 self.api.get_all(self.ctx))
159 def test_check_attach_volume_status_error(self):
160 volume = {'status': 'error'}
161 self.assertRaises(exception.InvalidVolume,
162 self.api.check_attach, self.ctx, volume)
164 def test_check_attach_volume_already_attached(self):
165 volume = {'status': 'available'}
166 volume['attach_status'] = "attached"
167 self.assertRaises(exception.InvalidVolume,
168 self.api.check_attach, self.ctx, volume)
170 def test_check_attach_availability_zone_differs(self):
171 volume = {'status': 'available'}
172 volume['attach_status'] = "detached"
173 instance = {'availability_zone': 'zone1'}
174 volume['availability_zone'] = 'zone2'
175 cinder.CONF.set_override('cross_az_attach', False, 'cinder')
176 self.assertRaises(exception.InvalidVolume,
177 self.api.check_attach, self.ctx, volume, instance)
178 volume['availability_zone'] = 'zone1'
179 self.assertIsNone(self.api.check_attach(self.ctx, volume, instance))
180 cinder.CONF.reset()
182 def test_check_attach(self):
183 volume = {'status': 'available'}
184 volume['attach_status'] = "detached"
185 volume['availability_zone'] = 'zone1'
186 instance = {'availability_zone': 'zone1'}
187 cinder.CONF.set_override('cross_az_attach', False, 'cinder')
188 self.assertIsNone(self.api.check_attach(self.ctx, volume, instance))
189 cinder.CONF.reset()
191 def test_check_detach(self):
192 volume = {'status': 'available'}
193 self.assertRaises(exception.InvalidVolume,
194 self.api.check_detach, self.ctx, volume)
195 volume['status'] = 'non-available'
196 self.assertIsNone(self.api.check_detach(self.ctx, volume))
198 def test_update(self):
199 fake_volume = {'fake': 'fake'}
200 self.mock_object(self.cinderclient.volumes, 'get',
201 mock.Mock(return_value=fake_volume))
202 self.mock_object(self.cinderclient.volumes, 'update')
203 fake_volume_id = 'fake_volume'
204 fake_data = {'test': 'test'}
206 self.api.update(self.ctx, fake_volume_id, fake_data)
208 self.cinderclient.volumes.get.assert_called_once_with(fake_volume_id)
209 self.cinderclient.volumes.update.assert_called_once_with(fake_volume,
210 **fake_data)
212 def test_reserve_volume(self):
213 self.mock_object(self.cinderclient.volumes, 'reserve')
214 self.api.reserve_volume(self.ctx, 'id1')
215 self.cinderclient.volumes.reserve.assert_called_once_with('id1')
217 def test_unreserve_volume(self):
218 self.mock_object(self.cinderclient.volumes, 'unreserve')
219 self.api.unreserve_volume(self.ctx, 'id1')
220 self.cinderclient.volumes.unreserve.assert_called_once_with('id1')
222 def test_begin_detaching(self):
223 self.mock_object(self.cinderclient.volumes, 'begin_detaching')
224 self.api.begin_detaching(self.ctx, 'id1')
225 self.cinderclient.volumes.begin_detaching.assert_called_once_with(
226 'id1')
228 def test_roll_detaching(self):
229 self.mock_object(self.cinderclient.volumes, 'roll_detaching')
230 self.api.roll_detaching(self.ctx, 'id1')
231 self.cinderclient.volumes.roll_detaching.assert_called_once_with('id1')
233 def test_attach(self):
234 self.mock_object(self.cinderclient.volumes, 'attach')
235 self.api.attach(self.ctx, 'id1', 'uuid', 'point')
236 self.cinderclient.volumes.attach.assert_called_once_with('id1',
237 'uuid',
238 'point')
240 def test_detach(self):
241 self.mock_object(self.cinderclient.volumes, 'detach')
242 self.api.detach(self.ctx, 'id1')
243 self.cinderclient.volumes.detach.assert_called_once_with('id1')
245 def test_initialize_connection(self):
246 self.mock_object(self.cinderclient.volumes, 'initialize_connection')
247 self.api.initialize_connection(self.ctx, 'id1', 'connector')
248 (self.cinderclient.volumes.initialize_connection.
249 assert_called_once_with('id1', 'connector'))
251 def test_terminate_connection(self):
252 self.mock_object(self.cinderclient.volumes, 'terminate_connection')
253 self.api.terminate_connection(self.ctx, 'id1', 'connector')
254 (self.cinderclient.volumes.terminate_connection.
255 assert_called_once_with('id1', 'connector'))
257 def test_delete(self):
258 self.mock_object(self.cinderclient.volumes, 'delete')
259 self.api.delete(self.ctx, 'id1')
260 self.cinderclient.volumes.delete.assert_called_once_with('id1')
262 def test_get_snapshot(self):
263 snapshot_id = 'snapshot_id1'
264 result = self.api.get_snapshot(self.ctx, snapshot_id)
265 self.assertEqual(snapshot_id, result['id'])
267 def test_get_snapshot_failed(self):
268 cinder.cinderclient.side_effect = cinder_exception.NotFound(404)
269 snapshot_id = 'snapshot_id'
270 self.assertRaises(exception.VolumeSnapshotNotFound,
271 self.api.get_snapshot, self.ctx, snapshot_id)
273 def test_get_all_snapshots(self):
274 cinder._untranslate_snapshot_summary_view.return_value = ['id1', 'id2']
275 self.assertEqual([{'id': 'id1'}, {'id': 'id2'}],
276 self.api.get_all_snapshots(self.ctx))
278 def test_create_snapshot(self):
279 result = self.api.create_snapshot(self.ctx, {'id': 'id1'}, '', '')
280 self.assertEqual('created_id', result['id'])
282 def test_create_force(self):
283 result = self.api.create_snapshot_force(self.ctx,
284 {'id': 'id1'}, '', '')
285 self.assertEqual('created_id', result['id'])
287 def test_delete_snapshot(self):
288 self.mock_object(self.cinderclient.volume_snapshots, 'delete')
289 self.api.delete_snapshot(self.ctx, 'id1')
290 self.cinderclient.volume_snapshots.delete.assert_called_once_with(
291 'id1')
293 def test_wait_for_available_volume(self):
294 fake_volume = {'status': 'creating', 'id': 'fake'}
295 fake_available_volume = {'status': 'available', 'id': 'fake'}
296 self.mock_object(self.cinderclient.volumes, 'get',
297 mock.Mock(return_value=fake_available_volume))
299 actual_result = self.api.wait_for_available_volume(
300 fake_volume, 5, "error", "timeout")
302 self.assertEqual(fake_available_volume, actual_result)
303 self.cinderclient.volumes.get.assert_called_once_with(
304 fake_volume['id'])
306 def test_wait_for_available_volume_error_extending(self):
307 fake_volume = {'status': const.STATUS_EXTENDING_ERROR, 'id': 'fake'}
308 self.mock_object(self.cinderclient.volumes, 'get',
309 mock.Mock(return_value=fake_volume))
310 self.assertRaises(exception.ManilaException,
311 self.api.wait_for_available_volume,
312 fake_volume, 5, 'error', 'timeout')
314 @mock.patch('time.sleep')
315 def test_wait_for_extending_volume(self, mock_sleep):
316 initial_size = 1
317 expected_size = 2
318 mock_volume = fake_volume.FakeVolume(status='available',
319 size=initial_size)
320 mock_extending_vol = fake_volume.FakeVolume(status='extending',
321 size=initial_size)
322 mock_extended_vol = fake_volume.FakeVolume(status='available',
323 size=expected_size)
325 self.mock_object(self.cinderclient.volumes, 'get',
326 mock.Mock(side_effect=[mock_extending_vol,
327 mock_extended_vol]))
329 result = self.api.wait_for_available_volume(
330 mock_volume, 5, "error", "timeout",
331 expected_size=expected_size)
333 expected_get_count = 2
335 self.assertEqual(mock_extended_vol, result)
336 self.cinderclient.volumes.get.assert_has_calls(
337 [mock.call(mock_volume['id'])] * expected_get_count
338 )
339 # utils.retry sleeps before retries, so 1 sleep for 2 get calls
340 self.assertEqual(1, mock_sleep.call_count)
342 def test_wait_for_available_volume_timeout(self):
343 fake_volume = {'status': 'creating', 'id': 'fake'}
344 self.mock_object(self.cinderclient.volumes, 'get',
345 mock.Mock(return_value=fake_volume))
347 self.assertRaises(
348 exception.ManilaException,
349 self.api.wait_for_available_volume,
350 fake_volume, 1, "error", "timeout"
351 )
353 def test_wait_for_available_volume_error(self):
354 fake_volume = {'status': 'creating', 'id': 'fake'}
355 error_volume = {'status': 'error', 'id': 'fake'}
356 self.mock_object(self.cinderclient.volumes, 'get',
357 mock.Mock(return_value=error_volume))
359 self.assertRaises(
360 exception.ManilaException,
361 self.api.wait_for_available_volume,
362 fake_volume, 1, "error", "timeout"
363 )