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

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. 

15 

16import copy 

17import datetime 

18import itertools 

19from unittest import mock 

20 

21import ddt 

22from oslo_config import cfg 

23from oslo_serialization import jsonutils 

24from oslo_utils import uuidutils 

25import webob 

26import webob.exc 

27 

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 

46 

47CONF = cfg.CONF 

48 

49LATEST_MICROVERSION = api_version._MAX_API_VERSION 

50 

51 

52@ddt.ddt 

53class ShareAPITest(test.TestCase): 

54 """Share API Test.""" 

55 

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 } 

134 

135 CONF.set_default("default_share_type", None) 

136 self.mock_object(policy, 'check_policy') 

137 

138 def _process_expected_share_detailed_response(self, shr_dict, req_version): 

139 """Sets version based parameters on share dictionary.""" 

140 

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 } 

156 

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) 

161 

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) 

168 

169 return share_dict 

170 

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 } 

220 

221 def test__revert(self): 

222 

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') 

248 

249 response = self.controller._revert(req, '1', body=body) 

250 

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) 

260 

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) 

274 

275 def test__revert_not_supported(self): 

276 

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)) 

290 

291 self.assertRaises(webob.exc.HTTPBadRequest, 

292 self.controller._revert, 

293 req, 

294 '1', 

295 body=body) 

296 

297 def test__revert_id_mismatch(self): 

298 

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)) 

313 

314 self.assertRaises(webob.exc.HTTPBadRequest, 

315 self.controller._revert, 

316 req, 

317 '1', 

318 body=body) 

319 

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): 

337 

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)) 

352 

353 self.assertRaises(webob.exc.HTTPConflict, 

354 self.controller._revert, 

355 req, 

356 '1', 

357 body=body) 

358 

359 def test__revert_snapshot_latest_not_found(self): 

360 

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)) 

377 

378 self.assertRaises(webob.exc.HTTPBadRequest, 

379 self.controller._revert, 

380 req, 

381 '1', 

382 body=body) 

383 

384 def test__revert_snapshot_access_applying(self): 

385 

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') 

408 

409 self.assertRaises(webob.exc.HTTPConflict, 

410 self.controller._revert, 

411 req, 

412 '1', 

413 body=body) 

414 

415 def test__revert_snapshot_not_latest(self): 

416 

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)) 

436 

437 self.assertRaises(webob.exc.HTTPConflict, 

438 self.controller._revert, 

439 req, 

440 '1', 

441 body=body) 

442 

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): 

469 

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))) 

476 

477 self.assertRaises(thrown, 

478 self.controller._revert, 

479 req, 

480 '1', 

481 body=body) 

482 

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) 

488 

489 res_dict = self.controller.create(req, body) 

490 

491 expected = self._get_expected_share_detailed_response( 

492 self.share, version=microversion) 

493 self.assertEqual(expected, res_dict) 

494 

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) 

500 

501 res_dict = self.controller.create(req, body) 

502 

503 expected = self._get_expected_share_detailed_response( 

504 self.share, version=microversion) 

505 self.assertEqual(expected, res_dict) 

506 

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) 

512 

513 res_dict = self.controller.create(req, body) 

514 

515 expected = self._get_expected_share_detailed_response( 

516 self.share, version="2.31") 

517 self.assertEqual(expected, res_dict) 

518 

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) 

539 

540 self.controller.create(req, body) 

541 

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) 

560 

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) 

582 

583 self.assertRaises( 

584 exception.InvalidInput, self.controller.create, req, body) 

585 

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) 

591 

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) 

607 

608 self.assertRaises( 

609 webob.exc.HTTPNotFound, self.controller.create, req, body) 

610 

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) 

615 

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 } 

630 

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)) 

649 

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' 

659 

660 self.mock_object(share_types, 'get_share_type_by_name', mock.Mock( 

661 return_value=stype)) 

662 

663 share['encryption_key_ref'] = 'fake_key_uuid' 

664 

665 request_args = copy.deepcopy(share) 

666 request_args['share_type'] = 'fake_volume_type_name' 

667 

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) 

680 

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) 

686 

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) 

690 

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) 

696 

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() 

708 

