Coverage for manila/tests/api/v1/test_shares.py: 99%
798 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 2012 NetApp
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.
16import copy
17import datetime
18from unittest import mock
20import ddt
21from oslo_config import cfg
22from oslo_serialization import jsonutils
23import webob
25from manila.api import common
26from manila.api.v1 import shares
27from manila.common import constants
28from manila import context
29from manila import db
30from manila import exception
31from manila.lock import api as resource_locks
32from manila import policy
33from manila.share import api as share_api
34from manila.share import share_types
35from manila import test
36from manila.tests.api.contrib import stubs
37from manila.tests.api import fakes
38from manila.tests import db_utils
39from manila import utils
41CONF = cfg.CONF
44@ddt.ddt
45class ShareAPITest(test.TestCase):
46 """Share API Test."""
48 def setUp(self):
49 super(ShareAPITest, self).setUp()
50 self.controller = shares.ShareController()
51 self.mock_object(db, 'availability_zone_get')
52 self.mock_object(share_api.API, 'get_all',
53 stubs.stub_get_all_shares)
54 self.mock_object(share_api.API, 'get',
55 stubs.stub_share_get)
56 self.mock_object(share_api.API, 'update', stubs.stub_share_update)
57 self.mock_object(share_api.API, 'delete', stubs.stub_share_delete)
58 self.mock_object(share_api.API, 'get_snapshot',
59 stubs.stub_snapshot_get)
60 self.mock_object(share_types, 'get_share_type',
61 stubs.stub_share_type_get)
62 self.mock_object(
63 common, 'validate_public_share_policy',
64 mock.Mock(side_effect=lambda *args, **kwargs: args[1]))
65 self.resource_name = self.controller.resource_name
66 self.mock_policy_check = self.mock_object(policy, 'check_policy')
67 self.maxDiff = None
68 self.share = {
69 "size": 100,
70 "display_name": "Share Test Name",
71 "display_description": "Share Test Desc",
72 "share_proto": "fakeproto",
73 "availability_zone": "zone1:host1",
74 "is_public": False,
75 }
76 self.create_mock = mock.Mock(
77 return_value=stubs.stub_share(
78 '1',
79 display_name=self.share['display_name'],
80 display_description=self.share['display_description'],
81 size=100,
82 share_proto=self.share['share_proto'].upper(),
83 availability_zone=self.share['availability_zone'])
84 )
85 self.vt = {
86 'id': 'fake_volume_type_id',
87 'name': 'fake_volume_type_name',
88 'required_extra_specs': {
89 'driver_handles_share_servers': 'False'
90 },
91 'extra_specs': {
92 'driver_handles_share_servers': 'False'
93 }
94 }
96 CONF.set_default("default_share_type", None)
98 def _get_expected_share_detailed_response(self, values=None, admin=False):
99 share = {
100 'id': '1',
101 'name': 'displayname',
102 'availability_zone': 'fakeaz',
103 'description': 'displaydesc',
104 'export_location': 'fake_location',
105 'export_locations': ['fake_location', 'fake_location2'],
106 'project_id': 'fakeproject',
107 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
108 'share_proto': 'FAKEPROTO',
109 'metadata': {},
110 'size': 1,
111 'snapshot_id': '2',
112 'share_network_id': None,
113 'status': 'fakestatus',
114 'share_type': '1',
115 'volume_type': '1',
116 'snapshot_support': True,
117 'is_public': False,
118 'links': [
119 {
120 'href': 'http://localhost/share/v1/fake/shares/1',
121 'rel': 'self'
122 },
123 {
124 'href': 'http://localhost/share/fake/shares/1',
125 'rel': 'bookmark'
126 }
127 ],
128 }
129 if values:
130 if 'display_name' in values:
131 values['name'] = values.pop('display_name')
132 if 'display_description' in values:
133 values['description'] = values.pop('display_description')
134 share.update(values)
135 if share.get('share_proto'): 135 ↛ 137line 135 didn't jump to line 137 because the condition on line 135 was always true
136 share['share_proto'] = share['share_proto'].upper()
137 if admin:
138 share['share_server_id'] = 'fake_share_server_id'
139 share['host'] = 'fakehost'
140 return {'share': share}
142 @ddt.data("1.0", "2.0", "2.1")
143 def test_share_create_original(self, microversion):
144 self.mock_object(share_api.API, 'create', self.create_mock)
145 body = {"share": copy.deepcopy(self.share)}
146 req = fakes.HTTPRequest.blank('/fake/shares', version=microversion)
148 res_dict = self.controller.create(req, body)
150 self.mock_policy_check.assert_called_once_with(
151 req.environ['manila.context'], self.resource_name, 'create')
152 expected = self._get_expected_share_detailed_response(self.share)
153 expected['share'].pop('snapshot_support')
154 self.assertEqual(expected, res_dict)
156 @ddt.data("2.2", "2.3")
157 def test_share_create_with_snapshot_support_without_cg(self, microversion):
158 self.mock_object(share_api.API, 'create', self.create_mock)
159 body = {"share": copy.deepcopy(self.share)}
160 req = fakes.HTTPRequest.blank('/v1/fake/shares', version=microversion)
162 res_dict = self.controller.create(req, body)
164 self.mock_policy_check.assert_called_once_with(
165 req.environ['manila.context'], self.resource_name, 'create')
166 expected = self._get_expected_share_detailed_response(self.share)
167 self.assertEqual(expected, res_dict)
169 def test_share_create_with_valid_default_share_type(self):
170 self.mock_object(share_types, 'get_share_type_by_name',
171 mock.Mock(return_value=self.vt))
172 CONF.set_default("default_share_type", self.vt['name'])
173 self.mock_object(share_api.API, 'create', self.create_mock)
175 body = {"share": copy.deepcopy(self.share)}
176 req = fakes.HTTPRequest.blank('/v1/fake/shares')
177 res_dict = self.controller.create(req, body)
179 self.mock_policy_check.assert_called_once_with(
180 req.environ['manila.context'], self.resource_name, 'create')
181 expected = self._get_expected_share_detailed_response(self.share)
182 expected['share'].pop('snapshot_support')
183 share_types.get_share_type_by_name.assert_called_once_with(
184 utils.IsAMatcher(context.RequestContext), self.vt['name'])
185 self.assertEqual(expected, res_dict)
187 def test_share_create_with_invalid_default_share_type(self):
188 self.mock_object(
189 share_types, 'get_default_share_type',
190 mock.Mock(side_effect=exception.ShareTypeNotFoundByName(
191 self.vt['name'])),
192 )
193 CONF.set_default("default_share_type", self.vt['name'])
194 req = fakes.HTTPRequest.blank('/v1/fake/shares')
196 self.assertRaises(exception.ShareTypeNotFoundByName,
197 self.controller.create, req, {'share': self.share})
198 self.mock_policy_check.assert_called_once_with(
199 req.environ['manila.context'], self.resource_name, 'create')
200 share_types.get_default_share_type.assert_called_once_with()
202 def test_share_create_with_dhss_true_and_network_notexist(self):
203 fake_share_type = {
204 'id': 'fake_volume_type_id',
205 'name': 'fake_volume_type_name',
206 'extra_specs': {
207 'driver_handles_share_servers': True,
208 }
209 }
210 self.mock_object(
211 share_types, 'get_default_share_type',
212 mock.Mock(return_value=fake_share_type),
213 )
214 CONF.set_default("default_share_type", fake_share_type['name'])
215 req = fakes.HTTPRequest.blank('/v1/fake/shares')
217 self.assertRaises(webob.exc.HTTPBadRequest,
218 self.controller.create, req, {'share': self.share})
219 self.mock_policy_check.assert_called_once_with(
220 req.environ['manila.context'], self.resource_name, 'create')
221 share_types.get_default_share_type.assert_called_once_with()
223 def test_share_create_with_share_net(self):
224 shr = {
225 "size": 100,
226 "name": "Share Test Name",
227 "description": "Share Test Desc",
228 "share_proto": "fakeproto",
229 "availability_zone": "zone1:host1",
230 "share_network_id": "fakenetid"
231 }
232 fake_network = {'id': 'fakenetid'}
233 create_mock = mock.Mock(return_value=stubs.stub_share('1',
234 display_name=shr['name'],
235 display_description=shr['description'],
236 size=shr['size'],
237 share_proto=shr['share_proto'].upper(),
238 availability_zone=shr['availability_zone'],
239 share_network_id=shr['share_network_id']))
240 self.mock_object(share_api.API, 'create', create_mock)
241 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
242 return_value=fake_network))
243 self.mock_object(common, 'check_share_network_is_active',
244 mock.Mock(return_value=True))
245 self.mock_object(
246 db, 'share_network_subnets_get_all_by_availability_zone_id',
247 mock.Mock(return_value={'id': 'fakesubnetid'}))
249 body = {"share": copy.deepcopy(shr)}
250 req = fakes.HTTPRequest.blank('/v1/fake/shares')
251 res_dict = self.controller.create(req, body)
253 self.mock_policy_check.assert_called_once_with(
254 req.environ['manila.context'], self.resource_name, 'create')
255 expected = self._get_expected_share_detailed_response(shr)
256 expected['share'].pop('snapshot_support')
257 common.check_share_network_is_active.assert_called_once_with(
258 fake_network)
259 self.assertEqual(expected, res_dict)
260 # pylint: disable=unsubscriptable-object
261 self.assertEqual("fakenetid",
262 create_mock.call_args[1]['share_network_id'])
264 def test_share_create_mount_point_name(self):
265 shr = {
266 "size": 100,
267 "name": "Share Test Name",
268 "description": "Share Test Desc",
269 "share_proto": "fakeproto",
270 "mount_point_name": "fake_mp"
271 }
272 fake_network = {'id': 'fakenetid'}
273 create_mock = mock.Mock(return_value=stubs.stub_share('1',
274 display_name=shr['name'],
275 display_description=shr['description'],
276 size=shr['size'],
277 share_proto=shr['share_proto'].upper(),
278 mount_point_name=shr['mount_point_name']))
279 self.mock_object(share_api.API, 'create', create_mock)
280 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
281 return_value=fake_network))
282 self.mock_object(common, 'check_share_network_is_active',
283 mock.Mock(return_value=True))
284 self.mock_object(
285 db, 'share_network_subnets_get_all_by_availability_zone_id',
286 mock.Mock(return_value={'id': 'fakesubnetid'}))
288 body = {"share": copy.deepcopy(shr)}
289 req = fakes.HTTPRequest.blank('/v1/fake/shares')
290 self.controller.create(req, body)
292 self.mock_policy_check.assert_called_once_with(
293 req.environ['manila.context'], self.resource_name, 'create')
295 def test_share_create_with_share_net_not_active(self):
296 shr = {
297 "size": 100,
298 "name": "Share Test Name",
299 "description": "Share Test Desc",
300 "share_proto": "fakeproto",
301 "availability_zone": "zone1:host1",
302 "share_network_id": "fakenetid"
303 }
304 share_network = db_utils.create_share_network(
305 status=constants.STATUS_NETWORK_CHANGE)
306 create_mock = mock.Mock(return_value=stubs.stub_share('1',
307 display_name=shr['name'],
308 display_description=shr['description'],
309 size=shr['size'],
310 share_proto=shr['share_proto'].upper(),
311 availability_zone=shr['availability_zone'],
312 share_network_id=shr['share_network_id']))
313 self.mock_object(share_api.API, 'create', create_mock)
314 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
315 return_value=share_network))
316 self.mock_object(common, 'check_share_network_is_active',
317 mock.Mock(side_effect=webob.exc.HTTPBadRequest()))
319 body = {"share": copy.deepcopy(shr)}
320 req = fakes.HTTPRequest.blank('/shares')
321 self.assertRaises(
322 webob.exc.HTTPBadRequest,
323 self.controller.create,
324 req,
325 body)
327 self.mock_policy_check.assert_called_once_with(
328 req.environ['manila.context'], self.resource_name, 'create')
329 common.check_share_network_is_active.assert_called_once_with(
330 share_network)
332 def test_share_create_from_snapshot_without_share_net_no_parent(self):
333 shr = {
334 "size": 100,
335 "name": "Share Test Name",
336 "description": "Share Test Desc",
337 "share_proto": "fakeproto",
338 "availability_zone": "zone1:host1",
339 "snapshot_id": 333,
340 "share_network_id": None,
341 }
342 create_mock = mock.Mock(return_value=stubs.stub_share('1',
343 display_name=shr['name'],
344 display_description=shr['description'],
345 size=shr['size'],
346 share_proto=shr['share_proto'].upper(),
347 availability_zone=shr['availability_zone'],
348 snapshot_id=shr['snapshot_id'],
349 share_network_id=shr['share_network_id']))
350 self.mock_object(share_api.API, 'create', create_mock)
351 body = {"share": copy.deepcopy(shr)}
352 req = fakes.HTTPRequest.blank('/v1/fake/shares')
354 res_dict = self.controller.create(req, body)
356 self.mock_policy_check.assert_called_once_with(
357 req.environ['manila.context'], self.resource_name, 'create')
358 expected = self._get_expected_share_detailed_response(shr)
359 expected['share'].pop('snapshot_support')
360 self.assertEqual(expected, res_dict)
362 def test_share_create_from_snapshot_without_share_net_parent_exists(self):
363 shr = {
364 "size": 100,
365 "name": "Share Test Name",
366 "description": "Share Test Desc",
367 "share_proto": "fakeproto",
368 "availability_zone": "zone1:host1",
369 "snapshot_id": 333,
370 "share_network_id": None,
371 }
372 parent_share_net = 444
373 fake_share_net = {'id': parent_share_net}
374 share_net_subnets = [db_utils.create_share_network_subnet(
375 id='fake_subnet_id', share_network_id=fake_share_net['id'])]
376 create_mock = mock.Mock(return_value=stubs.stub_share('1',
377 display_name=shr['name'],
378 display_description=shr['description'],
379 size=shr['size'],
380 share_proto=shr['share_proto'].upper(),
381 snapshot_id=shr['snapshot_id'],
382 instance=dict(
383 availability_zone=shr['availability_zone'],
384 share_network_id=shr['share_network_id'])))
385 self.mock_object(share_api.API, 'create', create_mock)
386 self.mock_object(share_api.API, 'get_snapshot',
387 stubs.stub_snapshot_get)
388 parent_share = stubs.stub_share(
389 '1', instance={'share_network_id': parent_share_net},
390 create_share_from_snapshot_support=True)
391 self.mock_object(share_api.API, 'get', mock.Mock(
392 return_value=parent_share))
393 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
394 return_value=fake_share_net))
395 self.mock_object(common, 'check_share_network_is_active',
396 mock.Mock(return_value=True))
397 self.mock_object(
398 db, 'share_network_subnets_get_all_by_availability_zone_id',
399 mock.Mock(return_value=share_net_subnets))
401 body = {"share": copy.deepcopy(shr)}
402 req = fakes.HTTPRequest.blank('/v1/fake/shares')
404 res_dict = self.controller.create(req, body)
406 self.mock_policy_check.assert_called_once_with(
407 req.environ['manila.context'], self.resource_name, 'create')
408 expected = self._get_expected_share_detailed_response(shr)
409 expected['share'].pop('snapshot_support')
410 common.check_share_network_is_active.assert_called_once_with(
411 fake_share_net)
412 self.assertEqual(expected, res_dict)
413 # pylint: disable=unsubscriptable-object
414 self.assertEqual(parent_share_net,
415 create_mock.call_args[1]['share_network_id'])
417 def test_share_create_from_snapshot_with_share_net_equals_parent(self):
418 parent_share_net = 444
419 shr = {
420 "size": 100,
421 "name": "Share Test Name",
422 "description": "Share Test Desc",
423 "share_proto": "fakeproto",
424 "availability_zone": "zone1:host1",
425 "snapshot_id": 333,
426 "share_network_id": parent_share_net
427 }
428 fake_share_net = {'id': parent_share_net}
429 share_net_subnets = [db_utils.create_share_network_subnet(
430 id='fake_subnet_id', share_network_id=fake_share_net['id'])]
431 create_mock = mock.Mock(return_value=stubs.stub_share('1',
432 display_name=shr['name'],
433 display_description=shr['description'],
434 size=shr['size'],
435 share_proto=shr['share_proto'].upper(),
436 snapshot_id=shr['snapshot_id'],
437 instance=dict(
438 availability_zone=shr['availability_zone'],
439 share_network_id=shr['share_network_id'])))
440 self.mock_object(share_api.API, 'create', create_mock)
441 self.mock_object(share_api.API, 'get_snapshot',
442 stubs.stub_snapshot_get)
443 self.mock_object(common, 'check_share_network_is_active',
444 mock.Mock(return_value=True))
445 parent_share = stubs.stub_share(
446 '1', instance={'share_network_id': parent_share_net},
447 create_share_from_snapshot_support=True)
448 self.mock_object(share_api.API, 'get', mock.Mock(
449 return_value=parent_share))
450 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
451 return_value=fake_share_net))
452 self.mock_object(
453 db, 'share_network_subnets_get_all_by_availability_zone_id',
454 mock.Mock(return_value=share_net_subnets))
456 body = {"share": copy.deepcopy(shr)}
457 req = fakes.HTTPRequest.blank('/v1/fake/shares')
459 res_dict = self.controller.create(req, body)
461 self.mock_policy_check.assert_called_once_with(
462 req.environ['manila.context'], self.resource_name, 'create')
463 expected = self._get_expected_share_detailed_response(shr)
464 expected['share'].pop('snapshot_support')
465 common.check_share_network_is_active.assert_called_once_with(
466 fake_share_net)
467 self.assertEqual(expected, res_dict)
468 # pylint: disable=unsubscriptable-object
469 self.assertEqual(parent_share_net,
470 create_mock.call_args[1]['share_network_id'])
472 def test_share_create_from_snapshot_invalid_share_net(self):
473 self.mock_object(share_api.API, 'create')
474 shr = {
475 "size": 100,
476 "name": "Share Test Name",
477 "description": "Share Test Desc",
478 "share_proto": "fakeproto",
479 "availability_zone": "zone1:host1",
480 "snapshot_id": 333,
481 "share_network_id": 1234
482 }
483 body = {"share": shr}
484 req = fakes.HTTPRequest.blank('/v1/fake/shares')
486 self.assertRaises(webob.exc.HTTPBadRequest,
487 self.controller.create,
488 req,
489 body)
490 self.mock_policy_check.assert_called_once_with(
491 req.environ['manila.context'], self.resource_name, 'create')
493 def test_share_create_from_mount_point_name(self):
494 parent_share_net = 444
495 shr = {
496 "size": 100,
497 "name": "Share Test Name",
498 "description": "Share Test Desc",
499 "share_proto": "fakeproto",
500 "availability_zone": "zone1:host1",
501 "snapshot_id": 333,
502 "share_network_id": parent_share_net,
503 "mount_point_name": "fake_mp"
504 }
505 fake_share_net = {'id': parent_share_net}
506 share_net_subnets = [db_utils.create_share_network_subnet(
507 id='fake_subnet_id', share_network_id=fake_share_net['id'])]
508 create_mock = mock.Mock(return_value=stubs.stub_share('1',
509 display_name=shr['name'],
510 display_description=shr['description'],
511 size=shr['size'],
512 share_proto=shr['share_proto'].upper(),
513 snapshot_id=shr['snapshot_id'],
514 mount_point_name=shr['mount_point_name'],
515 instance=dict(
516 availability_zone=shr['availability_zone'],
517 share_network_id=shr['share_network_id'],
518 )))
519 self.mock_object(share_api.API, 'create', create_mock)
520 self.mock_object(share_api.API, 'get_snapshot',
521 stubs.stub_snapshot_get)
522 self.mock_object(common, 'check_share_network_is_active',
523 mock.Mock(return_value=True))
524 parent_share = stubs.stub_share(
525 '1', instance={'share_network_id': parent_share_net},
526 create_share_from_snapshot_support=True)
527 self.mock_object(share_api.API, 'get', mock.Mock(
528 return_value=parent_share))
529 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
530 return_value=fake_share_net))
531 self.mock_object(
532 db, 'share_network_subnets_get_all_by_availability_zone_id',
533 mock.Mock(return_value=share_net_subnets))
535 body = {"share": copy.deepcopy(shr)}
536 req = fakes.HTTPRequest.blank('/v1/fake/shares', version='2.84')
537 res_dict = self.controller.create(req, body)
538 self.assertEqual(res_dict['share']['project_id'], 'fakeproject')
540 @ddt.data(
541 {'name': 'name1', 'description': 'x' * 256},
542 {'name': 'x' * 256, 'description': 'description1'},
543 )
544 @ddt.unpack
545 def test_share_create_invalid_input(self, name, description):
546 self.mock_object(share_api.API, 'create')
547 shr = {
548 "size": 100,
549 "name": name,
550 "description": description,
551 "share_proto": "fakeproto",
552 "availability_zone": "zone1:host1",
553 }
554 body = {"share": shr}
555 req = fakes.HTTPRequest.blank('/v1/fake/shares')
557 self.assertRaises(exception.InvalidInput,
558 self.controller.create,
559 req,
560 body)
561 self.mock_policy_check.assert_called_once_with(
562 req.environ['manila.context'], self.resource_name, 'create')
564 @ddt.data("1.0", "2.0")
565 def test_share_create_from_snapshot_not_supported(self, microversion):
566 # This create operation should work, because the 1.0 API doesn't check
567 # create_share_from_snapshot_support.
569 parent_share_net = 444
570 shr = {
571 "size": 100,
572 "name": "Share Test Name",
573 "description": "Share Test Desc",
574 "share_proto": "fakeproto",
575 "availability_zone": "zone1:host1",
576 "snapshot_id": 333,
577 "share_network_id": parent_share_net
578 }
579 fake_share_net = {'id': parent_share_net}
580 share_net_subnets = [db_utils.create_share_network_subnet(
581 id='fake_subnet_id', share_network_id=fake_share_net['id'])]
582 create_mock = mock.Mock(return_value=stubs.stub_share('1',
583 display_name=shr['name'],
584 display_description=shr['description'],
585 size=shr['size'],
586 share_proto=shr['share_proto'].upper(),
587 snapshot_id=shr['snapshot_id'],
588 instance=dict(
589 availability_zone=shr['availability_zone'],
590 share_network_id=shr['share_network_id'])))
591 self.mock_object(share_api.API, 'create', create_mock)
592 self.mock_object(share_api.API, 'get_snapshot',
593 stubs.stub_snapshot_get)
594 self.mock_object(common, 'check_share_network_is_active',
595 mock.Mock(return_value=True))
596 parent_share = stubs.stub_share(
597 '1', instance={'share_network_id': parent_share_net},
598 create_share_from_snapshot_support=False)
599 self.mock_object(share_api.API, 'get', mock.Mock(
600 return_value=parent_share))
601 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
602 return_value=fake_share_net))
603 self.mock_object(
604 db, 'share_network_subnets_get_all_by_availability_zone_id',
605 mock.Mock(return_value=share_net_subnets))
607 body = {"share": copy.deepcopy(shr)}
608 req = fakes.HTTPRequest.blank('/v1/fake/shares', version=microversion)
610 res_dict = self.controller.create(req, body)
612 self.mock_policy_check.assert_called_once_with(
613 req.environ['manila.context'], self.resource_name, 'create')
614 expected = self._get_expected_share_detailed_response(shr)
615 expected['share'].pop('snapshot_support')
616 common.check_share_network_is_active.assert_called_once_with(
617 fake_share_net)
618 self.assertDictEqual(expected, res_dict)
619 # pylint: disable=unsubscriptable-object
620 self.assertEqual(parent_share_net,
621 create_mock.call_args[1]['share_network_id'])
623 def test_share_creation_fails_with_bad_size(self):
624 shr = {"size": '',
625 "name": "Share Test Name",
626 "description": "Share Test Desc",
627 "share_proto": "fakeproto",
628 "availability_zone": "zone1:host1"}
629 body = {"share": shr}
630 req = fakes.HTTPRequest.blank('/fake/shares')
631 self.assertRaises(exception.InvalidInput,
632 self.controller.create,
633 req,
634 body)
635 self.mock_policy_check.assert_called_once_with(
636 req.environ['manila.context'], self.resource_name, 'create')
638 def test_share_create_no_body(self):
639 body = {}
640 req = fakes.HTTPRequest.blank('/fake/shares')
641 self.assertRaises(webob.exc.HTTPUnprocessableEntity,
642 self.controller.create,
643 req,
644 body)
645 self.mock_policy_check.assert_called_once_with(
646 req.environ['manila.context'], self.resource_name, 'create')
648 def test_share_creation_fails_with_invalid_share_type(self):
649 shr = {
650 "size": 1,
651 "name": "Share Test Name",
652 "description": "Share Test Desc",
653 "share_proto": "fakeproto",
654 "availability_zone": "zone1:host1",
655 "share_type": "Invalid share type"
656 }
657 body = {"share": shr}
658 req = fakes.HTTPRequest.blank('/fake/shares')
659 with mock.patch('manila.share.share_types.get_share_type_by_name',
660 side_effect=exception.InvalidShareType(reason='')):
661 self.assertRaises(webob.exc.HTTPBadRequest,
662 self.controller.create,
663 req,
664 body)
665 self.mock_policy_check.assert_called_once_with(
666 req.environ['manila.context'], self.resource_name, 'create')
668 def test_share_create_invalid_availability_zone(self):
669 self.mock_object(
670 db,
671 'availability_zone_get',
672 mock.Mock(side_effect=exception.AvailabilityZoneNotFound(id='id'))
673 )
674 body = {"share": copy.deepcopy(self.share)}
676 req = fakes.HTTPRequest.blank('/fake/shares')
677 self.assertRaises(webob.exc.HTTPNotFound,
678 self.controller.create,
679 req,
680 body)
682 @ddt.data((exception.ShareNetworkNotFound(share_network_id='fake'),
683 webob.exc.HTTPNotFound),
684 (mock.Mock(), webob.exc.HTTPBadRequest))
685 @ddt.unpack
686 def test_share_create_invalid_subnet(self, share_network_side_effect,
687 exception_to_raise):
688 fake_share_with_sn = copy.deepcopy(self.share)
689 fake_share_with_sn['share_network_id'] = 'fakenetid'
690 self.mock_object(db, 'share_network_get',
691 mock.Mock(side_effect=share_network_side_effect))
692 self.mock_object(
693 db, 'share_network_subnets_get_all_by_availability_zone_id',
694 mock.Mock(return_value=None))
695 self.mock_object(common, 'check_share_network_is_active')
697 body = {"share": fake_share_with_sn}
699 req = fakes.HTTPRequest.blank('/fake/shares')
700 self.assertRaises(exception_to_raise,
701 self.controller.create,
702 req,
703 body)
705 def test_share_show(self):
706 req = fakes.HTTPRequest.blank('/fake/shares/1')
707 expected = self._get_expected_share_detailed_response()
708 expected['share'].pop('snapshot_support')
710 res_dict = self.controller.show(req, '1')
712 self.assertEqual(expected, res_dict)
714 def test_share_show_with_share_type_name(self):
715 req = fakes.HTTPRequest.blank('/fake/shares/1', version='2.6')
716 res_dict = self.controller.show(req, '1')
717 expected = self._get_expected_share_detailed_response()
718 expected['share']['share_type_name'] = None
719 expected['share']['task_state'] = None
720 self.assertEqual(expected, res_dict)
722 def test_share_show_admin(self):
723 req = fakes.HTTPRequest.blank('/fake/shares/1', use_admin_context=True)
724 expected = self._get_expected_share_detailed_response(admin=True)
725 expected['share'].pop('snapshot_support')
727 res_dict = self.controller.show(req, '1')
729 self.assertEqual(expected, res_dict)
731 def test_share_show_no_share(self):
732 self.mock_object(share_api.API, 'get',
733 stubs.stub_share_get_notfound)
734 req = fakes.HTTPRequest.blank('/fake/shares/1')
735 self.assertRaises(webob.exc.HTTPNotFound,
736 self.controller.show,
737 req, '1')
739 def test_share_delete(self):
740 req = fakes.HTTPRequest.blank('/fake/shares/1')
741 resp = self.controller.delete(req, 1)
742 self.assertEqual(202, resp.status_int)
744 def test_share_update(self):
745 shr = self.share
746 body = {"share": shr}
748 req = fakes.HTTPRequest.blank('/share/1')
750 res_dict = self.controller.update(req, 1, body)
752 self.mock_policy_check.assert_called_once_with(
753 req.environ['manila.context'], self.resource_name, 'update')
754 self.assertEqual(shr["display_name"], res_dict['share']["name"])
755 self.assertEqual(shr["display_description"],
756 res_dict['share']["description"])
757 self.assertEqual(shr['is_public'],
758 res_dict['share']['is_public'])
760 def test_share_not_updates_size(self):
761 req = fakes.HTTPRequest.blank('/share/1')
763 res_dict = self.controller.update(req, 1, {"share": self.share})
765 self.mock_policy_check.assert_called_once_with(
766 req.environ['manila.context'], self.resource_name, 'update')
767 self.assertNotEqual(res_dict['share']["size"], self.share["size"])
769 def test_share_delete_no_share(self):
770 self.mock_object(share_api.API, 'get',
771 stubs.stub_share_get_notfound)
772 req = fakes.HTTPRequest.blank('/fake/shares/1')
773 self.assertRaises(webob.exc.HTTPNotFound,
774 self.controller.delete,
775 req,
776 1)
778 def _share_list_summary_with_search_opts(self, use_admin_context):
779 search_opts = {
780 'name': 'fake_name',
781 'status': constants.STATUS_AVAILABLE,
782 'share_server_id': 'fake_share_server_id',
783 'share_type_id': 'fake_share_type_id',
784 'snapshot_id': 'fake_snapshot_id',
785 'share_network_id': 'fake_share_network_id',
786 'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
787 'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
788 'sort_key': 'fake_sort_key',
789 'sort_dir': 'fake_sort_dir',
790 'limit': '1',
791 'offset': '1',
792 'is_public': 'False',
793 }
794 if use_admin_context:
795 search_opts['host'] = 'fake_host'
796 # fake_key should be filtered for non-admin
797 url = '/fake/shares?fake_key=fake_value'
798 for k, v in search_opts.items():
799 url = url + '&' + k + '=' + v
800 req = fakes.HTTPRequest.blank(url, use_admin_context=use_admin_context)
802 shares = [
803 {'id': 'id1', 'display_name': 'n1'},
804 {'id': 'id2', 'display_name': 'n2'},
805 {'id': 'id3', 'display_name': 'n3'},
806 ]
807 self.mock_object(share_api.API, 'get_all',
808 mock.Mock(return_value=[shares[1]]))
810 result = self.controller.index(req)
812 search_opts_expected = {
813 'display_name': search_opts['name'],
814 'status': search_opts['status'],
815 'share_server_id': search_opts['share_server_id'],
816 'share_type_id': search_opts['share_type_id'],
817 'snapshot_id': search_opts['snapshot_id'],
818 'share_network_id': search_opts['share_network_id'],
819 'metadata': {'k1': 'v1'},
820 'extra_specs': {'k2': 'v2'},
821 'is_public': 'False',
822 'limit': '1',
823 'offset': '1'
824 }
826 if use_admin_context:
827 search_opts_expected.update({'fake_key': 'fake_value'})
828 search_opts_expected['host'] = search_opts['host']
829 share_api.API.get_all.assert_called_once_with(
830 req.environ['manila.context'],
831 sort_key=search_opts['sort_key'],
832 sort_dir=search_opts['sort_dir'],
833 search_opts=search_opts_expected,
834 )
835 self.assertEqual(1, len(result['shares']))
836 self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
837 self.assertEqual(
838 shares[1]['display_name'], result['shares'][0]['name'])
840 def test_share_list_summary_with_search_opts_by_non_admin(self):
841 self._share_list_summary_with_search_opts(use_admin_context=False)
843 def test_share_list_summary_with_search_opts_by_admin(self):
844 self._share_list_summary_with_search_opts(use_admin_context=True)
846 def test_share_list_summary(self):
847 self.mock_object(share_api.API, 'get_all',
848 stubs.stub_share_get_all_by_project)
849 req = fakes.HTTPRequest.blank('/fake/shares')
850 res_dict = self.controller.index(req)
851 expected = {
852 'shares': [
853 {
854 'name': 'displayname',
855 'id': '1',
856 'links': [
857 {
858 'href': 'http://localhost/share/v1/fake/shares/1',
859 'rel': 'self'
860 },
861 {
862 'href': 'http://localhost/share/fake/shares/1',
863 'rel': 'bookmark'
864 }
865 ],
866 }
867 ]
868 }
869 self.assertEqual(expected, res_dict)
871 def _share_list_detail_with_search_opts(self, use_admin_context):
872 search_opts = {
873 'name': 'fake_name',
874 'status': constants.STATUS_AVAILABLE,
875 'share_server_id': 'fake_share_server_id',
876 'share_type_id': 'fake_share_type_id',
877 'snapshot_id': 'fake_snapshot_id',
878 'share_network_id': 'fake_share_network_id',
879 'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
880 'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
881 'sort_key': 'fake_sort_key',
882 'sort_dir': 'fake_sort_dir',
883 'limit': '1',
884 'offset': '1',
885 'is_public': 'False',
886 }
887 if use_admin_context:
888 search_opts['host'] = 'fake_host'
889 # fake_key should be filtered for non-admin
890 url = '/fake/shares/detail?fake_key=fake_value'
891 for k, v in search_opts.items():
892 url = url + '&' + k + '=' + v
893 req = fakes.HTTPRequest.blank(url, use_admin_context=use_admin_context)
895 shares = [
896 {'id': 'id1', 'display_name': 'n1'},
897 {
898 'id': 'id2',
899 'display_name': 'n2',
900 'status': constants.STATUS_AVAILABLE,
901 'snapshot_id': 'fake_snapshot_id',
902 'instance': {'host': 'fake_host',
903 'share_network_id': 'fake_share_network_id',
904 'share_type_id': 'fake_share_type_id'},
905 },
906 {'id': 'id3', 'display_name': 'n3'},
907 ]
908 self.mock_object(share_api.API, 'get_all',
909 mock.Mock(return_value=[shares[1]]))
911 result = self.controller.detail(req)
913 search_opts_expected = {
914 'display_name': search_opts['name'],
915 'status': search_opts['status'],
916 'share_server_id': search_opts['share_server_id'],
917 'share_type_id': search_opts['share_type_id'],
918 'snapshot_id': search_opts['snapshot_id'],
919 'share_network_id': search_opts['share_network_id'],
920 'metadata': {'k1': 'v1'},
921 'extra_specs': {'k2': 'v2'},
922 'is_public': 'False',
923 'limit': '1',
924 'offset': '1'
925 }
926 if use_admin_context:
927 search_opts_expected.update({'fake_key': 'fake_value'})
928 search_opts_expected['host'] = search_opts['host']
929 share_api.API.get_all.assert_called_once_with(
930 req.environ['manila.context'],
931 sort_key=search_opts['sort_key'],
932 sort_dir=search_opts['sort_dir'],
933 search_opts=search_opts_expected,
934 )
935 self.assertEqual(1, len(result['shares']))
936 self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
937 self.assertEqual(
938 shares[1]['display_name'], result['shares'][0]['name'])
939 self.assertEqual(
940 shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
941 self.assertEqual(
942 shares[1]['status'], result['shares'][0]['status'])
943 self.assertEqual(
944 shares[1]['instance']['share_type_id'],
945 result['shares'][0]['share_type'])
946 self.assertEqual(
947 shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
948 if use_admin_context:
949 self.assertEqual(
950 shares[1]['instance']['host'], result['shares'][0]['host'])
951 self.assertEqual(
952 shares[1]['instance']['share_network_id'],
953 result['shares'][0]['share_network_id'])
955 def test_share_list_detail_with_search_opts_by_non_admin(self):
956 self._share_list_detail_with_search_opts(use_admin_context=False)
958 def test_share_list_detail_with_search_opts_by_admin(self):
959 self._share_list_detail_with_search_opts(use_admin_context=True)
961 def _list_detail_common_expected(self, admin=False):
962 share_dict = {
963 'status': 'fakestatus',
964 'description': 'displaydesc',
965 'export_location': 'fake_location',
966 'export_locations': ['fake_location', 'fake_location2'],
967 'availability_zone': 'fakeaz',
968 'name': 'displayname',
969 'share_proto': 'FAKEPROTO',
970 'metadata': {},
971 'project_id': 'fakeproject',
973 'id': '1',
974 'snapshot_id': '2',
975 'snapshot_support': True,
976 'share_network_id': None,
977 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
978 'size': 1,
979 'share_type': '1',
980 'volume_type': '1',
981 'is_public': False,
982 'links': [
983 {
984 'href': 'http://localhost/share/v1/fake/shares/1',
985 'rel': 'self'
986 },
987 {
988 'href': 'http://localhost/share/fake/shares/1',
989 'rel': 'bookmark'
990 }
991 ],
992 }
993 if admin: 993 ↛ 994line 993 didn't jump to line 994 because the condition on line 993 was never true
994 share_dict['host'] = 'fakehost'
995 return {'shares': [share_dict]}
997 def _list_detail_test_common(self, req, expected):
998 self.mock_object(share_api.API, 'get_all',
999 stubs.stub_share_get_all_by_project)
1000 res_dict = self.controller.detail(req)
1001 self.assertEqual(expected, res_dict)
1002 self.assertEqual(res_dict['shares'][0]['volume_type'],
1003 res_dict['shares'][0]['share_type'])
1005 def test_share_list_detail(self):
1006 env = {'QUERY_STRING': 'name=Share+Test+Name'}
1007 req = fakes.HTTPRequest.blank('/fake/shares/detail', environ=env)
1008 expected = self._list_detail_common_expected()
1009 expected['shares'][0].pop('snapshot_support')
1010 self._list_detail_test_common(req, expected)
1012 def test_share_list_detail_with_task_state(self):
1013 env = {'QUERY_STRING': 'name=Share+Test+Name'}
1014 req = fakes.HTTPRequest.blank('/fake/shares/detail', environ=env,
1015 version="2.5")
1016 expected = self._list_detail_common_expected()
1017 expected['shares'][0]['task_state'] = None
1018 self._list_detail_test_common(req, expected)
1020 def test_remove_invalid_options(self):
1021 ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=False)
1022 search_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
1023 expected_opts = {'a': 'a', 'c': 'c'}
1024 allowed_opts = ['a', 'c']
1025 common.remove_invalid_options(ctx, search_opts, allowed_opts)
1026 self.assertEqual(expected_opts, search_opts)
1028 def test_remove_invalid_options_admin(self):
1029 ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=True)
1030 search_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
1031 expected_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
1032 allowed_opts = ['a', 'c']
1033 common.remove_invalid_options(ctx, search_opts, allowed_opts)
1034 self.assertEqual(expected_opts, search_opts)
1037def _fake_access_get(self, ctxt, access_id):
1039 class Access(object):
1040 def __init__(self, **kwargs):
1041 self.STATE_NEW = 'fake_new'
1042 self.STATE_ACTIVE = 'fake_active'
1043 self.STATE_ERROR = 'fake_error'
1044 self.params = kwargs
1045 self.params['state'] = self.STATE_NEW
1046 self.share_id = kwargs.get('share_id')
1047 self.id = access_id
1049 def __getitem__(self, item):
1050 return self.params[item]
1052 access = Access(access_id=access_id, share_id='fake_share_id')
1053 return access
1056@ddt.ddt
1057class ShareActionsTest(test.TestCase):
1059 def setUp(self):
1060 super(ShareActionsTest, self).setUp()
1061 self.controller = shares.ShareController()
1062 self.mock_object(share_api.API, 'get', stubs.stub_share_get)
1063 self.mock_policy_check = self.mock_object(policy, 'check_policy')
1065 @ddt.data(
1066 {'access_type': 'ip', 'access_to': '127.0.0.1'},
1067 {'access_type': 'user', 'access_to': '1' * 4},
1068 {'access_type': 'user', 'access_to': '1' * 255},
1069 {'access_type': 'user', 'access_to': 'fake{.-_\'`}'},
1070 {'access_type': 'user', 'access_to': 'MYDOMAIN-Administrator'},
1071 {'access_type': 'user', 'access_to': 'test group name'},
1072 {'access_type': 'user', 'access_to': 'group$.-_\'`{}'},
1073 {'access_type': 'cert', 'access_to': 'x'},
1074 {'access_type': 'cert', 'access_to': 'tenant.example.com'},
1075 {'access_type': 'cert', 'access_to': 'x' * 64},
1076 {'access_type': 'cert', 'access_to': 'x' * 64,
1077 'lock_visibility': True},
1078 {'access_type': 'cert', 'access_to': 'x' * 64, 'lock_deletion': True},
1079 {'access_type': 'cert', 'access_to': 'x' * 64, 'lock_deletion': True},
1080 {'access_type': 'cert', 'access_to': 'x' * 64, 'lock_deletion': True,
1081 'lock_visibility': True, 'lock_reason': 'locked_for_testing'},
1082 )
1083 def test_allow_access(self, access):
1084 self.mock_object(share_api.API,
1085 'allow_access',
1086 mock.Mock(return_value={'fake': 'fake'}))
1087 self.mock_object(self.controller._access_view_builder, 'view',
1088 mock.Mock(return_value={'access':
1089 {'fake': 'fake'}}))
1090 self.mock_object(self.controller, '_create_access_locks')
1092 id = 'fake_share_id'
1093 body = {'os-allow_access': access}
1094 expected = {'access': {'fake': 'fake'}}
1095 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id)
1096 lock_visibility = access.pop('lock_visibility', None)
1097 lock_deletion = access.pop('lock_deletion', None)
1098 lock_reason = access.pop('lock_reason', None)
1100 res = self.controller._allow_access(
1101 req, id, body, lock_visibility=lock_visibility,
1102 lock_deletion=lock_deletion, lock_reason=lock_reason
1103 )
1105 self.assertEqual(expected, res)
1106 self.mock_policy_check.assert_called_once_with(
1107 req.environ['manila.context'], 'share', 'allow_access')
1108 if lock_visibility or lock_deletion:
1109 self.controller._create_access_locks.assert_called_once_with(
1110 req.environ['manila.context'],
1111 expected['access'],
1112 lock_deletion=lock_deletion,
1113 lock_visibility=lock_visibility,
1114 lock_reason=lock_reason
1115 )
1117 @ddt.data(
1118 {'lock_visibility': True, 'lock_deletion': True,
1119 'lock_reason': 'test lock reason'},
1120 {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': None},
1121 {'lock_visibility': False, 'lock_deletion': True, 'lock_reason': None},
1122 )
1123 @ddt.unpack
1124 def test__create_access_locks(self, lock_visibility, lock_deletion,
1125 lock_reason):
1126 access = {
1127 'id': 'fake',
1128 'access_type': 'ip',
1129 'access_to': '127.0.0.1',
1130 'share_id': 'fake_share_id'
1131 }
1132 mock_deletion_lock_create = mock.Mock()
1133 lock_id = 'fake_lock_id'
1134 if lock_deletion:
1135 mock_deletion_lock_create = mock.Mock(
1136 side_effect=[
1137 {'id': lock_id},
1138 {'id': f'{lock_id}2'},
1139 {'id': f'{lock_id}3'}
1140 ]
1141 )
1142 self.mock_object(
1143 resource_locks.API, 'create', mock_deletion_lock_create
1144 )
1146 id = 'fake_share_id'
1147 req = fakes.HTTPRequest.blank(
1148 '/tenant1/shares/%s/action' % id, version='2.82')
1149 context = req.environ['manila.context']
1150 access['project_id'] = context.project_id
1151 access['user_id'] = context.user_id
1153 self.controller._create_access_locks(
1154 req.environ['manila.context'],
1155 access,
1156 lock_deletion=lock_deletion,
1157 lock_visibility=lock_visibility,
1158 lock_reason=lock_reason
1159 )
1161 restrict_calls = []
1162 if lock_deletion:
1163 share_lock_reason = (
1164 constants.SHARE_LOCKED_BY_ACCESS_LOCK_REASON %
1165 {'lock_id': lock_id}
1166 )
1167 restrict_calls.append(
1168 mock.call(
1169 context, resource_id=access['id'],
1170 resource_type='access_rule',
1171 resource_action=constants.RESOURCE_ACTION_DELETE,
1172 resource=access,
1173 lock_reason=lock_reason
1174 )
1175 )
1176 restrict_calls.append(
1177 mock.call(
1178 context, resource_id=access['share_id'],
1179 resource_type='share',
1180 resource_action=constants.RESOURCE_ACTION_DELETE,
1181 lock_reason=share_lock_reason
1182 )
1183 )
1184 if lock_visibility:
1185 restrict_calls.append(
1186 mock.call(
1187 context, resource_id=access['id'],
1188 resource_type='access_rule',
1189 resource_action=constants.RESOURCE_ACTION_SHOW,
1190 resource=access,
1191 lock_reason=lock_reason
1192 )
1193 )
1194 resource_locks.API.create.assert_has_calls(restrict_calls)
1196 def test__create_access_visibility_locks_creation_failed(self):
1197 access = {
1198 'id': 'fake',
1199 'access_type': 'ip',
1200 'access_to': '127.0.0.1',
1201 }
1202 lock_reason = 'locked for testing'
1203 self.mock_object(
1204 resource_locks.API, 'create',
1205 mock.Mock(side_effect=exception.NotAuthorized)
1206 )
1208 id = 'fake_share_id'
1209 req = fakes.HTTPRequest.blank(
1210 '/tenant1/shares/%s/action' % id, version='2.82')
1211 context = req.environ['manila.context']
1212 access['project_id'] = context.project_id
1213 access['user_id'] = context.user_id
1215 self.assertRaises(
1216 webob.exc.HTTPBadRequest,
1217 self.controller._create_access_locks,
1218 req.environ['manila.context'],
1219 access,
1220 lock_deletion=False,
1221 lock_visibility=True,
1222 lock_reason=lock_reason
1223 )
1225 resource_locks.API.create.assert_called_once_with(
1226 context, resource_id=access['id'], resource_type='access_rule',
1227 resource_action=constants.RESOURCE_ACTION_SHOW, resource=access,
1228 lock_reason=lock_reason)
1230 def test__create_access_deletion_locks_creation_failed(self):
1231 access = {
1232 'id': 'fake',
1233 'access_type': 'ip',
1234 'access_to': '127.0.0.1',
1235 }
1236 lock_reason = 'locked for testing'
1237 self.mock_object(
1238 resource_locks.API, 'create',
1239 mock.Mock(side_effect=exception.NotAuthorized)
1240 )
1242 id = 'fake_share_id'
1243 req = fakes.HTTPRequest.blank(
1244 '/tenant1/shares/%s/action' % id, version='2.82')
1245 context = req.environ['manila.context']
1246 access['project_id'] = context.project_id
1247 access['user_id'] = context.user_id
1249 self.assertRaises(
1250 webob.exc.HTTPBadRequest,
1251 self.controller._create_access_locks,
1252 req.environ['manila.context'],
1253 access,
1254 lock_deletion=True,
1255 lock_visibility=False,
1256 lock_reason=lock_reason
1257 )
1259 resource_locks.API.create.assert_called_once_with(
1260 context, resource_id=access['id'], resource_type='access_rule',
1261 resource_action=constants.RESOURCE_ACTION_DELETE, resource=access,
1262 lock_reason=lock_reason)
1264 @ddt.data(
1265 {'lock_visibility': True, 'lock_deletion': True,
1266 'lock_reason': 'test lock reason'},
1267 {'lock_visibility': True, 'lock_deletion': False, 'lock_reason': None},
1268 {'lock_visibility': False, 'lock_deletion': True, 'lock_reason': None},
1269 )
1270 @ddt.unpack
1271 def test_allow_access_visibility_restrictions(self, lock_visibility,
1272 lock_deletion, lock_reason):
1273 access = {'id': 'fake', 'share_id': 'fake_share_id'}
1274 expected_access = {'access': {'fake_key': 'fake_value'}}
1275 self.mock_object(share_api.API,
1276 'allow_access',
1277 mock.Mock(return_value=access))
1278 self.mock_object(self.controller._access_view_builder, 'view',
1279 mock.Mock(return_value=expected_access))
1280 self.mock_object(self.controller, '_create_access_locks')
1282 id = 'fake_share_id'
1283 body = {
1284 'allow_access': {
1285 'access_type': 'ip',
1286 'access_to': '127.0.0.1',
1287 'lock_visibility': lock_visibility,
1288 'lock_deletion': lock_deletion,
1289 'lock_reason': lock_reason
1290 }
1291 }
1292 req = fakes.HTTPRequest.blank(
1293 '/tenant1/shares/%s/action' % id, version='2.82')
1294 context = req.environ['manila.context']
1295 access['project_id'] = context.project_id
1296 access['user_id'] = context.user_id
1298 res = self.controller._allow_access(
1299 req, id, body, lock_visibility=lock_visibility,
1300 lock_deletion=lock_deletion, lock_reason=lock_reason)
1302 self.assertEqual(expected_access, res)
1303 self.mock_policy_check.assert_called_once_with(
1304 context, 'share', 'allow_access')
1305 self.controller._create_access_locks.assert_called_once_with(
1306 context, access, lock_deletion=lock_deletion,
1307 lock_visibility=lock_visibility, lock_reason=lock_reason
1308 )
1310 def test_allow_access_with_network_id(self):
1311 share_network = db_utils.create_share_network()
1312 share = db_utils.create_share(share_network_id=share_network['id'])
1313 access = {'access_type': 'user', 'access_to': '1' * 4}
1315 self.mock_object(share_api.API,
1316 'allow_access',
1317 mock.Mock(return_value={'fake': 'fake'}))
1318 self.mock_object(self.controller._access_view_builder, 'view',
1319 mock.Mock(return_value={'access': {'fake': 'fake'}}))
1320 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
1322 id = 'fake_share_id'
1323 body = {'os-allow_access': access}
1324 expected = {'access': {'fake': 'fake'}}
1325 req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
1327 res = self.controller._allow_access(req, id, body)
1329 self.assertEqual(expected, res)
1330 self.mock_policy_check.assert_called_once_with(
1331 req.environ['manila.context'], 'share', 'allow_access')
1333 @ddt.data(
1334 {'access_type': 'error_type', 'access_to': '127.0.0.1'},
1335 {'access_type': 'ip', 'access_to': 'localhost'},
1336 {'access_type': 'ip', 'access_to': '127.0.0.*'},
1337 {'access_type': 'ip', 'access_to': '127.0.0.0/33'},
1338 {'access_type': 'ip', 'access_to': '127.0.0.256'},
1339 {'access_type': 'user', 'access_to': '1'},
1340 {'access_type': 'user', 'access_to': '1' * 3},
1341 {'access_type': 'user', 'access_to': '1' * 256},
1342 {'access_type': 'user', 'access_to': 'root<>'},
1343 {'access_type': 'user', 'access_to': 'group\\'},
1344 {'access_type': 'user', 'access_to': '+=*?group'},
1345 {'access_type': 'cert', 'access_to': ''},
1346 {'access_type': 'cert', 'access_to': ' '},
1347 {'access_type': 'cert', 'access_to': 'x' * 65},
1348 {'access_type': 'cephx', 'access_to': 'alice'},
1349 {'access_type': 'ip', 'access_to': '127.0.0.0/24',
1350 'lock_reason': 'fake_lock_reason'},
1351 )
1352 def test_allow_access_error(self, access):
1353 id = 'fake_share_id'
1354 lock_reason = access.pop('lock_reason', None)
1355 body = {'os-allow_access': access}
1356 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id)
1358 self.assertRaises(webob.exc.HTTPBadRequest,
1359 self.controller._allow_access, req, id, body,
1360 lock_reason=lock_reason)
1361 self.mock_policy_check.assert_called_once_with(
1362 req.environ['manila.context'], 'share', 'allow_access')
1364 def test_deny_access(self):
1365 def _stub_deny_access(*args, **kwargs):
1366 pass
1368 self.mock_object(share_api.API, "deny_access", _stub_deny_access)
1369 self.mock_object(share_api.API, "access_get", _fake_access_get)
1370 self.mock_object(self.controller, '_check_for_access_rule_locks')
1372 id = 'fake_share_id'
1373 body = {"os-deny_access": {"access_id": 'fake_acces_id'}}
1374 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id)
1376 res = self.controller._deny_access(req, id, body)
1378 self.assertEqual(202, res.status_int)
1379 self.mock_policy_check.assert_called_once_with(
1380 req.environ['manila.context'], 'share', 'deny_access')
1382 def test_deny_access_with_share_network_id(self):
1383 self.mock_object(share_api.API, "deny_access", mock.Mock())
1384 self.mock_object(share_api.API, "access_get", _fake_access_get)
1385 share_network = db_utils.create_share_network()
1386 share = db_utils.create_share(share_network_id=share_network['id'])
1387 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
1388 self.mock_object(self.controller, '_check_for_access_rule_locks')
1390 id = 'fake_share_id'
1391 access_data = {"access_id": 'fake_acces_id'}
1392 body = {"os-deny_access": access_data}
1393 req = fakes.HTTPRequest.blank('/v1/tenant1/shares/%s/action' % id)
1395 res = self.controller._deny_access(req, id, body)
1397 self.assertEqual(202, res.status_int)
1398 self.controller._check_for_access_rule_locks.assert_called_once_with(
1399 req.environ['manila.context'], access_data,
1400 access_data['access_id'], id
1401 )
1402 self.mock_policy_check.assert_called_once_with(
1403 req.environ['manila.context'], 'share', 'deny_access')
1405 def test_deny_access_not_found(self):
1406 def _stub_deny_access(*args, **kwargs):
1407 pass
1409 self.mock_object(share_api.API, "deny_access", _stub_deny_access)
1410 self.mock_object(share_api.API, "access_get", _fake_access_get)
1411 self.mock_object(self.controller, '_check_for_access_rule_locks')
1413 id = 'super_fake_share_id'
1414 body = {"os-deny_access": {"access_id": 'fake_acces_id'}}
1415 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id)
1417 self.assertRaises(webob.exc.HTTPNotFound,
1418 self.controller._deny_access,
1419 req,
1420 id,
1421 body)
1422 self.mock_policy_check.assert_called_once_with(
1423 req.environ['manila.context'], 'share', 'deny_access')
1425 def test_deny_access_delete_locks(self):
1426 def _stub_deny_access(*args, **kwargs):
1427 pass
1429 self.mock_object(share_api.API, "deny_access", _stub_deny_access)
1430 self.mock_object(share_api.API, "access_get", _fake_access_get)
1431 self.mock_object(self.controller, '_check_for_access_rule_locks')
1433 id = 'fake_share_id'
1434 body_data = {"access_id": 'fake_acces_id'}
1435 body = {"deny_access": body_data}
1436 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1437 version='2.82')
1438 context = req.environ['manila.context']
1440 res = self.controller._deny_access(req, id, body)
1442 self.assertEqual(202, res.status_int)
1443 self.mock_policy_check.assert_called_once_with(
1444 req.environ['manila.context'], 'share', 'deny_access')
1445 self.controller._check_for_access_rule_locks.assert_called_once_with(
1446 context, body['deny_access'], body_data['access_id'], id
1447 )
1449 def test__check_for_access_rule_locks_no_locks(self):
1450 self.mock_object(
1451 resource_locks.API, "get_all", mock.Mock(return_value=([], 0)))
1453 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1454 version='2.82')
1455 context = req.environ['manila.context']
1456 access_id = 'fake_access_id'
1457 share_id = 'fake_share_id'
1459 self.mock_object(context, 'elevated', mock.Mock(return_value=context))
1460 self.controller._check_for_access_rule_locks(
1461 context, {}, access_id, share_id)
1463 delete_search_opts = {
1464 'resource_id': access_id,
1465 'resource_action': constants.RESOURCE_ACTION_DELETE,
1466 'all_projects': True,
1467 }
1469 resource_locks.API.get_all.assert_called_once_with(
1470 context, search_opts=delete_search_opts, show_count=True
1471 )
1473 def test__check_for_access_rules_locks_too_many_locks(self):
1474 locks = [{'id': f'lock_id_{i}'} for i in range(4)]
1475 self.mock_object(
1476 resource_locks.API, "get_all",
1477 mock.Mock(return_value=(locks, len(locks))))
1479 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1480 version='2.82')
1481 context = req.environ['manila.context']
1482 access_id = 'fake_access_id'
1483 share_id = 'fake_share_id'
1485 self.mock_object(context, 'elevated', mock.Mock(return_value=context))
1486 self.assertRaises(
1487 webob.exc.HTTPForbidden,
1488 self.controller._check_for_access_rule_locks,
1489 context, {}, access_id, share_id)
1491 delete_search_opts = {
1492 'resource_id': access_id,
1493 'resource_action': constants.RESOURCE_ACTION_DELETE,
1494 'all_projects': True,
1495 }
1497 resource_locks.API.get_all.assert_called_once_with(
1498 context, search_opts=delete_search_opts, show_count=True
1499 )
1501 def test__check_for_access_rules_cant_manipulate_lock(self):
1502 locks = [{
1503 'id': 'fake_lock_id',
1504 'resource_action': constants.RESOURCE_ACTION_DELETE
1505 }]
1506 self.mock_object(
1507 resource_locks.API, "get_all",
1508 mock.Mock(return_value=(locks, len(locks))))
1509 self.mock_object(
1510 resource_locks.API, "ensure_context_can_delete_lock",
1511 mock.Mock(side_effect=exception.NotAuthorized))
1513 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1514 version='2.82')
1515 context = req.environ['manila.context']
1516 access_id = 'fake_access_id'
1517 share_id = 'fake_share_id'
1519 self.mock_object(context, 'elevated', mock.Mock(return_value=context))
1520 self.assertRaises(
1521 webob.exc.HTTPForbidden,
1522 self.controller._check_for_access_rule_locks,
1523 context, {'unrestrict': True}, access_id, share_id)
1525 delete_search_opts = {
1526 'resource_id': access_id,
1527 'resource_action': constants.RESOURCE_ACTION_DELETE,
1528 'all_projects': True,
1529 }
1531 resource_locks.API.get_all.assert_called_once_with(
1532 context, search_opts=delete_search_opts, show_count=True
1533 )
1534 (resource_locks.API.ensure_context_can_delete_lock
1535 .assert_called_once_with(
1536 context, locks[0]['id']))
1538 def test__check_for_access_rules_locks_unauthorized(self):
1539 locks = [{
1540 'id': 'fake_lock_id',
1541 'resource_action': constants.RESOURCE_ACTION_DELETE
1542 }]
1543 self.mock_object(
1544 resource_locks.API, "get_all",
1545 mock.Mock(return_value=(locks, len(locks))))
1546 self.mock_object(
1547 resource_locks.API, "ensure_context_can_delete_lock",
1548 mock.Mock(side_effect=exception.NotAuthorized))
1549 self.mock_object(
1550 resource_locks.API, "delete",
1551 mock.Mock(side_effect=exception.NotAuthorized))
1553 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1554 version='2.82')
1555 context = req.environ['manila.context']
1556 access_id = 'fake_access_id'
1557 share_id = 'fake_share_id'
1559 self.mock_object(context, 'elevated', mock.Mock(return_value=context))
1560 self.assertRaises(
1561 webob.exc.HTTPForbidden,
1562 self.controller._check_for_access_rule_locks,
1563 context, {'unrestrict': True}, access_id, share_id
1564 )
1565 delete_search_opts = {
1566 'resource_id': access_id,
1567 'resource_action': constants.RESOURCE_ACTION_DELETE,
1568 'all_projects': True,
1569 }
1570 resource_locks.API.get_all.assert_called_once_with(
1571 context, search_opts=delete_search_opts, show_count=True
1572 )
1573 (resource_locks.API.ensure_context_can_delete_lock
1574 .assert_called_once_with(
1575 context, locks[0]['id']))
1577 def test_check_for_access_rules_locks(self):
1578 locks = [{
1579 'id': 'fake_lock_id',
1580 'resource_action': constants.RESOURCE_ACTION_DELETE
1581 }]
1582 self.mock_object(
1583 resource_locks.API, "get_all",
1584 mock.Mock(return_value=(locks, len(locks))))
1585 self.mock_object(
1586 resource_locks.API, "ensure_context_can_delete_lock")
1587 self.mock_object(resource_locks.API, "delete")
1589 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id,
1590 version='2.82')
1591 context = req.environ['manila.context']
1592 access_id = 'fake_access_id'
1593 share_id = 'fake_share_id'
1595 self.mock_object(context, 'elevated', mock.Mock(return_value=context))
1596 self.controller._check_for_access_rule_locks(
1597 context, {'unrestrict': True}, access_id, share_id)
1599 delete_search_opts = {
1600 'resource_id': access_id,
1601 'resource_action': constants.RESOURCE_ACTION_DELETE,
1602 'all_projects': True,
1603 }
1604 resource_locks.API.get_all.assert_called_once_with(
1605 context.elevated(), search_opts=delete_search_opts,
1606 show_count=True
1607 )
1608 (resource_locks.API.ensure_context_can_delete_lock
1609 .assert_called_once_with(
1610 context, locks[0]['id']))
1612 @ddt.data('_allow_access', '_deny_access')
1613 def test_allow_access_deny_access_policy_not_authorized(self, method):
1614 req = fakes.HTTPRequest.blank('/tenant1/shares/someuuid/action')
1615 action = method[1:]
1616 body = {action: None}
1617 noauthexc = exception.PolicyNotAuthorized(action=action)
1618 with mock.patch.object(
1619 policy, 'check_policy', mock.Mock(side_effect=noauthexc)):
1620 method = getattr(self.controller, method)
1622 self.assertRaises(
1623 webob.exc.HTTPForbidden, method, req, body, 'someuuid')
1624 policy.check_policy.assert_called_once_with(
1625 req.environ['manila.context'], 'share', action)
1627 def test_access_list(self):
1628 fake_access_list = [
1629 {
1630 "state": "fakestatus",
1631 "id": "fake_access_id",
1632 "access_type": "fakeip",
1633 "access_to": "127.0.0.1",
1634 }
1635 ]
1636 self.mock_object(self.controller._access_view_builder, 'list_view',
1637 mock.Mock(return_value={'access_list':
1638 fake_access_list}))
1639 id = 'fake_share_id'
1640 body = {"os-access_list": None}
1641 req = fakes.HTTPRequest.blank('/tenant1/shares/%s/action' % id)
1643 res_dict = self.controller._access_list(req, id, body)
1644 self.assertEqual({'access_list': fake_access_list}, res_dict)
1646 def test_extend(self):
1647 id = 'fake_share_id'
1648 share = stubs.stub_share_get(None, None, id)
1649 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
1650 self.mock_object(share_api.API, "extend")
1652 size = '123'
1653 body = {"os-extend": {'new_size': size}}
1654 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1656 actual_response = self.controller._extend(req, id, body)
1658 share_api.API.get.assert_called_once_with(mock.ANY, id)
1659 share_api.API.extend.assert_called_once_with(
1660 mock.ANY, share, int(size), force=False)
1661 self.assertEqual(202, actual_response.status_int)
1663 @ddt.data({"os-extend": ""},
1664 {"os-extend": {"new_size": "foo"}},
1665 {"os-extend": {"new_size": {'foo': 'bar'}}})
1666 def test_extend_invalid_body(self, body):
1667 id = 'fake_share_id'
1668 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1670 self.assertRaises(webob.exc.HTTPBadRequest,
1671 self.controller._extend, req, id, body)
1673 @ddt.data({'source': exception.InvalidInput,
1674 'target': webob.exc.HTTPBadRequest},
1675 {'source': exception.InvalidShare,
1676 'target': webob.exc.HTTPBadRequest},
1677 {'source': exception.ShareSizeExceedsAvailableQuota,
1678 'target': webob.exc.HTTPForbidden})
1679 @ddt.unpack
1680 def test_extend_exception(self, source, target):
1681 id = 'fake_share_id'
1682 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1683 body = {"os-extend": {'new_size': '123'}}
1684 self.mock_object(share_api.API, "extend",
1685 mock.Mock(side_effect=source('fake')))
1687 self.assertRaises(target, self.controller._extend, req, id, body)
1689 def test_shrink(self):
1690 id = 'fake_share_id'
1691 share = stubs.stub_share_get(None, None, id)
1692 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
1693 self.mock_object(share_api.API, "shrink")
1695 size = '123'
1696 body = {"os-shrink": {'new_size': size}}
1697 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1699 actual_response = self.controller._shrink(req, id, body)
1701 share_api.API.get.assert_called_once_with(mock.ANY, id)
1702 share_api.API.shrink.assert_called_once_with(
1703 mock.ANY, share, int(size))
1704 self.assertEqual(202, actual_response.status_int)
1706 @ddt.data({"os-shrink": ""},
1707 {"os-shrink": {"new_size": "foo"}},
1708 {"os-shrink": {"new_size": {'foo': 'bar'}}})
1709 def test_shrink_invalid_body(self, body):
1710 id = 'fake_share_id'
1711 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1713 self.assertRaises(webob.exc.HTTPBadRequest,
1714 self.controller._shrink, req, id, body)
1716 @ddt.data({'source': exception.InvalidInput,
1717 'target': webob.exc.HTTPBadRequest},
1718 {'source': exception.InvalidShare,
1719 'target': webob.exc.HTTPBadRequest})
1720 @ddt.unpack
1721 def test_shrink_exception(self, source, target):
1722 id = 'fake_share_id'
1723 req = fakes.HTTPRequest.blank('/fake/shares/%s/action' % id)
1724 body = {"os-shrink": {'new_size': '123'}}
1725 self.mock_object(share_api.API, "shrink",
1726 mock.Mock(side_effect=source('fake')))
1728 self.assertRaises(target, self.controller._shrink, req, id, body)
1731@ddt.ddt
1732class ShareAdminActionsAPITest(test.TestCase):
1734 def setUp(self):
1735 super(ShareAdminActionsAPITest, self).setUp()
1736 CONF.set_default("default_share_type", None)
1737 self.flags(transport_url='rabbit://fake:fake@mqhost:5672')
1738 self.share_api = share_api.API()
1739 self.admin_context = context.RequestContext('admin', 'fake', True)
1740 self.member_context = context.RequestContext('fake', 'fake')
1742 def _get_context(self, role):
1743 return getattr(self, '%s_context' % role)
1745 def _setup_share_data(self, share=None):
1746 if share is None:
1747 share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
1748 size='1',
1749 override_defaults=True)
1750 req = webob.Request.blank('/v2/fake/shares/%s/action' % share['id'])
1751 return share, req
1753 def _reset_status(self, ctxt, model, req, db_access_method,
1754 valid_code, valid_status=None, body=None):
1755 if body is None:
1756 body = {'os-reset_status': {'status': constants.STATUS_ERROR}}
1757 req.method = 'POST'
1758 req.headers['content-type'] = 'application/json'
1759 req.body = jsonutils.dumps(body).encode("utf-8")
1760 req.environ['manila.context'] = ctxt
1762 resp = req.get_response(fakes.app())
1764 # validate response code and model status
1765 self.assertEqual(valid_code, resp.status_int)
1767 if valid_code == 404:
1768 self.assertRaises(exception.NotFound,
1769 db_access_method,
1770 ctxt,
1771 model['id'])
1772 else:
1773 actual_model = db_access_method(ctxt, model['id'])
1774 self.assertEqual(valid_status, actual_model['status'])
1776 @ddt.data(
1777 {
1778 'role': 'admin',
1779 'valid_code': 202,
1780 'valid_status': constants.STATUS_ERROR,
1781 },
1782 {
1783 'role': 'member',
1784 'valid_code': 403,
1785 'valid_status': constants.STATUS_AVAILABLE,
1786 },
1787 )
1788 @ddt.unpack
1789 def test_share_reset_status_with_different_roles(self, role, valid_code,
1790 valid_status):
1791 share, req = self._setup_share_data()
1792 ctxt = self._get_context(role)
1794 self._reset_status(ctxt, share, req, db.share_get, valid_code,
1795 valid_status)
1797 @ddt.data(*fakes.fixture_invalid_reset_status_body)
1798 def test_share_invalid_reset_status_body(self, body):
1799 share, req = self._setup_share_data()
1800 ctxt = self.admin_context
1802 self._reset_status(ctxt, share, req, db.share_get, 400,
1803 constants.STATUS_AVAILABLE, body)
1805 def test_share_reset_status_for_missing(self):
1806 fake_share = {'id': 'missing-share-id'}
1807 req = webob.Request.blank('/fake/shares/%s/action' %
1808 fake_share['id'])
1810 self._reset_status(self.admin_context, fake_share, req,
1811 db.share_snapshot_get, 404)
1813 def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
1814 check_model_in_db=False):
1815 req.method = 'POST'
1816 req.headers['content-type'] = 'application/json'
1817 req.body = jsonutils.dumps({'os-force_delete': {}}).encode("utf-8")
1818 req.environ['manila.context'] = ctxt
1820 resp = req.get_response(fakes.app())
1822 # validate response
1823 self.assertEqual(valid_code, resp.status_int)
1825 if valid_code == 202 and check_model_in_db:
1826 self.assertRaises(exception.NotFound,
1827 db_access_method,
1828 ctxt,
1829 model['id'])
1831 @ddt.data(
1832 {'role': 'admin', 'resp_code': 202},
1833 {'role': 'member', 'resp_code': 403},
1834 )
1835 @ddt.unpack
1836 def test_share_force_delete_with_different_roles(self, role, resp_code):
1837 share, req = self._setup_share_data()
1838 ctxt = self._get_context(role)
1840 self._force_delete(ctxt, share, req, db.share_get, resp_code,
1841 check_model_in_db=True)
1843 def test_share_force_delete_missing(self):
1844 share, req = self._setup_share_data(share={'id': 'fake'})
1845 ctxt = self._get_context('admin')
1847 self._force_delete(ctxt, share, req, db.share_get, 404)