Coverage for manila/tests/share/test_access.py: 100%

362 statements  

« prev     ^ index     » next       coverage.py v7.11.0, created at 2026-02-18 22:19 +0000

1# Copyright 2016 Hitachi Data Systems 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 itertools 

17import random 

18from unittest import mock 

19 

20import ddt 

21 

22from manila.common import constants 

23from manila import context 

24from manila import db 

25from manila import exception 

26from manila.share import access 

27from manila import test 

28from manila.tests import db_utils 

29from manila import utils 

30 

31 

32class LockedOperationsTestCase(test.TestCase): 

33 

34 class FakeAccessHelper(object): 

35 

36 @access.locked_access_rules_operation 

37 def some_access_rules_operation(self, context, share_instance_id=None): 

38 pass 

39 

40 def setUp(self): 

41 super(LockedOperationsTestCase, self).setUp() 

42 self.access_helper = self.FakeAccessHelper() 

43 self.context = context.RequestContext('fake_user', 'fake_project') 

44 self.lock_call = self.mock_object( 

45 utils, 'synchronized', mock.Mock(return_value=lambda f: f)) 

46 

47 def test_locked_access_rules_operation(self, **replica): 

48 

49 self.access_helper.some_access_rules_operation( 

50 self.context, share_instance_id='FAKE_INSTANCE_ID') 

51 

52 self.lock_call.assert_called_once_with( 

53 "locked_access_rules_operation_by_share_instance_FAKE_INSTANCE_ID", 

54 external=True) 

55 

56 

57@ddt.ddt 

58class ShareInstanceAccessDatabaseMixinTestCase(test.TestCase): 

59 

60 def setUp(self): 

61 super(ShareInstanceAccessDatabaseMixinTestCase, self).setUp() 

62 self.driver = mock.Mock() 

63 self.access_helper = access.ShareInstanceAccess(db, self.driver) 

64 self.context = context.RequestContext('fake_user', 'fake_project') 

65 self.mock_object( 

66 utils, 'synchronized', mock.Mock(return_value=lambda f: f)) 

67 

68 def test_get_and_update_access_rules_status_force_status(self): 

69 share = db_utils.create_share( 

70 access_rule_status=constants.STATUS_ACTIVE, 

71 status=constants.STATUS_AVAILABLE) 

72 share = db.share_get(self.context, share['id']) 

73 self.assertEqual(constants.STATUS_ACTIVE, share['access_rules_status']) 

74 

75 self.access_helper.get_and_update_share_instance_access_rules_status( 

76 self.context, status=constants.SHARE_INSTANCE_RULES_SYNCING, 

77 share_instance_id=share['instance']['id']) 

78 

79 share = db.share_get(self.context, share['id']) 

80 self.assertEqual(constants.SHARE_INSTANCE_RULES_SYNCING, 

81 share['access_rules_status']) 

82 

83 @ddt.data((constants.SHARE_INSTANCE_RULES_SYNCING, True), 

84 (constants.STATUS_ERROR, False)) 

85 @ddt.unpack 

86 def test_get_and_update_access_rules_status_conditionally_change( 

87 self, initial_status, change_allowed): 

88 share = db_utils.create_share(access_rules_status=initial_status, 

89 status=constants.STATUS_AVAILABLE) 

90 share = db.share_get(self.context, share['id']) 

91 self.assertEqual(initial_status, share['access_rules_status']) 

92 

93 conditionally_change = { 

94 constants.SHARE_INSTANCE_RULES_SYNCING: constants.STATUS_ACTIVE, 

95 } 

96 

97 updated_instance = ( 

98 self.access_helper. 

99 get_and_update_share_instance_access_rules_status( 

100 self.context, conditionally_change=conditionally_change, 

101 share_instance_id=share['instance']['id']) 

102 ) 

103 

104 share = db.share_get(self.context, share['id']) 

105 if change_allowed: 

106 self.assertEqual(constants.STATUS_ACTIVE, 

107 share['access_rules_status']) 

108 self.assertIsNotNone(updated_instance) 

109 else: 

110 self.assertEqual(initial_status, share['access_rules_status']) 

111 self.assertIsNone(updated_instance) 

112 

113 def test_get_and_update_all_access_rules_just_get(self): 

114 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

115 rule_1 = db_utils.create_access(share_id=share['id']) 

116 rule_2 = db_utils.create_access(share_id=share['id']) 

117 self.mock_object(db, 'share_instance_access_update') 

118 

119 rules = self.access_helper.get_and_update_share_instance_access_rules( 

120 self.context, share_instance_id=share['instance']['id']) 

121 

122 self.assertEqual(2, len(rules)) 

123 rule_ids = [r['access_id'] for r in rules] 

124 self.assertIn(rule_1['id'], rule_ids) 

125 self.assertIn(rule_2['id'], rule_ids) 

126 self.assertFalse(db.share_instance_access_update.called) 

127 