709 def test_share_create_with_replication(self): 

710 self.mock_object(share_api.API, 'create', self.create_mock) 

711 

712 body = {"share": copy.deepcopy(self.share)} 

713 req = fakes.HTTPRequest.blank( 

714 '/v2/fake/shares', 

715 version=share_replicas.MIN_SUPPORTED_API_VERSION) 

716 

717 res_dict = self.controller.create(req, body) 

718 

719 expected = self._get_expected_share_detailed_response( 

720 self.share, version=share_replicas.MIN_SUPPORTED_API_VERSION) 

721 

722 self.assertEqual(expected, res_dict) 

723 

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)) 

751 

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) 

755 

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) 

764 

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) 

770 

771 res_dict = self.controller.create(req, body) 

772 

773 expected = self._get_expected_share_detailed_response( 

774 self.share, version=microversion) 

775 

776 self.assertEqual(expected, res_dict) 

777 

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)) 

793 

794 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=version) 

795 

796 res_dict = self.controller.create(req, {'share': create_args}) 

797 

798 expected = self._get_expected_share_detailed_response( 

799 values=self.share, version=version) 

800 

801 self.assertEqual(expected, res_dict) 

802 

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) 

826 

827 req = fakes.HTTPRequest.blank('/v2/fake/shares', version=version) 

828 

829 self.assertRaises(webob.exc.HTTPBadRequest, 

830 self.controller.create, 

831 req, {'share': create_args}) 

832 share_api.API.create.assert_not_called() 

833 

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'] 

846 

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)) 

851 

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 } 

863 

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)) 

867 

868 response = self.controller.migration_start(req, share['id'], body=body) 

869 

870 self.assertEqual(202, response.status_int) 

871 

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') 

880 

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 

889 

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 } 

899 

900 self.mock_object(share_api.API, 'migration_start', 

901 mock.Mock(side_effect=exception.Conflict(err='err'))) 

902 

903 self.assertRaises(webob.exc.HTTPConflict, 

904 self.controller.migration_start, 

905 req, share['id'], body=body) 

906 

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 

918 

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 } 

928 

929 if param == 'body': 

930 body.pop('migration_start') 

931 else: 

932 body['migration_start'].pop(param) 

933 

934 self.mock_object(share_api.API, 'migration_start') 

935 self.mock_object(share_api.API, 'get', 

936 mock.Mock(return_value=share)) 

937 

938 self.assertRaises(exception.ValidationError, 

939 self.controller.migration_start, 

940 req, 'fake_id', body=body) 

941 

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 

953 

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 } 

963 

964 body['migration_start'][param] = None 

965 

966 self.mock_object(share_api.API, 'migration_start') 

967 self.mock_object(share_api.API, 'get', 

968 mock.Mock(return_value=share)) 

969 

970 self.assertRaises(exception.ValidationError, 

971 self.controller.migration_start, 

972 req, 'fake_id', body=body) 

973 

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 

980 

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 } 

990 

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) 

996 

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 

1007 

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'}} 

1016 

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') 

1023 

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 

1034 

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'}} 

1043 

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') 

1050 

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 

1060 

1061 body = {'migration_start': {'host': 'fake_host', 

1062 'force_host_assisted_migration': 'fake'}} 

1063 

1064 self.assertRaises(exception.ValidationError, 

1065 self.controller.migration_start, 

1066 req, share['id'], body=body) 

1067 

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 

1079 

1080 body = {'migration_start': {'host': 'fake_host', 

1081 parameter: 'invalid'}} 

1082 

1083 self.assertRaises(exception.ValidationError, 

1084 self.controller.migration_start, req, share['id'], 

1085 body=body) 

1086 

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 

1097 

1098 update = {'task_state': task_state} 

1099 body = {'reset_task_state': update} 

1100 

1101 self.mock_object(db, 'share_update') 

1102 

1103 response = self.controller.reset_task_state( 

1104 req, share['id'], body=body) 

1105 

1106 self.assertEqual(202, response.status_int) 

1107 

1108 db.share_update.assert_called_once_with(utils.IsAMatcher( 

1109 context.RequestContext), share['id'], update) 

1110 

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 

1120 

