# Copyright 2016 Hitachi Data Systems inc. # All Rights Reserved. # # Licensed under the Apache License, Version 2.0 (the "License"); you may # not use this file except in compliance with the License. You may obtain # a copy of the License at # # http://www.apache.org/licenses/LICENSE-2.0 # # Unless required by applicable law or agreed to in writing, software # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the # License for the specific language governing permissions and limitations # under the License. import itertools import random from unittest import mock import ddt from manila.common import constants from manila import context from manila import db from manila import exception from manila.share import access from manila import test from manila.tests import db_utils from manila import utils class LockedOperationsTestCase(test.TestCase): class FakeAccessHelper(object): @access.locked_access_rules_operation def some_access_rules_operation(self, context, share_instance_id=None): pass def setUp(self): super(LockedOperationsTestCase, self).setUp() self.access_helper = self.FakeAccessHelper() self.context = context.RequestContext('fake_user', 'fake_project') self.lock_call = self.mock_object( utils, 'synchronized', mock.Mock(return_value=lambda f: f)) def test_locked_access_rules_operation(self, **replica): self.access_helper.some_access_rules_operation( self.context, share_instance_id='FAKE_INSTANCE_ID') self.lock_call.assert_called_once_with( "locked_access_rules_operation_by_share_instance_FAKE_INSTANCE_ID", external=True) @ddt.ddt class ShareInstanceAccessDatabaseMixinTestCase(test.TestCase): def setUp(self): super(ShareInstanceAccessDatabaseMixinTestCase, self).setUp() self.driver = mock.Mock() self.access_helper = access.ShareInstanceAccess(db, self.driver) self.context = context.RequestContext('fake_user', 'fake_project') self.mock_object( utils, 'synchronized', mock.Mock(return_value=lambda f: f)) def test_get_and_update_access_rules_status_force_status(self): share = db_utils.create_share( access_rule_status=constants.STATUS_ACTIVE, status=constants.STATUS_AVAILABLE) share = db.share_get(self.context, share['id']) self.assertEqual(constants.STATUS_ACTIVE, share['access_rules_status']) self.access_helper.get_and_update_share_instance_access_rules_status( self.context, status=constants.SHARE_INSTANCE_RULES_SYNCING, share_instance_id=share['instance']['id']) share = db.share_get(self.context, share['id']) self.assertEqual(constants.SHARE_INSTANCE_RULES_SYNCING, share['access_rules_status']) @ddt.data((constants.SHARE_INSTANCE_RULES_SYNCING, True), (constants.STATUS_ERROR, False)) @ddt.unpack def test_get_and_update_access_rules_status_conditionally_change( self, initial_status, change_allowed): share = db_utils.create_share(access_rules_status=initial_status, status=constants.STATUS_AVAILABLE) share = db.share_get(self.context, share['id']) self.assertEqual(initial_status, share['access_rules_status']) conditionally_change = { constants.SHARE_INSTANCE_RULES_SYNCING: constants.STATUS_ACTIVE, } updated_instance = ( self.access_helper. get_and_update_share_instance_access_rules_status( self.context, conditionally_change=conditionally_change, share_instance_id=share['instance']['id']) ) share = db.share_get(self.context, share['id']) if change_allowed: self.assertEqual(constants.STATUS_ACTIVE, share['access_rules_status']) self.assertIsNotNone(updated_instance) else: self.assertEqual(initial_status, share['access_rules_status']) self.assertIsNone(updated_instance) def test_get_and_update_all_access_rules_just_get(self): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) rule_1 = db_utils.create_access(share_id=share['id']) rule_2 = db_utils.create_access(share_id=share['id']) self.mock_object(db, 'share_instance_access_update') rules = self.access_helper.get_and_update_share_instance_access_rules( self.context, share_instance_id=share['instance']['id']) self.assertEqual(2, len(rules)) rule_ids = [r['access_id'] for r in rules] self.assertIn(rule_1['id'], rule_ids) self.assertIn(rule_2['id'], rule_ids) self.assertFalse(db.share_instance_access_update.called) @ddt.data( ([constants.ACCESS_STATE_QUEUED_TO_APPLY], 2), ([constants.ACCESS_STATE_QUEUED_TO_APPLY, constants.STATUS_ACTIVE], 1), ([constants.ACCESS_STATE_APPLYING], 2), ([constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_ERROR], 1), ([constants.ACCESS_STATE_ACTIVE, constants.ACCESS_STATE_DENYING], 0)) @ddt.unpack def test_get_and_update_all_access_rules_updates_conditionally_changed( self, statuses, changes_allowed): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) db_utils.create_access(share_id=share['id'], state=statuses[0]) db_utils.create_access(share_id=share['id'], state=statuses[-1]) self.mock_object(db, 'share_instance_access_update', mock.Mock( side_effect=db.share_instance_access_update)) updates = { 'access_key': 'renfrow2stars' } expected_updates = { 'access_key': 'renfrow2stars', 'state': constants.ACCESS_STATE_QUEUED_TO_DENY, } conditionally_change = { constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_QUEUED_TO_DENY, constants.ACCESS_STATE_QUEUED_TO_APPLY: constants.ACCESS_STATE_QUEUED_TO_DENY, } rules = self.access_helper.get_and_update_share_instance_access_rules( self.context, share_instance_id=share['instance']['id'], updates=updates, conditionally_change=conditionally_change) state_changed_rules = [ r for r in rules if r['state'] == constants.ACCESS_STATE_QUEUED_TO_DENY ] self.assertEqual(changes_allowed, len(state_changed_rules)) self.assertEqual(2, db.share_instance_access_update.call_count) db.share_instance_access_update.assert_has_calls([ mock.call(self.context, mock.ANY, share['instance']['id'], expected_updates), ] * changes_allowed) def test_get_and_update_access_rule_just_get(self): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) expected_rule = db_utils.create_access(share_id=share['id']) self.mock_object(db, 'share_instance_access_update') actual_rule = ( self.access_helper.get_and_update_share_instance_access_rule( self.context, expected_rule['id'], share_instance_id=share['instance']['id']) ) self.assertEqual(expected_rule['id'], actual_rule['access_id']) self.assertFalse(db.share_instance_access_update.called) @ddt.data(constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING, constants.ACCESS_STATE_ACTIVE, constants.ACCESS_STATE_QUEUED_TO_APPLY) def test_get_and_update_access_rule_updates_conditionally_changed( self, initial_state): mock_debug_log = self.mock_object(access.LOG, 'debug') share = db_utils.create_share(status=constants.STATUS_AVAILABLE) rule = db_utils.create_access(share_id=share['id'], state=initial_state) self.mock_object(db, 'share_instance_access_update', mock.Mock( side_effect=db.share_instance_access_update)) updates = { 'access_key': 'renfrow2stars' } conditionally_change = { constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_QUEUED_TO_DENY, constants.ACCESS_STATE_DENYING: constants.ACCESS_STATE_QUEUED_TO_DENY, } actual_rule = ( self.access_helper.get_and_update_share_instance_access_rule( self.context, rule['id'], updates=updates, share_instance_id=share['instance']['id'], conditionally_change=conditionally_change) ) self.assertEqual(rule['id'], actual_rule['access_id']) if 'ing' in initial_state: self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_DENY, actual_rule['state']) self.assertFalse(mock_debug_log.called) else: self.assertEqual(initial_state, actual_rule['state']) mock_debug_log.assert_called_once() @ddt.ddt class ShareInstanceAccessTestCase(test.TestCase): def setUp(self): super(ShareInstanceAccessTestCase, self).setUp() self.driver = self.mock_class("manila.share.driver.ShareDriver", mock.Mock()) self.access_helper = access.ShareInstanceAccess(db, self.driver) self.context = context.RequestContext('fake_user', 'fake_project') @ddt.data(constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING) def test_update_access_rules_an_update_is_in_progress(self, initial_state): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) share_instance = share['instance'] db_utils.create_access(share_id=share['id'], state=initial_state) mock_debug_log = self.mock_object(access.LOG, 'debug') self.mock_object(self.access_helper, '_update_access_rules') get_and_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules)) retval = self.access_helper.update_access_rules( self.context, share_instance['id']) expected_filters = { 'state': (constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING), } self.assertIsNone(retval) mock_debug_log.assert_called_once() get_and_update_call.assert_called_once_with( self.context, filters=expected_filters, share_instance_id=share_instance['id']) self.assertFalse(self.access_helper._update_access_rules.called) def test_update_access_rules_nothing_to_update(self): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) share_instance = share['instance'] db_utils.create_access(share_id=share['id'], state=constants.STATUS_ACTIVE) mock_debug_log = self.mock_object(access.LOG, 'debug') self.mock_object(self.access_helper, '_update_access_rules') get_and_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules)) retval = self.access_helper.update_access_rules( self.context, share_instance['id']) expected_rule_filter_1 = { 'state': (constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING), } expected_rule_filter_2 = { 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, constants.ACCESS_STATE_QUEUED_TO_DENY), } expected_conditionally_change = { constants.ACCESS_STATE_QUEUED_TO_APPLY: constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_QUEUED_TO_DENY: constants.ACCESS_STATE_DENYING, } self.assertIsNone(retval) mock_debug_log.assert_called_once() get_and_update_call.assert_has_calls( [ mock.call(self.context, filters=expected_rule_filter_1, share_instance_id=share_instance['id']), mock.call(self.context, filters=expected_rule_filter_2, share_instance_id=share_instance['id'], conditionally_change=expected_conditionally_change), ]) self.assertFalse(self.access_helper._update_access_rules.called) @ddt.data(True, False) def test_update_access_rules_delete_all_rules(self, delete_all_rules): share = db_utils.create_share(status=constants.STATUS_AVAILABLE) share_instance = share['instance'] db_utils.create_access( share_id=share['id'], state=constants.STATUS_ACTIVE) db_utils.create_access( share_id=share['id'], state=constants.ACCESS_STATE_QUEUED_TO_APPLY) db_utils.create_access( share_id=share['id'], state=constants.ACCESS_STATE_QUEUED_TO_DENY) mock_debug_log = self.mock_object(access.LOG, 'debug') self.mock_object(self.access_helper, '_update_access_rules') get_and_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules)) retval = self.access_helper.update_access_rules( self.context, share_instance['id'], delete_all_rules=delete_all_rules) expected_rule_filter_1 = { 'state': (constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_DENYING), } expected_rule_filter_2 = { 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, constants.ACCESS_STATE_QUEUED_TO_DENY), } expected_conditionally_change = { constants.ACCESS_STATE_QUEUED_TO_APPLY: constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_QUEUED_TO_DENY: constants.ACCESS_STATE_DENYING, } expected_get_and_update_calls = [] if delete_all_rules: deny_all_updates = { 'state': constants.ACCESS_STATE_QUEUED_TO_DENY, } expected_get_and_update_calls = [ mock.call(self.context, updates=deny_all_updates, share_instance_id=share_instance['id']), ] expected_get_and_update_calls.extend([ mock.call(self.context, filters=expected_rule_filter_1, share_instance_id=share_instance['id']), mock.call(self.context, filters=expected_rule_filter_2, share_instance_id=share_instance['id'], conditionally_change=expected_conditionally_change), ]) self.assertIsNone(retval) mock_debug_log.assert_called_once() get_and_update_call.assert_has_calls(expected_get_and_update_calls) self.access_helper._update_access_rules.assert_called_once_with( self.context, share_instance['id'], share_server=None) @ddt.data(*itertools.product( (True, False), (constants.ACCESS_STATE_ERROR, constants.ACCESS_STATE_ACTIVE))) @ddt.unpack def test__update_access_rules_with_driver_updates( self, driver_returns_updates, access_state): expected_access_rules_status = ( constants.STATUS_ACTIVE if access_state == constants.ACCESS_STATE_ACTIVE else constants.SHARE_INSTANCE_RULES_ERROR ) share = db_utils.create_share( status=constants.STATUS_AVAILABLE, access_rules_status=expected_access_rules_status) share_instance_id = share['instance']['id'] rule_1 = db_utils.create_access( share_id=share['id'], state=access_state) rule_1 = db.share_instance_access_get( self.context, rule_1['id'], share_instance_id) rule_2 = db_utils.create_access( share_id=share['id'], state=constants.ACCESS_STATE_APPLYING) rule_2 = db.share_instance_access_get( self.context, rule_2['id'], share_instance_id) rule_3 = db_utils.create_access( share_id=share['id'], state=constants.ACCESS_STATE_DENYING) rule_3 = db.share_instance_access_get( self.context, rule_3['id'], share_instance_id) if driver_returns_updates: driver_rule_updates = { rule_3['access_id']: {'access_key': 'alic3h4sAcc355'}, rule_2['access_id']: {'state': access_state} } else: driver_rule_updates = None shr_instance_access_rules_status_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules_status', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules_status)) all_access_rules_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules)) one_access_rule_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rule', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rule)) driver_call = self.mock_object( self.access_helper.driver, 'update_access', mock.Mock(return_value=driver_rule_updates)) self.mock_object(self.access_helper, '_check_needs_refresh', mock.Mock(return_value=False)) retval = self.access_helper._update_access_rules( self.context, share_instance_id, share_server='fake_server') # Expected Values: if access_state != constants.ACCESS_STATE_ERROR: expected_rules_to_be_on_share = [r['id'] for r in (rule_1, rule_2)] else: expected_rules_to_be_on_share = [rule_2['id']] expected_filters_1 = { 'state': (constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_ACTIVE, constants.ACCESS_STATE_DENYING), } expected_filters_2 = {'state': constants.STATUS_ERROR} expected_get_and_update_calls = [ mock.call(self.context, filters=expected_filters_1, share_instance_id=share_instance_id), mock.call(self.context, filters=expected_filters_2, share_instance_id=share_instance_id), ] expected_access_rules_status_change_cond1 = { constants.STATUS_ACTIVE: constants.SHARE_INSTANCE_RULES_SYNCING, } if access_state == constants.SHARE_INSTANCE_RULES_ERROR: expected_access_rules_status_change_cond2 = { constants.SHARE_INSTANCE_RULES_SYNCING: constants.SHARE_INSTANCE_RULES_ERROR, } else: expected_access_rules_status_change_cond2 = { constants.SHARE_INSTANCE_RULES_SYNCING: constants.STATUS_ACTIVE, constants.SHARE_INSTANCE_RULES_ERROR: constants.STATUS_ACTIVE, } call_args = driver_call.call_args_list[0][0] call_kwargs = driver_call.call_args_list[0][1] access_rules_to_be_on_share = [r['id'] for r in call_args[2]] # Asserts self.assertIsNone(retval) self.assertEqual(share_instance_id, call_args[1]['id']) self.assertIsInstance(access_rules_to_be_on_share, list) self.assertEqual(len(expected_rules_to_be_on_share), len(access_rules_to_be_on_share)) for pool in expected_rules_to_be_on_share: self.assertIn(pool, access_rules_to_be_on_share) self.assertEqual(1, len(call_kwargs['add_rules'])) self.assertEqual(rule_2['id'], call_kwargs['add_rules'][0]['id']) self.assertEqual(1, len(call_kwargs['delete_rules'])) self.assertEqual(rule_3['id'], call_kwargs['delete_rules'][0]['id']) self.assertEqual('fake_server', call_kwargs['share_server']) shr_instance_access_rules_status_update_call.assert_has_calls([ mock.call( self.context, share_instance_id=share_instance_id, conditionally_change=expected_access_rules_status_change_cond1 ), mock.call( self.context, share_instance_id=share_instance_id, conditionally_change=expected_access_rules_status_change_cond2 ), ]) if driver_returns_updates: expected_conditional_state_updates = { constants.ACCESS_STATE_APPLYING: access_state, constants.ACCESS_STATE_DENYING: access_state, constants.ACCESS_STATE_ACTIVE: access_state, } expected_access_rule_update_calls = [ mock.call( self.context, rule_3['access_id'], updates={'access_key': 'alic3h4sAcc355'}, share_instance_id=share_instance_id, conditionally_change={}), mock.call( self.context, rule_2['access_id'], updates=mock.ANY, share_instance_id=share_instance_id, conditionally_change=expected_conditional_state_updates) ] one_access_rule_update_call.assert_has_calls( expected_access_rule_update_calls, any_order=True) else: self.assertFalse(one_access_rule_update_call.called) expected_conditionally_change = { constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ACTIVE, } expected_get_and_update_calls.append( mock.call(self.context, share_instance_id=share_instance_id, conditionally_change=expected_conditionally_change)) all_access_rules_update_call.assert_has_calls( expected_get_and_update_calls, any_order=True) share_instance = db.share_instance_get( self.context, share_instance_id) self.assertEqual(expected_access_rules_status, share_instance['access_rules_status']) @ddt.data(True, False) def test__update_access_rules_recursive_driver_exception(self, drv_exc): other = access.ShareInstanceAccess(db, None) share = db_utils.create_share( status=constants.STATUS_AVAILABLE, access_rules_status=constants.SHARE_INSTANCE_RULES_SYNCING) share_instance_id = share['instance']['id'] rule_4 = [] get_and_update_count = [1] drv_count = [1] def _get_and_update_side_effect(*args, **kwargs): # The third call to this method needs to create a new access rule mtd = other.get_and_update_share_instance_access_rules if get_and_update_count[0] == 3: rule_4.append( db_utils.create_access( state=constants.ACCESS_STATE_QUEUED_TO_APPLY, share_id=share['id'])) get_and_update_count[0] += 1 return mtd(*args, **kwargs) def _driver_side_effect(*args, **kwargs): if drv_exc and drv_count[0] == 2: raise exception.ManilaException('fake') drv_count[0] += 1 rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} rule_1 = db_utils.create_access(state=constants.ACCESS_STATE_APPLYING, **rule_kwargs) rule_2 = db_utils.create_access(state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) rule_3 = db_utils.create_access(state=constants.ACCESS_STATE_DENYING, **rule_kwargs) self.mock_object(self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=_get_and_update_side_effect)) self.mock_object(self.access_helper.driver, 'update_access', mock.Mock(side_effect=_driver_side_effect)) if drv_exc: self.assertRaises(exception.ManilaException, self.access_helper._update_access_rules, self.context, share_instance_id) else: retval = self.access_helper._update_access_rules(self.context, share_instance_id) self.assertIsNone(retval) expected_filters_1 = { 'state': (constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_ACTIVE, constants.ACCESS_STATE_DENYING), } conditionally_change_2 = { constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ACTIVE, } expected_filters_3 = { 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, constants.ACCESS_STATE_QUEUED_TO_DENY), } expected_conditionally_change_3 = { constants.ACCESS_STATE_QUEUED_TO_APPLY: constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_QUEUED_TO_DENY: constants.ACCESS_STATE_DENYING, } expected_conditionally_change_4 = { constants.ACCESS_STATE_APPLYING: constants.ACCESS_STATE_ERROR, constants.ACCESS_STATE_DENYING: constants.ACCESS_STATE_ERROR, } expected_get_and_update_calls = [ mock.call(self.context, filters=expected_filters_1, share_instance_id=share_instance_id), mock.call(self.context, share_instance_id=share_instance_id, conditionally_change=conditionally_change_2), mock.call(self.context, filters=expected_filters_3, share_instance_id=share_instance_id, conditionally_change=expected_conditionally_change_3), mock.call(self.context, filters=expected_filters_1, share_instance_id=share_instance_id), ] if drv_exc: expected_get_and_update_calls.append( mock.call( self.context, share_instance_id=share_instance_id, conditionally_change=expected_conditionally_change_4)) else: expected_get_and_update_calls.append( mock.call(self.context, share_instance_id=share_instance_id, conditionally_change=conditionally_change_2)) # Verify rule changes: # 'denying' rule must not exist self.assertRaises(exception.NotFound, db.share_access_get, self.context, rule_3['id']) # 'applying' rule must be set to 'active' rules_that_must_be_active = (rule_1, rule_2) if not drv_exc: rules_that_must_be_active += (rule_4[0], ) for rule in rules_that_must_be_active: rule = db.share_access_get(self.context, rule['id']) self.assertEqual(constants.ACCESS_STATE_ACTIVE, rule['state']) # access_rules_status must be as expected expected_access_rules_status = ( constants.SHARE_INSTANCE_RULES_ERROR if drv_exc else constants.STATUS_ACTIVE) share_instance = db.share_instance_get(self.context, share_instance_id) self.assertEqual( expected_access_rules_status, share_instance['access_rules_status']) def test__update_access_rules_for_migration(self): share = db_utils.create_share() instance = db_utils.create_share_instance( status=constants.STATUS_MIGRATING, access_rules_status=constants.STATUS_ACTIVE, cast_rules_to_readonly=True, share_id=share['id']) rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} rule_1 = db_utils.create_access( state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) rule_1 = db.share_instance_access_get( self.context, rule_1['id'], instance['id']) rule_2 = db_utils.create_access( state=constants.ACCESS_STATE_APPLYING, share_id=share['id'], access_level='ro') rule_2 = db.share_instance_access_get( self.context, rule_2['id'], instance['id']) driver_call = self.mock_object( self.access_helper.driver, 'update_access', mock.Mock(return_value=None)) self.mock_object(self.access_helper, '_check_needs_refresh', mock.Mock(return_value=False)) retval = self.access_helper._update_access_rules( self.context, instance['id'], share_server='fake_server') call_args = driver_call.call_args_list[0][0] call_kwargs = driver_call.call_args_list[0][1] access_rules_to_be_on_share = [r['id'] for r in call_args[2]] access_levels = [r['access_level'] for r in call_args[2]] expected_rules_to_be_on_share = ([rule_1['id'], rule_2['id']]) self.assertIsNone(retval) self.assertEqual(instance['id'], call_args[1]['id']) self.assertIsInstance(access_rules_to_be_on_share, list) self.assertEqual(len(expected_rules_to_be_on_share), len(access_rules_to_be_on_share)) for pool in expected_rules_to_be_on_share: self.assertIn(pool, access_rules_to_be_on_share) self.assertEqual(['ro'] * len(expected_rules_to_be_on_share), access_levels) self.assertEqual(0, len(call_kwargs['add_rules'])) self.assertEqual(0, len(call_kwargs['delete_rules'])) self.assertEqual('fake_server', call_kwargs['share_server']) @ddt.data(True, False) def test__check_needs_refresh(self, expected_needs_refresh): states = ( [constants.ACCESS_STATE_QUEUED_TO_DENY, constants.ACCESS_STATE_QUEUED_TO_APPLY] if expected_needs_refresh else [constants.ACCESS_STATE_ACTIVE] ) share = db_utils.create_share( status=constants.STATUS_AVAILABLE, access_rules_status=constants.SHARE_INSTANCE_RULES_SYNCING) share_instance_id = share['instance']['id'] rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} rule_1 = db_utils.create_access(state=states[0], **rule_kwargs) db_utils.create_access(state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) db_utils.create_access(state=constants.ACCESS_STATE_DENYING, **rule_kwargs) rule_4 = db_utils.create_access(state=states[-1], **rule_kwargs) get_and_update_call = self.mock_object( self.access_helper, 'get_and_update_share_instance_access_rules', mock.Mock(side_effect=self.access_helper. get_and_update_share_instance_access_rules)) needs_refresh = self.access_helper._check_needs_refresh( self.context, share_instance_id) expected_filter = { 'state': (constants.ACCESS_STATE_QUEUED_TO_APPLY, constants.ACCESS_STATE_QUEUED_TO_DENY), } expected_conditionally_change = { constants.ACCESS_STATE_QUEUED_TO_APPLY: constants.ACCESS_STATE_APPLYING, constants.ACCESS_STATE_QUEUED_TO_DENY: constants.ACCESS_STATE_DENYING, } self.assertEqual(expected_needs_refresh, needs_refresh) get_and_update_call.assert_called_once_with( self.context, filters=expected_filter, share_instance_id=share_instance_id, conditionally_change=expected_conditionally_change) rule_1 = db.share_instance_access_get( self.context, rule_1['id'], share_instance_id) rule_4 = db.share_instance_access_get( self.context, rule_4['id'], share_instance_id) if expected_needs_refresh: self.assertEqual(constants.ACCESS_STATE_DENYING, rule_1['state']) self.assertEqual(constants.ACCESS_STATE_APPLYING, rule_4['state']) else: self.assertEqual(states[0], rule_1['state']) self.assertEqual(states[-1], rule_4['state']) @ddt.data(('nfs', True, False), ('nfs', False, True), ('cifs', True, False), ('cifs', False, False), ('cephx', True, False), ('cephx', False, False)) @ddt.unpack def test__update_rules_through_share_driver(self, proto, enable_ipv6, filtered): self.driver.ipv6_implemented = enable_ipv6 share_instance = {'share_proto': proto} pass_rules, fail_rules = self._get_pass_rules_and_fail_rules() pass_add_rules, fail_add_rules = self._get_pass_rules_and_fail_rules() pass_delete_rules, fail_delete_rules = ( self._get_pass_rules_and_fail_rules()) test_rules = pass_rules + fail_rules test_add_rules = pass_add_rules + fail_add_rules test_delete_rules = pass_delete_rules + fail_delete_rules fake_expect_driver_update_rules = pass_rules update_access_call = self.mock_object( self.access_helper.driver, 'update_access', mock.Mock(return_value=pass_rules)) driver_update_rules = ( self.access_helper._update_rules_through_share_driver( self.context, share_instance=share_instance, access_rules_to_be_on_share=test_rules, add_rules=test_add_rules, delete_rules=test_delete_rules, rules_to_be_removed_from_db=test_rules, share_server=None)) if filtered: update_access_call.assert_called_once_with( self.context, share_instance, pass_rules, add_rules=pass_add_rules, delete_rules=pass_delete_rules, share_server=None) else: update_access_call.assert_called_once_with( self.context, share_instance, test_rules, add_rules=test_add_rules, delete_rules=test_delete_rules, share_server=None) self.assertEqual(fake_expect_driver_update_rules, driver_update_rules) def _get_pass_rules_and_fail_rules(self): random_value = str(random.randint(10, 32)) pass_rules = [ { 'access_type': 'ip', 'access_to': '1.1.1.' + random_value, }, { 'access_type': 'ip', 'access_to': '1.1.%s.0/24' % random_value, }, { 'access_type': 'user', 'access_to': 'fake_user' + random_value, }, ] fail_rules = [ { 'access_type': 'ip', 'access_to': '1001::' + random_value, }, { 'access_type': 'ip', 'access_to': '%s::/64' % random_value, }, ] return pass_rules, fail_rules def test_update_share_instances_access_rules_status(self): mock_db_instances_update = self.mock_object( db, 'share_instance_status_update') share_instances = ['fake_instance_id', 'fake_instance_id_2'] self.access_helper.update_share_instances_access_rules_status( self.context, 'fake_status', share_instances) mock_db_instances_update.assert_called_once_with( self.context, share_instances, {'access_rules_status': 'fake_status'}) @ddt.data(True, False) def test_reset_rules_to_queueing_states(self, reset_active): share = db_utils.create_share( status=constants.STATUS_AVAILABLE, # if rules are applying/denying, status would be 'syncing', but, # lets test the transition to syncing when asked to reset_active access_rules_status=constants.STATUS_ACTIVE) share_instance_id = share['instance']['id'] rule_kwargs = {'share_id': share['id'], 'access_level': 'rw'} r1 = db_utils.create_access( state=constants.ACCESS_STATE_APPLYING, **rule_kwargs) r2 = db_utils.create_access( state=constants.ACCESS_STATE_DENYING, **rule_kwargs) r3 = db_utils.create_access( state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) r4 = db_utils.create_access( state=constants.ACCESS_STATE_ACTIVE, **rule_kwargs) self.access_helper.reset_rules_to_queueing_states( self.context, share_instance_id, reset_active=reset_active) rules = db.share_access_get_all_for_instance( self.context, share_instance_id) share_instance = db.share_instance_get(self.context, share_instance_id) rules_dict = {r['access_id']: r for r in rules} self.assertEqual(constants.SHARE_INSTANCE_RULES_SYNCING, share_instance['access_rules_status']) self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_APPLY, rules_dict[r1['id']]['state']) self.assertEqual(constants.ACCESS_STATE_QUEUED_TO_DENY, rules_dict[r2['id']]['state']) expected_state_for_previously_active = ( constants.ACCESS_STATE_QUEUED_TO_APPLY if reset_active else constants.ACCESS_STATE_ACTIVE ) self.assertEqual(expected_state_for_previously_active, rules_dict[r3['id']]['state']) self.assertEqual(expected_state_for_previously_active, rules_dict[r4['id']]['state'])