128 @ddt.data( 

129 ([constants.ACCESS_STATE_QUEUED_TO_APPLY], 2), 

130 ([constants.ACCESS_STATE_QUEUED_TO_APPLY, 

131 constants.STATUS_ACTIVE], 1), 

132 ([constants.ACCESS_STATE_APPLYING], 2), 

133 ([constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_ERROR], 1), 

134 ([constants.ACCESS_STATE_ACTIVE, constants.ACCESS_STATE_DENYING], 0)) 

135 @ddt.unpack 

136 def test_get_and_update_all_access_rules_updates_conditionally_changed( 

137 self, statuses, changes_allowed): 

138 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

139 db_utils.create_access(share_id=share['id'], state=statuses[0]) 

140 db_utils.create_access(share_id=share['id'], state=statuses[-1]) 

141 self.mock_object(db, 'share_instance_access_update', mock.Mock( 

142 side_effect=db.share_instance_access_update)) 

143 updates = { 

144 'access_key': 'renfrow2stars' 

145 } 

146 expected_updates = { 

147 'access_key': 'renfrow2stars', 

148 'state': constants.ACCESS_STATE_QUEUED_TO_DENY, 

149 } 

150 conditionally_change = { 

151 constants.ACCESS_STATE_APPLYING: 

152 constants.ACCESS_STATE_QUEUED_TO_DENY, 

153 constants.ACCESS_STATE_QUEUED_TO_APPLY: 

154 constants.ACCESS_STATE_QUEUED_TO_DENY, 

155 } 

156 

157 rules = self.access_helper.get_and_update_share_instance_access_rules( 

158 self.context, share_instance_id=share['instance']['id'], 

159 updates=updates, conditionally_change=conditionally_change) 

160 

161 state_changed_rules = [ 

162 r for r in rules if 

163 r['state'] == constants.ACCESS_STATE_QUEUED_TO_DENY 

164 ] 

165 self.assertEqual(changes_allowed, len(state_changed_rules)) 

166 self.assertEqual(2, db.share_instance_access_update.call_count) 

167 db.share_instance_access_update.assert_has_calls([ 

168 mock.call(self.context, mock.ANY, share['instance']['id'], 

169 expected_updates), 

170 ] * changes_allowed) 

171 

172 def test_get_and_update_access_rule_just_get(self): 

173 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

174 expected_rule = db_utils.create_access(share_id=share['id']) 

175 self.mock_object(db, 'share_instance_access_update') 

176 

177 actual_rule = ( 

178 self.access_helper.get_and_update_share_instance_access_rule( 

179 self.context, expected_rule['id'], 

180 share_instance_id=share['instance']['id']) 

181 ) 

182 

183 self.assertEqual(expected_rule['id'], actual_rule['access_id']) 

184 self.assertFalse(db.share_instance_access_update.called) 

185 

186 @ddt.data(constants.ACCESS_STATE_APPLYING, 

187 constants.ACCESS_STATE_DENYING, 

188 constants.ACCESS_STATE_ACTIVE, 

189 constants.ACCESS_STATE_QUEUED_TO_APPLY) 

190 def test_get_and_update_access_rule_updates_conditionally_changed( 

191 self, initial_state): 

192 mock_debug_log = self.mock_object(access.LOG, 'debug') 

193 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

194 rule = db_utils.create_access(share_id=share['id'], 

195 state=initial_state) 

196 self.mock_object(db, 'share_instance_access_update', mock.Mock( 

197 side_effect=db.share_instance_access_update)) 

198 updates = { 

199 'access_key': 'renfrow2stars' 

200 } 

201 conditionally_change = { 

202 constants.ACCESS_STATE_APPLYING: 

203 constants.ACCESS_STATE_QUEUED_TO_DENY, 

204 constants.ACCESS_STATE_DENYING: 

205 constants.ACCESS_STATE_QUEUED_TO_DENY, 

206 } 

207 

208 actual_rule = ( 

209 self.access_helper.get_and_update_share_instance_access_rule( 

210 self.context, rule['id'], updates=updates, 

211 share_instance_id=share['instance']['id'], 

212 conditionally_change=conditionally_change) 

213 ) 

214 self.assertEqual(rule['id'], actual_rule['access_id']) 

215 if 'ing' in initial_state: 

216 self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_DENY, 

217 actual_rule['state']) 

218 self.assertFalse(mock_debug_log.called) 

219 else: 

220 self.assertEqual(initial_state, actual_rule['state']) 

221 mock_debug_log.assert_called_once() 

222 

223 

224@ddt.ddt 

225class ShareInstanceAccessTestCase(test.TestCase): 

226 def setUp(self): 

227 super(ShareInstanceAccessTestCase, self).setUp() 

228 self.driver = self.mock_class("manila.share.driver.ShareDriver", 

229 mock.Mock()) 

230 self.access_helper = access.ShareInstanceAccess(db, self.driver) 

231 self.context = context.RequestContext('fake_user', 'fake_project') 

232 

233 @ddt.data(constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING) 

234 def test_update_access_rules_an_update_is_in_progress(self, initial_state): 

235 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

236 share_instance = share['instance'] 

237 db_utils.create_access(share_id=share['id'], state=initial_state) 

238 mock_debug_log = self.mock_object(access.LOG, 'debug') 