1121 update = {'error': 'error'} 

1122 body = {'reset_task_state': update} 

1123 

1124 self.assertRaises(webob.exc.HTTPBadRequest, 

1125 self.controller.reset_task_state, req, share['id'], 

1126 body=body) 

1127 

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 

1137 

1138 update = {'task_state': 'error'} 

1139 body = {'reset_task_state': update} 

1140 

1141 self.assertRaises(exception.ValidationError, 

1142 self.controller.reset_task_state, req, share['id'], 

1143 body=body) 

1144 

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 

1154 

1155 update = {'task_state': constants.TASK_STATE_MIGRATION_ERROR} 

1156 body = {'reset_task_state': update} 

1157 

1158 self.mock_object(share_api.API, 'get', 

1159 mock.Mock(side_effect=exception.NotFound)) 

1160 self.mock_object(db, 'share_update') 

1161 

1162 self.assertRaises(exception.NotFound, 

1163 self.controller.reset_task_state, req, share['id'], 

1164 body=body) 

1165 

1166 share_api.API.get.assert_called_once_with(utils.IsAMatcher( 

1167 context.RequestContext), share['id']) 

1168 db.share_update.assert_not_called() 

1169 

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} 

1180 

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) 

1194 

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)) 

1208 

1209 self.assertRaises(webob.exc.HTTPForbidden, 

1210 self.controller.reset_task_state, req, share['id'], 

1211 body=body) 

1212 

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 

1222 

1223 body = {'migration_complete': None} 

1224 

1225 self.mock_object(share_api.API, 'get', 

1226 mock.Mock(return_value=share)) 

1227 

1228 self.mock_object(share_api.API, 'migration_complete') 

1229 

1230 response = self.controller.migration_complete( 

1231 req, share['id'], body=body) 

1232 

1233 self.assertEqual(202, response.status_int) 

1234 

1235 share_api.API.migration_complete.assert_called_once_with( 

1236 utils.IsAMatcher(context.RequestContext), share) 

1237 

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 

1247 

1248 body = {'migration_complete': None} 

1249 

1250 self.mock_object(share_api.API, 'get', 

1251 mock.Mock(side_effect=exception.NotFound())) 

1252 self.mock_object(share_api.API, 'migration_complete') 

1253 

1254 self.assertRaises(webob.exc.HTTPNotFound, 

1255 self.controller.migration_complete, req, share['id'], 

1256 body=body) 

1257 

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 

1267 

1268 body = {'migration_cancel': None} 

1269 

1270 self.mock_object(share_api.API, 'get', 

1271 mock.Mock(return_value=share)) 

1272 

1273 self.mock_object(share_api.API, 'migration_cancel') 

1274 

1275 response = self.controller.migration_cancel( 

1276 req, share['id'], body=body) 

1277 

1278 self.assertEqual(202, response.status_int) 

1279 

1280 share_api.API.migration_cancel.assert_called_once_with( 

1281 utils.IsAMatcher(context.RequestContext), share) 

1282 

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 

1292 

1293 body = {'migration_cancel': None} 

1294 

1295 self.mock_object(share_api.API, 'get', 

1296 mock.Mock(side_effect=exception.NotFound())) 

1297 self.mock_object(share_api.API, 'migration_cancel') 

1298 

1299 self.assertRaises(webob.exc.HTTPNotFound, 

1300 self.controller.migration_cancel, req, share['id'], 

1301 body=body) 

1302 

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 

1313 

1314 body = {'migration_get_progress': None} 

1315 expected = { 

1316 'total_progress': 50, 

1317 'task_state': constants.TASK_STATE_MIGRATION_SUCCESS, 

1318 } 

1319 

1320 self.mock_object(share_api.API, 'get', 

1321 mock.Mock(return_value=share)) 

1322 

1323 self.mock_object(share_api.API, 'migration_get_progress', 

1324 mock.Mock(return_value=copy.deepcopy(expected))) 

1325 

1326 response = self.controller.migration_get_progress(req, share['id'], 

1327 body=body) 

1328 

1329 self.assertEqual(expected, response) 

1330 

1331 share_api.API.migration_get_progress.assert_called_once_with( 

1332 utils.IsAMatcher(context.RequestContext), share) 

