text: # 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 ddt
  import mock
  import random
  
  import itertools
  import six
  
  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(self.__class__, 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(self.__class__, 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.assertTrue(isinstance(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.assertTrue(isinstance(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 = six.text_type(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
  