239 self.mock_object(self.access_helper, '_update_access_rules') 

240 get_and_update_call = self.mock_object( 

241 self.access_helper, 'get_and_update_share_instance_access_rules', 

242 mock.Mock(side_effect=self.access_helper. 

243 get_and_update_share_instance_access_rules)) 

244 

245 retval = self.access_helper.update_access_rules( 

246 self.context, share_instance['id']) 

247 

248 expected_filters = { 

249 'state': (constants.ACCESS_STATE_APPLYING, 

250 constants.ACCESS_STATE_DENYING, 

251 constants.ACCESS_STATE_UPDATING), 

252 } 

253 self.assertIsNone(retval) 

254 mock_debug_log.assert_called_once() 

255 get_and_update_call.assert_called_once_with( 

256 self.context, filters=expected_filters, 

257 share_instance_id=share_instance['id']) 

258 self.assertFalse(self.access_helper._update_access_rules.called) 

259 

260 def test_update_access_rules_nothing_to_update(self): 

261 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

262 share_instance = share['instance'] 

263 db_utils.create_access(share_id=share['id'], 

264 state=constants.STATUS_ACTIVE) 

265 mock_debug_log = self.mock_object(access.LOG, 'debug') 

266 self.mock_object(self.access_helper, '_update_access_rules') 

267 get_and_update_call = self.mock_object( 

268 self.access_helper, 'get_and_update_share_instance_access_rules', 

269 mock.Mock(side_effect=self.access_helper. 

270 get_and_update_share_instance_access_rules)) 

271 

272 retval = self.access_helper.update_access_rules( 

273 self.context, share_instance['id']) 

274 

275 expected_rule_filter_1 = { 

276 'state': (constants.ACCESS_STATE_APPLYING, 

277 constants.ACCESS_STATE_DENYING, 

278 constants.ACCESS_STATE_UPDATING), 

279 } 

280 expected_rule_filter_2 = { 

281 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, 

282 constants.ACCESS_STATE_QUEUED_TO_DENY, 

283 constants.ACCESS_STATE_QUEUED_TO_UPDATE), 

284 } 

285 expected_conditionally_change = { 

286 constants.ACCESS_STATE_QUEUED_TO_APPLY: 

287 constants.ACCESS_STATE_APPLYING, 

288 constants.ACCESS_STATE_QUEUED_TO_DENY: 

289 constants.ACCESS_STATE_DENYING, 

290 constants.ACCESS_STATE_QUEUED_TO_UPDATE: 

291 constants.ACCESS_STATE_UPDATING, 

292 } 

293 self.assertIsNone(retval) 

294 mock_debug_log.assert_called_once() 

295 get_and_update_call.assert_has_calls( 

296 [ 

297 mock.call(self.context, filters=expected_rule_filter_1, 

298 share_instance_id=share_instance['id']), 

299 mock.call(self.context, filters=expected_rule_filter_2, 

300 share_instance_id=share_instance['id'], 

301 conditionally_change=expected_conditionally_change), 

302 ]) 

303 self.assertFalse(self.access_helper._update_access_rules.called) 

304 

305 @ddt.data(True, False) 

306 def test_update_access_rules_delete_all_rules(self, delete_all_rules): 

307 share = db_utils.create_share(status=constants.STATUS_AVAILABLE) 

308 share_instance = share['instance'] 

309 db_utils.create_access( 

310 share_id=share['id'], state=constants.STATUS_ACTIVE) 

311 db_utils.create_access( 

312 share_id=share['id'], state=constants.ACCESS_STATE_QUEUED_TO_APPLY) 

313 db_utils.create_access( 

314 share_id=share['id'], state=constants.ACCESS_STATE_QUEUED_TO_DENY) 

315 mock_debug_log = self.mock_object(access.LOG, 'debug') 

316 self.mock_object(self.access_helper, '_update_access_rules') 

317 get_and_update_call = self.mock_object( 

318 self.access_helper, 'get_and_update_share_instance_access_rules', 

319 mock.Mock(side_effect=self.access_helper. 

320 get_and_update_share_instance_access_rules)) 

321 

322 retval = self.access_helper.update_access_rules( 

323 self.context, share_instance['id'], 

324 delete_all_rules=delete_all_rules) 

325 

326 expected_rule_filter_1 = { 

327 'state': (constants.ACCESS_STATE_APPLYING, 

328 constants.ACCESS_STATE_DENYING, 

329 constants.ACCESS_STATE_UPDATING), 

330 } 

331 expected_rule_filter_2 = { 

332 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, 

333 constants.ACCESS_STATE_QUEUED_TO_DENY, 

334 constants.ACCESS_STATE_QUEUED_TO_UPDATE), 

335 } 

336 expected_conditionally_change = { 

337 constants.ACCESS_STATE_QUEUED_TO_APPLY: 

338 constants.ACCESS_STATE_APPLYING, 

339 constants.ACCESS_STATE_QUEUED_TO_DENY: 

340 constants.ACCESS_STATE_DENYING, 

341 constants.ACCESS_STATE_QUEUED_TO_UPDATE: 

342 constants.ACCESS_STATE_UPDATING, 

343 } 