1333 

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 

1343 

1344 body = {'migration_get_progress': None} 

1345 

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') 

1349 

1350 self.assertRaises(webob.exc.HTTPNotFound, 

1351 self.controller.migration_get_progress, req, 

1352 share['id'], body=body) 

1353 

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') 

1376 

1377 res_dict = self.controller.create(req, body) 

1378 

1379 expected = self._get_expected_share_detailed_response( 

1380 shr, version='2.7') 

1381 self.assertEqual(expected, res_dict) 

1382 

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)) 

1421 

1422 body = {"share": copy.deepcopy(shr)} 

1423 req = fakes.HTTPRequest.blank('/v2/fake/shares', version='2.7') 

1424 

1425 res_dict = self.controller.create(req, body) 

1426 

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) 

1435 

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)) 

1473 

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']) 

1483 

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) 

1499 

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})) 

1519 

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) 

1524 

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) 

1535 

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, {}) 

1540 

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)} 

1548 

1549 req = fakes.HTTPRequest.blank('/v2/shares', version='2.7') 

1550 self.assertRaises(webob.exc.HTTPNotFound, 

1551 self.controller.create, 

1552 req, 

1553 body) 

1554 

1555 def test_share_show(self): 

1556 req = fakes.HTTPRequest.blank('/v2/fake/shares/1') 

1557 expected = self._get_expected_share_detailed_response() 

1558 

1559 res_dict = self.controller.show(req, '1') 

1560 

1561 self.assertEqual(expected, res_dict) 

1562 

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') 

1567 

1568 res_dict = self.controller.show(req, '1') 

1569 

1570 self.assertDictEqual(expected, res_dict) 

1571 

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') 

1576 

1577 res_dict = self.controller.show(req, '1') 

1578 

1579 self.assertDictEqual(expected, res_dict) 

1580 

1581 def test_share_show_with_share_type_name(self): 

1582 req = fakes.HTTPRequest.blank('/v2/fake/shares/1', version='2.6') 

1583 

1584 res_dict = self.controller.show(req, '1') 

1585 

1586 expected = self._get_expected_share_detailed_response(version='2.6') 

1587 self.assertEqual(expected, res_dict) 

1588 

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) 

1593 

1594 res_dict = self.controller.show(req, '1') 

1595 

1596 expected = self._get_expected_share_detailed_response( 

1597 version=microversion) 

1598 

1599 self.assertEqual(expected, res_dict) 

1600 

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) 

1605 

1606 res_dict = self.controller.show(req, '1') 

1607 

1608 self.assertEqual(expected, res_dict) 

1609 

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') 

1617 

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') 

1622 

1623 expected = self._get_expected_share_detailed_response(version=api_vers) 

1624 

1625 self.assertEqual(expected, res_dict) 

1626 

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) 

1637 

1638 res_dict = self.controller.show(req, share['id']) 

1639 

1640 expected = (constants.STATUS_OUT_OF_SYNC if translated else 

1641 constants.SHARE_INSTANCE_RULES_SYNCING) 

1642 

1643 self.assertEqual(expected, res_dict['share']['access_rules_status']) 

1644 

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) 

1651 

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'))) 

1661 

1662 self.assertRaises( 

1663 webob.exc.HTTPForbidden, self.controller.share_soft_delete, 

1664 req, 1, body=body) 

1665 

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'))) 

1674 

1675 self.assertRaises( 

1676 webob.exc.HTTPConflict, self.controller.share_soft_delete, 

1677 req, 1, body=body) 

1678 

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) 

1687 

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) 

1698 

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) 

1703 

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'))) 

1710 

1711 self.assertRaises( 

1712 webob.exc.HTTPConflict, self.controller.delete, req, 1) 

1713 

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) 

1722 

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) 

1732 

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) 

1742 

1743 def test_share_update(self): 

1744 shr = self.share 

1745 body = {"share": shr} 

1746 

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']) 

1754 

1755 def test_share_update_with_share_group(self): 

1756 shr = self.share 

1757 body = {"share": shr} 

1758 

1759 req = fakes.HTTPRequest.blank( 

1760 '/v2/fake/share/1', version="2.31", experimental=True) 

