Coverage for manila/tests/api/v2/test_share_instances.py: 99%
213 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# Licensed under the Apache License, Version 2.0 (the "License"); you may
2# not use this file except in compliance with the License. You may obtain
3# a copy of the License at
4#
5# http://www.apache.org/licenses/LICENSE-2.0
6#
7# Unless required by applicable law or agreed to in writing, software
8# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
9# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
10# License for the specific language governing permissions and limitations
11# under the License.
13from unittest import mock
15import ddt
16from oslo_config import cfg
17from oslo_serialization import jsonutils
18from webob import exc as webob_exc
20from manila.api.openstack import api_version_request
21from manila.api.v2 import share_instances
22from manila.common import constants
23from manila import context
24from manila import db
25from manila import exception
26from manila import policy
27from manila import test
28from manila.tests.api import fakes
29from manila.tests import db_utils
31CONF = cfg.CONF
34@ddt.ddt
35class ShareInstancesAPITest(test.TestCase):
36 """Share instances API Test."""
38 def setUp(self):
39 super(ShareInstancesAPITest, self).setUp()
40 self.controller = share_instances.ShareInstancesController()
41 self.resource_name = self.controller.resource_name
42 self.mock_policy_check = self.mock_object(
43 policy, 'check_policy', mock.Mock(return_value=True))
44 self.admin_context = context.RequestContext('admin', 'fake', True)
45 self.member_context = context.RequestContext('fake', 'fake')
47 def _get_context(self, role):
48 return getattr(self, '%s_context' % role)
50 def _setup_share_instance_data(self, instance=None, version='2.7'):
51 if instance is None:
52 instance = db_utils.create_share(status=constants.STATUS_AVAILABLE,
53 size='1').instance
54 path = '/v2/fake/share_instances/%s/action' % instance['id']
55 req = fakes.HTTPRequest.blank(path, script_name=path, version=version)
56 return instance, req
58 def _get_request(self, uri, context=None, version="2.3"):
59 if context is None:
60 context = self.admin_context
61 req = fakes.HTTPRequest.blank(uri, version=version)
62 req.environ['manila.context'] = context
63 return req
65 def _validate_ids_in_share_instances_list(self, expected, actual):
66 self.assertEqual(len(expected), len(actual))
67 self.assertEqual([i['id'] for i in expected],
68 [i['id'] for i in actual])
70 @ddt.data("2.3", "2.34", "2.35", "2.69")
71 def test_index(self, version):
72 url = '/share_instances'
73 if (api_version_request.APIVersionRequest(version) >=
74 api_version_request.APIVersionRequest('2.35')):
75 url += "?export_location_path=/admin/export/location"
76 if (api_version_request.APIVersionRequest(version) >=
77 api_version_request.APIVersionRequest('2.69')):
78 url += "&is_soft_deleted=true"
79 req = self._get_request(url, version=version)
80 req_context = req.environ['manila.context']
81 last_instance = [db_utils.create_share(size=1,
82 is_soft_deleted=True).instance]
83 share_instances_count = 3
84 other_instances = [
85 db_utils.create_share(size=s + 1).instance
86 for s in range(0, share_instances_count)
87 ]
88 test_instances = other_instances + last_instance
90 db.export_locations_update(
91 self.admin_context, test_instances[0]['id'],
92 '/admin/export/location', False)
94 actual_result = self.controller.index(req)
96 if (api_version_request.APIVersionRequest(version) >=
97 api_version_request.APIVersionRequest('2.69')):
98 test_instances = []
99 elif (api_version_request.APIVersionRequest(version) >=
100 api_version_request.APIVersionRequest('2.35')):
101 test_instances = test_instances[:1]
102 else:
103 test_instances = other_instances
104 self._validate_ids_in_share_instances_list(
105 test_instances, actual_result['share_instances'])
106 self.mock_policy_check.assert_called_once_with(
107 req_context, self.resource_name, 'index')
109 def test_index_with_limit(self):
110 req = self._get_request('/v2/fake/share_instances')
111 req_context = req.environ['manila.context']
112 share_instances_count = 3
113 test_instances = [
114 db_utils.create_share(size=s + 1).instance
115 for s in range(0, share_instances_count)
116 ]
117 expect_links = [
118 {
119 'href': (
120 'http://localhost/share/v2/fake/share_instances?'
121 'limit=3&marker=%s' % test_instances[2]['id']),
122 'rel': 'next',
123 }
124 ]
126 url = '/v2/fake/share_instances?limit=3'
127 req = self._get_request(url)
128 actual_result = self.controller.index(req)
130 self._validate_ids_in_share_instances_list(
131 test_instances, actual_result['share_instances'])
132 self.assertEqual(expect_links, actual_result['share_instances_links'])
133 self.mock_policy_check.assert_called_once_with(
134 req_context, self.resource_name, 'index')
136 @ddt.data('2.3', '2.54', '2.71')
137 def test_show(self, version):
138 test_instance = db_utils.create_share(size=1).instance
139 id = test_instance['id']
141 actual_result = self.controller.show(
142 self._get_request('fake', version=version), id)
144 self.assertEqual(id, actual_result['share_instance']['id'])
145 if (api_version_request.APIVersionRequest(version) >=
146 api_version_request.APIVersionRequest("2.54")):
147 self.assertIn("progress", actual_result['share_instance'])
148 else:
149 self.assertNotIn("progress", actual_result['share_instance'])
150 if (api_version_request.APIVersionRequest(version) >=
151 api_version_request.APIVersionRequest("2.71")):
152 self.assertIn("updated_at", actual_result['share_instance'])
153 else:
154 self.assertNotIn("updated_at", actual_result['share_instance'])
156 self.mock_policy_check.assert_called_once_with(
157 self.admin_context, self.resource_name, 'show')
159 def test_show_with_export_locations(self):
160 test_instance = db_utils.create_share(size=1).instance
161 req = self._get_request('fake', version="2.8")
162 id = test_instance['id']
164 actual_result = self.controller.show(req, id)
166 self.assertEqual(id, actual_result['share_instance']['id'])
167 self.assertIn("export_location", actual_result['share_instance'])
168 self.assertIn("export_locations", actual_result['share_instance'])
169 self.mock_policy_check.assert_called_once_with(
170 self.admin_context, self.resource_name, 'show')
172 def test_show_without_export_locations(self):
173 test_instance = db_utils.create_share(size=1).instance
174 req = self._get_request('fake', version="2.9")
175 id = test_instance['id']
177 actual_result = self.controller.show(req, id)
179 self.assertEqual(id, actual_result['share_instance']['id'])
180 self.assertNotIn("export_location", actual_result['share_instance'])
181 self.assertNotIn("export_locations", actual_result['share_instance'])
182 self.mock_policy_check.assert_called_once_with(
183 self.admin_context, self.resource_name, 'show')
185 def test_show_with_replica_state(self):
186 test_instance = db_utils.create_share(size=1).instance
187 req = self._get_request('fake', version="2.11")
188 id = test_instance['id']
190 actual_result = self.controller.show(req, id)
192 self.assertEqual(id, actual_result['share_instance']['id'])
193 self.assertIn("replica_state", actual_result['share_instance'])
194 self.mock_policy_check.assert_called_once_with(
195 self.admin_context, self.resource_name, 'show')
197 @ddt.data("2.3", "2.8", "2.9", "2.11")
198 def test_get_share_instances(self, version):
199 test_share = db_utils.create_share(size=1)
200 id = test_share['id']
201 req = self._get_request('fake', version=version)
202 req_context = req.environ['manila.context']
203 share_policy_check_call = mock.call(
204 req_context, 'share', 'get', mock.ANY, do_raise=False)
205 get_instances_policy_check_call = mock.call(
206 req_context, 'share_instance', 'index')
208 actual_result = self.controller.get_share_instances(req, id)
210 self._validate_ids_in_share_instances_list(
211 [test_share.instance],
212 actual_result['share_instances']
213 )
214 self.assertEqual(1, len(actual_result.get("share_instances", 0)))
215 for instance in actual_result["share_instances"]:
216 if (api_version_request.APIVersionRequest(version) >
217 api_version_request.APIVersionRequest("2.8")):
218 assert_method = self.assertNotIn
219 else:
220 assert_method = self.assertIn
221 assert_method("export_location", instance)
222 assert_method("export_locations", instance)
223 if (api_version_request.APIVersionRequest(version) >
224 api_version_request.APIVersionRequest("2.10")):
225 self.assertIn("replica_state", instance)
226 self.mock_policy_check.assert_has_calls([
227 get_instances_policy_check_call, share_policy_check_call])
229 @ddt.data('show', 'get_share_instances')
230 def test_not_found(self, target_method_name):
231 method = getattr(self.controller, target_method_name)
232 action = (target_method_name if target_method_name == 'show' else
233 'index')
234 self.assertRaises(webob_exc.HTTPNotFound, method,
235 self._get_request('fake'), 'fake')
236 self.mock_policy_check.assert_called_once_with(
237 self.admin_context, self.resource_name, action)
239 @ddt.data(('show', 2), ('get_share_instances', 2), ('index', 1))
240 @ddt.unpack
241 def test_access(self, target_method_name, args_count):
242 user_context = context.RequestContext('fake', 'fake')
243 req = self._get_request('fake', user_context)
244 policy_exception = exception.PolicyNotAuthorized(
245 action=target_method_name)
246 target_method = getattr(self.controller, target_method_name)
247 args = [i for i in range(1, args_count)]
249 with mock.patch.object(policy, 'check_policy', mock.Mock(
250 side_effect=policy_exception)):
251 self.assertRaises(
252 webob_exc.HTTPForbidden, target_method, req, *args)
254 def _reset_status(self, ctxt, model, req, db_access_method,
255 valid_code, valid_status=None, body=None, version='2.7'):
256 if float(version) > 2.6:
257 action_name = 'reset_status'
258 else:
259 action_name = 'os-reset_status'
260 if body is None:
261 body = {action_name: {'status': constants.STATUS_ERROR}}
262 req.method = 'POST'
263 req.headers['content-type'] = 'application/json'
264 req.headers['X-Openstack-Manila-Api-Version'] = version
265 req.body = jsonutils.dumps(body).encode("utf-8")
266 req.environ['manila.context'] = ctxt
268 with mock.patch.object(
269 policy, 'check_policy', fakes.mock_fake_admin_check):
270 resp = req.get_response(fakes.app())
272 # validate response code and model status
273 self.assertEqual(valid_code, resp.status_int)
275 actual_model = db_access_method(ctxt, model['id'])
276 self.assertEqual(valid_status, actual_model['status'])
278 @ddt.data(*fakes.fixture_reset_status_with_different_roles)
279 @ddt.unpack
280 def test_share_instances_reset_status_with_different_roles(self, role,
281 valid_code,
282 valid_status,
283 version):
284 ctxt = self._get_context(role)
285 instance, req = self._setup_share_instance_data(version=version)
287 self._reset_status(ctxt, instance, req, db.share_instance_get,
288 valid_code, valid_status, version=version)
290 @ddt.data(*fakes.fixture_valid_reset_status_body)
291 @ddt.unpack
292 def test_share_instance_reset_status(self, body, version):
293 instance, req = self._setup_share_instance_data()
294 req.headers['X-Openstack-Manila-Api-Version'] = version
296 if float(version) > 2.6:
297 state = body['reset_status']['status']
298 else:
299 state = body['os-reset_status']['status']
300 self._reset_status(self.admin_context, instance, req,
301 db.share_instance_get, 202,
302 state, body, version=version)
304 @ddt.data(
305 ({'os-reset_status': {'x-status': 'bad'}}, '2.6'),
306 ({'os-reset_status': {'status': 'invalid'}}, '2.6'),
307 ({'reset_status': {'x-status': 'bad'}}, '2.7'),
308 ({'reset_status': {'status': 'invalid'}}, '2.7'),
309 )
310 @ddt.unpack
311 def test_share_instance_invalid_reset_status_body(self, body, version):
312 instance, req = self._setup_share_instance_data()
313 req.headers['X-Openstack-Manila-Api-Version'] = version
315 self._reset_status(self.admin_context, instance, req,
316 db.share_instance_get, 400,
317 constants.STATUS_AVAILABLE, body, version=version)
319 def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
320 check_model_in_db=False, version='2.7'):
321 if float(version) > 2.6:
322 action_name = 'force_delete'
323 else:
324 action_name = 'os-force_delete'
325 body = {action_name: {'status': constants.STATUS_ERROR}}
326 req.method = 'POST'
327 req.headers['content-type'] = 'application/json'
328 req.headers['X-Openstack-Manila-Api-Version'] = version
329 req.body = jsonutils.dumps(body).encode("utf-8")
330 req.environ['manila.context'] = ctxt
332 with mock.patch.object(
333 policy, 'check_policy', fakes.mock_fake_admin_check):
334 resp = req.get_response(fakes.app())
336 # validate response
337 self.assertEqual(valid_code, resp.status_int)
339 if valid_code == 202 and check_model_in_db: 339 ↛ 340line 339 didn't jump to line 340 because the condition on line 339 was never true
340 self.assertRaises(exception.NotFound,
341 db_access_method,
342 ctxt,
343 model['id'])
345 @ddt.data(*fakes.fixture_force_delete_with_different_roles)
346 @ddt.unpack
347 def test_instance_force_delete_with_different_roles(self, role, resp_code,
348 version):
349 instance, req = self._setup_share_instance_data(version=version)
350 ctxt = self._get_context(role)
352 self._force_delete(ctxt, instance, req, db.share_instance_get,
353 resp_code, version=version)
355 def test_instance_force_delete_missing(self):
356 instance, req = self._setup_share_instance_data(
357 instance={'id': 'fake'})
358 ctxt = self._get_context('admin')
360 self._force_delete(ctxt, instance, req, db.share_instance_get, 404)