344 expected_get_and_update_calls = [] 

345 if delete_all_rules: 

346 deny_all_updates = { 

347 'state': constants.ACCESS_STATE_QUEUED_TO_DENY, 

348 } 

349 expected_get_and_update_calls = [ 

350 mock.call(self.context, updates=deny_all_updates, 

351 share_instance_id=share_instance['id']), 

352 ] 

353 expected_get_and_update_calls.extend([ 

354 mock.call(self.context, filters=expected_rule_filter_1, 

355 share_instance_id=share_instance['id']), 

356 mock.call(self.context, filters=expected_rule_filter_2, 

357 share_instance_id=share_instance['id'], 

358 conditionally_change=expected_conditionally_change), 

359 ]) 

360 

361 self.assertIsNone(retval) 

362 mock_debug_log.assert_called_once() 

363 get_and_update_call.assert_has_calls(expected_get_and_update_calls) 

364 self.access_helper._update_access_rules.assert_called_once_with( 

365 self.context, share_instance['id'], share_server=None) 

366 

367 @ddt.data(*itertools.product( 

368 (True, False), (constants.ACCESS_STATE_ERROR, 

369 constants.ACCESS_STATE_ACTIVE))) 

370 @ddt.unpack 

371 def test__update_access_rules_with_driver_updates( 

372 self, driver_returns_updates, access_state): 

373 expected_access_rules_status = ( 

374 constants.STATUS_ACTIVE 

375 if access_state == constants.ACCESS_STATE_ACTIVE 

376 else constants.SHARE_INSTANCE_RULES_ERROR 

377 ) 

378 share = db_utils.create_share( 

379 status=constants.STATUS_AVAILABLE, 

380 access_rules_status=expected_access_rules_status) 

381 share_instance_id = share['instance']['id'] 

382 rule_1 = db_utils.create_access( 

383 share_id=share['id'], state=access_state) 

384 rule_1 = db.share_instance_access_get( 

385 self.context, rule_1['id'], share_instance_id) 

386 rule_2 = db_utils.create_access( 

387 share_id=share['id'], state=constants.ACCESS_STATE_APPLYING) 

388 rule_2 = db.share_instance_access_get( 

389 self.context, rule_2['id'], share_instance_id) 

390 rule_3 = db_utils.create_access( 

391 share_id=share['id'], state=constants.ACCESS_STATE_DENYING) 

392 rule_3 = db.share_instance_access_get( 

393 self.context, rule_3['id'], share_instance_id) 

394 if driver_returns_updates: 

395 driver_rule_updates = { 

396 rule_3['access_id']: {'access_key': 'alic3h4sAcc355'}, 

397 rule_2['access_id']: {'state': access_state} 

398 } 

399 else: 

400 driver_rule_updates = None 

401 

402 shr_instance_access_rules_status_update_call = self.mock_object( 

403 self.access_helper, 

404 'get_and_update_share_instance_access_rules_status', 

405 mock.Mock(side_effect=self.access_helper. 

406 get_and_update_share_instance_access_rules_status)) 

407 all_access_rules_update_call = self.mock_object( 

408 self.access_helper, 'get_and_update_share_instance_access_rules', 

409 mock.Mock(side_effect=self.access_helper. 

410 get_and_update_share_instance_access_rules)) 

411 one_access_rule_update_call = self.mock_object( 

412 self.access_helper, 'get_and_update_share_instance_access_rule', 

413 mock.Mock(side_effect=self.access_helper. 

414 get_and_update_share_instance_access_rule)) 

415 

416 driver_call = self.mock_object( 

417 self.access_helper.driver, 'update_access', 

418 mock.Mock(return_value=driver_rule_updates)) 

419 self.mock_object(self.access_helper, '_check_needs_refresh', 

420 mock.Mock(return_value=False)) 

421 

422 retval = self.access_helper._update_access_rules( 

423 self.context, share_instance_id, share_server='fake_server') 

424 

425 # Expected Values: 

426 if access_state != constants.ACCESS_STATE_ERROR: 

427 expected_rules_to_be_on_share = [r['id'] for r in (rule_1, rule_2)] 

428 else: 

429 expected_rules_to_be_on_share = [rule_2['id']] 

430 

431 expected_filters_1 = { 

432 'state': (constants.ACCESS_STATE_APPLYING, 

433 constants.ACCESS_STATE_ACTIVE, 

434 constants.ACCESS_STATE_DENYING, 

435 constants.ACCESS_STATE_UPDATING), 

436 } 

437 expected_filters_2 = {'state': constants.STATUS_ERROR} 

438 expected_get_and_update_calls = [ 

439 mock.call(self.context, filters=expected_filters_1, 

440 share_instance_id=share_instance_id), 

441 mock.call(self.context, filters=expected_filters_2, 

442 share_instance_id=share_instance_id), 

443 ] 

444 expected_access_rules_status_change_cond1 = { 

445 constants.STATUS_ACTIVE: constants.SHARE_INSTANCE_RULES_SYNCING, 

446 } 