1761 

1762 res_dict = self.controller.update(req, 1, body) 

1763 

1764 self.assertIsNone(res_dict['share']["share_group_id"]) 

1765 self.assertIsNone( 

1766 res_dict['share']["source_share_group_snapshot_member_id"]) 

1767 

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"]) 

1772 

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) 

1781 

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 ] 

1826 

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) 

1841 

1842 mock_get_all = ( 

1843 self.mock_object(share_api.API, method, mock.Mock(**mock_action))) 

1844 

1845 result = self.controller.index(req) 

1846 

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']) 

1875 

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']) 

1896 

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) 

1916 

1917 self.mock_object(share_api.API, 'get_all_with_count', 

1918 mock.Mock(side_effect=[(0, [])])) 

1919 

1920 result = self.controller.index(req) 

1921 

1922 search_opts_expected = {} 

1923 

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']) 

1939 

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) 

1968 

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 ] 

2015 

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) 

2034 

2035 mock_get_all = self.mock_object(share_api.API, method, 

2036 mock.Mock(**mock_action)) 

2037 

2038 result = self.controller.detail(req) 

2039 

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 } 

2053 

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'] 

2067 

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']) 

2105 

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]} 

2140 

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) 

2144 

2145 res_dict = self.controller.detail(req) 

2146 

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']) 

2154 

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) 

2161 

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) 

2182 

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) 

2190 

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) 

2201 

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) 

2209 

2210 res_dict = self.controller.detail(req) 

2211 

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']) 

2256 

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) 

2264 

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) 

2272 

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') 

2283 

2284 req = fakes.HTTPRequest.blank( 

2285 '/v2/shares/%s/metadata' % id) 

2286 

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) 

2292 

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') 

2303 

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) 

2310 

2311 def test_delete_metadata(self): 

2312 mock_delete = self.mock_object( 

2313 self.controller, '_delete_metadata', mock.Mock()) 

2314 

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') 

2319 

2320 

2321def _fake_access_get(self, ctxt, access_id): 

2322 

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 

2332 

2333 def __getitem__(self, item): 

2334 return self.params[item] 

2335 

2336 access = Access(access_id=access_id, share_id='fake_share_id') 

2337 return access 

2338 

2339 

2340@ddt.ddt 

2341class ShareActionsTest(test.TestCase): 

2342 

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') 

2348 

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) 

2403 

2404 res = self.controller.allow_access(req, id, body) 

2405 

2406 self.assertEqual(expected, res) 

2407 

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) 

2463 

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'}})) 

2478 

2479 req = fakes.HTTPRequest.blank( 

2480 '/v2/shares/%s/action' % share_id, version=version) 

2481 

2482 body = {'allow_access': 

2483 { 

2484 'access_type': 'cephx', 

2485 'access_to': access_to, 

2486 'access_level': 'rw' 

2487 }} 

2488 

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) 

2496 

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) 

2501 

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 

2513 

2514 body = { 

2515 key: { 

2516 'access_type': 'user', 

2517 'access_to': 'crimsontide', 

2518 'access_level': 'rw', 

2519 } 

2520 } 

2521 

2522 self.assertRaises(webob.exc.HTTPBadRequest, 

2523 method, req, share['id'], body) 

2524 self.assertFalse(share_api.API.allow_access.called) 

2525 

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 }) 

2556 

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} 

2571 

2572 access = method(req, share['id'], body) 

2573 

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) 

2578 

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 }) 

2599 

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} 

2607 

2608 access = self.controller.allow_access(req, share['id'], body) 

2609 

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 }) 

2617 

2618 if api_version.APIVersionRequest(version) >= ( 

2619 api_version.APIVersionRequest("2.45")): 

2620 expected_access.update( 

2621 { 

2622 'metadata': {}, 

2623 }) 

2624 

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 

2630 

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) 

2635 

2636 def test_deny_access(self): 

2637 def _stub_deny_access(*args, **kwargs): 

2638 pass 

2639 

2640 self.mock_object(share_api.API, "deny_access", _stub_deny_access) 

2641 self.mock_object(share_api.API, "access_get", _fake_access_get) 

