Coverage for manila/tests/api/v2/test_shares.py: 99%
1473 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 (c) 2015 Mirantis inc.
2# All Rights Reserved.
3#
4# Licensed under the Apache License, Version 2.0 (the "License"); you may
5# not use this file except in compliance with the License. You may obtain
6# a copy of the License at
7#
8# http://www.apache.org/licenses/LICENSE-2.0
9#
10# Unless required by applicable law or agreed to in writing, software
11# distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12# WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13# License for the specific language governing permissions and limitations
14# under the License.
16import copy
17import datetime
18import itertools
19from unittest import mock
21import ddt
22from oslo_config import cfg
23from oslo_serialization import jsonutils
24from oslo_utils import uuidutils
25import webob
26import webob.exc
28from manila.api import common
29from manila.api.openstack import api_version_request as api_version
30from manila.api.v2 import share_replicas
31from manila.api.v2 import shares
32from manila.common import constants
33from manila import context
34from manila import db
35from manila import exception
36from manila import policy
37from manila.share import api as share_api
38from manila.share import share_types
39from manila import test
40from manila.tests.api.contrib import stubs
41from manila.tests.api import fakes
42from manila.tests import db_utils
43from manila.tests import fake_share
44from manila.tests import utils as test_utils
45from manila import utils
47CONF = cfg.CONF
49LATEST_MICROVERSION = api_version._MAX_API_VERSION
52@ddt.ddt
53class ShareAPITest(test.TestCase):
54 """Share API Test."""
56 def setUp(self):
57 super(ShareAPITest, self).setUp()
58 self.controller = shares.ShareController()
59 self.mock_object(db, 'availability_zone_get')
60 self.mock_object(share_api.API, 'get_all',
61 stubs.stub_get_all_shares)
62 self.mock_object(share_api.API, 'get',
63 stubs.stub_share_get)
64 self.mock_object(share_api.API, 'update', stubs.stub_share_update)
65 self.mock_object(share_api.API, 'delete', stubs.stub_share_delete)
66 self.mock_object(share_api.API, 'soft_delete',
67 stubs.stub_share_soft_delete)
68 self.mock_object(share_api.API, 'restore', stubs.stub_share_restore)
69 self.mock_object(share_api.API, 'get_snapshot',
70 stubs.stub_snapshot_get)
71 self.mock_object(share_types, 'get_share_type',
72 stubs.stub_share_type_get)
73 self.maxDiff = None
74 self.share = {
75 "id": "1",
76 "size": 100,
77 "display_name": "Share Test Name",
78 "display_description": "Share Test Desc",
79 "share_proto": "fakeproto",
80 "availability_zone": "zone1:host1",
81 "is_public": False,
82 "task_state": None
83 }
84 self.share_in_recycle_bin = {
85 "id": "1",
86 "size": 100,
87 "display_name": "Share Test Name",
88 "display_description": "Share Test Desc",
89 "share_proto": "fakeproto",
90 "availability_zone": "zone1:host1",
91 "is_public": False,
92 "task_state": None,
93 "is_soft_deleted": True,
94 "status": "available"
95 }
96 self.share_in_recycle_bin_is_deleting = {
97 "id": "1",
98 "size": 100,
99 "display_name": "Share Test Name",
100 "display_description": "Share Test Desc",
101 "share_proto": "fakeproto",
102 "availability_zone": "zone1:host1",
103 "is_public": False,
104 "task_state": None,
105 "is_soft_deleted": True,
106 "status": "deleting"
107 }
108 self.create_mock = mock.Mock(
109 return_value=stubs.stub_share(
110 '1',
111 display_name=self.share['display_name'],
112 display_description=self.share['display_description'],
113 size=100,
114 share_proto=self.share['share_proto'].upper(),
115 instance={
116 'availability_zone': self.share['availability_zone'],
117 })
118 )
119 self.vt = {
120 'id': 'fake_volume_type_id',
121 'name': 'fake_volume_type_name',
122 'required_extra_specs': {
123 'driver_handles_share_servers': 'False'
124 },
125 'extra_specs': {
126 'driver_handles_share_servers': 'False'
127 }
128 }
129 self.snapshot = {
130 'id': '2',
131 'share_id': '1',
132 'status': constants.STATUS_AVAILABLE,
133 }
135 CONF.set_default("default_share_type", None)
136 self.mock_object(policy, 'check_policy')
138 def _process_expected_share_detailed_response(self, shr_dict, req_version):
139 """Sets version based parameters on share dictionary."""
141 share_dict = copy.deepcopy(shr_dict)
142 changed_parameters = {
143 '2.2': {'snapshot_support': True},
144 '2.5': {'task_state': None},
145 '2.6': {'share_type_name': None},
146 '2.10': {'access_rules_status': constants.ACCESS_STATE_ACTIVE},
147 '2.11': {'replication_type': None, 'has_replicas': False},
148 '2.16': {'user_id': 'fakeuser'},
149 '2.24': {'create_share_from_snapshot_support': True},
150 '2.27': {'revert_to_snapshot_support': False},
151 '2.31': {'share_group_id': None,
152 'source_share_group_snapshot_member_id': None},
153 '2.32': {'mount_snapshot_support': False},
154 '2.90': {'encryption_key_ref': None},
155 }
157 # Apply all the share transformations
158 if self.is_microversion_ge(req_version, '2.9'):
159 share_dict.pop('export_locations', None)
160 share_dict.pop('export_location', None)
162 for version, parameters in changed_parameters.items():
163 for param, default in parameters.items():
164 if self.is_microversion_ge(req_version, version):
165 share_dict[param] = share_dict.get(param, default)
166 else:
167 share_dict.pop(param, None)
169 return share_dict
171 def _get_expected_share_detailed_response(self, values=None,
172 admin=False, version='2.0'):
173 share = {
174 'id': '1',
175 'name': 'displayname',
176 'availability_zone': 'fakeaz',
177 'description': 'displaydesc',
178 'export_location': 'fake_location',
179 'export_locations': ['fake_location', 'fake_location2'],
180 'project_id': 'fakeproject',
181 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
182 'share_proto': 'FAKEPROTO',
183 'metadata': {},
184 'size': 1,
185 'snapshot_id': '2',
186 'share_network_id': None,
187 'status': 'fakestatus',
188 'share_type': '1',
189 'volume_type': '1',
190 'snapshot_support': True,
191 'is_public': False,
192 'task_state': None,
193 'share_type_name': None,
194 'links': [
195 {
196 'href': 'http://localhost/share/v2/fake/shares/1',
197 'rel': 'self'
198 },
199 {
200 'href': 'http://localhost/share/fake/shares/1',
201 'rel': 'bookmark'
202 }
203 ],
204 }
205 if values:
206 if 'display_name' in values:
207 values['name'] = values.pop('display_name')
208 if 'display_description' in values:
209 values['description'] = values.pop('display_description')
210 share.update(values)
211 if share.get('share_proto'): 211 ↛ 213line 211 didn't jump to line 213 because the condition on line 211 was always true
212 share['share_proto'] = share['share_proto'].upper()
213 if admin:
214 share['share_server_id'] = 'fake_share_server_id'
215 share['host'] = 'fakehost'
216 return {
217 'share': self._process_expected_share_detailed_response(
218 share, version)
219 }
221 def test__revert(self):
223 share = copy.deepcopy(self.share)
224 share['status'] = constants.STATUS_AVAILABLE
225 share['revert_to_snapshot_support'] = True
226 share["instances"] = [
227 {
228 "id": "fakeid",
229 "access_rules_status": constants.ACCESS_STATE_ACTIVE,
230 },
231 ]
232 share = fake_share.fake_share(**share)
233 snapshot = copy.deepcopy(self.snapshot)
234 snapshot['status'] = constants.STATUS_AVAILABLE
235 body = {'revert': {'snapshot_id': '2'}}
236 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
237 use_admin_context=False,
238 version='2.27')
239 mock_get = self.mock_object(
240 share_api.API, 'get', mock.Mock(return_value=share))
241 mock_get_snapshot = self.mock_object(
242 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
243 mock_get_latest_snapshot_for_share = self.mock_object(
244 share_api.API, 'get_latest_snapshot_for_share',
245 mock.Mock(return_value=snapshot))
246 mock_revert_to_snapshot = self.mock_object(
247 share_api.API, 'revert_to_snapshot')
249 response = self.controller._revert(req, '1', body=body)
251 self.assertEqual(202, response.status_int)
252 mock_get.assert_called_once_with(
253 utils.IsAMatcher(context.RequestContext), '1')
254 mock_get_snapshot.assert_called_once_with(
255 utils.IsAMatcher(context.RequestContext), '2')
256 mock_get_latest_snapshot_for_share.assert_called_once_with(
257 utils.IsAMatcher(context.RequestContext), '1')
258 mock_revert_to_snapshot.assert_called_once_with(
259 utils.IsAMatcher(context.RequestContext), share, snapshot)
261 def test__revert_share_has_been_soft_deleted(self):
262 snapshot = copy.deepcopy(self.snapshot)
263 body = {'revert': {'snapshot_id': '2'}}
264 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
265 use_admin_context=False,
266 version='2.27')
267 self.mock_object(share_api.API, 'get',
268 mock.Mock(return_value=self.share_in_recycle_bin))
269 self.mock_object(
270 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
271 self.assertRaises(
272 webob.exc.HTTPForbidden, self.controller._revert,
273 req, 1, body)
275 def test__revert_not_supported(self):
277 share = copy.deepcopy(self.share)
278 share['revert_to_snapshot_support'] = False
279 share = fake_share.fake_share(**share)
280 snapshot = copy.deepcopy(self.snapshot)
281 snapshot['status'] = constants.STATUS_AVAILABLE
282 snapshot['share_id'] = 'wrong_id'
283 body = {'revert': {'snapshot_id': '2'}}
284 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
285 use_admin_context=False,
286 version='2.27')
287 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
288 self.mock_object(
289 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
291 self.assertRaises(webob.exc.HTTPBadRequest,
292 self.controller._revert,
293 req,
294 '1',
295 body=body)
297 def test__revert_id_mismatch(self):
299 share = copy.deepcopy(self.share)
300 share['status'] = constants.STATUS_AVAILABLE
301 share['revert_to_snapshot_support'] = True
302 share = fake_share.fake_share(**share)
303 snapshot = copy.deepcopy(self.snapshot)
304 snapshot['status'] = constants.STATUS_AVAILABLE
305 snapshot['share_id'] = 'wrong_id'
306 body = {'revert': {'snapshot_id': '2'}}
307 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
308 use_admin_context=False,
309 version='2.27')
310 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
311 self.mock_object(
312 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
314 self.assertRaises(webob.exc.HTTPBadRequest,
315 self.controller._revert,
316 req,
317 '1',
318 body=body)
320 @ddt.data(
321 {
322 'share_status': constants.STATUS_ERROR,
323 'share_is_busy': False,
324 'snapshot_status': constants.STATUS_AVAILABLE,
325 }, {
326 'share_status': constants.STATUS_AVAILABLE,
327 'share_is_busy': True,
328 'snapshot_status': constants.STATUS_AVAILABLE,
329 }, {
330 'share_status': constants.STATUS_AVAILABLE,
331 'share_is_busy': False,
332 'snapshot_status': constants.STATUS_ERROR,
333 })
334 @ddt.unpack
335 def test__revert_invalid_status(self, share_status, share_is_busy,
336 snapshot_status):
338 share = copy.deepcopy(self.share)
339 share['status'] = share_status
340 share['is_busy'] = share_is_busy
341 share['revert_to_snapshot_support'] = True
342 share = fake_share.fake_share(**share)
343 snapshot = copy.deepcopy(self.snapshot)
344 snapshot['status'] = snapshot_status
345 body = {'revert': {'snapshot_id': '2'}}
346 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
347 use_admin_context=False,
348 version='2.27')
349 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
350 self.mock_object(
351 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
353 self.assertRaises(webob.exc.HTTPConflict,
354 self.controller._revert,
355 req,
356 '1',
357 body=body)
359 def test__revert_snapshot_latest_not_found(self):
361 share = copy.deepcopy(self.share)
362 share['status'] = constants.STATUS_AVAILABLE
363 share['revert_to_snapshot_support'] = True
364 share = fake_share.fake_share(**share)
365 snapshot = copy.deepcopy(self.snapshot)
366 snapshot['status'] = constants.STATUS_AVAILABLE
367 body = {'revert': {'snapshot_id': '2'}}
368 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
369 use_admin_context=False,
370 version='2.27')
371 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
372 self.mock_object(
373 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
374 self.mock_object(
375 share_api.API, 'get_latest_snapshot_for_share',
376 mock.Mock(return_value=None))
378 self.assertRaises(webob.exc.HTTPBadRequest,
379 self.controller._revert,
380 req,
381 '1',
382 body=body)
384 def test__revert_snapshot_access_applying(self):
386 share = copy.deepcopy(self.share)
387 share['status'] = constants.STATUS_AVAILABLE
388 share['revert_to_snapshot_support'] = True
389 share["instances"] = [
390 {
391 "id": "fakeid",
392 "access_rules_status": constants.SHARE_INSTANCE_RULES_SYNCING,
393 },
394 ]
395 share = fake_share.fake_share(**share)
396 snapshot = copy.deepcopy(self.snapshot)
397 snapshot['status'] = constants.STATUS_AVAILABLE
398 body = {'revert': {'snapshot_id': '2'}}
399 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
400 use_admin_context=False,
401 version='2.27')
402 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
403 self.mock_object(share_api.API, 'get_snapshot',
404 mock.Mock(return_value=snapshot))
405 self.mock_object(share_api.API, 'get_latest_snapshot_for_share',
406 mock.Mock(return_value=snapshot))
407 self.mock_object(share_api.API, 'revert_to_snapshot')
409 self.assertRaises(webob.exc.HTTPConflict,
410 self.controller._revert,
411 req,
412 '1',
413 body=body)
415 def test__revert_snapshot_not_latest(self):
417 share = copy.deepcopy(self.share)
418 share['status'] = constants.STATUS_AVAILABLE
419 share['revert_to_snapshot_support'] = True
420 share = fake_share.fake_share(**share)
421 snapshot = copy.deepcopy(self.snapshot)
422 snapshot['status'] = constants.STATUS_AVAILABLE
423 latest_snapshot = copy.deepcopy(self.snapshot)
424 latest_snapshot['status'] = constants.STATUS_AVAILABLE
425 latest_snapshot['id'] = '3'
426 body = {'revert': {'snapshot_id': '2'}}
427 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
428 use_admin_context=False,
429 version='2.27')
430 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
431 self.mock_object(
432 share_api.API, 'get_snapshot', mock.Mock(return_value=snapshot))
433 self.mock_object(
434 share_api.API, 'get_latest_snapshot_for_share',
435 mock.Mock(return_value=latest_snapshot))
437 self.assertRaises(webob.exc.HTTPConflict,
438 self.controller._revert,
439 req,
440 '1',
441 body=body)
443 @ddt.data(
444 {
445 'caught': exception.ShareNotFound,
446 'exc_args': {
447 'share_id': '1',
448 },
449 'thrown': webob.exc.HTTPNotFound,
450 }, {
451 'caught': exception.ShareSnapshotNotFound,
452 'exc_args': {
453 'snapshot_id': '2',
454 },
455 'thrown': webob.exc.HTTPBadRequest,
456 }, {
457 'caught': exception.ShareSizeExceedsAvailableQuota,
458 'exc_args': {},
459 'thrown': webob.exc.HTTPForbidden,
460 }, {
461 'caught': exception.ReplicationException,
462 'exc_args': {
463 'reason': 'catastrophic failure',
464 },
465 'thrown': webob.exc.HTTPBadRequest,
466 })
467 @ddt.unpack
468 def test__revert_exception(self, caught, exc_args, thrown):
470 body = {'revert': {'snapshot_id': '2'}}
471 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
472 use_admin_context=False,
473 version='2.27')
474 self.mock_object(
475 share_api.API, 'get', mock.Mock(side_effect=caught(**exc_args)))
477 self.assertRaises(thrown,
478 self.controller._revert,
479 req,
480 '1',
481 body=body)
483 @ddt.data("2.0", "2.1")
484 def test_share_create_original(self, microversion):
485 self.mock_object(share_api.API, 'create', self.create_mock)
486 body = {"share": copy.deepcopy(self.share)}
487 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=microversion)
489 res_dict = self.controller.create(req, body)
491 expected = self._get_expected_share_detailed_response(
492 self.share, version=microversion)
493 self.assertEqual(expected, res_dict)
495 @ddt.data("2.2", "2.3")
496 def test_share_create_with_snapshot_support_without_cg(self, microversion):
497 self.mock_object(share_api.API, 'create', self.create_mock)
498 body = {"share": copy.deepcopy(self.share)}
499 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=microversion)
501 res_dict = self.controller.create(req, body)
503 expected = self._get_expected_share_detailed_response(
504 self.share, version=microversion)
505 self.assertEqual(expected, res_dict)
507 def test_share_create_with_share_group(self):
508 self.mock_object(share_api.API, 'create', self.create_mock)
509 body = {"share": copy.deepcopy(self.share)}
510 req = fakes.HTTPRequest.blank('/v2/fake/shares', version="2.31",
511 experimental=True)
513 res_dict = self.controller.create(req, body)
515 expected = self._get_expected_share_detailed_response(
516 self.share, version="2.31")
517 self.assertEqual(expected, res_dict)
519 def test_share_create_with_sg_and_availability_zone(self):
520 sg_id = 'fake_sg_id'
521 az_id = 'bar_az_id'
522 az_name = 'fake_name'
523 self.mock_object(share_api.API, 'create', self.create_mock)
524 self.mock_object(
525 db, 'availability_zone_get',
526 mock.Mock(return_value=type(
527 'ReqAZ', (object, ), {"id": az_id, "name": az_name})))
528 self.mock_object(
529 db, 'share_group_get',
530 mock.Mock(return_value={"availability_zone_id": az_id}))
531 body = {"share": {
532 "size": 100,
533 "share_proto": "fakeproto",
534 "availability_zone": az_id,
535 "share_group_id": sg_id,
536 }}
537 req = fakes.HTTPRequest.blank(
538 '/v2/fake/shares', version="2.31", experimental=True)
540 self.controller.create(req, body)
542 self.assertEqual(db.availability_zone_get.call_count, 2)
543 db.availability_zone_get.assert_called_with(
544 req.environ['manila.context'], az_id)
545 db.share_group_get.assert_called_once_with(
546 req.environ['manila.context'], sg_id)
547 share_api.API.create.assert_called_once_with(
548 req.environ['manila.context'],
549 body['share']['share_proto'].upper(),
550 body['share']['size'],
551 None,
552 None,
553 share_group_id=body['share']['share_group_id'],
554 is_public=False,
555 metadata=None,
556 snapshot_id=None,
557 availability_zone=az_name,
558 scheduler_hints=None,
559 encryption_key_ref=None)
561 def test_share_create_with_sg_and_different_availability_zone(self):
562 sg_id = 'fake_sg_id'
563 sg_az = 'foo_az_id'
564 req_az = 'bar_az_id'
565 req_az_name = 'fake_az_name'
566 self.mock_object(share_api.API, 'create', self.create_mock)
567 self.mock_object(
568 db, 'availability_zone_get',
569 mock.Mock(return_value=type('ReqAZ', (object, ), {
570 "id": req_az, "name": req_az_name})))
571 self.mock_object(
572 db, 'share_group_get',
573 mock.Mock(return_value={"availability_zone_id": sg_az}))
574 body = {"share": {
575 "size": 100,
576 "share_proto": "fakeproto",
577 "availability_zone": req_az,
578 "share_group_id": sg_id,
579 }}
580 req = fakes.HTTPRequest.blank(
581 '/v2/fake/shares', version="2.31", experimental=True)
583 self.assertRaises(
584 exception.InvalidInput, self.controller.create, req, body)
586 db.availability_zone_get.assert_called_once_with(
587 req.environ['manila.context'], req_az)
588 db.share_group_get.assert_called_once_with(
589 req.environ['manila.context'], sg_id)
590 self.assertEqual(0, share_api.API.create.call_count)
592 def test_share_create_with_nonexistent_share_group(self):
593 sg_id = 'fake_sg_id'
594 self.mock_object(share_api.API, 'create', self.create_mock)
595 self.mock_object(db, 'availability_zone_get')
596 self.mock_object(
597 db, 'share_group_get',
598 mock.Mock(side_effect=exception.ShareGroupNotFound(
599 share_group_id=sg_id)))
600 body = {"share": {
601 "size": 100,
602 "share_proto": "fakeproto",
603 "share_group_id": sg_id,
604 }}
605 req = fakes.HTTPRequest.blank(
606 '/v2/fake/shares', version="2.31", experimental=True)
608 self.assertRaises(
609 webob.exc.HTTPNotFound, self.controller.create, req, body)
611 self.assertEqual(0, db.availability_zone_get.call_count)
612 self.assertEqual(0, share_api.API.create.call_count)
613 db.share_group_get.assert_called_once_with(
614 req.environ['manila.context'], sg_id)
616 @ddt.data({'encryption_support': True, 'dhss': True},
617 {'encryption_support': True, 'dhss': False},
618 {'encryption_support': False, 'dhss': True},
619 {'encryption_support': False, 'dhss': False})
620 @ddt.unpack
621 def test_share_create_with_encryption(self, encryption_support, dhss):
622 share = {
623 "id": "1",
624 "size": 100,
625 "display_name": "Share Test Name",
626 "display_description": "Share Test Desc",
627 "share_proto": "fakeproto",
628 "share_network_id": "fakenetid"
629 }
631 fake_network = {'id': 'fakenetid'}
632 create_mock = mock.Mock(
633 return_value=stubs.stub_share(
634 '1',
635 size=100,
636 display_name=share['display_name'],
637 display_description=share['display_description'],
638 share_proto=share['share_proto'].upper(),
639 share_network_id=share['share_network_id'],
640 instance={
641 'encryption_key_ref': 'fake_key_uuid',
642 })
643 )
644 self.mock_object(share_api.API, 'create', create_mock)
645 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
646 return_value=fake_network))
647 self.mock_object(common, 'check_share_network_is_active',
648 mock.Mock(return_value=True))
650 stype = copy.deepcopy(self.vt)
651 if dhss:
652 stype['extra_specs']['driver_handles_share_servers'] = 'true'
653 else:
654 stype['extra_specs']['driver_handles_share_servers'] = 'false'
655 if encryption_support:
656 stype['extra_specs']['encryption_support'] = 'share'
657 else:
658 stype['extra_specs']['encryption_support'] = 'fake'
660 self.mock_object(share_types, 'get_share_type_by_name', mock.Mock(
661 return_value=stype))
663 share['encryption_key_ref'] = 'fake_key_uuid'
665 request_args = copy.deepcopy(share)
666 request_args['share_type'] = 'fake_volume_type_name'
668 body = {"share": request_args}
669 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.90')
670 if dhss and encryption_support:
671 result = self.controller.create(req, body)
672 expected = self._get_expected_share_detailed_response(
673 share, version='2.90')
674 self.assertEqual(
675 expected['share']['encryption_key_ref'],
676 result['share']['encryption_key_ref'])
677 else:
678 self.assertRaises(webob.exc.HTTPBadRequest,
679 self.controller.create, req, body)
681 def test_share_create_with_valid_default_share_type(self):
682 self.mock_object(share_types, 'get_share_type_by_name',
683 mock.Mock(return_value=self.vt))
684 CONF.set_default("default_share_type", self.vt['name'])
685 self.mock_object(share_api.API, 'create', self.create_mock)
687 body = {"share": copy.deepcopy(self.share)}
688 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
689 res_dict = self.controller.create(req, body)
691 expected = self._get_expected_share_detailed_response(self.share,
692 version='2.7')
693 share_types.get_share_type_by_name.assert_called_once_with(
694 utils.IsAMatcher(context.RequestContext), self.vt['name'])
695 self.assertEqual(expected, res_dict)
697 def test_share_create_with_invalid_default_share_type(self):
698 self.mock_object(
699 share_types, 'get_default_share_type',
700 mock.Mock(side_effect=exception.ShareTypeNotFoundByName(
701 self.vt['name'])),
702 )
703 CONF.set_default("default_share_type", self.vt['name'])
704 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
705 self.assertRaises(exception.ShareTypeNotFoundByName,
706 self.controller.create, req, {'share': self.share})
707 share_types.get_default_share_type.assert_called_once_with()
709 def test_share_create_with_replication(self):
710 self.mock_object(share_api.API, 'create', self.create_mock)
712 body = {"share": copy.deepcopy(self.share)}
713 req = fakes.HTTPRequest.blank(
714 '/v2/fake/shares',
715 version=share_replicas.MIN_SUPPORTED_API_VERSION)
717 res_dict = self.controller.create(req, body)
719 expected = self._get_expected_share_detailed_response(
720 self.share, version=share_replicas.MIN_SUPPORTED_API_VERSION)
722 self.assertEqual(expected, res_dict)
724 def test_share_create_with_share_net(self):
725 shr = {
726 "size": 100,
727 "name": "Share Test Name",
728 "description": "Share Test Desc",
729 "share_proto": "fakeproto",
730 "availability_zone": "zone1:host1",
731 "share_network_id": "fakenetid"
732 }
733 fake_network = {'id': 'fakenetid'}
734 share_net_subnets = [db_utils.create_share_network_subnet(
735 id='fake_subnet_id', share_network_id=fake_network['id'])]
736 create_mock = mock.Mock(return_value=stubs.stub_share('1',
737 display_name=shr['name'],
738 display_description=shr['description'],
739 size=shr['size'],
740 share_proto=shr['share_proto'].upper(),
741 availability_zone=shr['availability_zone'],
742 share_network_id=shr['share_network_id']))
743 self.mock_object(share_api.API, 'create', create_mock)
744 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
745 return_value=fake_network))
746 self.mock_object(common, 'check_share_network_is_active',
747 mock.Mock(return_value=True))
748 self.mock_object(
749 db, 'share_network_subnets_get_all_by_availability_zone_id',
750 mock.Mock(return_value=share_net_subnets))
752 body = {"share": copy.deepcopy(shr)}
753 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
754 res_dict = self.controller.create(req, body)
756 expected = self._get_expected_share_detailed_response(
757 shr, version='2.7')
758 self.assertDictEqual(expected, res_dict)
759 # pylint: disable=unsubscriptable-object
760 self.assertEqual("fakenetid",
761 create_mock.call_args[1]['share_network_id'])
762 common.check_share_network_is_active.assert_called_once_with(
763 fake_network)
765 @ddt.data("2.15", "2.16")
766 def test_share_create_original_with_user_id(self, microversion):
767 self.mock_object(share_api.API, 'create', self.create_mock)
768 body = {"share": copy.deepcopy(self.share)}
769 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=microversion)
771 res_dict = self.controller.create(req, body)
773 expected = self._get_expected_share_detailed_response(
774 self.share, version=microversion)
776 self.assertEqual(expected, res_dict)
778 @ddt.data(test_utils.annotated('/v2.0_az_unsupported', ('2.0', False)),
779 test_utils.annotated('/v2.0_az_supported', ('2.0', True)),
780 test_utils.annotated('/v2.47_az_unsupported', ('2.47', False)),
781 test_utils.annotated('/v2.47_az_supported', ('2.47', True)))
782 @ddt.unpack
783 def test_share_create_with_share_type_azs(self, version, az_supported):
784 """For API version<2.48, AZ validation should not be performed."""
785 self.mock_object(share_api.API, 'create', self.create_mock)
786 create_args = copy.deepcopy(self.share)
787 create_args['availability_zone'] = 'az1' if az_supported else 'az2'
788 create_args['share_type'] = uuidutils.generate_uuid()
789 stype_with_azs = copy.deepcopy(self.vt)
790 stype_with_azs['extra_specs']['availability_zones'] = 'az1,az3'
791 self.mock_object(share_types, 'get_share_type', mock.Mock(
792 return_value=stype_with_azs))
794 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=version)
796 res_dict = self.controller.create(req, {'share': create_args})
798 expected = self._get_expected_share_detailed_response(
799 values=self.share, version=version)
801 self.assertEqual(expected, res_dict)
803 @ddt.data(*set([
804 test_utils.annotated('v2.48_share_from_snap', ('2.48', True)),
805 test_utils.annotated('v2.48_share_not_from_snap', ('2.48', False)),
806 test_utils.annotated('v%s_share_from_snap' % LATEST_MICROVERSION,
807 (LATEST_MICROVERSION, True)),
808 test_utils.annotated('v%s_share_not_from_snap' % LATEST_MICROVERSION,
809 (LATEST_MICROVERSION, False))]))
810 @ddt.unpack
811 def test_share_create_az_not_in_share_type(self, version, snap):
812 """For API version>=2.48, AZ validation should be performed."""
813 self.mock_object(share_api.API, 'create', self.create_mock)
814 create_args = copy.deepcopy(self.share)
815 create_args['availability_zone'] = 'az2'
816 create_args['share_type'] = (uuidutils.generate_uuid() if not snap
817 else None)
818 create_args['snapshot_id'] = (uuidutils.generate_uuid() if snap
819 else None)
820 stype_with_azs = copy.deepcopy(self.vt)
821 stype_with_azs['extra_specs']['availability_zones'] = 'az1 , az3'
822 self.mock_object(share_types, 'get_share_type', mock.Mock(
823 return_value=stype_with_azs))
824 self.mock_object(share_api.API, 'get_snapshot',
825 stubs.stub_snapshot_get)
827 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=version)
829 self.assertRaises(webob.exc.HTTPBadRequest,
830 self.controller.create,
831 req, {'share': create_args})
832 share_api.API.create.assert_not_called()
834 def test_migration_start(self):
835 share = db_utils.create_share()
836 share_network = db_utils.create_share_network()
837 share_type = {'share_type_id': 'fake_type_id'}
838 req = fakes.HTTPRequest.blank(
839 '/v2/fake/shares/%s/action' % share['id'],
840 use_admin_context=True,
841 version='2.29')
842 req.method = 'POST'
843 req.headers['content-type'] = 'application/json'
844 req.api_version_request.experimental = True
845 context = req.environ['manila.context']
847 self.mock_object(db, 'share_network_get', mock.Mock(
848 return_value=share_network))
849 self.mock_object(db, 'share_type_get', mock.Mock(
850 return_value=share_type))
852 body = {
853 'migration_start': {
854 'host': 'fake_host',
855 'preserve_metadata': True,
856 'preserve_snapshots': True,
857 'writable': True,
858 'nondisruptive': True,
859 'new_share_network_id': 'fake_net_id',
860 'new_share_type_id': 'fake_type_id',
861 }
862 }
864 self.mock_object(share_api.API, 'migration_start',
865 mock.Mock(return_value=202))
866 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
868 response = self.controller.migration_start(req, share['id'], body=body)
870 self.assertEqual(202, response.status_int)
872 share_api.API.get.assert_called_once_with(context, share['id'])
873 share_api.API.migration_start.assert_called_once_with(
874 context, share, 'fake_host', False, True, True, True, True,
875 new_share_network=share_network, new_share_type=share_type)
876 db.share_network_get.assert_called_once_with(
877 context, 'fake_net_id')
878 db.share_type_get.assert_called_once_with(
879 context, 'fake_type_id')
881 def test_migration_start_conflict(self):
882 share = db_utils.create_share()
883 req = fakes.HTTPRequest.blank(
884 '/v2/fake/shares/%s/action' % share['id'], use_admin_context=True)
885 req.method = 'POST'
886 req.headers['content-type'] = 'application/json'
887 req.api_version_request = api_version.APIVersionRequest('2.29')
888 req.api_version_request.experimental = True
890 body = {
891 'migration_start': {
892 'host': 'fake_host',
893 'preserve_metadata': True,
894 'preserve_snapshots': True,
895 'writable': True,
896 'nondisruptive': True,
897 }
898 }
900 self.mock_object(share_api.API, 'migration_start',
901 mock.Mock(side_effect=exception.Conflict(err='err')))
903 self.assertRaises(webob.exc.HTTPConflict,
904 self.controller.migration_start,
905 req, share['id'], body=body)
907 @ddt.data('nondisruptive', 'writable', 'preserve_metadata',
908 'preserve_snapshots', 'host', 'body')
909 def test_migration_start_missing_mandatory(self, param):
910 share = db_utils.create_share()
911 req = fakes.HTTPRequest.blank(
912 '/v2/fake/shares/%s/action' % share['id'],
913 use_admin_context=True,
914 version='2.29')
915 req.method = 'POST'
916 req.headers['content-type'] = 'application/json'
917 req.api_version_request.experimental = True
919 body = {
920 'migration_start': {
921 'host': 'fake_host',
922 'preserve_metadata': True,
923 'preserve_snapshots': True,
924 'writable': True,
925 'nondisruptive': True,
926 }
927 }
929 if param == 'body':
930 body.pop('migration_start')
931 else:
932 body['migration_start'].pop(param)
934 self.mock_object(share_api.API, 'migration_start')
935 self.mock_object(share_api.API, 'get',
936 mock.Mock(return_value=share))
938 self.assertRaises(exception.ValidationError,
939 self.controller.migration_start,
940 req, 'fake_id', body=body)
942 @ddt.data('nondisruptive', 'writable', 'preserve_metadata',
943 'preserve_snapshots', 'force_host_assisted_migration')
944 def test_migration_start_non_boolean(self, param):
945 share = db_utils.create_share()
946 req = fakes.HTTPRequest.blank(
947 '/v2/fake/shares/%s/action' % share['id'],
948 use_admin_context=True,
949 version='2.29')
950 req.method = 'POST'
951 req.headers['content-type'] = 'application/json'
952 req.api_version_request.experimental = True
954 body = {
955 'migration_start': {
956 'host': 'fake_host',
957 'preserve_metadata': True,
958 'preserve_snapshots': True,
959 'writable': True,
960 'nondisruptive': True,
961 }
962 }
964 body['migration_start'][param] = None
966 self.mock_object(share_api.API, 'migration_start')
967 self.mock_object(share_api.API, 'get',
968 mock.Mock(return_value=share))
970 self.assertRaises(exception.ValidationError,
971 self.controller.migration_start,
972 req, 'fake_id', body=body)
974 def test_migration_start_no_share_id(self):
975 req = fakes.HTTPRequest.blank('/v2/fake/shares/%s/action' % 'fake_id',
976 use_admin_context=True, version='2.29')
977 req.method = 'POST'
978 req.headers['content-type'] = 'application/json'
979 req.api_version_request.experimental = True
981 body = {
982 'migration_start': {
983 'host': 'fake_host',
984 'preserve_metadata': True,
985 'preserve_snapshots': True,
986 'writable': True,
987 'nondisruptive': True,
988 }
989 }
991 self.mock_object(share_api.API, 'get',
992 mock.Mock(side_effect=[exception.NotFound]))
993 self.assertRaises(webob.exc.HTTPNotFound,
994 self.controller.migration_start,
995 req, 'fake_id', body=body)
997 def test_migration_start_new_share_network_not_found(self):
998 share = db_utils.create_share()
999 req = fakes.HTTPRequest.blank(
1000 '/v2/fake/shares/%s/action' % share['id'],
1001 use_admin_context=True,
1002 version='2.29')
1003 context = req.environ['manila.context']
1004 req.method = 'POST'
1005 req.headers['content-type'] = 'application/json'
1006 req.api_version_request.experimental = True
1008 body = {
1009 'migration_start': {
1010 'host': 'fake_host',
1011 'preserve_metadata': True,
1012 'preserve_snapshots': True,
1013 'writable': True,
1014 'nondisruptive': True,
1015 'new_share_network_id': 'nonexistent'}}
1017 self.mock_object(db, 'share_network_get',
1018 mock.Mock(side_effect=exception.NotFound()))
1019 self.assertRaises(webob.exc.HTTPBadRequest,
1020 self.controller.migration_start,
1021 req, share['id'], body=body)
1022 db.share_network_get.assert_called_once_with(context, 'nonexistent')
1024 def test_migration_start_new_share_type_not_found(self):
1025 share = db_utils.create_share()
1026 req = fakes.HTTPRequest.blank(
1027 '/v2/fake/shares/%s/action' % share['id'],
1028 use_admin_context=True,
1029 version='2.29')
1030 context = req.environ['manila.context']
1031 req.method = 'POST'
1032 req.headers['content-type'] = 'application/json'
1033 req.api_version_request.experimental = True
1035 body = {
1036 'migration_start': {
1037 'host': 'fake_host',
1038 'preserve_metadata': True,
1039 'preserve_snapshots': True,
1040 'writable': True,
1041 'nondisruptive': True,
1042 'new_share_type_id': 'nonexistent'}}
1044 self.mock_object(db, 'share_type_get',
1045 mock.Mock(side_effect=exception.NotFound()))
1046 self.assertRaises(webob.exc.HTTPBadRequest,
1047 self.controller.migration_start,
1048 req, share['id'], body=body)
1049 db.share_type_get.assert_called_once_with(context, 'nonexistent')
1051 def test_migration_start_invalid_force_host_assisted_migration(self):
1052 share = db_utils.create_share()
1053 req = fakes.HTTPRequest.blank(
1054 '/v2/fake/shares/%s/action' % share['id'],
1055 use_admin_context=True,
1056 version='2.29')
1057 req.method = 'POST'
1058 req.headers['content-type'] = 'application/json'
1059 req.api_version_request.experimental = True
1061 body = {'migration_start': {'host': 'fake_host',
1062 'force_host_assisted_migration': 'fake'}}
1064 self.assertRaises(exception.ValidationError,
1065 self.controller.migration_start,
1066 req, share['id'], body=body)
1068 @ddt.data('writable', 'preserve_metadata')
1069 def test_migration_start_invalid_writable_preserve_metadata(
1070 self, parameter):
1071 share = db_utils.create_share()
1072 req = fakes.HTTPRequest.blank(
1073 '/v2/fake/shares/%s/action' % share['id'],
1074 use_admin_context=True,
1075 version='2.29')
1076 req.method = 'POST'
1077 req.headers['content-type'] = 'application/json'
1078 req.api_version_request.experimental = True
1080 body = {'migration_start': {'host': 'fake_host',
1081 parameter: 'invalid'}}
1083 self.assertRaises(exception.ValidationError,
1084 self.controller.migration_start, req, share['id'],
1085 body=body)
1087 @ddt.data(constants.TASK_STATE_MIGRATION_ERROR, None)
1088 def test_reset_task_state(self, task_state):
1089 share = db_utils.create_share()
1090 req = fakes.HTTPRequest.blank(
1091 '/v2/fake/shares/%s/action' % share['id'],
1092 use_admin_context=True,
1093 version='2.22')
1094 req.method = 'POST'
1095 req.headers['content-type'] = 'application/json'
1096 req.api_version_request.experimental = True
1098 update = {'task_state': task_state}
1099 body = {'reset_task_state': update}
1101 self.mock_object(db, 'share_update')
1103 response = self.controller.reset_task_state(
1104 req, share['id'], body=body)
1106 self.assertEqual(202, response.status_int)
1108 db.share_update.assert_called_once_with(utils.IsAMatcher(
1109 context.RequestContext), share['id'], update)
1111 def test_reset_task_state_error_body(self):
1112 share = db_utils.create_share()
1113 req = fakes.HTTPRequest.blank(
1114 '/v2/fake/shares/%s/action' % share['id'],
1115 use_admin_context=True,
1116 version='2.22')
1117 req.method = 'POST'
1118 req.headers['content-type'] = 'application/json'
1119 req.api_version_request.experimental = True
1121 update = {'error': 'error'}
1122 body = {'reset_task_state': update}
1124 self.assertRaises(webob.exc.HTTPBadRequest,
1125 self.controller.reset_task_state, req, share['id'],
1126 body=body)
1128 def test_reset_task_state_error_invalid(self):
1129 share = db_utils.create_share()
1130 req = fakes.HTTPRequest.blank(
1131 '/v2/fake/shares/%s/action' % share['id'],
1132 use_admin_context=True,
1133 version='2.22')
1134 req.method = 'POST'
1135 req.headers['content-type'] = 'application/json'
1136 req.api_version_request.experimental = True
1138 update = {'task_state': 'error'}
1139 body = {'reset_task_state': update}
1141 self.assertRaises(exception.ValidationError,
1142 self.controller.reset_task_state, req, share['id'],
1143 body=body)
1145 def test_reset_task_state_not_found(self):
1146 share = db_utils.create_share()
1147 req = fakes.HTTPRequest.blank(
1148 '/v2/fake/shares/%s/action' % share['id'],
1149 use_admin_context=True,
1150 version='2.22')
1151 req.method = 'POST'
1152 req.headers['content-type'] = 'application/json'
1153 req.api_version_request.experimental = True
1155 update = {'task_state': constants.TASK_STATE_MIGRATION_ERROR}
1156 body = {'reset_task_state': update}
1158 self.mock_object(share_api.API, 'get',
1159 mock.Mock(side_effect=exception.NotFound))
1160 self.mock_object(db, 'share_update')
1162 self.assertRaises(exception.NotFound,
1163 self.controller.reset_task_state, req, share['id'],
1164 body=body)
1166 share_api.API.get.assert_called_once_with(utils.IsAMatcher(
1167 context.RequestContext), share['id'])
1168 db.share_update.assert_not_called()
1170 def test_reset_task_state_share_other_project_public_share(self):
1171 share = db_utils.create_share(is_public=True)
1172 req = fakes.HTTPRequest.blank(
1173 '/v2/fake/shares/%s/action' % share['id'],
1174 use_admin_context=True, version=LATEST_MICROVERSION)
1175 req.method = 'POST'
1176 req.headers['content-type'] = 'application/json'
1177 req.api_version_request.experimental = True
1178 update = {'task_state': constants.TASK_STATE_MIGRATION_ERROR}
1179 body = {'reset_task_state': update}
1181 # NOTE(gouthamr): we're testing a scenario where someone has access
1182 # to the RBAC rule share:reset_task_state, but doesn't own the share.
1183 # Ideally we'd override the default policy, but it's a shared
1184 # resource and we'll bleed into other tests, so we'll mock the
1185 # policy check to return False instead
1186 rbac_checks = [None, None, exception.NotAuthorized]
1187 with mock.patch.object(policy, 'check_policy',
1188 side_effect=rbac_checks):
1189 self.mock_object(share_api.API, 'get',
1190 mock.Mock(return_value=share))
1191 self.assertRaises(webob.exc.HTTPForbidden,
1192 self.controller.reset_task_state,
1193 req, share['id'], body=body)
1195 def test_reset_task_state_share_has_been_soft_deleted(self):
1196 share = self.share_in_recycle_bin
1197 req = fakes.HTTPRequest.blank(
1198 '/v2/fake/shares/%s/action' % share['id'],
1199 use_admin_context=True,
1200 version='2.22')
1201 req.method = 'POST'
1202 req.headers['content-type'] = 'application/json'
1203 req.api_version_request.experimental = True
1204 update = {'task_state': constants.TASK_STATE_MIGRATION_ERROR}
1205 body = {'reset_task_state': update}
1206 self.mock_object(share_api.API, 'get',
1207 mock.Mock(return_value=share))
1209 self.assertRaises(webob.exc.HTTPForbidden,
1210 self.controller.reset_task_state, req, share['id'],
1211 body=body)
1213 def test_migration_complete(self):
1214 share = db_utils.create_share()
1215 req = fakes.HTTPRequest.blank(
1216 '/v2/fake/shares/%s/action' % share['id'],
1217 use_admin_context=True,
1218 version='2.22')
1219 req.method = 'POST'
1220 req.headers['content-type'] = 'application/json'
1221 req.api_version_request.experimental = True
1223 body = {'migration_complete': None}
1225 self.mock_object(share_api.API, 'get',
1226 mock.Mock(return_value=share))
1228 self.mock_object(share_api.API, 'migration_complete')
1230 response = self.controller.migration_complete(
1231 req, share['id'], body=body)
1233 self.assertEqual(202, response.status_int)
1235 share_api.API.migration_complete.assert_called_once_with(
1236 utils.IsAMatcher(context.RequestContext), share)
1238 def test_migration_complete_not_found(self):
1239 share = db_utils.create_share()
1240 req = fakes.HTTPRequest.blank(
1241 '/v2/fake/shares/%s/action' % share['id'],
1242 use_admin_context=True,
1243 version='2.22')
1244 req.method = 'POST'
1245 req.headers['content-type'] = 'application/json'
1246 req.api_version_request.experimental = True
1248 body = {'migration_complete': None}
1250 self.mock_object(share_api.API, 'get',
1251 mock.Mock(side_effect=exception.NotFound()))
1252 self.mock_object(share_api.API, 'migration_complete')
1254 self.assertRaises(webob.exc.HTTPNotFound,
1255 self.controller.migration_complete, req, share['id'],
1256 body=body)
1258 def test_migration_cancel(self):
1259 share = db_utils.create_share()
1260 req = fakes.HTTPRequest.blank(
1261 '/v2/fake/shares/%s/action' % share['id'],
1262 use_admin_context=True,
1263 version='2.22')
1264 req.method = 'POST'
1265 req.headers['content-type'] = 'application/json'
1266 req.api_version_request.experimental = True
1268 body = {'migration_cancel': None}
1270 self.mock_object(share_api.API, 'get',
1271 mock.Mock(return_value=share))
1273 self.mock_object(share_api.API, 'migration_cancel')
1275 response = self.controller.migration_cancel(
1276 req, share['id'], body=body)
1278 self.assertEqual(202, response.status_int)
1280 share_api.API.migration_cancel.assert_called_once_with(
1281 utils.IsAMatcher(context.RequestContext), share)
1283 def test_migration_cancel_not_found(self):
1284 share = db_utils.create_share()
1285 req = fakes.HTTPRequest.blank(
1286 '/v2/fake/shares/%s/action' % share['id'],
1287 use_admin_context=True,
1288 version='2.22')
1289 req.method = 'POST'
1290 req.headers['content-type'] = 'application/json'
1291 req.api_version_request.experimental = True
1293 body = {'migration_cancel': None}
1295 self.mock_object(share_api.API, 'get',
1296 mock.Mock(side_effect=exception.NotFound()))
1297 self.mock_object(share_api.API, 'migration_cancel')
1299 self.assertRaises(webob.exc.HTTPNotFound,
1300 self.controller.migration_cancel, req, share['id'],
1301 body=body)
1303 def test_migration_get_progress(self):
1304 share = db_utils.create_share(
1305 task_state=constants.TASK_STATE_MIGRATION_SUCCESS)
1306 req = fakes.HTTPRequest.blank(
1307 '/v2/fake/shares/%s/action' % share['id'],
1308 use_admin_context=True,
1309 version='2.22')
1310 req.method = 'POST'
1311 req.headers['content-type'] = 'application/json'
1312 req.api_version_request.experimental = True
1314 body = {'migration_get_progress': None}
1315 expected = {
1316 'total_progress': 50,
1317 'task_state': constants.TASK_STATE_MIGRATION_SUCCESS,
1318 }
1320 self.mock_object(share_api.API, 'get',
1321 mock.Mock(return_value=share))
1323 self.mock_object(share_api.API, 'migration_get_progress',
1324 mock.Mock(return_value=copy.deepcopy(expected)))
1326 response = self.controller.migration_get_progress(req, share['id'],
1327 body=body)
1329 self.assertEqual(expected, response)
1331 share_api.API.migration_get_progress.assert_called_once_with(
1332 utils.IsAMatcher(context.RequestContext), share)
1334 def test_migration_get_progress_not_found(self):
1335 share = db_utils.create_share()
1336 req = fakes.HTTPRequest.blank(
1337 '/v2/fake/shares/%s/action' % share['id'],
1338 use_admin_context=True,
1339 version='2.22')
1340 req.method = 'POST'
1341 req.headers['content-type'] = 'application/json'
1342 req.api_version_request.experimental = True
1344 body = {'migration_get_progress': None}
1346 self.mock_object(share_api.API, 'get',
1347 mock.Mock(side_effect=exception.NotFound()))
1348 self.mock_object(share_api.API, 'migration_get_progress')
1350 self.assertRaises(webob.exc.HTTPNotFound,
1351 self.controller.migration_get_progress, req,
1352 share['id'], body=body)
1354 def test_share_create_from_snapshot_without_share_net_no_parent(self):
1355 shr = {
1356 "size": 100,
1357 "name": "Share Test Name",
1358 "description": "Share Test Desc",
1359 "share_proto": "fakeproto",
1360 "availability_zone": "zone1:host1",
1361 "snapshot_id": 333,
1362 "share_network_id": None,
1363 }
1364 create_mock = mock.Mock(return_value=stubs.stub_share('1',
1365 display_name=shr['name'],
1366 display_description=shr['description'],
1367 size=shr['size'],
1368 share_proto=shr['share_proto'].upper(),
1369 snapshot_id=shr['snapshot_id'],
1370 instance=dict(
1371 availability_zone=shr['availability_zone'],
1372 share_network_id=shr['share_network_id'])))
1373 self.mock_object(share_api.API, 'create', create_mock)
1374 body = {"share": copy.deepcopy(shr)}
1375 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
1377 res_dict = self.controller.create(req, body)
1379 expected = self._get_expected_share_detailed_response(
1380 shr, version='2.7')
1381 self.assertEqual(expected, res_dict)
1383 def test_share_create_from_snapshot_without_share_net_parent_exists(self):
1384 shr = {
1385 "size": 100,
1386 "name": "Share Test Name",
1387 "description": "Share Test Desc",
1388 "share_proto": "fakeproto",
1389 "availability_zone": "zone1:host1",
1390 "snapshot_id": 333,
1391 "share_network_id": None,
1392 }
1393 parent_share_net = 444
1394 fake_network = {'id': parent_share_net}
1395 share_net_subnets = [db_utils.create_share_network_subnet(
1396 id='fake_subnet_id', share_network_id=fake_network['id'])]
1397 create_mock = mock.Mock(return_value=stubs.stub_share('1',
1398 display_name=shr['name'],
1399 display_description=shr['description'],
1400 size=shr['size'],
1401 share_proto=shr['share_proto'].upper(),
1402 snapshot_id=shr['snapshot_id'],
1403 instance=dict(
1404 availability_zone=shr['availability_zone'],
1405 share_network_id=shr['share_network_id'])))
1406 self.mock_object(share_api.API, 'create', create_mock)
1407 self.mock_object(share_api.API, 'get_snapshot',
1408 stubs.stub_snapshot_get)
1409 self.mock_object(common, 'check_share_network_is_active',
1410 mock.Mock(return_value=True))
1411 parent_share = stubs.stub_share(
1412 '1', instance={'share_network_id': parent_share_net},
1413 create_share_from_snapshot_support=True)
1414 self.mock_object(share_api.API, 'get', mock.Mock(
1415 return_value=parent_share))
1416 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
1417 return_value=fake_network))
1418 self.mock_object(
1419 db, 'share_network_subnets_get_all_by_availability_zone_id',
1420 mock.Mock(return_value=share_net_subnets))
1422 body = {"share": copy.deepcopy(shr)}
1423 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
1425 res_dict = self.controller.create(req, body)
1427 expected = self._get_expected_share_detailed_response(
1428 shr, version='2.7')
1429 self.assertEqual(expected, res_dict)
1430 # pylint: disable=unsubscriptable-object
1431 self.assertEqual(parent_share_net,
1432 create_mock.call_args[1]['share_network_id'])
1433 common.check_share_network_is_active.assert_called_once_with(
1434 fake_network)
1436 def test_share_create_from_snapshot_with_share_net_equals_parent(self):
1437 parent_share_net = 444
1438 shr = {
1439 "size": 100,
1440 "name": "Share Test Name",
1441 "description": "Share Test Desc",
1442 "share_proto": "fakeproto",
1443 "availability_zone": "zone1:host1",
1444 "snapshot_id": 333,
1445 "share_network_id": parent_share_net,
1446 }
1447 share_net_subnets = [db_utils.create_share_network_subnet(
1448 id='fake_subnet_id', share_network_id=parent_share_net)]
1449 create_mock = mock.Mock(return_value=stubs.stub_share('1',
1450 display_name=shr['name'],
1451 display_description=shr['description'],
1452 size=shr['size'],
1453 share_proto=shr['share_proto'].upper(),
1454 snapshot_id=shr['snapshot_id'],
1455 instance=dict(
1456 availability_zone=shr['availability_zone'],
1457 share_network_id=shr['share_network_id'])))
1458 self.mock_object(share_api.API, 'create', create_mock)
1459 self.mock_object(share_api.API, 'get_snapshot',
1460 stubs.stub_snapshot_get)
1461 parent_share = stubs.stub_share(
1462 '1', instance={'share_network_id': parent_share_net},
1463 create_share_from_snapshot_support=True)
1464 self.mock_object(share_api.API, 'get', mock.Mock(
1465 return_value=parent_share))
1466 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
1467 return_value={'id': parent_share_net}))
1468 self.mock_object(common, 'check_share_network_is_active',
1469 mock.Mock(return_value=True))
1470 self.mock_object(
1471 db, 'share_network_subnets_get_all_by_availability_zone_id',
1472 mock.Mock(return_value=share_net_subnets))
1474 body = {"share": copy.deepcopy(shr)}
1475 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
1476 res_dict = self.controller.create(req, body)
1477 expected = self._get_expected_share_detailed_response(
1478 shr, version='2.7')
1479 self.assertDictEqual(expected, res_dict)
1480 # pylint: disable=unsubscriptable-object
1481 self.assertEqual(parent_share_net,
1482 create_mock.call_args[1]['share_network_id'])
1484 def test_share_create_from_snapshot_invalid_share_net(self):
1485 self.mock_object(share_api.API, 'create')
1486 shr = {
1487 "size": 100,
1488 "name": "Share Test Name",
1489 "description": "Share Test Desc",
1490 "share_proto": "fakeproto",
1491 "availability_zone": "zone1:host1",
1492 "snapshot_id": 333,
1493 "share_network_id": 1234,
1494 }
1495 body = {"share": shr}
1496 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7')
1497 self.assertRaises(webob.exc.HTTPBadRequest,
1498 self.controller.create, req, body)
1500 def test_share_create_from_snapshot_not_supported(self):
1501 parent_share_net = 444
1502 self.mock_object(share_api.API, 'create')
1503 shr = {
1504 "size": 100,
1505 "name": "Share Test Name",
1506 "description": "Share Test Desc",
1507 "share_proto": "fakeproto",
1508 "availability_zone": "zone1:host1",
1509 "snapshot_id": 333,
1510 "share_network_id": parent_share_net,
1511 }
1512 parent_share = stubs.stub_share(
1513 '1', instance={'share_network_id': parent_share_net},
1514 create_share_from_snapshot_support=False)
1515 self.mock_object(share_api.API, 'get', mock.Mock(
1516 return_value=parent_share))
1517 self.mock_object(share_api.API, 'get_share_network', mock.Mock(
1518 return_value={'id': parent_share_net}))
1520 body = {"share": shr}
1521 req = fakes.HTTPRequest.blank('/shares', version='2.24')
1522 self.assertRaises(webob.exc.HTTPBadRequest,
1523 self.controller.create, req, body)
1525 def test_share_creation_fails_with_bad_size(self):
1526 shr = {"size": '',
1527 "name": "Share Test Name",
1528 "description": "Share Test Desc",
1529 "share_proto": "fakeproto",
1530 "availability_zone": "zone1:host1"}
1531 body = {"share": shr}
1532 req = fakes.HTTPRequest.blank('/shares', version='2.7')
1533 self.assertRaises(exception.InvalidInput,
1534 self.controller.create, req, body)
1536 def test_share_create_no_body(self):
1537 req = fakes.HTTPRequest.blank('/shares', version='2.7')
1538 self.assertRaises(webob.exc.HTTPUnprocessableEntity,
1539 self.controller.create, req, {})
1541 def test_share_create_invalid_availability_zone(self):
1542 self.mock_object(
1543 db,
1544 'availability_zone_get',
1545 mock.Mock(side_effect=exception.AvailabilityZoneNotFound(id='id'))
1546 )
1547 body = {"share": copy.deepcopy(self.share)}
1549 req = fakes.HTTPRequest.blank('/v2/shares', version='2.7')
1550 self.assertRaises(webob.exc.HTTPNotFound,
1551 self.controller.create,
1552 req,
1553 body)
1555 def test_share_show(self):
1556 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1557 expected = self._get_expected_share_detailed_response()
1559 res_dict = self.controller.show(req, '1')
1561 self.assertEqual(expected, res_dict)
1563 def test_share_show_with_share_group(self):
1564 req = fakes.HTTPRequest.blank(
1565 '/v2/fake/shares/1', version='2.31', experimental=True)
1566 expected = self._get_expected_share_detailed_response(version='2.31')
1568 res_dict = self.controller.show(req, '1')
1570 self.assertDictEqual(expected, res_dict)
1572 def test_share_show_with_share_group_earlier_version(self):
1573 req = fakes.HTTPRequest.blank(
1574 '/v2/fake/shares/1', version='2.23', experimental=True)
1575 expected = self._get_expected_share_detailed_response(version='2.23')
1577 res_dict = self.controller.show(req, '1')
1579 self.assertDictEqual(expected, res_dict)
1581 def test_share_show_with_share_type_name(self):
1582 req = fakes.HTTPRequest.blank('/v2/fake/shares/1', version='2.6')
1584 res_dict = self.controller.show(req, '1')
1586 expected = self._get_expected_share_detailed_response(version='2.6')
1587 self.assertEqual(expected, res_dict)
1589 @ddt.data("2.15", "2.16")
1590 def test_share_show_with_user_id(self, microversion):
1591 req = fakes.HTTPRequest.blank('/v2/fake/shares/1',
1592 version=microversion)
1594 res_dict = self.controller.show(req, '1')
1596 expected = self._get_expected_share_detailed_response(
1597 version=microversion)
1599 self.assertEqual(expected, res_dict)
1601 def test_share_show_admin(self):
1602 req = fakes.HTTPRequest.blank('/v2/fake/shares/1',
1603 use_admin_context=True)
1604 expected = self._get_expected_share_detailed_response(admin=True)
1606 res_dict = self.controller.show(req, '1')
1608 self.assertEqual(expected, res_dict)
1610 def test_share_show_no_share(self):
1611 self.mock_object(share_api.API, 'get',
1612 stubs.stub_share_get_notfound)
1613 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1614 self.assertRaises(webob.exc.HTTPNotFound,
1615 self.controller.show,
1616 req, '1')
1618 def test_share_show_with_replication_type(self):
1619 api_vers = share_replicas.MIN_SUPPORTED_API_VERSION
1620 req = fakes.HTTPRequest.blank('/v2/fake/shares/1', version=api_vers)
1621 res_dict = self.controller.show(req, '1')
1623 expected = self._get_expected_share_detailed_response(version=api_vers)
1625 self.assertEqual(expected, res_dict)
1627 @ddt.data(('2.10', True), ('2.27', True), ('2.28', False))
1628 @ddt.unpack
1629 def test_share_show_access_rules_status_translated(self, version,
1630 translated):
1631 share = db_utils.create_share(
1632 access_rules_status=constants.SHARE_INSTANCE_RULES_SYNCING,
1633 status=constants.STATUS_AVAILABLE)
1634 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
1635 req = fakes.HTTPRequest.blank(
1636 '/v2/fake/shares/%s' % share['id'], version=version)
1638 res_dict = self.controller.show(req, share['id'])
1640 expected = (constants.STATUS_OUT_OF_SYNC if translated else
1641 constants.SHARE_INSTANCE_RULES_SYNCING)
1643 self.assertEqual(expected, res_dict['share']['access_rules_status'])
1645 def test_share_soft_delete(self):
1646 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
1647 version='2.69')
1648 body = {"soft_delete": None}
1649 resp = self.controller.share_soft_delete(req, 1, body=body)
1650 self.assertEqual(202, resp.status_int)
1652 def test_share_soft_delete_has_been_soft_deleted_already(self):
1653 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
1654 version='2.69')
1655 body = {"soft_delete": None}
1656 self.mock_object(share_api.API, 'get',
1657 mock.Mock(return_value=self.share_in_recycle_bin))
1658 self.mock_object(share_api.API, 'soft_delete',
1659 mock.Mock(
1660 side_effect=exception.InvalidShare(reason='err')))
1662 self.assertRaises(
1663 webob.exc.HTTPForbidden, self.controller.share_soft_delete,
1664 req, 1, body=body)
1666 def test_share_soft_delete_has_replicas(self):
1667 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
1668 version='2.69')
1669 body = {"soft_delete": None}
1670 self.mock_object(share_api.API, 'get',
1671 mock.Mock(return_value=self.share))
1672 self.mock_object(share_api.API, 'soft_delete',
1673 mock.Mock(side_effect=exception.Conflict(err='err')))
1675 self.assertRaises(
1676 webob.exc.HTTPConflict, self.controller.share_soft_delete,
1677 req, 1, body=body)
1679 def test_share_restore(self):
1680 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
1681 version='2.69')
1682 body = {"restore": None}
1683 self.mock_object(share_api.API, 'get',
1684 mock.Mock(return_value=self.share_in_recycle_bin))
1685 resp = self.controller.share_restore(req, 1, body=body)
1686 self.assertEqual(202, resp.status_int)
1688 def test_share_restore_with_deleting_status(self):
1689 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
1690 version='2.69')
1691 body = {"restore": None}
1692 self.mock_object(
1693 share_api.API, 'get',
1694 mock.Mock(return_value=self.share_in_recycle_bin_is_deleting))
1695 self.assertRaises(
1696 webob.exc.HTTPForbidden, self.controller.share_restore,
1697 req, 1, body=body)
1699 def test_share_delete(self):
1700 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1701 resp = self.controller.delete(req, 1)
1702 self.assertEqual(202, resp.status_int)
1704 def test_share_delete_has_replicas(self):
1705 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1706 self.mock_object(share_api.API, 'get',
1707 mock.Mock(return_value=self.share))
1708 self.mock_object(share_api.API, 'delete',
1709 mock.Mock(side_effect=exception.Conflict(err='err')))
1711 self.assertRaises(
1712 webob.exc.HTTPConflict, self.controller.delete, req, 1)
1714 def test_share_delete_in_share_group_param_not_provided(self):
1715 fake_share = stubs.stub_share('fake_share',
1716 share_group_id='fake_group_id')
1717 self.mock_object(share_api.API, 'get',
1718 mock.Mock(return_value=fake_share))
1719 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1720 self.assertRaises(webob.exc.HTTPBadRequest,
1721 self.controller.delete, req, 1)
1723 def test_share_delete_in_share_group(self):
1724 fake_share = stubs.stub_share('fake_share',
1725 share_group_id='fake_group_id')
1726 self.mock_object(share_api.API, 'get',
1727 mock.Mock(return_value=fake_share))
1728 req = fakes.HTTPRequest.blank(
1729 '/v2/fake/shares/1?share_group_id=fake_group_id')
1730 resp = self.controller.delete(req, 1)
1731 self.assertEqual(202, resp.status_int)
1733 def test_share_delete_in_share_group_wrong_id(self):
1734 fake_share = stubs.stub_share('fake_share',
1735 share_group_id='fake_group_id')
1736 self.mock_object(share_api.API, 'get',
1737 mock.Mock(return_value=fake_share))
1738 req = fakes.HTTPRequest.blank(
1739 '/v2/fake/shares/1?share_group_id=not_fake_group_id')
1740 self.assertRaises(webob.exc.HTTPBadRequest,
1741 self.controller.delete, req, 1)
1743 def test_share_update(self):
1744 shr = self.share
1745 body = {"share": shr}
1747 req = fakes.HTTPRequest.blank('/v2/fake/share/1')
1748 res_dict = self.controller.update(req, 1, body)
1749 self.assertEqual(shr["display_name"], res_dict['share']["name"])
1750 self.assertEqual(shr["display_description"],
1751 res_dict['share']["description"])
1752 self.assertEqual(shr['is_public'],
1753 res_dict['share']['is_public'])
1755 def test_share_update_with_share_group(self):
1756 shr = self.share
1757 body = {"share": shr}
1759 req = fakes.HTTPRequest.blank(
1760 '/v2/fake/share/1', version="2.31", experimental=True)
1762 res_dict = self.controller.update(req, 1, body)
1764 self.assertIsNone(res_dict['share']["share_group_id"])
1765 self.assertIsNone(
1766 res_dict['share']["source_share_group_snapshot_member_id"])
1768 def test_share_not_updates_size(self):
1769 req = fakes.HTTPRequest.blank('/v2/fake/share/1')
1770 res_dict = self.controller.update(req, 1, {"share": self.share})
1771 self.assertNotEqual(res_dict['share']["size"], self.share["size"])
1773 def test_share_delete_no_share(self):
1774 self.mock_object(share_api.API, 'get',
1775 stubs.stub_share_get_notfound)
1776 req = fakes.HTTPRequest.blank('/v2/fake/shares/1')
1777 self.assertRaises(webob.exc.HTTPNotFound,
1778 self.controller.delete,
1779 req,
1780 1)
1782 @ddt.data({'use_admin_context': False, 'version': '2.4'},
1783 {'use_admin_context': True, 'version': '2.4'},
1784 {'use_admin_context': True, 'version': '2.35'},
1785 {'use_admin_context': False, 'version': '2.35'},
1786 {'use_admin_context': True, 'version': '2.36'},
1787 {'use_admin_context': False, 'version': '2.36'},
1788 {'use_admin_context': True, 'version': '2.42'},
1789 {'use_admin_context': False, 'version': '2.42'},
1790 {'use_admin_context': False, 'version': '2.69'},
1791 {'use_admin_context': True, 'version': '2.69'})
1792 @ddt.unpack
1793 def test_share_list_summary_with_search_opts(self, use_admin_context,
1794 version):
1795 search_opts = {
1796 'name': 'fake_name',
1797 'status': constants.STATUS_AVAILABLE,
1798 'share_server_id': 'fake_share_server_id',
1799 'share_type_id': 'fake_share_type_id',
1800 'snapshot_id': 'fake_snapshot_id',
1801 'share_network_id': 'fake_share_network_id',
1802 'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
1803 'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
1804 'sort_key': 'fake_sort_key',
1805 'sort_dir': 'fake_sort_dir',
1806 'limit': '1',
1807 'offset': '1',
1808 'is_public': 'False',
1809 'export_location_id': 'fake_export_location_id',
1810 'export_location_path': 'fake_export_location_path',
1811 }
1812 if (api_version.APIVersionRequest(version) >=
1813 api_version.APIVersionRequest('2.36')):
1814 search_opts.update(
1815 {'display_name~': 'fake',
1816 'display_description~': 'fake'})
1817 if (api_version.APIVersionRequest(version) >=
1818 api_version.APIVersionRequest('2.69')):
1819 search_opts.update({'is_soft_deleted': True})
1820 method = 'get_all'
1821 shares = [
1822 {'id': 'id1', 'display_name': 'n1'},
1823 {'id': 'id2', 'display_name': 'n2'},
1824 {'id': 'id3', 'display_name': 'n3'},
1825 ]
1827 mock_action = {'return_value': [shares[1]]}
1828 if (api_version.APIVersionRequest(version) >=
1829 api_version.APIVersionRequest('2.42')):
1830 search_opts.update({'with_count': 'true'})
1831 method = 'get_all_with_count'
1832 mock_action = {'side_effect': [(1, [shares[1]])]}
1833 if use_admin_context:
1834 search_opts['host'] = 'fake_host'
1835 # fake_key should be filtered for non-admin
1836 url = '/v2/fake/shares?fake_key=fake_value'
1837 for k, v in search_opts.items():
1838 url = url + '&' + k + '=' + str(v)
1839 req = fakes.HTTPRequest.blank(url, version=version,
1840 use_admin_context=use_admin_context)
1842 mock_get_all = (
1843 self.mock_object(share_api.API, method, mock.Mock(**mock_action)))
1845 result = self.controller.index(req)
1847 search_opts_expected = {
1848 'display_name': search_opts['name'],
1849 'status': search_opts['status'],
1850 'share_server_id': search_opts['share_server_id'],
1851 'share_type_id': search_opts['share_type_id'],
1852 'snapshot_id': search_opts['snapshot_id'],
1853 'share_network_id': search_opts['share_network_id'],
1854 'metadata': {'k1': 'v1'},
1855 'extra_specs': {'k2': 'v2'},
1856 'is_public': 'False',
1857 'limit': '1',
1858 'offset': '1'
1859 }
1860 if (api_version.APIVersionRequest(version) >=
1861 api_version.APIVersionRequest('2.35')):
1862 search_opts_expected['export_location_id'] = (
1863 search_opts['export_location_id'])
1864 search_opts_expected['export_location_path'] = (
1865 search_opts['export_location_path'])
1866 if (api_version.APIVersionRequest(version) >=
1867 api_version.APIVersionRequest('2.36')):
1868 search_opts_expected.update(
1869 {'display_name~': search_opts['display_name~'],
1870 'display_description~': search_opts['display_description~']})
1871 if (api_version.APIVersionRequest(version) >=
1872 api_version.APIVersionRequest('2.69')):
1873 search_opts_expected['is_soft_deleted'] = (
1874 search_opts['is_soft_deleted'])
1876 policy.check_policy.assert_called_once_with(
1877 req.environ['manila.context'],
1878 'share',
1879 'get_all')
1880 if use_admin_context:
1881 search_opts_expected.update({'fake_key': 'fake_value'})
1882 search_opts_expected['host'] = search_opts['host']
1883 mock_get_all.assert_called_once_with(
1884 req.environ['manila.context'],
1885 sort_key=search_opts['sort_key'],
1886 sort_dir=search_opts['sort_dir'],
1887 search_opts=search_opts_expected,
1888 )
1889 self.assertEqual(1, len(result['shares']))
1890 self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
1891 self.assertEqual(
1892 shares[1]['display_name'], result['shares'][0]['name'])
1893 if (api_version.APIVersionRequest(version) >=
1894 api_version.APIVersionRequest('2.42')):
1895 self.assertEqual(1, result['count'])
1897 @ddt.data({'use_admin_context': True, 'version': '2.42'},
1898 {'use_admin_context': False, 'version': '2.42'})
1899 @ddt.unpack
1900 def test_share_list_summary_with_search_opt_count_0(self,
1901 use_admin_context,
1902 version):
1903 search_opts = {
1904 'sort_key': 'fake_sort_key',
1905 'sort_dir': 'fake_sort_dir',
1906 'with_count': 'true'
1907 }
1908 if use_admin_context:
1909 search_opts['host'] = 'fake_host'
1910 # fake_key should be filtered
1911 url = '/v2/fake/shares?fake_key=fake_value'
1912 for k, v in search_opts.items():
1913 url = url + '&' + k + '=' + v
1914 req = fakes.HTTPRequest.blank(url, version=version,
1915 use_admin_context=use_admin_context)
1917 self.mock_object(share_api.API, 'get_all_with_count',
1918 mock.Mock(side_effect=[(0, [])]))
1920 result = self.controller.index(req)
1922 search_opts_expected = {}
1924 policy.check_policy.assert_called_once_with(
1925 req.environ['manila.context'],
1926 'share',
1927 'get_all')
1928 if use_admin_context:
1929 search_opts_expected.update({'fake_key': 'fake_value'})
1930 search_opts_expected['host'] = search_opts['host']
1931 share_api.API.get_all_with_count.assert_called_once_with(
1932 req.environ['manila.context'],
1933 sort_key=search_opts['sort_key'],
1934 sort_dir=search_opts['sort_dir'],
1935 search_opts=search_opts_expected,
1936 )
1937 self.assertEqual(0, len(result['shares']))
1938 self.assertEqual(0, result['count'])
1940 def test_share_list_summary(self):
1941 self.mock_object(share_api.API, 'get_all',
1942 stubs.stub_share_get_all_by_project)
1943 req = fakes.HTTPRequest.blank('/v2/fake/shares')
1944 res_dict = self.controller.index(req)
1945 expected = {
1946 'shares': [
1947 {
1948 'name': 'displayname',
1949 'id': '1',
1950 'links': [
1951 {
1952 'href': 'http://localhost/share/v2/fake/shares/1',
1953 'rel': 'self'
1954 },
1955 {
1956 'href': 'http://localhost/share/fake/shares/1',
1957 'rel': 'bookmark'
1958 }
1959 ],
1960 }
1961 ]
1962 }
1963 policy.check_policy.assert_called_once_with(
1964 req.environ['manila.context'],
1965 'share',
1966 'get_all')
1967 self.assertEqual(expected, res_dict)
1969 @ddt.data({'use_admin_context': False, 'version': '2.4'},
1970 {'use_admin_context': True, 'version': '2.4'},
1971 {'use_admin_context': True, 'version': '2.35'},
1972 {'use_admin_context': False, 'version': '2.35'},
1973 {'use_admin_context': True, 'version': '2.42'},
1974 {'use_admin_context': False, 'version': '2.42'},
1975 {'use_admin_context': True, 'version': '2.69'},
1976 {'use_admin_context': False, 'version': '2.69'})
1977 @ddt.unpack
1978 def test_share_list_detail_with_search_opts(self, use_admin_context,
1979 version):
1980 search_opts = {
1981 'name': 'fake_name',
1982 'status': constants.STATUS_AVAILABLE,
1983 'share_server_id': 'fake_share_server_id',
1984 'share_type_id': 'fake_share_type_id',
1985 'snapshot_id': 'fake_snapshot_id',
1986 'share_network_id': 'fake_share_network_id',
1987 'metadata': '%7B%27k1%27%3A+%27v1%27%7D', # serialized k1=v1
1988 'extra_specs': '%7B%27k2%27%3A+%27v2%27%7D', # serialized k2=v2
1989 'sort_key': 'fake_sort_key',
1990 'sort_dir': 'fake_sort_dir',
1991 'limit': '1',
1992 'offset': '1',
1993 'is_public': 'False',
1994 'export_location_id': 'fake_export_location_id',
1995 'export_location_path': 'fake_export_location_path',
1996 }
1997 shares = [
1998 {'id': 'id1', 'display_name': 'n1'},
1999 {
2000 'id': 'id2',
2001 'display_name': 'n2',
2002 'status': constants.STATUS_AVAILABLE,
2003 'snapshot_id': 'fake_snapshot_id',
2004 'instance': {
2005 'host': 'fake_host',
2006 'share_network_id': 'fake_share_network_id',
2007 'share_type_id': 'fake_share_type_id',
2008 },
2009 'has_replicas': False,
2010 'is_soft_deleted': True,
2011 'scheduled_to_be_deleted_at': 'fake_datatime',
2012 },
2013 {'id': 'id3', 'display_name': 'n3'},
2014 ]
2016 method = 'get_all'
2017 mock_action = {'return_value': [shares[1]]}
2018 if (api_version.APIVersionRequest(version) >=
2019 api_version.APIVersionRequest('2.42')):
2020 search_opts.update({'with_count': 'true'})
2021 method = 'get_all_with_count'
2022 mock_action = {'side_effect': [(1, [shares[1]])]}
2023 if (api_version.APIVersionRequest(version) >=
2024 api_version.APIVersionRequest('2.69')):
2025 search_opts.update({'is_soft_deleted': True})
2026 if use_admin_context:
2027 search_opts['host'] = 'fake_host'
2028 # fake_key should be filtered for non-admin
2029 url = '/v2/fake/shares/detail?fake_key=fake_value'
2030 for k, v in search_opts.items():
2031 url = url + '&' + k + '=' + str(v)
2032 req = fakes.HTTPRequest.blank(url, version=version,
2033 use_admin_context=use_admin_context)
2035 mock_get_all = self.mock_object(share_api.API, method,
2036 mock.Mock(**mock_action))
2038 result = self.controller.detail(req)
2040 search_opts_expected = {
2041 'display_name': search_opts['name'],
2042 'status': search_opts['status'],
2043 'share_server_id': search_opts['share_server_id'],
2044 'share_type_id': search_opts['share_type_id'],
2045 'snapshot_id': search_opts['snapshot_id'],
2046 'share_network_id': search_opts['share_network_id'],
2047 'metadata': {'k1': 'v1'},
2048 'extra_specs': {'k2': 'v2'},
2049 'is_public': 'False',
2050 'limit': '1',
2051 'offset': '1'
2052 }
2054 if (api_version.APIVersionRequest(version) >=
2055 api_version.APIVersionRequest('2.35')):
2056 search_opts_expected['export_location_id'] = (
2057 search_opts['export_location_id'])
2058 search_opts_expected['export_location_path'] = (
2059 search_opts['export_location_path'])
2060 if (api_version.APIVersionRequest(version) >=
2061 api_version.APIVersionRequest('2.69')):
2062 search_opts_expected['is_soft_deleted'] = (
2063 search_opts['is_soft_deleted'])
2064 if use_admin_context:
2065 search_opts_expected.update({'fake_key': 'fake_value'})
2066 search_opts_expected['host'] = search_opts['host']
2068 policy.check_policy.assert_called_once_with(
2069 req.environ['manila.context'],
2070 'share',
2071 'get_all')
2072 mock_get_all.assert_called_once_with(
2073 req.environ['manila.context'],
2074 sort_key=search_opts['sort_key'],
2075 sort_dir=search_opts['sort_dir'],
2076 search_opts=search_opts_expected,
2077 )
2078 self.assertEqual(1, len(result['shares']))
2079 self.assertEqual(shares[1]['id'], result['shares'][0]['id'])
2080 self.assertEqual(
2081 shares[1]['display_name'], result['shares'][0]['name'])
2082 self.assertEqual(
2083 shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
2084 self.assertEqual(
2085 shares[1]['status'], result['shares'][0]['status'])
2086 self.assertEqual(
2087 shares[1]['instance']['share_type_id'],
2088 result['shares'][0]['share_type'])
2089 self.assertEqual(
2090 shares[1]['snapshot_id'], result['shares'][0]['snapshot_id'])
2091 if use_admin_context:
2092 self.assertEqual(
2093 shares[1]['instance']['host'], result['shares'][0]['host'])
2094 self.assertEqual(
2095 shares[1]['instance']['share_network_id'],
2096 result['shares'][0]['share_network_id'])
2097 if (api_version.APIVersionRequest(version) >=
2098 api_version.APIVersionRequest('2.42')):
2099 self.assertEqual(1, result['count'])
2100 if (api_version.APIVersionRequest(version) >=
2101 api_version.APIVersionRequest('2.69')):
2102 self.assertEqual(
2103 shares[1]['scheduled_to_be_deleted_at'],
2104 result['shares'][0]['scheduled_to_be_deleted_at'])
2106 def _list_detail_common_expected(self, admin=False):
2107 share_dict = {
2108 'status': 'fakestatus',
2109 'description': 'displaydesc',
2110 'export_location': 'fake_location',
2111 'export_locations': ['fake_location', 'fake_location2'],
2112 'availability_zone': 'fakeaz',
2113 'name': 'displayname',
2114 'share_proto': 'FAKEPROTO',
2115 'metadata': {},
2116 'project_id': 'fakeproject',
2117 'id': '1',
2118 'snapshot_id': '2',
2119 'snapshot_support': True,
2120 'share_network_id': None,
2121 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
2122 'size': 1,
2123 'share_type': '1',
2124 'volume_type': '1',
2125 'is_public': False,
2126 'links': [
2127 {
2128 'href': 'http://localhost/share/v2/fake/shares/1',
2129 'rel': 'self'
2130 },
2131 {
2132 'href': 'http://localhost/share/fake/shares/1',
2133 'rel': 'bookmark'
2134 }
2135 ],
2136 }
2137 if admin: 2137 ↛ 2138line 2137 didn't jump to line 2138 because the condition on line 2137 was never true
2138 share_dict['host'] = 'fakehost'
2139 return {'shares': [share_dict]}
2141 def _list_detail_test_common(self, req, expected):
2142 self.mock_object(share_api.API, 'get_all',
2143 stubs.stub_share_get_all_by_project)
2145 res_dict = self.controller.detail(req)
2147 policy.check_policy.assert_called_once_with(
2148 req.environ['manila.context'],
2149 'share',
2150 'get_all')
2151 self.assertDictListMatch(expected['shares'], res_dict['shares'])
2152 self.assertEqual(res_dict['shares'][0]['volume_type'],
2153 res_dict['shares'][0]['share_type'])
2155 def test_share_list_detail(self):
2156 env = {'QUERY_STRING': 'name=Share+Test+Name'}
2157 req = fakes.HTTPRequest.blank('/v2/fake/shares/detail', environ=env)
2158 expected = self._list_detail_common_expected()
2159 expected['shares'][0].pop('snapshot_support')
2160 self._list_detail_test_common(req, expected)
2162 def test_share_list_detail_with_share_group(self):
2163 env = {'QUERY_STRING': 'name=Share+Test+Name'}
2164 req = fakes.HTTPRequest.blank('/v2/fake/shares/detail',
2165 environ=env,
2166 version="2.31",
2167 experimental=True)
2168 expected = self._list_detail_common_expected()
2169 expected['shares'][0]['task_state'] = None
2170 expected['shares'][0]['share_type_name'] = None
2171 expected['shares'][0].pop('export_location')
2172 expected['shares'][0].pop('export_locations')
2173 expected['shares'][0]['access_rules_status'] = 'active'
2174 expected['shares'][0]['replication_type'] = None
2175 expected['shares'][0]['has_replicas'] = False
2176 expected['shares'][0]['user_id'] = 'fakeuser'
2177 expected['shares'][0]['create_share_from_snapshot_support'] = True
2178 expected['shares'][0]['revert_to_snapshot_support'] = False
2179 expected['shares'][0]['share_group_id'] = None
2180 expected['shares'][0]['source_share_group_snapshot_member_id'] = None
2181 self._list_detail_test_common(req, expected)
2183 def test_share_list_detail_with_task_state(self):
2184 env = {'QUERY_STRING': 'name=Share+Test+Name'}
2185 req = fakes.HTTPRequest.blank('/v2/fake/shares/detail', environ=env,
2186 version="2.5")
2187 expected = self._list_detail_common_expected()
2188 expected['shares'][0]['task_state'] = None
2189 self._list_detail_test_common(req, expected)
2191 def test_share_list_detail_without_export_locations(self):
2192 env = {'QUERY_STRING': 'name=Share+Test+Name'}
2193 req = fakes.HTTPRequest.blank('/v2/fake/shares/detail', environ=env,
2194 version="2.9")
2195 expected = self._list_detail_common_expected()
2196 expected['shares'][0]['task_state'] = None
2197 expected['shares'][0]['share_type_name'] = None
2198 expected['shares'][0].pop('export_location')
2199 expected['shares'][0].pop('export_locations')
2200 self._list_detail_test_common(req, expected)
2202 def test_share_list_detail_with_replication_type(self):
2203 self.mock_object(share_api.API, 'get_all',
2204 stubs.stub_share_get_all_by_project)
2205 env = {'QUERY_STRING': 'name=Share+Test+Name'}
2206 req = fakes.HTTPRequest.blank(
2207 '/v2/fake/shares/detail', environ=env,
2208 version=share_replicas.MIN_SUPPORTED_API_VERSION)
2210 res_dict = self.controller.detail(req)
2212 policy.check_policy.assert_called_once_with(
2213 req.environ['manila.context'],
2214 'share',
2215 'get_all')
2216 expected = {
2217 'shares': [
2218 {
2219 'status': 'fakestatus',
2220 'description': 'displaydesc',
2221 'availability_zone': 'fakeaz',
2222 'name': 'displayname',
2223 'share_proto': 'FAKEPROTO',
2224 'metadata': {},
2225 'project_id': 'fakeproject',
2226 'access_rules_status': 'active',
2227 'id': '1',
2228 'snapshot_id': '2',
2229 'share_network_id': None,
2230 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
2231 'size': 1,
2232 'share_type_name': None,
2233 'share_type': '1',
2234 'volume_type': '1',
2235 'is_public': False,
2236 'snapshot_support': True,
2237 'has_replicas': False,
2238 'replication_type': None,
2239 'task_state': None,
2240 'links': [
2241 {
2242 'href': 'http://localhost/share/v2/fake/shares/1',
2243 'rel': 'self'
2244 },
2245 {
2246 'href': 'http://localhost/share/fake/shares/1',
2247 'rel': 'bookmark'
2248 }
2249 ],
2250 }
2251 ]
2252 }
2253 self.assertEqual(expected, res_dict)
2254 self.assertEqual(res_dict['shares'][0]['volume_type'],
2255 res_dict['shares'][0]['share_type'])
2257 def test_remove_invalid_options(self):
2258 ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=False)
2259 search_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
2260 expected_opts = {'a': 'a', 'c': 'c'}
2261 allowed_opts = ['a', 'c']
2262 common.remove_invalid_options(ctx, search_opts, allowed_opts)
2263 self.assertEqual(expected_opts, search_opts)
2265 def test_remove_invalid_options_admin(self):
2266 ctx = context.RequestContext('fakeuser', 'fakeproject', is_admin=True)
2267 search_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
2268 expected_opts = {'a': 'a', 'b': 'b', 'c': 'c', 'd': 'd'}
2269 allowed_opts = ['a', 'c']
2270 common.remove_invalid_options(ctx, search_opts, allowed_opts)
2271 self.assertEqual(expected_opts, search_opts)
2273 def test_create_metadata(self):
2274 id = 'fake_share_id'
2275 body = {'metadata': {'key1': 'val1', 'key2': 'val2'}}
2276 mock_validate = self.mock_object(
2277 self.controller, '_validate_metadata_for_update',
2278 mock.Mock(return_value=body['metadata']))
2279 mock_create = self.mock_object(
2280 self.controller, '_create_metadata',
2281 mock.Mock(return_value=body))
2282 self.mock_object(share_api.API, 'update_share_from_metadata')
2284 req = fakes.HTTPRequest.blank(
2285 '/v2/shares/%s/metadata' % id)
2287 res = self.controller.create_metadata(req, id, body)
2288 self.assertEqual(body, res)
2289 mock_validate.assert_called_once_with(req, id, body['metadata'],
2290 delete=False)
2291 mock_create.assert_called_once_with(req, id, body)
2293 def test_update_all_metadata(self):
2294 id = 'fake_share_id'
2295 body = {'metadata': {'key1': 'val1', 'key2': 'val2'}}
2296 mock_validate = self.mock_object(
2297 self.controller, '_validate_metadata_for_update',
2298 mock.Mock(return_value=body['metadata']))
2299 mock_update = self.mock_object(
2300 self.controller, '_update_all_metadata',
2301 mock.Mock(return_value=body))
2302 self.mock_object(share_api.API, 'update_share_from_metadata')
2304 req = fakes.HTTPRequest.blank(
2305 '/v2/shares/%s/metadata' % id)
2306 res = self.controller.update_all_metadata(req, id, body)
2307 self.assertEqual(body, res)
2308 mock_validate.assert_called_once_with(req, id, body['metadata'])
2309 mock_update.assert_called_once_with(req, id, body)
2311 def test_delete_metadata(self):
2312 mock_delete = self.mock_object(
2313 self.controller, '_delete_metadata', mock.Mock())
2315 req = fakes.HTTPRequest.blank(
2316 '/v2/shares/%s/metadata/fake_key' % id)
2317 self.controller.delete_metadata(req, id, 'fake_key')
2318 mock_delete.assert_called_once_with(req, id, 'fake_key')
2321def _fake_access_get(self, ctxt, access_id):
2323 class Access(object):
2324 def __init__(self, **kwargs):
2325 self.STATE_NEW = 'fake_new'
2326 self.STATE_ACTIVE = 'fake_active'
2327 self.STATE_ERROR = 'fake_error'
2328 self.params = kwargs
2329 self.params['state'] = self.STATE_NEW
2330 self.share_id = kwargs.get('share_id')
2331 self.id = access_id
2333 def __getitem__(self, item):
2334 return self.params[item]
2336 access = Access(access_id=access_id, share_id='fake_share_id')
2337 return access
2340@ddt.ddt
2341class ShareActionsTest(test.TestCase):
2343 def setUp(self):
2344 super(ShareActionsTest, self).setUp()
2345 self.controller = shares.ShareController()
2346 self.mock_object(share_api.API, 'get', stubs.stub_share_get)
2347 self.mock_object(policy, 'check_policy')
2349 @ddt.unpack
2350 @ddt.data(
2351 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1'},
2352 "version": "2.7"},
2353 {"access": {'access_type': 'user', 'access_to': '1' * 4},
2354 "version": "2.7"},
2355 {"access": {'access_type': 'user', 'access_to': '1' * 255},
2356 "version": "2.7"},
2357 {"access": {'access_type': 'user', 'access_to': 'fake{.-_\'`}'},
2358 "version": "2.7"},
2359 {"access": {'access_type': 'user',
2360 'access_to': 'MYDOMAIN-Administrator'},
2361 "version": "2.7"},
2362 {"access": {'access_type': 'user',
2363 'access_to': 'test group name'},
2364 "version": "2.7"},
2365 {"access": {'access_type': 'user',
2366 'access_to': 'group$.-_\'`{}'},
2367 "version": "2.7"},
2368 {"access": {'access_type': 'cert', 'access_to': 'x'},
2369 "version": "2.7"},
2370 {"access": {'access_type': 'cert', 'access_to': 'tenant.example.com'},
2371 "version": "2.7"},
2372 {"access": {'access_type': 'cert', 'access_to': 'x' * 64},
2373 "version": "2.7"},
2374 {"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2'},
2375 "version": "2.38"},
2376 {"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::'},
2377 "version": "2.38"},
2378 {"access": {'access_type': 'ip', 'access_to': 'AD80::/36'},
2379 "version": "2.38"},
2380 {"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::/128'},
2381 "version": "2.38"},
2382 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1'},
2383 "version": "2.38"},
2384 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1',
2385 'metadata': {'test_key': 'test_value'}},
2386 "version": "2.45"},
2387 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1',
2388 'metadata': {'k' * 255: 'v' * 1023}},
2389 "version": "2.45"},
2390 )
2391 def test_allow_access(self, access, version):
2392 self.mock_object(share_api.API,
2393 'allow_access',
2394 mock.Mock(return_value={'fake': 'fake'}))
2395 self.mock_object(self.controller._access_view_builder, 'view',
2396 mock.Mock(return_value={'access':
2397 {'fake': 'fake'}}))
2398 id = 'fake_share_id'
2399 body = {'allow_access': access}
2400 expected = {'access': {'fake': 'fake'}}
2401 req = fakes.HTTPRequest.blank(
2402 '/v2/tenant1/shares/%s/action' % id, version=version)
2404 res = self.controller.allow_access(req, id, body)
2406 self.assertEqual(expected, res)
2408 @ddt.unpack
2409 @ddt.data(
2410 {"access": {'access_type': 'error_type',
2411 'access_to': '127.0.0.1'},
2412 "version": "2.7"},
2413 {"access": {'access_type': 'ip', 'access_to': 'localhost'},
2414 "version": "2.7"},
2415 {"access": {'access_type': 'ip', 'access_to': '127.0.0.*'},
2416 "version": "2.7"},
2417 {"access": {'access_type': 'ip', 'access_to': '127.0.0.0/33'},
2418 "version": "2.7"},
2419 {"access": {'access_type': 'ip', 'access_to': '127.0.0.256'},
2420 "version": "2.7"},
2421 {"access": {'access_type': 'user', 'access_to': '1'},
2422 "version": "2.7"},
2423 {"access": {'access_type': 'user', 'access_to': '1' * 3},
2424 "version": "2.7"},
2425 {"access": {'access_type': 'user', 'access_to': '1' * 256},
2426 "version": "2.7"},
2427 {"access": {'access_type': 'user', 'access_to': 'root<>'},
2428 "version": "2.7"},
2429 {"access": {'access_type': 'user', 'access_to': 'group\\'},
2430 "version": "2.7"},
2431 {"access": {'access_type': 'user', 'access_to': '+=*?group'},
2432 "version": "2.7"},
2433 {"access": {'access_type': 'cert', 'access_to': ''},
2434 "version": "2.7"},
2435 {"access": {'access_type': 'cert', 'access_to': ' '},
2436 "version": "2.7"},
2437 {"access": {'access_type': 'cert', 'access_to': 'x' * 65},
2438 "version": "2.7"},
2439 {"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2'},
2440 "version": "2.37"},
2441 {"access": {'access_type': 'ip', 'access_to': '127.4.0.3/33'},
2442 "version": "2.38"},
2443 {"access": {'access_type': 'ip', 'access_to': 'AD80:ABAA::*'},
2444 "version": "2.38"},
2445 {"access": {'access_type': 'ip', 'access_to': 'AD80::/129'},
2446 "version": "2.38"},
2447 {"access": {'access_type': 'ip', 'access_to': 'ad80::abaa:0:c2:2/64'},
2448 "version": "2.38"},
2449 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1',
2450 'metadata': {'k' * 256: 'v' * 1024}},
2451 "version": "2.45"},
2452 {"access": {'access_type': 'ip', 'access_to': '127.0.0.1',
2453 'metadata': {'key': None}},
2454 "version": "2.45"},
2455 )
2456 def test_allow_access_error(self, access, version):
2457 id = 'fake_share_id'
2458 body = {'allow_access': access}
2459 req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id,
2460 version=version)
2461 self.assertRaises(webob.exc.HTTPBadRequest,
2462 self.controller.allow_access, req, id, body)
2464 @ddt.unpack
2465 @ddt.data(
2466 {'exc': None, 'access_to': 'alice', 'version': '2.13'},
2467 {'exc': webob.exc.HTTPBadRequest, 'access_to': 'alice',
2468 'version': '2.11'}
2469 )
2470 def test_allow_access_ceph(self, exc, access_to, version):
2471 share_id = "fake_id"
2472 self.mock_object(share_api.API,
2473 'allow_access',
2474 mock.Mock(return_value={'fake': 'fake'}))
2475 self.mock_object(self.controller._access_view_builder, 'view',
2476 mock.Mock(return_value={'access':
2477 {'fake': 'fake'}}))
2479 req = fakes.HTTPRequest.blank(
2480 '/v2/shares/%s/action' % share_id, version=version)
2482 body = {'allow_access':
2483 {
2484 'access_type': 'cephx',
2485 'access_to': access_to,
2486 'access_level': 'rw'
2487 }}
2489 if exc:
2490 self.assertRaises(exc, self.controller.allow_access, req, share_id,
2491 body)
2492 else:
2493 expected = {'access': {'fake': 'fake'}}
2494 res = self.controller.allow_access(req, id, body)
2495 self.assertEqual(expected, res)
2497 @ddt.data('2.1', '2.27')
2498 def test_allow_access_access_rules_status_is_in_error(self, version):
2499 share = db_utils.create_share(
2500 access_rules_status=constants.SHARE_INSTANCE_RULES_ERROR)
2502 req = fakes.HTTPRequest.blank(
2503 '/v2/shares/%s/action' % share['id'], version=version)
2504 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2505 self.mock_object(share_api.API, 'allow_access')
2506 if (api_version.APIVersionRequest(version) >=
2507 api_version.APIVersionRequest('2.7')):
2508 key = 'allow_access'
2509 method = self.controller.allow_access
2510 else:
2511 key = 'os-allow_access'
2512 method = self.controller.allow_access_legacy
2514 body = {
2515 key: {
2516 'access_type': 'user',
2517 'access_to': 'crimsontide',
2518 'access_level': 'rw',
2519 }
2520 }
2522 self.assertRaises(webob.exc.HTTPBadRequest,
2523 method, req, share['id'], body)
2524 self.assertFalse(share_api.API.allow_access.called)
2526 @ddt.data(*itertools.product(
2527 ('2.1', '2.27'), (constants.SHARE_INSTANCE_RULES_SYNCING,
2528 constants.STATUS_ACTIVE)))
2529 @ddt.unpack
2530 def test_allow_access_no_transitional_states(self, version, status):
2531 share = db_utils.create_share(access_rules_status=status,
2532 status=constants.STATUS_AVAILABLE)
2533 req = fakes.HTTPRequest.blank(
2534 '/v2/shares/%s/action' % share['id'], version=version)
2535 ctxt = req.environ['manila.context']
2536 access = {
2537 'access_type': 'user',
2538 'access_to': 'clemsontigers',
2539 'access_level': 'rw',
2540 }
2541 expected_mapping = {
2542 constants.SHARE_INSTANCE_RULES_SYNCING: constants.STATUS_NEW,
2543 constants.SHARE_INSTANCE_RULES_ERROR:
2544 constants.ACCESS_STATE_ERROR,
2545 constants.STATUS_ACTIVE: constants.ACCESS_STATE_ACTIVE,
2546 }
2547 share = db.share_get(ctxt, share['id'])
2548 updated_access = db_utils.create_access(share_id=share['id'], **access)
2549 expected_access = access
2550 expected_access.update(
2551 {
2552 'id': updated_access['id'],
2553 'state': expected_mapping[share['access_rules_status']],
2554 'share_id': updated_access['share_id'],
2555 })
2557 if (api_version.APIVersionRequest(version) >=
2558 api_version.APIVersionRequest('2.7')):
2559 key = 'allow_access'
2560 method = self.controller.allow_access
2561 else:
2562 key = 'os-allow_access'
2563 method = self.controller.allow_access_legacy
2564 if (api_version.APIVersionRequest(version) >=
2565 api_version.APIVersionRequest('2.13')):
2566 expected_access['access_key'] = None
2567 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2568 self.mock_object(share_api.API, 'allow_access',
2569 mock.Mock(return_value=updated_access))
2570 body = {key: access}
2572 access = method(req, share['id'], body)
2574 self.assertEqual(expected_access, access['access'])
2575 share_api.API.allow_access.assert_called_once_with(
2576 req.environ['manila.context'], share, 'user',
2577 'clemsontigers', 'rw', None, False)
2579 @ddt.data(*itertools.product(
2580 set(['2.28', api_version._MAX_API_VERSION]),
2581 (constants.SHARE_INSTANCE_RULES_ERROR,
2582 constants.SHARE_INSTANCE_RULES_SYNCING, constants.STATUS_ACTIVE)))
2583 @ddt.unpack
2584 def test_allow_access_access_rules_status_dont_care(self, version, status):
2585 access = {
2586 'access_type': 'user',
2587 'access_to': 'clemsontigers',
2588 'access_level': 'rw',
2589 }
2590 updated_access = db_utils.create_access(**access)
2591 expected_access = access
2592 expected_access.update(
2593 {
2594 'id': updated_access['id'],
2595 'state': updated_access['state'],
2596 'share_id': updated_access['share_id'],
2597 'access_key': None,
2598 })
2600 share = db_utils.create_share(access_rules_status=status)
2601 req = fakes.HTTPRequest.blank(
2602 '/v2/shares/%s/action' % share['id'], version=version)
2603 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2604 self.mock_object(share_api.API, 'allow_access',
2605 mock.Mock(return_value=updated_access))
2606 body = {'allow_access': access}
2608 access = self.controller.allow_access(req, share['id'], body)
2610 if api_version.APIVersionRequest(version) >= (
2611 api_version.APIVersionRequest("2.33")):
2612 expected_access.update(
2613 {
2614 'created_at': updated_access['created_at'],
2615 'updated_at': updated_access['updated_at'],
2616 })
2618 if api_version.APIVersionRequest(version) >= (
2619 api_version.APIVersionRequest("2.45")):
2620 expected_access.update(
2621 {
2622 'metadata': {},
2623 })
2625 if api_version.APIVersionRequest(version) >= (
2626 api_version.APIVersionRequest("2.74")):
2627 allow_on_error_state = True
2628 else:
2629 allow_on_error_state = False
2631 self.assertEqual(expected_access, access['access'])
2632 share_api.API.allow_access.assert_called_once_with(
2633 req.environ['manila.context'], share, 'user',
2634 'clemsontigers', 'rw', None, allow_on_error_state)
2636 def test_deny_access(self):
2637 def _stub_deny_access(*args, **kwargs):
2638 pass
2640 self.mock_object(share_api.API, "deny_access", _stub_deny_access)
2641 self.mock_object(share_api.API, "access_get", _fake_access_get)
2643 id = 'fake_share_id'
2644 body = {"os-deny_access": {"access_id": 'fake_acces_id'}}
2645 req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id)
2646 res = self.controller._deny_access(req, id, body)
2647 self.assertEqual(202, res.status_int)
2649 def test_deny_access_not_found(self):
2650 def _stub_deny_access(*args, **kwargs):
2651 pass
2653 self.mock_object(share_api.API, "deny_access", _stub_deny_access)
2654 self.mock_object(share_api.API, "access_get", _fake_access_get)
2656 id = 'super_fake_share_id'
2657 body = {"os-deny_access": {"access_id": 'fake_acces_id'}}
2658 req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id)
2659 self.assertRaises(webob.exc.HTTPNotFound,
2660 self.controller._deny_access,
2661 req,
2662 id,
2663 body)
2665 def test_access_list(self):
2666 fake_access_list = [
2667 {
2668 "state": "fakestatus",
2669 "id": "fake_access_id",
2670 "access_type": "fakeip",
2671 "access_to": "127.0.0.1",
2672 }
2673 ]
2674 self.mock_object(self.controller._access_view_builder, 'list_view',
2675 mock.Mock(return_value={'access_list':
2676 fake_access_list}))
2677 id = 'fake_share_id'
2678 body = {"os-access_list": None}
2679 req = fakes.HTTPRequest.blank('/v2/tenant1/shares/%s/action' % id)
2681 res_dict = self.controller._access_list(req, id, body)
2682 self.assertEqual({'access_list': fake_access_list}, res_dict)
2684 @ddt.unpack
2685 @ddt.data(
2686 {'body': {'os-extend': {'new_size': 2}}, 'version': '2.6'},
2687 {'body': {'extend': {'new_size': 2}}, 'version': '2.7'},
2688 )
2689 def test_extend(self, body, version):
2690 id = 'fake_share_id'
2691 share = stubs.stub_share_get(None, None, id)
2692 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2693 self.mock_object(share_api.API, "extend")
2695 size = '2'
2696 req = fakes.HTTPRequest.blank(
2697 '/v2/shares/%s/action' % id, version=version)
2698 actual_response = self.controller._extend(req, id, body=body)
2700 share_api.API.get.assert_called_once_with(mock.ANY, id)
2701 share_api.API.extend.assert_called_once_with(
2702 mock.ANY, share, int(size), force=False)
2703 self.assertEqual(202, actual_response.status_int)
2705 @ddt.data({"os-extend": ""},
2706 {"os-extend": {"new_size": "foo"}},
2707 {"os-extend": {"new_size": {'foo': 'bar'}}})
2708 def test_extend_invalid_body(self, body):
2709 id = 'fake_share_id'
2710 req = fakes.HTTPRequest.blank('/v2/shares/%s/action' % id)
2712 self.assertRaises(webob.exc.HTTPBadRequest,
2713 self.controller._extend, req, id, body=body)
2715 @ddt.data({'source': exception.InvalidInput,
2716 'target': webob.exc.HTTPBadRequest},
2717 {'source': exception.InvalidShare,
2718 'target': webob.exc.HTTPBadRequest},
2719 {'source': exception.ShareSizeExceedsAvailableQuota,
2720 'target': webob.exc.HTTPForbidden})
2721 @ddt.unpack
2722 def test_extend_exception(self, source, target):
2723 id = 'fake_share_id'
2724 req = fakes.HTTPRequest.blank('/v2/shares/%s/action' % id)
2725 body = {"os-extend": {'new_size': '123'}}
2726 self.mock_object(share_api.API, "extend",
2727 mock.Mock(side_effect=source('fake')))
2729 self.assertRaises(target, self.controller._extend, req, id, body=body)
2731 @ddt.unpack
2732 @ddt.data(
2733 {'body': {'os-shrink': {'new_size': 1}}, 'version': '2.6'},
2734 {'body': {'shrink': {'new_size': 1}}, 'version': '2.7'},
2735 )
2736 def test_shrink(self, body, version):
2737 id = 'fake_share_id'
2738 share = stubs.stub_share_get(None, None, id)
2739 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2740 self.mock_object(share_api.API, "shrink")
2742 size = '1'
2743 req = fakes.HTTPRequest.blank(
2744 '/v2/shares/%s/action' % id, version=version)
2745 actual_response = self.controller._shrink(req, id, body=body)
2747 share_api.API.get.assert_called_once_with(mock.ANY, id)
2748 share_api.API.shrink.assert_called_once_with(
2749 mock.ANY, share, int(size))
2750 self.assertEqual(202, actual_response.status_int)
2752 @ddt.data({"os-shrink": ""},
2753 {"os-shrink": {"new_size": "foo"}},
2754 {"os-shrink": {"new_size": {'foo': 'bar'}}})
2755 def test_shrink_invalid_body(self, body):
2756 id = 'fake_share_id'
2757 req = fakes.HTTPRequest.blank('/v2/shares/%s/action' % id)
2759 self.assertRaises(webob.exc.HTTPBadRequest,
2760 self.controller._shrink, req, id, body=body)
2762 @ddt.data({'source': exception.InvalidInput,
2763 'target': webob.exc.HTTPBadRequest},
2764 {'source': exception.InvalidShare,
2765 'target': webob.exc.HTTPBadRequest})
2766 @ddt.unpack
2767 def test_shrink_exception(self, source, target):
2768 id = 'fake_share_id'
2769 req = fakes.HTTPRequest.blank('/v2/shares/%s/action' % id)
2770 body = {"os-shrink": {'new_size': '123'}}
2771 self.mock_object(share_api.API, "shrink",
2772 mock.Mock(side_effect=source('fake')))
2774 self.assertRaises(target, self.controller._shrink, req, id, body=body)
2777@ddt.ddt
2778class ShareAdminActionsAPITest(test.TestCase):
2780 def setUp(self):
2781 super(ShareAdminActionsAPITest, self).setUp()
2782 CONF.set_default("default_share_type", None)
2783 self.flags(transport_url='rabbit://fake:fake@mqhost:5672')
2784 self.share_api = share_api.API()
2785 self.admin_context = context.RequestContext('admin', 'fake', True)
2786 self.member_context = context.RequestContext('fake', 'fake')
2788 def _get_context(self, role):
2789 return getattr(self, '%s_context' % role)
2791 def _setup_share_data(self, share=None, version='2.7'):
2792 if share is None:
2793 share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
2794 size='1',
2795 override_defaults=True)
2796 path = '/v2/fake/shares/%s/action' % share['id']
2797 req = fakes.HTTPRequest.blank(path, script_name=path, version=version)
2798 return share, req
2800 def _reset_status(self, ctxt, model, req, db_access_method,
2801 valid_code, valid_status=None, body=None, version='2.7'):
2802 if float(version) > 2.6:
2803 action_name = 'reset_status'
2804 else:
2805 action_name = 'os-reset_status'
2806 if body is None:
2807 body = {action_name: {'status': constants.STATUS_ERROR}}
2808 req.method = 'POST'
2809 req.headers['content-type'] = 'application/json'
2810 req.headers['X-Openstack-Manila-Api-Version'] = version
2811 req.body = jsonutils.dumps(body).encode("utf-8")
2812 req.environ['manila.context'] = ctxt
2814 resp = req.get_response(fakes.app(), catch_exc_info=True)
2816 # validate response code and model status
2817 self.assertEqual(valid_code, resp.status_int)
2819 if valid_code == 404 and db_access_method is not None:
2820 self.assertRaises(exception.NotFound,
2821 db_access_method,
2822 ctxt,
2823 model['id'])
2824 elif db_access_method:
2825 actual_model = db_access_method(ctxt, model['id'])
2826 self.assertEqual(valid_status, actual_model['status'])
2828 @ddt.data(*fakes.fixture_reset_status_with_different_roles)
2829 @ddt.unpack
2830 def test_share_reset_status_with_different_roles(self, role, valid_code,
2831 valid_status, version):
2832 share, req = self._setup_share_data(version=version)
2833 ctxt = self._get_context(role)
2834 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2836 self._reset_status(ctxt, share, req, db.share_get, valid_code,
2837 valid_status, version=version)
2839 @ddt.data(*fakes.fixture_invalid_reset_status_body)
2840 def test_share_invalid_reset_status_body(self, body):
2841 share, req = self._setup_share_data(version='2.6')
2842 ctxt = self.admin_context
2843 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2845 self._reset_status(ctxt, share, req, db.share_get, 400,
2846 constants.STATUS_AVAILABLE, body, version='2.6')
2848 @ddt.data('2.6', '2.7')
2849 def test_share_reset_status_for_missing(self, version):
2850 fake_share = {'id': 'missing-share-id', 'is_soft_deleted': False}
2851 req = fakes.HTTPRequest.blank(
2852 '/v2/fake/shares/%s/action' % fake_share['id'], version=version)
2854 self._reset_status(self.admin_context, fake_share, req,
2855 db.share_get, 404, version=version)
2857 @ddt.data('2.6', '2.7')
2858 def test_reset_status_other_project_public_share(self, version):
2859 # NOTE(gouthamr): we're testing a scenario where someone has access
2860 # to the RBAC rule share:reset_status, but doesn't own the share.
2861 # Ideally we'd override the default policy, but it's a shared
2862 # resource and we'll bleed into other tests, so we'll mock the
2863 # policy check to return False instead
2864 share, req = self._setup_share_data(version=version)
2865 share['is_public'] = True
2866 rbac_checks = [None, exception.NotAuthorized]
2867 with mock.patch.object(policy, 'authorize', side_effect=rbac_checks):
2868 self.mock_object(share_api.API, 'get',
2869 mock.Mock(return_value=share))
2870 self._reset_status(
2871 self.member_context, share, req, None, 403, version=version)
2873 def _force_delete(self, ctxt, model, req, db_access_method, valid_code,
2874 check_model_in_db=False, version='2.7'):
2875 if float(version) > 2.6:
2876 action_name = 'force_delete'
2877 else:
2878 action_name = 'os-force_delete'
2879 req.method = 'POST'
2880 req.headers['content-type'] = 'application/json'
2881 req.headers['X-Openstack-Manila-Api-Version'] = version
2882 req.body = jsonutils.dumps({action_name: {}}).encode("utf-8")
2883 req.environ['manila.context'] = ctxt
2885 resp = req.get_response(fakes.app())
2887 # validate response
2888 self.assertEqual(valid_code, resp.status_int)
2890 if valid_code == 202 and check_model_in_db:
2891 self.assertRaises(exception.NotFound,
2892 db_access_method,
2893 ctxt,
2894 model['id'])
2896 @ddt.data(*fakes.fixture_force_delete_with_different_roles)
2897 @ddt.unpack
2898 def test_share_force_delete_with_different_roles(self, role, resp_code,
2899 version):
2900 share, req = self._setup_share_data(version=version)
2901 ctxt = self._get_context(role)
2903 self._force_delete(ctxt, share, req, db.share_get, resp_code,
2904 check_model_in_db=True, version=version)
2906 @ddt.data('2.6', '2.7')
2907 def test_share_force_delete_missing(self, version):
2908 share, req = self._setup_share_data(
2909 share={'id': 'fake'}, version=version)
2910 ctxt = self._get_context('admin')
2912 self._force_delete(
2913 ctxt, share, req, db.share_get, 404, version=version)
2916@ddt.ddt
2917class ShareUnmanageTest(test.TestCase):
2919 def setUp(self):
2920 super(ShareUnmanageTest, self).setUp()
2921 self.controller = shares.ShareController()
2922 self.mock_object(share_api.API, 'get_all',
2923 stubs.stub_get_all_shares)
2924 self.mock_object(share_api.API, 'get',
2925 stubs.stub_share_get)
2926 self.mock_object(share_api.API, 'update', stubs.stub_share_update)
2927 self.mock_object(share_api.API, 'delete', stubs.stub_share_delete)
2928 self.mock_object(share_api.API, 'get_snapshot',
2929 stubs.stub_snapshot_get)
2930 self.share_id = 'fake'
2931 self.request = fakes.HTTPRequest.blank(
2932 '/v2/fake/share/%s/unmanage' % self.share_id,
2933 use_admin_context=True, version='2.7',
2934 )
2936 def test_unmanage_share(self):
2937 share = dict(status=constants.STATUS_AVAILABLE, id='foo_id',
2938 instance={})
2939 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
2940 self.mock_object(share_api.API, 'unmanage', mock.Mock())
2941 self.mock_object(
2942 self.controller.share_api.db, 'share_snapshot_get_all_for_share',
2943 mock.Mock(return_value=[]))
2945 actual_result = self.controller.unmanage(
2946 self.request, share['id'], body={'unmanage': None})
2948 self.assertEqual(202, actual_result.status_int)
2949 (self.controller.share_api.db.share_snapshot_get_all_for_share.
2950 assert_called_once_with(
2951 self.request.environ['manila.context'], share['id']))
2952 self.controller.share_api.get.assert_called_once_with(
2953 self.request.environ['manila.context'], share['id'])
2954 share_api.API.unmanage.assert_called_once_with(
2955 self.request.environ['manila.context'], share)
2957 def test_unmanage_v249(self):
2958 body = {'unmanage': None}
2959 req = fakes.HTTPRequest.blank('/v2/fake/shares/1/action',
2960 use_admin_context=False,
2961 version='2.49')
2962 share = dict(status=constants.STATUS_AVAILABLE, id='foo_id',
2963 instance={})
2964 mock_unmanage = self.mock_object(
2965 self.controller, '_unmanage',
2966 mock.Mock(return_value=None))
2968 self.controller.unmanage(req, share['id'], body=body)
2970 mock_unmanage.assert_called_once_with(
2971 req, share['id'], body, allow_dhss_true=True
2972 )
2974 def test_unmanage_share_that_has_snapshots(self):
2975 share = dict(status=constants.STATUS_AVAILABLE, id='foo_id',
2976 instance={})
2977 snapshots = ['foo', 'bar']
2978 self.mock_object(self.controller.share_api, 'unmanage')
2979 self.mock_object(
2980 self.controller.share_api.db, 'share_snapshot_get_all_for_share',
2981 mock.Mock(return_value=snapshots))
2982 self.mock_object(
2983 self.controller.share_api, 'get',
2984 mock.Mock(return_value=share))
2986 self.assertRaises(
2987 webob.exc.HTTPForbidden,
2988 self.controller.unmanage, self.request, share['id'],
2989 body={'unmanage': None})
2991 self.assertFalse(self.controller.share_api.unmanage.called)
2992 (self.controller.share_api.db.share_snapshot_get_all_for_share.
2993 assert_called_once_with(
2994 self.request.environ['manila.context'], share['id']))
2995 self.controller.share_api.get.assert_called_once_with(
2996 self.request.environ['manila.context'], share['id'])
2998 def test_unmanage_share_based_on_share_server(self):
2999 share = dict(instance=dict(share_server_id='foo_id'), id='bar_id')
3000 self.mock_object(
3001 self.controller.share_api, 'get',
3002 mock.Mock(return_value=share))
3004 self.assertRaises(
3005 webob.exc.HTTPForbidden,
3006 self.controller.unmanage, self.request, share['id'],
3007 body={'unmanage': None})
3009 self.controller.share_api.get.assert_called_once_with(
3010 self.request.environ['manila.context'], share['id'])
3012 @ddt.data(*constants.TRANSITIONAL_STATUSES)
3013 def test_unmanage_share_with_transitional_state(self, share_status):
3014 share = dict(status=share_status, id='foo_id', instance={})
3015 self.mock_object(
3016 self.controller.share_api, 'get',
3017 mock.Mock(return_value=share))
3019 self.assertRaises(
3020 webob.exc.HTTPForbidden,
3021 self.controller.unmanage, self.request, share['id'],
3022 body={'unmanage': None})
3024 self.controller.share_api.get.assert_called_once_with(
3025 self.request.environ['manila.context'], share['id'])
3027 def test_unmanage_share_not_found(self):
3028 self.mock_object(share_api.API, 'get', mock.Mock(
3029 side_effect=exception.NotFound))
3030 self.mock_object(share_api.API, 'unmanage', mock.Mock())
3032 self.assertRaises(
3033 webob.exc.HTTPNotFound,
3034 self.controller.unmanage, self.request, self.share_id,
3035 body={'unmanage': None})
3037 @ddt.data(exception.InvalidShare(reason="fake"),
3038 exception.PolicyNotAuthorized(action="fake"),)
3039 def test_unmanage_share_invalid(self, side_effect):
3040 share = dict(status=constants.STATUS_AVAILABLE, id='foo_id',
3041 instance={})
3042 self.mock_object(share_api.API, 'get', mock.Mock(return_value=share))
3043 self.mock_object(share_api.API, 'unmanage', mock.Mock(
3044 side_effect=side_effect))
3046 self.assertRaises(
3047 webob.exc.HTTPForbidden,
3048 self.controller.unmanage, self.request, self.share_id,
3049 body={'unmanage': None})
3051 def test_wrong_permissions(self):
3052 share_id = 'fake'
3053 req = fakes.HTTPRequest.blank('/v2/fake/share/%s/unmanage' % share_id,
3054 use_admin_context=False, version='2.7')
3056 self.assertRaises(
3057 webob.exc.HTTPForbidden,
3058 self.controller.unmanage, req, share_id, body={'unmanage': None})
3060 def test_unsupported_version(self):
3061 share_id = 'fake'
3062 req = fakes.HTTPRequest.blank('/v2/fake/share/%s/unmanage' % share_id,
3063 use_admin_context=False, version='2.6')
3065 self.assertRaises(
3066 exception.VersionNotFoundForAPIMethod,
3067 self.controller.unmanage, req, share_id, body={'unmanage': None})
3070def get_fake_manage_body(export_path='/fake', service_host='fake@host#POOL',
3071 protocol='fake', share_type='fake', **kwargs):
3072 fake_share = {
3073 'export_path': export_path,
3074 'service_host': service_host,
3075 'protocol': protocol,
3076 'share_type': share_type,
3077 }
3078 fake_share.update(kwargs)
3079 return {'share': fake_share}
3082@ddt.ddt
3083class ShareManageTest(test.TestCase):
3085 def setUp(self):
3086 super(ShareManageTest, self).setUp()
3087 self.controller = shares.ShareController()
3088 self.resource_name = self.controller.resource_name
3089 self.request = fakes.HTTPRequest.blank(
3090 '/v2/shares/manage', use_admin_context=True, version='2.7')
3091 self.mock_policy_check = self.mock_object(
3092 policy, 'check_policy', mock.Mock(return_value=True))
3094 def _setup_manage_mocks(self, service_is_up=True):
3095 self.mock_object(db, 'service_get_by_host_and_topic', mock.Mock(
3096 return_value={'host': 'fake'}))
3097 self.mock_object(share_types, 'get_share_type_by_name_or_id',
3098 mock.Mock(return_value={'id': 'fake'}))
3099 self.mock_object(utils, 'service_is_up', mock.Mock(
3100 return_value=service_is_up))
3101 if service_is_up:
3102 self.mock_object(utils, 'validate_service_host')
3103 else:
3104 self.mock_object(
3105 utils,
3106 'validate_service_host',
3107 mock.Mock(side_effect=exception.ServiceIsDown(service='fake')))
3109 def test__manage(self):
3110 body = {}
3111 req = fakes.HTTPRequest.blank(
3112 '/v2/shares/manage', use_admin_context=True, version='2.49')
3113 mock_manage = self.mock_object(self.controller, '_manage')
3115 self.controller.manage(req, body)
3117 mock_manage.assert_called_once_with(
3118 req, body, allow_dhss_true=True
3119 )
3121 @ddt.data({},
3122 {'shares': {}},
3123 {'share': get_fake_manage_body('', None, None)})
3124 def test_share_manage_invalid_body(self, body):
3125 self.assertRaises(webob.exc.HTTPUnprocessableEntity,
3126 self.controller.manage,
3127 self.request,
3128 body)
3130 def test_share_manage_service_not_found(self):
3131 body = get_fake_manage_body()
3132 self.mock_object(db, 'service_get_by_host_and_topic', mock.Mock(
3133 side_effect=exception.ServiceNotFound(service_id='fake')))
3135 self.assertRaises(webob.exc.HTTPNotFound,
3136 self.controller.manage,
3137 self.request,
3138 body)
3140 def test_share_manage_share_type_not_found(self):
3141 body = get_fake_manage_body()
3142 self.mock_object(db, 'service_get_by_host_and_topic', mock.Mock())
3143 self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
3144 self.mock_object(db, 'share_type_get_by_name', mock.Mock(
3145 side_effect=exception.ShareTypeNotFoundByName(
3146 share_type_name='fake')))
3148 self.assertRaises(webob.exc.HTTPNotFound,
3149 self.controller.manage,
3150 self.request,
3151 body)
3153 @ddt.data({'service_is_up': False, 'service_host': 'fake@host#POOL'},
3154 {'service_is_up': True, 'service_host': 'fake@host'})
3155 def test_share_manage_bad_request(self, settings):
3156 body = get_fake_manage_body(service_host=settings.pop('service_host'))
3157 self._setup_manage_mocks(**settings)
3159 self.assertRaises(webob.exc.HTTPBadRequest,
3160 self.controller.manage,
3161 self.request,
3162 body)
3164 def test_share_manage_duplicate_share(self):
3165 body = get_fake_manage_body()
3166 exc = exception.InvalidShare(reason="fake")
3167 self._setup_manage_mocks()
3168 self.mock_object(share_api.API, 'manage', mock.Mock(side_effect=exc))
3170 self.assertRaises(webob.exc.HTTPConflict,
3171 self.controller.manage,
3172 self.request,
3173 body)
3175 def test_share_manage_forbidden_manage(self):
3176 body = get_fake_manage_body()
3177 self._setup_manage_mocks()
3178 error = mock.Mock(side_effect=exception.PolicyNotAuthorized(action=''))
3179 self.mock_object(share_api.API, 'manage', error)
3181 self.assertRaises(webob.exc.HTTPForbidden,
3182 self.controller.manage,
3183 self.request,
3184 body)
3186 def test_share_manage_forbidden_validate_service_host(self):
3187 body = get_fake_manage_body()
3188 self._setup_manage_mocks()
3189 error = mock.Mock(side_effect=exception.PolicyNotAuthorized(action=''))
3190 self.mock_object(
3191 utils, 'validate_service_host', mock.Mock(side_effect=error))
3193 self.assertRaises(webob.exc.HTTPForbidden,
3194 self.controller.manage,
3195 self.request,
3196 body)
3198 @ddt.data(
3199 get_fake_manage_body(name='foo', description='bar'),
3200 get_fake_manage_body(display_name='foo', description='bar'),
3201 get_fake_manage_body(name='foo', display_description='bar'),
3202 get_fake_manage_body(display_name='foo', display_description='bar'),
3203 get_fake_manage_body(display_name='foo', display_description='bar',
3204 driver_options=dict(volume_id='quuz')),
3205 )
3206 def test_share_manage(self, data):
3207 self._test_share_manage(data, "2.7")
3209 @ddt.data(
3210 get_fake_manage_body(name='foo', description='bar', is_public=True),
3211 get_fake_manage_body(name='foo', description='bar', is_public=False)
3212 )
3213 def test_share_manage_with_is_public(self, data):
3214 self._test_share_manage(data, "2.8")
3216 def test_share_manage_with_user_id(self):
3217 self._test_share_manage(get_fake_manage_body(
3218 name='foo', description='bar', is_public=True), "2.16")
3220 def _test_share_manage(self, data, version):
3221 expected = {
3222 'share': {
3223 'status': 'fakestatus',
3224 'description': 'displaydesc',
3225 'availability_zone': 'fakeaz',
3226 'name': 'displayname',
3227 'share_proto': 'FAKEPROTO',
3228 'metadata': {},
3229 'project_id': 'fakeproject',
3230 'host': 'fakehost',
3231 'id': 'fake',
3232 'snapshot_id': '2',
3233 'share_network_id': None,
3234 'created_at': datetime.datetime(1, 1, 1, 1, 1, 1),
3235 'size': 1,
3236 'share_type_name': None,
3237 'share_server_id': 'fake_share_server_id',
3238 'share_type': '1',
3239 'volume_type': '1',
3240 'is_public': False,
3241 'snapshot_support': True,
3242 'task_state': None,
3243 'links': [
3244 {
3245 'href': 'http://localhost/share/v2/fake/shares/fake',
3246 'rel': 'self'
3247 },
3248 {
3249 'href': 'http://localhost/share/fake/shares/fake',
3250 'rel': 'bookmark'
3251 }
3252 ],
3253 }
3254 }
3255 self._setup_manage_mocks()
3256 return_share = mock.Mock(
3257 return_value=stubs.stub_share(
3258 'fake',
3259 instance={
3260 'share_type_id': '1',
3261 })
3262 )
3263 self.mock_object(
3264 share_api.API, 'manage', return_share)
3265 self.mock_object(
3266 common, 'validate_public_share_policy',
3267 mock.Mock(side_effect=lambda *args, **kwargs: args[1]))
3268 share = {
3269 'host': data['share']['service_host'],
3270 'export_location_path': data['share']['export_path'],
3271 'share_proto': data['share']['protocol'].upper(),
3272 'share_type_id': 'fake',
3273 'display_name': 'foo',
3274 'display_description': 'bar',
3275 }
3276 driver_options = data['share'].get('driver_options', {})
3278 if (api_version.APIVersionRequest(version) <=
3279 api_version.APIVersionRequest('2.8')):
3280 expected['share']['export_location'] = 'fake_location'
3281 expected['share']['export_locations'] = (
3282 ['fake_location', 'fake_location2'])
3284 if (api_version.APIVersionRequest(version) >=
3285 api_version.APIVersionRequest('2.10')):
3286 expected['share']['access_rules_status'] = (
3287 constants.STATUS_ACTIVE)
3288 if (api_version.APIVersionRequest(version) >=
3289 api_version.APIVersionRequest('2.11')):
3290 expected['share']['has_replicas'] = False
3291 expected['share']['replication_type'] = None
3293 if (api_version.APIVersionRequest(version) >=
3294 api_version.APIVersionRequest('2.16')):
3295 expected['share']['user_id'] = 'fakeuser'
3297 if (api_version.APIVersionRequest(version) >=
3298 api_version.APIVersionRequest('2.8')):
3299 share['is_public'] = data['share']['is_public']
3301 if (api_version.APIVersionRequest(version) >= 3301 ↛ 3303line 3301 didn't jump to line 3303 because the condition on line 3301 was never true
3302 api_version.APIVersionRequest('2.80')):
3303 share['source_backup_id'] = None
3305 req = fakes.HTTPRequest.blank('/v2/fake/shares/manage',
3306 version=version,
3307 use_admin_context=True)
3309 actual_result = self.controller.manage(req, data)
3311 share_api.API.manage.assert_called_once_with(
3312 mock.ANY, share, driver_options)
3314 self.assertIsNotNone(actual_result)
3315 self.assertEqual(expected, actual_result)
3316 self.mock_policy_check.assert_called_once_with(
3317 req.environ['manila.context'], self.resource_name, 'manage')
3319 def test_wrong_permissions(self):
3320 body = get_fake_manage_body()
3322 self.assertRaises(
3323 webob.exc.HTTPForbidden,
3324 self.controller.manage,
3325 fakes.HTTPRequest.blank('/v2/fake/share/manage',
3326 use_admin_context=False,
3327 version='2.7'),
3328 body,
3329 )
3331 def test_unsupported_version(self):
3332 share_id = 'fake'
3333 req = fakes.HTTPRequest.blank(
3334 '/v2/fake/share/manage', use_admin_context=False, version='2.6')
3336 self.assertRaises(exception.VersionNotFoundForAPIMethod,
3337 self.controller.manage,
3338 req,
3339 share_id)
3341 def test_revert(self):
3342 mock_revert = self.mock_object(
3343 self.controller, '_revert',
3344 mock.Mock(return_value=None))
3345 req = fakes.HTTPRequest.blank('/v2/fake/shares/fake_id/action',
3346 use_admin_context=False,
3347 version='2.27')
3349 body = {'revert': {'snapshot_id': uuidutils.generate_uuid()}}
3350 result = self.controller.revert(req, 'fake_id', body=body)
3352 self.assertIsNone(result)
3353 mock_revert.assert_called_once_with(req, 'fake_id', body)
3355 def test_revert_unsupported(self):
3356 body = {'revert': {'snapshot_id': uuidutils.generate_uuid()}}
3357 req = fakes.HTTPRequest.blank('/v2/shares/fake_id/action',
3358 use_admin_context=False,
3359 version='2.24')
3361 self.assertRaises(exception.VersionNotFoundForAPIMethod,
3362 self.controller.revert,
3363 req,
3364 'fake_id',
3365 body=body)