447 if access_state == constants.SHARE_INSTANCE_RULES_ERROR: 

448 expected_access_rules_status_change_cond2 = { 

449 constants.SHARE_INSTANCE_RULES_SYNCING: 

450 constants.SHARE_INSTANCE_RULES_ERROR, 

451 } 

452 else: 

453 expected_access_rules_status_change_cond2 = { 

454 constants.SHARE_INSTANCE_RULES_SYNCING: 

455 constants.STATUS_ACTIVE, 

456 constants.SHARE_INSTANCE_RULES_ERROR: 

457 constants.STATUS_ACTIVE, 

458 } 

459 call_args = driver_call.call_args_list[0][0] 

460 call_kwargs = driver_call.call_args_list[0][1] 

461 access_rules_to_be_on_share = [r['id'] for r in call_args[2]] 

462 

463 # Asserts 

464 self.assertIsNone(retval) 

465 self.assertEqual(share_instance_id, call_args[1]['id']) 

466 self.assertIsInstance(access_rules_to_be_on_share, list) 

467 self.assertEqual(len(expected_rules_to_be_on_share), 

468 len(access_rules_to_be_on_share)) 

469 for pool in expected_rules_to_be_on_share: 

470 self.assertIn(pool, access_rules_to_be_on_share) 

471 self.assertEqual(1, len(call_kwargs['add_rules'])) 

472 self.assertEqual(rule_2['id'], call_kwargs['add_rules'][0]['id']) 

473 self.assertEqual(1, len(call_kwargs['delete_rules'])) 

474 self.assertEqual(rule_3['id'], call_kwargs['delete_rules'][0]['id']) 

475 self.assertEqual('fake_server', call_kwargs['share_server']) 

476 shr_instance_access_rules_status_update_call.assert_has_calls([ 

477 mock.call( 

478 self.context, share_instance_id=share_instance_id, 

479 conditionally_change=expected_access_rules_status_change_cond1 

480 ), 

481 mock.call( 

482 self.context, share_instance_id=share_instance_id, 

483 conditionally_change=expected_access_rules_status_change_cond2 

484 ), 

485 ]) 

486 

487 if driver_returns_updates: 

488 expected_conditional_state_updates = { 

489 constants.ACCESS_STATE_APPLYING: access_state, 

490 constants.ACCESS_STATE_DENYING: access_state, 

491 constants.ACCESS_STATE_UPDATING: access_state, 

492 constants.ACCESS_STATE_ACTIVE: access_state, 

493 } 

494 expected_access_rule_update_calls = [ 

495 mock.call( 

496 self.context, rule_3['access_id'], 

497 updates={'access_key': 'alic3h4sAcc355'}, 

498 share_instance_id=share_instance_id, 

499 conditionally_change={}), 

500 mock.call( 

501 self.context, rule_2['access_id'], 

502 updates=mock.ANY, share_instance_id=share_instance_id, 

503 conditionally_change=expected_conditional_state_updates) 

504 ] 

505 one_access_rule_update_call.assert_has_calls( 

506 expected_access_rule_update_calls, any_order=True) 

507 else: 

508 self.assertFalse(one_access_rule_update_call.called) 

509 expected_conditionally_change = { 

510 constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ACTIVE, 

511 constants.ACCESS_STATE_UPDATING: constants.ACCESS_STATE_ACTIVE, 

512 } 

513 expected_get_and_update_calls.append( 

514 mock.call(self.context, share_instance_id=share_instance_id, 

515 conditionally_change=expected_conditionally_change)) 

516 

517 all_access_rules_update_call.assert_has_calls( 

518 expected_get_and_update_calls, any_order=True) 

519 

520 share_instance = db.share_instance_get( 

521 self.context, share_instance_id) 

522 self.assertEqual(expected_access_rules_status, 

523 share_instance['access_rules_status']) 

524 

525 @ddt.data(True, False) 

526 def test__update_access_rules_recursive_driver_exception(self, drv_exc): 

527 other = access.ShareInstanceAccess(db, None) 

528 share = db_utils.create_share( 

529 status=constants.STATUS_AVAILABLE, 

530 access_rules_status=constants.SHARE_INSTANCE_RULES_SYNCING) 

531 share_instance_id = share['instance']['id'] 

532 rule_4 = [] 

533 get_and_update_count = [1] 

534 drv_count = [1] 

535 

536 def _get_and_update_side_effect(*args, **kwargs): 

537 # The third call to this method needs to create a new access rule 

538 mtd = other.get_and_update_share_instance_access_rules 

539 if get_and_update_count[0] == 3: 

540 rule_4.append( 

541 db_utils.create_access( 

542 state=constants.ACCESS_STATE_QUEUED_TO_APPLY, 

543 share_id=share['id'])) 

544 get_and_update_count[0] += 1 

545 return mtd(*args, **kwargs) 

546 

547 def _driver_side_effect(*args, **kwargs): 

548 if drv_exc and drv_count[0] == 2: 

549 raise exception.ManilaException('fake') 

550 drv_count[0] += 1 