2642 

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) 

2648 

2649 def test_deny_access_not_found(self): 

2650 def _stub_deny_access(*args, **kwargs): 

2651 pass 

2652 

2653 self.mock_object(share_api.API, "deny_access", _stub_deny_access) 

2654 self.mock_object(share_api.API, "access_get", _fake_access_get) 

2655 

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) 

2664 

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) 

2680 

2681 res_dict = self.controller._access_list(req, id, body) 

2682 self.assertEqual({'access_list': fake_access_list}, res_dict) 

2683 

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") 

2694 

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) 

2699 

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) 

2704 

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) 

2711 

2712 self.assertRaises(webob.exc.HTTPBadRequest, 

2713 self.controller._extend, req, id, body=body) 

2714 

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'))) 

2728 

2729 self.assertRaises(target, self.controller._extend, req, id, body=body) 

2730 

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") 

2741 

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) 

2746 

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) 

2751 

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) 

2758 

2759 self.assertRaises(webob.exc.HTTPBadRequest, 

2760 self.controller._shrink, req, id, body=body) 

2761 

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'))) 

2773 

2774 self.assertRaises(target, self.controller._shrink, req, id, body=body) 

2775 

2776 

2777@ddt.ddt 

2778class ShareAdminActionsAPITest(test.TestCase): 

2779 

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') 

2787 

2788 def _get_context(self, role): 

2789 return getattr(self, '%s_context' % role) 

2790 

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 

2799 

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 

2813 

2814 resp = req.get_response(fakes.app(), catch_exc_info=True) 

2815 

2816 # validate response code and model status 

2817 self.assertEqual(valid_code, resp.status_int) 

2818 

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']) 

2827 

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)) 

2835 

2836 self._reset_status(ctxt, share, req, db.share_get, valid_code, 

2837 valid_status, version=version) 

2838 

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)) 

2844 

2845 self._reset_status(ctxt, share, req, db.share_get, 400, 

2846 constants.STATUS_AVAILABLE, body, version='2.6') 

2847 

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) 

2853 

2854 self._reset_status(self.admin_context, fake_share, req, 

2855 db.share_get, 404, version=version) 

2856 

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) 

2872 

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 

2884 

2885 resp = req.get_response(fakes.app()) 

2886 

2887 # validate response 

2888 self.assertEqual(valid_code, resp.status_int) 

2889 

2890 if valid_code == 202 and check_model_in_db: 

2891 self.assertRaises(exception.NotFound, 

2892 db_access_method, 

2893 ctxt, 

2894 model['id']) 

2895 

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) 

2902 

2903 self._force_delete(ctxt, share, req, db.share_get, resp_code, 

2904 check_model_in_db=True, version=version) 

2905 

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') 

2911 

2912 self._force_delete( 

2913 ctxt, share, req, db.share_get, 404, version=version) 

2914 

2915 

2916@ddt.ddt 

2917class ShareUnmanageTest(test.TestCase): 

2918 

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 ) 

2935 

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=[])) 

2944 

2945 actual_result = self.controller.unmanage( 

2946 self.request, share['id'], body={'unmanage': None}) 

2947 

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) 

2956 

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)) 

2967 

2968 self.controller.unmanage(req, share['id'], body=body) 

2969 

2970 mock_unmanage.assert_called_once_with( 

2971 req, share['id'], body, allow_dhss_true=True 

2972 ) 

2973 

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)) 

2985 

2986 self.assertRaises( 

2987 webob.exc.HTTPForbidden, 

2988 self.controller.unmanage, self.request, share['id'], 

2989 body={'unmanage': None}) 

2990 

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']) 

2997 

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)) 

3003 

3004 self.assertRaises( 

3005 webob.exc.HTTPForbidden, 

3006 self.controller.unmanage, self.request, share['id'], 

3007 body={'unmanage': None}) 

3008 

3009 self.controller.share_api.get.assert_called_once_with( 

3010 self.request.environ['manila.context'], share['id']) 

3011 

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)) 

3018 

3019 self.assertRaises( 

3020 webob.exc.HTTPForbidden, 

3021 self.controller.unmanage, self.request, share['id'], 

3022 body={'unmanage': None}) 