551 

552 rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} 

553 rule_1 = db_utils.create_access(state=constants.ACCESS_STATE_APPLYING, 

554 **rule_kwargs) 

555 rule_2 = db_utils.create_access(state=constants.ACCESS_STATE_ACTIVE, 

556 **rule_kwargs) 

557 rule_3 = db_utils.create_access(state=constants.ACCESS_STATE_DENYING, 

558 **rule_kwargs) 

559 

560 self.mock_object(self.access_helper, 

561 'get_and_update_share_instance_access_rules', 

562 mock.Mock(side_effect=_get_and_update_side_effect)) 

563 self.mock_object(self.access_helper.driver, 'update_access', 

564 mock.Mock(side_effect=_driver_side_effect)) 

565 

566 if drv_exc: 

567 self.assertRaises(exception.ManilaException, 

568 self.access_helper._update_access_rules, 

569 self.context, share_instance_id) 

570 else: 

571 retval = self.access_helper._update_access_rules(self.context, 

572 share_instance_id) 

573 self.assertIsNone(retval) 

574 

575 expected_filters_1 = { 

576 'state': (constants.ACCESS_STATE_APPLYING, 

577 constants.ACCESS_STATE_ACTIVE, 

578 constants.ACCESS_STATE_DENYING), 

579 } 

580 conditionally_change_2 = { 

581 constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ACTIVE, 

582 } 

583 expected_filters_3 = { 

584 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, 

585 constants.ACCESS_STATE_QUEUED_TO_DENY, 

586 constants.ACCESS_STATE_QUEUED_TO_UPDATE), 

587 } 

588 expected_conditionally_change_3 = { 

589 constants.ACCESS_STATE_QUEUED_TO_APPLY: 

590 constants.ACCESS_STATE_APPLYING, 

591 constants.ACCESS_STATE_QUEUED_TO_DENY: 

592 constants.ACCESS_STATE_DENYING, 

593 constants.ACCESS_STATE_QUEUED_TO_UPDATE: 

594 constants.ACCESS_STATE_UPDATING, 

595 } 

596 expected_conditionally_change_4 = { 

597 constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ERROR, 

598 constants.ACCESS_STATE_DENYING: constants.ACCESS_STATE_ERROR, 

599 constants.ACCESS_STATE_UPDATING: constants.ACCESS_STATE_ERROR, 

600 } 

601 expected_get_and_update_calls = [ 

602 mock.call(self.context, filters=expected_filters_1, 

603 share_instance_id=share_instance_id), 

604 mock.call(self.context, share_instance_id=share_instance_id, 

605 conditionally_change=conditionally_change_2), 

606 mock.call(self.context, filters=expected_filters_3, 

607 share_instance_id=share_instance_id, 

608 conditionally_change=expected_conditionally_change_3), 

609 mock.call(self.context, filters=expected_filters_1, 

610 share_instance_id=share_instance_id), 

611 ] 

612 

613 if drv_exc: 

614 expected_get_and_update_calls.append( 

615 mock.call( 

616 self.context, share_instance_id=share_instance_id, 

617 conditionally_change=expected_conditionally_change_4)) 

618 else: 

619 expected_get_and_update_calls.append( 

620 mock.call(self.context, share_instance_id=share_instance_id, 

621 conditionally_change=conditionally_change_2)) 

622 

623 # Verify rule changes: 

624 # 'denying' rule must not exist 

625 self.assertRaises(exception.NotFound, 

626 db.share_access_get, 

627 self.context, rule_3['id']) 

628 # 'applying' rule must be set to 'active' 

629 rules_that_must_be_active = (rule_1, rule_2) 

630 if not drv_exc: 

631 rules_that_must_be_active += (rule_4[0], ) 

632 for rule in rules_that_must_be_active: 

633 rule = db.share_access_get(self.context, rule['id']) 

634 self.assertEqual(constants.ACCESS_STATE_ACTIVE, 

635 rule['state']) 

636 # access_rules_status must be as expected 

637 expected_access_rules_status = ( 

638 constants.SHARE_INSTANCE_RULES_ERROR if drv_exc 

639 else constants.STATUS_ACTIVE) 

640 share_instance = db.share_instance_get(self.context, share_instance_id) 

641 self.assertEqual( 

642 expected_access_rules_status, 

643 share_instance['access_rules_status']) 

644 

645 def test__update_access_rules_for_migration(self): 

646 share = db_utils.create_share() 

647 instance = db_utils.create_share_instance( 

648 status=constants.STATUS_MIGRATING, 

649 access_rules_status=constants.STATUS_ACTIVE, 

650 cast_rules_to_readonly=True, 

651 share_id=share['id']) 

652 rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} 

653 rule_1 = db_utils.create_access( 

654 state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) 

655 rule_1 = db.share_instance_access_get( 

656 self.context, rule_1['id'], instance['id']) 

657 rule_2 = db_utils.create_access( 

658 state=constants.ACCESS_STATE_APPLYING, share_id=share['id'], 

659 access_level='ro') 

660 rule_2 = db.share_instance_access_get( 

661 self.context, rule_2['id'], instance['id']) 

662 

663 driver_call = self.mock_object( 

664 self.access_helper.driver, 'update_access', 

665 mock.Mock(return_value=None)) 

666 self.mock_object(self.access_helper, '_check_needs_refresh', 

667 mock.Mock(return_value=False)) 

668 

669 retval = self.access_helper._update_access_rules( 

670 self.context, instance['id'], share_server='fake_server') 

671 

672 call_args = driver_call.call_args_list[0][0] 

673 call_kwargs = driver_call.call_args_list[0][1] 

674 access_rules_to_be_on_share = [r['id'] for r in call_args[2]] 

675 access_levels = [r['access_level'] for r in call_args[2]] 

676 expected_rules_to_be_on_share = ([rule_1['id'], rule_2['id']]) 

677 

678 self.assertIsNone(retval) 

679 self.assertEqual(instance['id'], call_args[1]['id']) 

680 self.assertIsInstance(access_rules_to_be_on_share, list) 

681 self.assertEqual(len(expected_rules_to_be_on_share), 

682 len(access_rules_to_be_on_share)) 

683 for pool in expected_rules_to_be_on_share: 

684 self.assertIn(pool, access_rules_to_be_on_share) 

685 self.assertEqual(['ro'] * len(expected_rules_to_be_on_share), 

686 access_levels) 

687 self.assertEqual(0, len(call_kwargs['add_rules'])) 

688 self.assertEqual(0, len(call_kwargs['delete_rules'])) 

689 self.assertEqual('fake_server', call_kwargs['share_server']) 

690 

691 @ddt.data(True, False) 

692 def test__check_needs_refresh(self, expected_needs_refresh): 

693 states = ( 

694 [constants.ACCESS_STATE_QUEUED_TO_DENY, 

695 constants.ACCESS_STATE_QUEUED_TO_APPLY] if expected_needs_refresh 

696 else [constants.ACCESS_STATE_ACTIVE] 

697 ) 

698 share = db_utils.create_share( 

699 status=constants.STATUS_AVAILABLE, 

700 access_rules_status=constants.SHARE_INSTANCE_RULES_SYNCING) 

701 share_instance_id = share['instance']['id'] 

702 rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} 

703 rule_1 = db_utils.create_access(state=states[0], **rule_kwargs) 

704 db_utils.create_access(state=constants.ACCESS_STATE_ACTIVE, 

705 **rule_kwargs) 

706 db_utils.create_access(state=constants.ACCESS_STATE_DENYING, 

707 **rule_kwargs) 

708 rule_4 = db_utils.create_access(state=states[-1], **rule_kwargs) 

709 

710 get_and_update_call = self.mock_object( 

711 self.access_helper, 'get_and_update_share_instance_access_rules', 

712 mock.Mock(side_effect=self.access_helper. 

713 get_and_update_share_instance_access_rules)) 

714 

715 needs_refresh = self.access_helper._check_needs_refresh( 

716 self.context, share_instance_id) 

717 

718 expected_filter = { 

719 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, 

720 constants.ACCESS_STATE_QUEUED_TO_DENY, 

721 constants.ACCESS_STATE_QUEUED_TO_UPDATE), 

722 } 

723 expected_conditionally_change = { 

724 constants.ACCESS_STATE_QUEUED_TO_APPLY: 

725 constants.ACCESS_STATE_APPLYING, 

726 constants.ACCESS_STATE_QUEUED_TO_DENY: 

727 constants.ACCESS_STATE_DENYING, 

728 constants.ACCESS_STATE_QUEUED_TO_UPDATE: 

729 constants.ACCESS_STATE_UPDATING, 

730 } 

731 

732 self.assertEqual(expected_needs_refresh, needs_refresh) 

733 get_and_update_call.assert_called_once_with( 

734 self.context, filters=expected_filter, 

735 share_instance_id=share_instance_id, 

736 conditionally_change=expected_conditionally_change) 

737 

738 rule_1 = db.share_instance_access_get( 

739 self.context, rule_1['id'], share_instance_id) 

740 rule_4 = db.share_instance_access_get( 

741 self.context, rule_4['id'], share_instance_id) 

742 

743 if expected_needs_refresh: 

744 self.assertEqual(constants.ACCESS_STATE_DENYING, rule_1['state']) 

745 self.assertEqual(constants.ACCESS_STATE_APPLYING, rule_4['state']) 

746 else: 

747 self.assertEqual(states[0], rule_1['state']) 

748 self.assertEqual(states[-1], rule_4['state']) 

749 

750 @ddt.data(('nfs', True, False), ('nfs', False, True), 

751 ('cifs', True, False), ('cifs', False, False), 

752 ('cephx', True, False), ('cephx', False, False)) 

753 @ddt.unpack 

754 def test__update_rules_through_share_driver(self, proto, 

755 enable_ipv6, filtered): 

756 self.driver.ipv6_implemented = enable_ipv6 

757 share_instance = {'share_proto': proto} 

758 pass_rules, fail_rules = self._get_pass_rules_and_fail_rules() 