3023 

3024 self.controller.share_api.get.assert_called_once_with( 

3025 self.request.environ['manila.context'], share['id']) 

3026 

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()) 

3031 

3032 self.assertRaises( 

3033 webob.exc.HTTPNotFound, 

3034 self.controller.unmanage, self.request, self.share_id, 

3035 body={'unmanage': None}) 

3036 

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)) 

3045 

3046 self.assertRaises( 

3047 webob.exc.HTTPForbidden, 

3048 self.controller.unmanage, self.request, self.share_id, 

3049 body={'unmanage': None}) 

3050 

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') 

3055 

3056 self.assertRaises( 

3057 webob.exc.HTTPForbidden, 

3058 self.controller.unmanage, req, share_id, body={'unmanage': None}) 

3059 

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') 

3064 

3065 self.assertRaises( 

3066 exception.VersionNotFoundForAPIMethod, 

3067 self.controller.unmanage, req, share_id, body={'unmanage': None}) 

3068 

3069 

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} 

3080 

3081 

3082@ddt.ddt 

3083class ShareManageTest(test.TestCase): 

3084 

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)) 

3093 

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'))) 

3108 

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') 

3114 

3115 self.controller.manage(req, body) 

3116 

3117 mock_manage.assert_called_once_with( 

3118 req, body, allow_dhss_true=True 

3119 ) 

3120 

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) 

3129 

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'))) 

3134 

3135 self.assertRaises(webob.exc.HTTPNotFound, 

3136 self.controller.manage, 

3137 self.request, 

3138 body) 

3139 

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'))) 

3147 

3148 self.assertRaises(webob.exc.HTTPNotFound, 

3149 self.controller.manage, 

3150 self.request, 

3151 body) 

3152 

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) 

3158 

3159 self.assertRaises(webob.exc.HTTPBadRequest, 

3160 self.controller.manage, 

3161 self.request, 

3162 body) 

3163 

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)) 

3169 

3170 self.assertRaises(webob.exc.HTTPConflict, 

3171 self.controller.manage, 

3172 self.request, 

3173 body) 

3174 

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) 

3180 

3181 self.assertRaises(webob.exc.HTTPForbidden, 

3182 self.controller.manage, 

3183 self.request, 

3184 body) 

3185 

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)) 

3192 

3193 self.assertRaises(webob.exc.HTTPForbidden, 

3194 self.controller.manage, 

3195 self.request, 

3196 body) 

3197 

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") 

3208 

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") 

3215 

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") 

3219 

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', {}) 

3277 

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']) 

3283 

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 

3292 

3293 if (api_version.APIVersionRequest(version) >= 

3294 api_version.APIVersionRequest('2.16')): 

3295 expected['share']['user_id'] = 'fakeuser' 

3296 

3297 if (api_version.APIVersionRequest(version) >= 

3298 api_version.APIVersionRequest('2.8')): 

3299 share['is_public'] = data['share']['is_public'] 

3300 

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 

3304 

3305 req = fakes.HTTPRequest.blank('/v2/fake/shares/manage', 

3306 version=version, 

3307 use_admin_context=True) 

3308 

3309 actual_result = self.controller.manage(req, data) 

3310 

3311 share_api.API.manage.assert_called_once_with( 

3312 mock.ANY, share, driver_options) 

3313 

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') 

3318 

3319 def test_wrong_permissions(self): 

3320 body = get_fake_manage_body() 

3321 

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 ) 

3330 

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') 

3335 

3336 self.assertRaises(exception.VersionNotFoundForAPIMethod, 

3337 self.controller.manage, 

3338 req, 

3339 share_id) 

3340 

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') 

3348 

3349 body = {'revert': {'snapshot_id': uuidutils.generate_uuid()}} 

3350 result = self.controller.revert(req, 'fake_id', body=body) 

3351 

3352 self.assertIsNone(result) 

3353 mock_revert.assert_called_once_with(req, 'fake_id', body) 

3354 

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') 

3360 

3361 self.assertRaises(exception.VersionNotFoundForAPIMethod, 

3362 self.controller.revert, 

3363 req, 

3364 'fake_id', 

3365 body=body)