759 pass_add_rules, fail_add_rules = self._get_pass_rules_and_fail_rules() 

760 pass_delete_rules, fail_delete_rules = ( 

761 self._get_pass_rules_and_fail_rules()) 

762 pass_update_rules, fail_update_rules = ( 

763 self._get_pass_rules_and_fail_rules()) 

764 test_rules = pass_rules + fail_rules 

765 test_add_rules = pass_add_rules + fail_add_rules 

766 test_delete_rules = pass_delete_rules + fail_delete_rules 

767 test_update_rules = pass_update_rules + fail_update_rules 

768 

769 fake_expect_driver_update_rules = pass_rules 

770 update_access_call = self.mock_object( 

771 self.access_helper.driver, 'update_access', 

772 mock.Mock(return_value=pass_rules)) 

773 driver_update_rules = ( 

774 self.access_helper._update_rules_through_share_driver( 

775 self.context, share_instance=share_instance, 

776 access_rules_to_be_on_share=test_rules, 

777 add_rules=test_add_rules, 

778 delete_rules=test_delete_rules, 

779 update_rules=test_update_rules, 

780 rules_to_be_removed_from_db=test_rules, 

781 share_server=None)) 

782 

783 if filtered: 

784 update_access_call.assert_called_once_with( 

785 self.context, share_instance, 

786 pass_rules, add_rules=pass_add_rules, 

787 delete_rules=pass_delete_rules, 

788 update_rules=pass_update_rules, 

789 share_server=None) 

790 else: 

791 update_access_call.assert_called_once_with( 

792 self.context, share_instance, test_rules, 

793 add_rules=test_add_rules, delete_rules=test_delete_rules, 

794 update_rules=test_update_rules, share_server=None) 

795 self.assertEqual(fake_expect_driver_update_rules, driver_update_rules) 

796 

797 def _get_pass_rules_and_fail_rules(self): 

798 random_value = str(random.randint(10, 32)) 

799 pass_rules = [ 

800 { 

801 'access_type': 'ip', 

802 'access_to': '1.1.1.' + random_value, 

803 }, 

804 { 

805 'access_type': 'ip', 

806 'access_to': '1.1.%s.0/24' % random_value, 

807 }, 

808 { 

809 'access_type': 'user', 

810 'access_to': 'fake_user' + random_value, 

811 }, 

812 ] 

813 fail_rules = [ 

814 { 

815 'access_type': 'ip', 

816 'access_to': '1001::' + random_value, 

817 }, 

818 { 

819 'access_type': 'ip', 

820 'access_to': '%s::/64' % random_value, 

821 }, 

822 ] 

823 return pass_rules, fail_rules 

824 

825 def test_update_share_instances_access_rules_status(self): 

826 mock_db_instances_update = self.mock_object( 

827 db, 'share_instance_status_update') 

828 share_instances = ['fake_instance_id', 'fake_instance_id_2'] 

829 

830 self.access_helper.update_share_instances_access_rules_status( 

831 self.context, 'fake_status', share_instances) 

832 

833 mock_db_instances_update.assert_called_once_with( 

834 self.context, share_instances, 

835 {'access_rules_status': 'fake_status'}) 

836 

837 @ddt.data(True, False) 

838 def test_reset_rules_to_queueing_states(self, reset_active): 

839 share = db_utils.create_share( 

840 status=constants.STATUS_AVAILABLE, 

841 # if rules are applying/denying, status would be 'syncing', but, 

842 # lets test the transition to syncing when asked to reset_active 

843 access_rules_status=constants.STATUS_ACTIVE) 

844 share_instance_id = share['instance']['id'] 

845 rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} 

846 r1 = db_utils.create_access( 

847 state=constants.ACCESS_STATE_APPLYING, **rule_kwargs) 

848 r2 = db_utils.create_access( 

849 state=constants.ACCESS_STATE_DENYING, **rule_kwargs) 

850 r3 = db_utils.create_access( 

851 state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) 

852 r4 = db_utils.create_access( 

853 state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) 

854 

855 self.access_helper.reset_rules_to_queueing_states( 

856 self.context, share_instance_id, reset_active=reset_active) 

857 

858 rules = db.share_access_get_all_for_instance( 

859 self.context, share_instance_id) 

860 share_instance = db.share_instance_get(self.context, share_instance_id) 

861 rules_dict = {r['access_id']: r for r in rules} 

862 self.assertEqual(constants.SHARE_INSTANCE_RULES_SYNCING, 

863 share_instance['access_rules_status']) 

864 self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_APPLY, 

865 rules_dict[r1['id']]['state']) 

866 self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_DENY, 

867 rules_dict[r2['id']]['state']) 

868 expected_state_for_previously_active = ( 

869 constants.ACCESS_STATE_QUEUED_TO_APPLY 

870 if reset_active else 

871 constants.ACCESS_STATE_ACTIVE 

872 ) 

873 self.assertEqual(expected_state_for_previously_active, 

874 rules_dict[r3['id']]['state']) 

875 self.assertEqual(expected_state_for_previously_active, 

876 rules_dict[r4['id']]['state'])