# Copyright 2012 NetApp.  All rights reserved.
# Copyright (c) 2015 Tom Barron.  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.
"""Unit tests for the Share API module."""

import copy
import datetime
import json
from unittest import mock


import ddt
from oslo_config import cfg
from oslo_utils import timeutils
from oslo_utils import uuidutils
from webob import exc as webob_exc

from manila.common import constants
from manila import context
from manila.data import rpcapi as data_rpc
from manila import db as db_api
from manila.db.sqlalchemy import models
from manila import exception
from manila.keymgr import barbican as barbican_api
from manila import policy
from manila import quota
from manila import share
from manila.share import api as share_api
from manila.share import share_types
from manila import test
from manila.tests import db_utils
from manila.tests import fake_share as fakes
from manila.tests import utils as test_utils
from manila import utils

CONF = cfg.CONF


_FAKE_LIST_OF_ALL_SHARES = [
    {
        'name': 'foo',
        'description': 'ds',
        'status': constants.STATUS_AVAILABLE,
        'project_id': 'fake_pid_1',
        'share_server_id': 'fake_server_1',
    },
    {
        'name': 'bar',
        'status': constants.STATUS_ERROR,
        'project_id': 'fake_pid_2',
        'share_server_id': 'fake_server_2',
    },
    {
        'name': 'foo1',
        'description': 'ds1',
        'status': constants.STATUS_AVAILABLE,
        'project_id': 'fake_pid_2',
        'share_server_id': 'fake_server_3',
    },
    {
        'name': 'bar',
        'status': constants.STATUS_ERROR,
        'project_id': 'fake_pid_2',
        'share_server_id': 'fake_server_3',
    },
]


_FAKE_LIST_OF_ALL_SNAPSHOTS = [
    {
        'name': 'foo',
        'status': constants.STATUS_AVAILABLE,
        'project_id': 'fake_pid_1',
        'share_id': 'fake_server_1',
    },
    {
        'name': 'bar',
        'status': constants.STATUS_ERROR,
        'project_id': 'fake_pid_2',
        'share_id': 'fake_server_2',
    },
    {
        'name': 'foo',
        'status': constants.STATUS_AVAILABLE,
        'project_id': 'fake_pid_2',
        'share_id': 'fake_share_id_3',
    },
    {
        'name': 'bar',
        'status': constants.STATUS_ERROR,
        'project_id': 'fake_pid_2',
        'share_id': 'fake_share_id_3',
    },
]


@ddt.ddt
class ShareAPITestCase(test.TestCase):

    def setUp(self):
        super(ShareAPITestCase, self).setUp()
        self.context = context.get_admin_context()
        self.scheduler_rpcapi = mock.Mock()
        self.share_rpcapi = mock.Mock()
        self.api = share.API()
        self.mock_object(self.api.db, 'resource_lock_get_all',
                         mock.Mock(return_value=([], None)))
        self.mock_object(self.api, 'scheduler_rpcapi', self.scheduler_rpcapi)
        self.mock_object(self.api, 'share_rpcapi', self.share_rpcapi)
        self.mock_object(quota.QUOTAS, 'reserve',
                         lambda *args, **kwargs: None)

        self.dt_utc = timeutils.utcnow()
        self.mock_object(timeutils, 'utcnow',
                         mock.Mock(return_value=self.dt_utc))
        self.mock_object(share_api.policy, 'check_policy')
        self._setup_sized_share_types()

    def _setup_sized_share_types(self):
        """create a share type with size limit"""
        spec_dict = {share_types.MIN_SIZE_KEY: 2,
                     share_types.MAX_SIZE_KEY: 4,
                     share_types.MAX_EXTEND_SIZE_KEY: 6}
        db_utils.create_share_type(name='limit', extra_specs=spec_dict)
        self.sized_sha_type = db_api.share_type_get_by_name(self.context,
                                                            'limit')

    def _setup_create_mocks(self, protocol='nfs', **kwargs):
        share = db_utils.create_share(
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            share_type_id=kwargs.pop('share_type_id', 'fake'),
            **kwargs
        )
        share_data = {
            'share_proto': protocol,
            'size': 1,
            'display_name': 'fakename',
            'display_description': 'fakedesc',
            'availability_zone': 'fakeaz'
        }

        self.mock_object(db_api, 'share_create', mock.Mock(return_value=share))
        self.mock_object(self.api, 'create_instance')

        return share, share_data

    def _setup_create_instance_mocks(self):
        host = 'fake'
        share_type_id = "fake_share_type"
        share = db_utils.create_share(
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            create_share_instance=False,
        )
        share_instance = db_utils.create_share_instance(
            share_id=share['id'],
            share_type_id=share_type_id)
        share_type = {
            'fake': 'fake',
            'mount_point_name_support': False
        }
        self.mock_object(db_api, 'share_instance_create',
                         mock.Mock(return_value=share_instance))
        self.mock_object(db_api, 'share_type_get',
                         mock.Mock(return_value=share_type))
        az_mock = mock.Mock()
        type(az_mock.return_value).id = mock.PropertyMock(
            return_value='fake_id')
        self.mock_object(db_api, 'availability_zone_get', az_mock)
        self.mock_object(self.api.share_rpcapi, 'create_share_instance')
        self.mock_object(self.api.scheduler_rpcapi, 'create_share_instance')

        return host, share, share_instance

    def _setup_create_from_snapshot_mocks(self, use_scheduler=True, host=None):
        CONF.set_default("use_scheduler_creating_share_from_snapshot",
                         use_scheduler)

        share_type = fakes.fake_share_type()

        original_share = db_utils.create_share(
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            status=constants.STATUS_AVAILABLE,
            host=host if host else 'fake',
            size=1,
            share_type_id=share_type['id'],
        )
        snapshot = db_utils.create_snapshot(
            share_id=original_share['id'],
            status=constants.STATUS_AVAILABLE,
            size=1
        )

        share, share_data = self._setup_create_mocks(
            snapshot_id=snapshot['id'], share_type_id=share_type['id'])
        request_spec = {
            'share_properties': share.to_dict(),
            'share_proto': share['share_proto'],
            'share_id': share['id'],
            'share_type': None,
            'snapshot_id': share['snapshot_id'],
        }

        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value='reservation'))
        self.mock_object(quota.QUOTAS, 'commit')
        self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))

        return snapshot, share, share_data, request_spec

    def _setup_delete_mocks(self, status, snapshots=None, **kwargs):
        if snapshots is None:
            snapshots = []
        share = db_utils.create_share(status=status, **kwargs)
        self.mock_object(db_api, 'share_delete')
        self.mock_object(db_api, 'share_server_update')
        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=snapshots))
        self.mock_object(db_api, 'share_backups_get_all',
                         mock.Mock(return_value=[]))
        self.mock_object(self.api, 'delete_instance')
        return share

    def _setup_delete_share_instance_mocks(self, **kwargs):
        share = db_utils.create_share(**kwargs)

        self.mock_object(db_api, 'share_instance_update',
                         mock.Mock(return_value=share.instance))
        self.mock_object(self.api.share_rpcapi, 'delete_share_instance')
        self.mock_object(db_api, 'share_server_update')
        self.mock_object(db_api, 'share_instance_delete')

        return share.instance

    def test_get_all_admin_default_filters(self):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)

        shares = self.api.get_all(ctx)

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_1',
            filters={'list_deferred_delete': True},
            is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)

    def test_get_all_admin_filter_by_all_tenants(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(db_api, 'share_get_all',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES))
        self.mock_object(share_api.policy, 'check_policy',
                         mock.Mock(return_value=True))

        shares = self.api.get_all(ctx, {'all_tenants': 1})

        db_api.share_get_all.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            filters={'list_deferred_delete': True})
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES, shares)

    def test_get_all_admin_filter_by_all_tenants_with_blank(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(db_api, 'share_get_all',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES))
        self.mock_object(share_api.policy, 'check_policy',
                         mock.Mock(return_value=True))

        shares = self.api.get_all(ctx, {'all_tenants': ''})

        db_api.share_get_all.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            filters={'list_deferred_delete': True})
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES, shares)

    def test_get_all_admin_filter_by_all_tenants_with_false(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))

        shares = self.api.get_all(ctx, {'all_tenants': 'false'})

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_1', filters={'list_deferred_delete': True},
            is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)

    def test_get_all_admin_filter_by_all_tenants_with_invaild_value(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(db_api, 'share_get_all')
        self.assertRaises(
            exception.InvalidInput,
            self.api.get_all, ctx, {'all_tenants': 'wonk'})

    @ddt.data(
        ({'share_server_id': 'fake_share_server'}, 'list_by_share_server_id'),
        ({'host': 'fake_host'}, 'list_by_host'),
    )
    @ddt.unpack
    def test_get_all_by_non_admin_using_admin_filter(self, filters, policy):

        def fake_policy_checker(*args, **kwargs):
            if policy == args[2] and not args[0].is_admin:
                raise exception.NotAuthorized

        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        mock_policy = self.mock_object(
            share_api.policy, 'check_policy',
            mock.Mock(side_effect=fake_policy_checker))

        self.assertRaises(
            exception.NotAuthorized,
            self.api.get_all, ctx, filters)

        mock_policy.assert_has_calls([
            mock.call(
                ctx, 'share',
                'list_shares_in_deferred_deletion_states',
                do_raise=False),
            mock.call(ctx, 'share', policy)])

    def test_get_all_admin_filter_by_share_server_and_all_tenants(self):
        # NOTE(vponomaryov): if share_server_id provided, 'all_tenants' opt
        #                    should not make any influence.
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(db_api, 'share_get_all_by_share_server',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[2:]))
        self.mock_object(db_api, 'share_get_all')
        self.mock_object(db_api, 'share_get_all_by_project')
        mock_policy = self.mock_object(share_api.policy, 'check_policy')
        shares = self.api.get_all(
            ctx, {'share_server_id': 'fake_server_3', 'all_tenants': 1})
        mock_policy.assert_has_calls([
            mock.call(
                ctx, 'share',
                'list_shares_in_deferred_deletion_states',
                do_raise=False),
            mock.call(ctx, 'share', 'list_all_projects', do_raise=False),
            mock.call(ctx, 'share', 'list_by_share_server_id')])

        db_api.share_get_all_by_share_server.assert_called_once_with(
            ctx, 'fake_server_3', sort_dir='desc', sort_key='created_at',
            filters={'list_deferred_delete': True},
        )
        db_api.share_get_all_by_project.assert_has_calls([])
        db_api.share_get_all.assert_has_calls([])
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[2:], shares)

    def test_get_all_admin_filter_by_name(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
        self.mock_object(
            db_api, 'share_get_all_by_project',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[1::2]))
        expected_filters = {'display_name': 'bar',
                            'list_deferred_delete': True}

        shares = self.api.get_all(ctx, {'display_name': 'bar'})

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters=expected_filters, is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1::2], shares)

    @ddt.data(({'display_name': 'fo'}, 0), ({'display_description': 'd'}, 0),
              ({'display_name': 'foo', 'display_description': 'd'}, 0),
              ({'display_name': 'foo'}, 1), ({'display_description': 'ds'}, 1),
              ({'display_name~': 'foo', 'display_description~': 'ds'}, 2),
              ({'display_name': 'foo', 'display_description~': 'ds'}, 1),
              ({'display_name~': 'foo', 'display_description': 'ds'}, 1))
    @ddt.unpack
    def test_get_all_admin_filter_by_name_and_description(
            self, search_opts, get_share_number):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)

        expected_result = []

        if get_share_number == 2:
            expected_result = _FAKE_LIST_OF_ALL_SHARES[0::2]
        elif get_share_number == 1:
            expected_result = _FAKE_LIST_OF_ALL_SHARES[:1]

        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=expected_result))
        expected_filters = copy.copy(search_opts)
        expected_filters.update({'list_deferred_delete': True})

        shares = self.api.get_all(ctx, search_opts)

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2',
            filters=expected_filters, is_public=False
        )
        self.assertEqual(get_share_number, len(shares))
        self.assertEqual(expected_result, shares)

    @ddt.data('id', 'path')
    def test_get_all_admin_filter_by_export_location(self, type):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[1:]))

        shares = self.api.get_all(ctx, {'export_location_' + type: 'test'})

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2',
            filters={'export_location_' + type: 'test',
                     'list_deferred_delete': True},
            is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1:], shares)

    def test_get_all_admin_filter_by_name_and_all_tenants(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
        self.mock_object(db_api, 'share_get_all',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[:1]))

        shares = self.api.get_all(ctx, {'name': 'foo', 'all_tenants': 1})

        db_api.share_get_all.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            filters={'list_deferred_delete': True})
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[:1], shares)

    def test_get_all_admin_filter_by_status(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
        expected_filter = {'status': constants.STATUS_AVAILABLE,
                           'list_deferred_delete': True}
        self.mock_object(
            db_api, 'share_get_all_by_project',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0::2]))

        shares = self.api.get_all(ctx, {'status': constants.STATUS_AVAILABLE})

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters=expected_filter, is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0::2], shares)

    def test_get_all_admin_filter_by_status_and_all_tenants(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=True)
        self.mock_object(
            db_api, 'share_get_all',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[1::2]))
        expected_filter = {'status': constants.STATUS_ERROR,
                           'list_deferred_delete': True}
        shares = self.api.get_all(
            ctx, {'status': constants.STATUS_ERROR, 'all_tenants': 1})
        db_api.share_get_all.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            filters=expected_filter)
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1::2], shares)

    def test_get_all_non_admin_filter_by_all_tenants(self):
        # Expected share list only by project of non-admin user
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=False)
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[1:]))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        shares = self.api.get_all(ctx, {'all_tenants': 1})

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters={}, is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1:], shares)

    def test_get_all_non_admin_with_name_and_status_filters(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2', is_admin=False)
        self.mock_object(
            db_api, 'share_get_all_by_project',
            mock.Mock(side_effect=[
                _FAKE_LIST_OF_ALL_SHARES[1::2],
                _FAKE_LIST_OF_ALL_SHARES[2::4]]))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        shares = self.api.get_all(
            ctx, {'name': 'bar', 'status': constants.STATUS_ERROR})

        expected_filter_1 = {'status': constants.STATUS_ERROR}
        expected_filter_2 = {'status': constants.STATUS_AVAILABLE}

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters=expected_filter_1, is_public=False
        )

        # two items expected, one filtered
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1::2], shares)

        # one item expected, two filtered
        shares = self.api.get_all(
            ctx, {'name': 'foo1', 'status': constants.STATUS_AVAILABLE})

        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[2::4], shares)
        db_api.share_get_all_by_project.assert_has_calls([
            mock.call(
                ctx, sort_dir='desc', sort_key='created_at',
                project_id='fake_pid_2', filters=expected_filter_1,
                is_public=False),
            mock.call(
                ctx, sort_dir='desc', sort_key='created_at',
                project_id='fake_pid_2', filters=expected_filter_2,
                is_public=False),
        ])

    @ddt.data('True', 'true', '1', 'yes', 'y', 'on', 't', True)
    def test_get_all_non_admin_public(self, is_public):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2',
                                     is_admin=False)
        self.mock_object(db_api, 'share_get_all_by_project', mock.Mock(
            return_value=_FAKE_LIST_OF_ALL_SHARES[1:]))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))
        shares = self.api.get_all(ctx, {'is_public': is_public})
        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters={}, is_public=True
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1:], shares)

    @ddt.data('False', 'false', '0', 'no', 'n', 'off', 'f', False)
    def test_get_all_non_admin_not_public(self, is_public):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2',
                                     is_admin=False)
        self.mock_object(db_api, 'share_get_all_by_project', mock.Mock(
            return_value=_FAKE_LIST_OF_ALL_SHARES[1:]))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        shares = self.api.get_all(ctx, {'is_public': is_public})
        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_2', filters={}, is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[1:], shares)

    @ddt.data('truefoo', 'bartrue')
    def test_get_all_invalid_public_value(self, is_public):
        ctx = context.RequestContext('fake_uid', 'fake_pid_2',
                                     is_admin=False)
        self.assertRaises(ValueError, self.api.get_all,
                          ctx, {'is_public': is_public})

    def test_get_all_with_sorting_valid(self):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        shares = self.api.get_all(ctx, sort_key='status', sort_dir='asc')

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='asc', sort_key='status',
            project_id='fake_pid_1', filters={}, is_public=False
        )
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)

    def test_get_all_sort_key_invalid(self):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.assertRaises(
            exception.InvalidInput,
            self.api.get_all,
            ctx,
            sort_key=1,
        )

    def test_get_all_sort_dir_invalid(self):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.assertRaises(
            exception.InvalidInput,
            self.api.get_all,
            ctx,
            sort_dir=1,
        )

    def _get_all_filter_metadata_or_extra_specs_valid(self, key):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        search_opts = {key: {'foo1': 'bar1', 'foo2': 'bar2'}}

        shares = self.api.get_all(ctx, search_opts=search_opts.copy())

        if key == 'extra_specs':
            mock_policy_check.assert_has_calls([
                mock.call(ctx, 'share_types_extra_spec', 'index'),
                mock.call(
                    ctx, 'share',
                    'list_shares_in_deferred_deletion_states',
                    do_raise=False)])
        else:
            mock_policy_check.assert_called_once_with(
                ctx, 'share',
                'list_shares_in_deferred_deletion_states',
                do_raise=False),

        db_api.share_get_all_by_project.assert_called_once_with(
            ctx, sort_dir='desc', sort_key='created_at',
            project_id='fake_pid_1', filters=search_opts, is_public=False)
        self.assertEqual(_FAKE_LIST_OF_ALL_SHARES[0], shares)

    def test_get_all_filter_by_metadata(self):
        self._get_all_filter_metadata_or_extra_specs_valid(key='metadata')

    def test_get_all_filter_by_extra_specs(self):
        self._get_all_filter_metadata_or_extra_specs_valid(key='extra_specs')

    def _get_all_filter_metadata_or_extra_specs_invalid(self, key):
        self.mock_object(db_api, 'share_get_all_by_project',
                         mock.Mock(return_value=_FAKE_LIST_OF_ALL_SHARES[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        search_opts = {key: "{'foo': 'bar'}"}
        self.assertRaises(exception.InvalidInput, self.api.get_all, ctx,
                          search_opts=search_opts)
        if key == 'extra_specs':
            share_api.policy.check_policy.assert_called_once_with(
                ctx, 'share_types_extra_spec', 'index')

    def test_get_all_filter_by_invalid_metadata(self):
        self._get_all_filter_metadata_or_extra_specs_invalid(key='metadata')

    def test_get_all_filter_by_invalid_extra_specs(self):
        self._get_all_filter_metadata_or_extra_specs_invalid(key='extra_specs')

    @ddt.data(True, False)
    def test_update_metadata_from_share_type_extra_specs(self, with_metadata):
        share_type = fakes.fake_share_type(
            extra_specs={
                'driver_handles_share_servers': 'False',
                'fake_driver:dedupe': 'True',
                'fake_driver:encrypt': 'True',
                'fake_driver:snapshot_policy': 'daily',
                'provisioning:max_share_size': '10',
            }
        )

        user_metadata = {}
        if with_metadata:
            user_metadata = {
                'snapshot_policy': 'monthly',
                'tag': 't1',
                'max_share_size': '5',
            }

        CONF.set_default(
            "driver_updatable_metadata",
            ['dedupe', 'snapshot_policy', 'thin_provisioning'],
        )

        result = self.api.update_metadata_from_share_type_extra_specs(
            self.context,
            share_type,
            user_metadata
        )

        if with_metadata:
            self.assertEqual(
                result,
                {'dedupe': 'True', 'snapshot_policy': 'monthly', 'tag': 't1',
                 'max_share_size': '5'})
        else:
            self.assertEqual(
                result,
                {'dedupe': 'True', 'snapshot_policy': 'daily'})

    def test_update_share_network_subnet_from_metadata(self):
        CONF.set_default(
            "driver_updatable_subnet_metadata",
            ['dedupe', 'snapshot_policy', 'thin_provisioning'],
        )
        metadata = {
            'test_key': 'True',
            'snapshot_policy': 'monthly',
        }
        backend_metadata = {
            k: v for k, v in
            metadata.items() if k in CONF.driver_updatable_subnet_metadata}

        self.mock_object(
            db_api, 'share_server_get_all_by_host_and_or_share_subnet',
            mock.Mock(return_value=['fake_share_server']))
        mock_call = self.mock_object(
            self.api.share_rpcapi,
            'update_share_network_subnet_from_metadata')

        self.api.update_share_network_subnet_from_metadata(
            self.context, 'fake_sn_id', 'fake_sn_subnet_id', metadata)
        mock_call.assert_called_once_with(
            self.context, 'fake_sn_id', 'fake_sn_subnet_id',
            'fake_share_server', backend_metadata)

    def test_update_share_from_metadata(self):
        CONF.set_default(
            "driver_updatable_metadata",
            ['dedupe', 'snapshot_policy', 'thin_provisioning'],
        )
        metadata = {
            'dedupe': 'True',
            'snapshot_policy': 'monthly',
            'max_share_size': '10'
        }
        backend_metadata = {
            k: v for k, v in metadata.items() if k != 'max_share_size'}

        self.mock_object(self.api, 'get', mock.Mock(return_value='fake_share'))
        mock_call = self.mock_object(
            self.api.share_rpcapi,
            'update_share_from_metadata'
        )

        self.api.update_share_from_metadata(self.context, 'fake_id', metadata)
        mock_call.assert_called_once_with(
            self.context, 'fake_share', backend_metadata)

    @ddt.data(True, False)
    def test_create_public_and_private_share(self, is_public):
        share, share_data = self._setup_create_mocks(is_public=is_public)
        az = share_data.pop('availability_zone')

        self.api.create(
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            availability_zone=az
        )

        share['status'] = constants.STATUS_CREATING
        share['host'] = None

        self.assertSubDictMatch(share_data,
                                db_api.share_create.call_args[0][1])

    @ddt.data(
        {},
        {
            constants.ExtraSpecs.SNAPSHOT_SUPPORT: True,
        },
        {
            constants.ExtraSpecs.SNAPSHOT_SUPPORT: False,
            constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT: False,
        },
        {
            constants.ExtraSpecs.SNAPSHOT_SUPPORT: True,
            constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT: False,
        },
        {
            constants.ExtraSpecs.SNAPSHOT_SUPPORT: True,
            constants.ExtraSpecs.CREATE_SHARE_FROM_SNAPSHOT_SUPPORT: True,
        }
    )
    def test_create_default_snapshot_semantics(self, extra_specs):
        share, share_data = self._setup_create_mocks(is_public=False)
        az = share_data.pop('availability_zone')
        share_type = fakes.fake_share_type(extra_specs=extra_specs)

        self.api.create(
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            availability_zone=az,
            share_type=share_type
        )

        share['status'] = constants.STATUS_CREATING
        share['host'] = None

        share_data.update(extra_specs)
        if extra_specs.get('snapshot_support') is None:
            share_data['snapshot_support'] = False
        if extra_specs.get('create_share_from_snapshot_support') is None:
            share_data['create_share_from_snapshot_support'] = False

        self.assertSubDictMatch(share_data,
                                db_api.share_create.call_args[0][1])

    @ddt.data(*constants.SUPPORTED_SHARE_PROTOCOLS)
    def test_create_share_valid_protocol(self, proto):
        share, share_data = self._setup_create_mocks(protocol=proto)
        az = share_data.pop('availability_zone')

        all_protos = ','.join(
            proto for proto in constants.SUPPORTED_SHARE_PROTOCOLS)
        data = dict(DEFAULT=dict(enabled_share_protocols=all_protos))
        with test_utils.create_temp_config_with_opts(data):
            self.api.create(
                self.context, proto, share_data['size'],
                share_data['display_name'],
                share_data['display_description'],
                availability_zone=az)

        share['status'] = constants.STATUS_CREATING
        share['host'] = None

        self.assertSubDictMatch(share_data,
                                db_api.share_create.call_args[0][1])

    @ddt.data(
        {'get_all_azs_return': [], 'subnet_by_az_side_effect': []},
        {'get_all_azs_return': [{'name': 'az1', 'id': 'az_id_1'}],
         'subnet_by_az_side_effect': [None]},
        {'get_all_azs_return': [{'name': 'az1', 'id': 'az_id_1'}],
         'subnet_by_az_side_effect': ['fake_sns_1']},
        {'get_all_azs_return': [{'name': 'az1', 'id': 'az_id_1'},
                                {'name': 'az2', 'id': 'az_id_2'}],
         'subnet_by_az_side_effect': [None, 'fake_sns_2']}
    )
    @ddt.unpack
    def test__get_all_availability_zones_with_subnets(
            self, get_all_azs_return, subnet_by_az_side_effect):
        fake_share_network_id = 'fake_sn_id'
        self.mock_object(db_api, 'availability_zone_get_all',
                         mock.Mock(return_value=get_all_azs_return))
        self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(side_effect=subnet_by_az_side_effect))
        expected_az_names = ([], {})
        expected_get_az_calls = []
        for index, value in enumerate(get_all_azs_return):
            expected_get_az_calls.append(mock.call(
                self.context, share_network_id=fake_share_network_id,
                availability_zone_id=value['id']))
            if subnet_by_az_side_effect[index] is not None:
                expected_az_names = ([value['name']], {value['id']: True})

        get_all_subnets = self.api._get_all_availability_zones_with_subnets
        compatible_azs = get_all_subnets(self.context, fake_share_network_id)

        db_api.availability_zone_get_all.assert_called_once_with(
            self.context)
        db_get_azs_with_subnet = (
            db_api.share_network_subnets_get_all_by_availability_zone_id)
        db_get_azs_with_subnet.assert_has_calls(expected_get_az_calls)

        self.assertEqual(expected_az_names, compatible_azs)

    def test_create_share_with_share_type_size_limit(self):
        self.assertRaises(exception.InvalidInput,
                          self.api.create,
                          self.context,
                          'nfs',
                          1,
                          'display_name',
                          'display_description',
                          share_type=self.sized_sha_type)
        self.assertRaises(exception.InvalidInput,
                          self.api.create,
                          self.context,
                          'nfs',
                          5,
                          'display_name',
                          'display_description',
                          share_type=self.sized_sha_type)

    @ddt.data(
        {'availability_zones': None, 'compatible_azs_name': ['fake_az_1'],
         'compatible_azs_multiple': {}},
        {'availability_zones': ['fake_az_2'],
         'compatible_azs_name': ['fake_az_2'], 'compatible_azs_multiple': {}},
        {'availability_zones': ['fake_az_1', 'faze_az_2', 'fake_az_3'],
         'compatible_azs_name': ['fake_az_3'],
         'compatible_azs_multiple': {'fake_az_3': 1}}
    )
    @ddt.unpack
    def test_create_share_with_subnets(self, availability_zones,
                                       compatible_azs_name,
                                       compatible_azs_multiple):
        share, share_data = self._setup_create_mocks()
        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(self.api, '_get_all_availability_zones_with_subnets',
                         mock.Mock(return_value=[compatible_azs_name,
                                                 compatible_azs_multiple]))
        self.mock_object(quota.QUOTAS, 'commit')
        self.mock_object(self.api, 'create_instance')
        self.mock_object(db_api, 'share_get')
        fake_share_network_id = 'fake_sn_id'

        if availability_zones:
            expected_azs = (
                [az for az in availability_zones if az in compatible_azs_name])
        else:
            expected_azs = compatible_azs_name

        az_multiple_sn_support_map = None
        if compatible_azs_multiple != {}:
            az_multiple_sn_support_map = compatible_azs_multiple

        self.api.create(
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            share_network_id=fake_share_network_id,
            availability_zones=availability_zones,
            az_request_multiple_subnet_support_map=az_multiple_sn_support_map)
        share['status'] = constants.STATUS_CREATING
        share['host'] = None

        quota.QUOTAS.reserve.assert_called_once()
        get_all_azs_sns = self.api._get_all_availability_zones_with_subnets
        get_all_azs_sns.assert_called_once_with(
            self.context, fake_share_network_id)
        quota.QUOTAS.commit.assert_called_once()
        self.api.create_instance.assert_called_once_with(
            self.context, share, share_network_id=fake_share_network_id,
            host=None, availability_zone=None, share_group=None,
            share_group_snapshot_member=None, share_type_id=None,
            availability_zones=expected_azs,
            az_request_multiple_subnet_support_map=compatible_azs_multiple,
            snapshot_host=None,
            scheduler_hints=None, mount_point_name=None,
            encryption_key_ref=None,
        )
        db_api.share_get.assert_called_once()

    @ddt.data(
        {'availability_zones': None, 'compatible_azs_name': [],
         'compatible_azs_multiple': []},
        {'availability_zones': ['fake_az_1'],
         'compatible_azs_name': ['fake_az_2'], 'compatible_azs_multiple': []}
    )
    @ddt.unpack
    def test_create_share_with_subnets_invalid_azs(self, availability_zones,
                                                   compatible_azs_name,
                                                   compatible_azs_multiple):
        share, share_data = self._setup_create_mocks()
        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(self.api, '_get_all_availability_zones_with_subnets',
                         mock.Mock(return_value=[compatible_azs_name,
                                                 compatible_azs_multiple]))
        self.mock_object(quota.QUOTAS, 'commit')
        self.mock_object(self.api, 'create_instance')
        self.mock_object(db_api, 'share_get')
        fake_share_network_id = 'fake_sn_id'

        self.assertRaises(
            exception.InvalidInput,
            self.api.create,
            self.context, share_data['share_proto'], share_data['size'],
            share_data['display_name'], share_data['display_description'],
            share_network_id=fake_share_network_id,
            availability_zones=availability_zones)

        quota.QUOTAS.reserve.assert_called_once()
        get_all_azs_sns = self.api._get_all_availability_zones_with_subnets
        get_all_azs_sns.assert_called_once_with(
            self.context, fake_share_network_id)

    def test_prefix_with_missing_extra_spec_mount_point_name_support(self):
        share, share_data = self._setup_create_mocks(is_public=True)
        az = share_data.pop('availability_zone')
        extra_specs = {'replication_type': 'readable',
                       'mount_point_name_support': False}
        self.mock_object(
            self.api, 'get_share_attributes_from_share_type',
            mock.Mock(return_value=extra_specs))

        self.assertRaises(
            exception.InvalidInput,
            self.api.create,
            self.context, share_data['share_proto'], share_data['size'],
            share_data['display_name'], share_data['display_description'],
            availability_zones=az,
            mount_point_name='fake_mp')

    def test_configure_default_prefix(self):
        share_type = {'extra_specs': {}}
        conf = dict(DEFAULT=dict(default_mount_point_prefix="manila_"))
        with test_utils.create_temp_config_with_opts(conf):
            self.context.project_id = 'project_id'
            mount_point_name = 'mount_point'
            result = self.api._prefix_mount_point_name(
                share_type, self.context, mount_point_name
            )
            self.assertEqual(result, 'manila_mount_point')

    def test_configure_empty_default_prefix(self):
        share_type = {'extra_specs': {}}
        conf = dict(DEFAULT=dict(default_mount_point_prefix=""))
        with test_utils.create_temp_config_with_opts(conf):
            self.context.project_id = 'project_id'
            mount_point_name = 'mount_point'
            result = self.api._prefix_mount_point_name(
                share_type, self.context, mount_point_name
            )
            self.assertEqual(result, 'mount_point')

    def test_prefix_with_valid_mount_point_name(self):
        share_type = {
            'extra_specs': {
                constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
                    'prefix_',
            }
        }
        self.context.project_id = 'project_id'
        mount_point_name = 'mount_point'
        result = self.api._prefix_mount_point_name(
            share_type, self.context, mount_point_name
        )
        self.assertEqual(result, 'prefix_mount_point')

    def test_empty_prefix_with_valid_mount_point_name(self):
        share_type = {
            'extra_specs': {
                constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX: '',
            }
        }
        self.context.project_id = 'project_id'
        mount_point_name = 'mount_point'
        result = self.api._prefix_mount_point_name(
            share_type, self.context, mount_point_name
        )
        self.assertEqual(result, 'mount_point')

    def test_prefix_with_valid_missing_extra_spec_mount_point_name(self):
        share_type = {
            'extra_specs': {},
        }
        self.context.project_id = 'project_id'
        mount_point_name = 'mount_point'
        result = self.api._prefix_mount_point_name(
            share_type, self.context, mount_point_name
        )
        self.assertEqual(result, 'project_id_mount_point')

    def test_prefix_with_invalid_mount_point_name(self):
        share_type = \
            {
                'extra_specs':
                    {
                        constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
                            'prefix',
                    }
            }
        self.context.project_id = 'project_id'
        mount_point_name = 'invalid*name'
        self.assertRaises(
            exception.InvalidInput,
            self.api._prefix_mount_point_name,
            share_type,
            self.context,
            mount_point_name
        )

    def test_prefix_with_too_long_mount_point_name(self):
        share_type = \
            {
                'extra_specs':
                    {
                        constants.ExtraSpecs.PROVISIONING_MOUNT_POINT_PREFIX:
                            'prefix',
                    }
            }
        self.context.project_id = 'project_id'
        mount_point_name = 'a' * 256
        self.assertRaises(
            exception.InvalidInput,
            self.api._prefix_mount_point_name,
            share_type,
            self.context,
            mount_point_name
        )

    @ddt.data(
        None, '', 'fake', 'nfsfake', 'cifsfake', 'glusterfsfake', 'hdfsfake')
    def test_create_share_invalid_protocol(self, proto):
        share, share_data = self._setup_create_mocks(protocol=proto)

        all_protos = ','.join(
            proto for proto in constants.SUPPORTED_SHARE_PROTOCOLS)
        data = dict(DEFAULT=dict(enabled_share_protocols=all_protos))
        with test_utils.create_temp_config_with_opts(data):
            self.assertRaises(
                exception.InvalidInput,
                self.api.create,
                self.context, proto, share_data['size'],
                share_data['display_name'],
                share_data['display_description'])

    @ddt.data({'overs': {'gigabytes': 'fake'},
               'expected_exception': exception.ShareSizeExceedsAvailableQuota,
               'replication_type': None},
              {'overs': {'shares': 'fake'},
               'expected_exception': exception.ShareLimitExceeded,
               'replication_type': None},
              {'overs': {'replica_gigabytes': 'fake'},
               'expected_exception':
                   exception.ShareReplicaSizeExceedsAvailableQuota,
               'replication_type': constants.REPLICATION_TYPE_READABLE},
              {'overs': {'share_replicas': 'fake'},
               'expected_exception': exception.ShareReplicasLimitExceeded,
               'replication_type': constants.REPLICATION_TYPE_READABLE})
    @ddt.unpack
    def test_create_share_over_quota(self, overs, expected_exception,
                                     replication_type):
        extra_specs = {'replication_type': replication_type}
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        share, share_data = self._setup_create_mocks(
            share_type_id=share_type['id'])

        az = share_data.pop('availability_zone')

        usages = {'gigabytes': {'reserved': 5, 'in_use': 5},
                  'shares': {'reserved': 10, 'in_use': 10},
                  'replica_gigabytes': {'reserved': 5, 'in_use': 5},
                  'share_replicas': {'reserved': 10, 'in_use': 10}}

        quotas = {'gigabytes': 5, 'shares': 10,
                  'replica_gigabytes': 5, 'share_replicas': 10}

        exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas)
        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))

        if replication_type:
            # Prevent the raising of an exception, to force the call to the
            # function check_if_replica_quotas_exceeded
            self.mock_object(self.api, 'check_if_share_quotas_exceeded')

        self.assertRaises(
            expected_exception,
            self.api.create,
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            availability_zone=az,
            share_type=share_type
        )

        if replication_type:
            quota.QUOTAS.reserve.assert_called_once_with(
                self.context, share_type_id=share_type['id'],
                gigabytes=1, shares=1, share_replicas=1, replica_gigabytes=1)
        else:
            quota.QUOTAS.reserve.assert_called_once_with(
                self.context, share_type_id=share_type['id'],
                shares=1, gigabytes=share_data['size'])

    @ddt.data({'overs': {'per_share_gigabytes': 'fake'},
               'expected_exception': exception.ShareSizeExceedsLimit})
    @ddt.unpack
    def test_create_share_over_per_share_quota(self, overs,
                                               expected_exception):
        share, share_data = self._setup_create_mocks()

        quota.CONF.set_default("per_share_gigabytes", 5, 'quota')
        share_data['size'] = 20

        usages = {'per_share_gigabytes': {'reserved': 0, 'in_use': 0}}
        quotas = {'per_share_gigabytes': 10}
        exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas)
        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))

        self.assertRaises(
            expected_exception,
            self.api.create,
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description']
        )

    @ddt.data(
        {'overs': {'encryption_keys': 'fake'},
         'expected_exception': (
            exception.EncryptionKeysLimitExceeded)
         })
    @ddt.unpack
    def test_create_share_over_encryption_keys_quota(self, overs,
                                                     expected_exception):
        share, share_data = self._setup_create_mocks()
        quota.CONF.set_default("encryption_keys", 2, 'quota')
        share_data['encryption_key_ref'] = uuidutils.generate_uuid()

        usages = {'encryption_keys': {'reserved': 0, 'in_use': 2}}
        quotas = {'encryption_keys': 2}
        exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas)
        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))
        self.mock_object(db_api, 'share_server_get_all_with_filters',
                         mock.Mock(return_value={}))
        self.mock_object(barbican_api, 'create_secret_access')
        self.mock_object(db_api, 'encryption_keys_get_count',
                         mock.Mock(return_value=2))
        self.assertRaises(
            expected_exception,
            self.api.create,
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            encryption_key_ref=share_data['encryption_key_ref']
        )

    @ddt.data(exception.QuotaError, exception.InvalidShare)
    def test_create_share_error_on_quota_commit(self, expected_exception):
        share, share_data = self._setup_create_mocks()
        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(quota.QUOTAS, 'commit',
                         mock.Mock(side_effect=expected_exception('fake')))
        self.mock_object(quota.QUOTAS, 'rollback')
        self.mock_object(db_api, 'share_delete')

        self.assertRaises(
            expected_exception,
            self.api.create,
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description']
        )

        quota.QUOTAS.rollback.assert_called_once_with(
            self.context, reservation, share_type_id=None)
        db_api.share_delete.assert_called_once_with(self.context, share['id'])

    def test_create_share_instance_with_host_and_az(self):
        host, share, share_instance = self._setup_create_instance_mocks()

        self.api.create_instance(self.context, share, host=host,
                                 availability_zone='fake',
                                 share_type_id='fake_share_type')

        db_api.share_instance_create.assert_called_once_with(
            self.context, share['id'],
            {
                'share_network_id': None,
                'status': constants.STATUS_CREATING,
                'scheduled_at': self.dt_utc,
                'host': host,
                'availability_zone_id': 'fake_id',
                'share_type_id': 'fake_share_type',
                'cast_rules_to_readonly': False,
                'mount_point_name': None,
                'encryption_key_ref': None
            }
        )
        db_api.share_type_get.assert_called_once_with(
            self.context, share_instance['share_type_id'])
        self.api.share_rpcapi.create_share_instance.assert_called_once_with(
            self.context,
            share_instance,
            host,
            request_spec=mock.ANY,
            filter_properties={'scheduler_hints': None},
            snapshot_id=share['snapshot_id'],
        )
        self.assertFalse(
            self.api.scheduler_rpcapi.create_share_instance.called)

    def test_create_share_instance_with_mount_point_name(self):
        host, share, share_instance = self._setup_create_instance_mocks()

        self.api.create_instance(self.context, share, host=host,
                                 availability_zone='fake',
                                 share_type_id='fake_share_type',
                                 mount_point_name='fake_mp')

        db_api.share_instance_create.assert_called_once_with(
            self.context, share['id'],
            {
                'share_network_id': None,
                'status': constants.STATUS_CREATING,
                'scheduled_at': self.dt_utc,
                'host': host,
                'availability_zone_id': 'fake_id',
                'share_type_id': 'fake_share_type',
                'cast_rules_to_readonly': False,
                'mount_point_name': 'fake_mp',
                'encryption_key_ref': None
            }
        )

    def test_create_share_instance_without_host(self):
        _, share, share_instance = self._setup_create_instance_mocks()

        self.api.create_instance(self.context, share)

        (self.api.scheduler_rpcapi.create_share_instance.
            assert_called_once_with(
                self.context, request_spec=mock.ANY,
                filter_properties={'scheduler_hints': None}))
        self.assertFalse(self.api.share_rpcapi.create_share_instance.called)

    def test_create_share_instance_from_snapshot(self):
        snapshot, share, _, _ = self._setup_create_from_snapshot_mocks()

        request_spec, share_instance = (
            self.api.create_share_instance_and_get_request_spec(
                self.context, share)
        )

        self.assertIsNotNone(share_instance)
        self.assertEqual(share['id'],
                         request_spec['share_instance_properties']['share_id'])
        self.assertEqual(share['snapshot_id'], request_spec['snapshot_id'])

        self.assertFalse(
            self.api.share_rpcapi.create_share_instance_and_get_request_spec
                .called)

    def test_create_instance_share_group_snapshot_member(self):
        fake_req_spec = {
            'share_properties': 'fake_share_properties',
            'share_instance_properties': 'fake_share_instance_properties',
        }
        share = fakes.fake_share()
        member_info = {
            'host': 'host',
            'share_network_id': 'share_network_id',
            'share_server_id': 'share_server_id',
        }

        fake_instance = fakes.fake_share_instance(
            share_id=share['id'], **member_info)
        sg_snap_member = {'share_instance': fake_instance}
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=True))
        mock_share_rpcapi_call = self.mock_object(self.share_rpcapi,
                                                  'create_share_instance')
        mock_scheduler_rpcapi_call = self.mock_object(self.scheduler_rpcapi,
                                                      'create_share_instance')
        mock_db_share_instance_update = self.mock_object(
            db_api, 'share_instance_update')
        self.mock_object(
            share_api.API, 'create_share_instance_and_get_request_spec',
            mock.Mock(return_value=(fake_req_spec, fake_instance)))

        retval = self.api.create_instance(
            self.context, fakes.fake_share(),
            share_group_snapshot_member=sg_snap_member)

        self.assertIsNone(retval)
        mock_db_share_instance_update.assert_called_once_with(
            self.context, fake_instance['id'], member_info)
        self.assertFalse(mock_scheduler_rpcapi_call.called)
        self.assertFalse(mock_share_rpcapi_call.called)

    def test_get_share_attributes_from_share_type(self):

        share_type = {
            'extra_specs': {
                'snapshot_support': True,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'replication_type': 'dr',
            }
        }

        result = self.api.get_share_attributes_from_share_type(share_type)

        self.assertEqual(share_type['extra_specs'], result)

    @ddt.data({}, {'extra_specs': {}}, None)
    def test_get_share_attributes_from_share_type_defaults(self, share_type):

        result = self.api.get_share_attributes_from_share_type(share_type)

        expected = {
            'snapshot_support': False,
            'create_share_from_snapshot_support': False,
            'revert_to_snapshot_support': False,
            'mount_snapshot_support': False,
            'mount_point_name_support': False,
            'replication_type': None,
        }
        self.assertEqual(expected, result)

    @ddt.data({'extra_specs': {'snapshot_support': 'fake'}},
              {'extra_specs': {'create_share_from_snapshot_support': 'fake'}})
    def test_get_share_attributes_from_share_type_invalid(self, share_type):

        self.assertRaises(exception.InvalidExtraSpec,
                          self.api.get_share_attributes_from_share_type,
                          share_type)

    @ddt.data(
        {'replication_type': 'dr', 'dhss': False, 'share_server_id': None},
        {'replication_type': 'readable', 'dhss': False,
         'share_server_id': None},
        {'replication_type': None, 'dhss': False, 'share_server_id': None},
        {'replication_type': None, 'dhss': True, 'share_server_id': 'fake'}
    )
    @ddt.unpack
    def test_manage_new(self, replication_type, dhss, share_server_id):
        share_data = {
            'host': 'fake',
            'export_location_path': 'fake',
            'share_proto': 'fake',
            'share_type_id': 'fake',
        }
        if dhss:
            share_data['share_server_id'] = share_server_id
        driver_options = {}
        date = datetime.datetime(1, 1, 1, 1, 1, 1)
        timeutils.utcnow.return_value = date
        fake_subnet = db_utils.create_share_network_subnet(
            share_network_id='fake')
        share_server = db_utils.create_share_server(
            status=constants.STATUS_ACTIVE, id=share_server_id,
            share_network_subnets=[fake_subnet])
        share_network = db_utils.create_share_network(id='fake')
        fake_share_data = {
            'id': 'fakeid',
            'status': constants.STATUS_CREATING,
        }
        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'replication_type': replication_type,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': dhss,
            },
        }

        share = db_api.share_create(self.context, fake_share_data)

        self.mock_object(self.scheduler_rpcapi, 'manage_share')
        self.mock_object(db_api, 'share_create',
                         mock.Mock(return_value=share))
        self.mock_object(db_api, 'export_locations_update')
        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=share))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.mock_object(db_api, 'share_server_get',
                         mock.Mock(return_value=share_server))
        self.mock_object(db_api, 'share_instance_get_all',
                         mock.Mock(return_value=[]))
        self.mock_object(db_api, 'share_network_get',
                         mock.Mock(return_value=share_network))

        self.api.manage(self.context, copy.deepcopy(share_data),
                        driver_options)

        share_data.update({
            'user_id': self.context.user_id,
            'project_id': self.context.project_id,
            'status': constants.STATUS_MANAGING,
            'scheduled_at': date,
            'snapshot_support': fake_type['extra_specs']['snapshot_support'],
            'create_share_from_snapshot_support':
                fake_type['extra_specs']['create_share_from_snapshot_support'],
            'revert_to_snapshot_support':
                fake_type['extra_specs']['revert_to_snapshot_support'],
            'mount_snapshot_support':
                fake_type['extra_specs']['mount_snapshot_support'],
            'mount_point_name_support':
                fake_type['extra_specs']['mount_point_name_support'],
            'replication_type': replication_type,
        })

        expected_request_spec = self._get_request_spec_dict(
            share, fake_type, self.context, size=0,
            share_proto=share_data['share_proto'],
            host=share_data['host'])

        if dhss:
            share_data.update({
                'share_network_id': fake_subnet['share_network_id']})
        export_location = share_data.pop('export_location_path')
        filters = {'export_location_path': export_location,
                   'host': share_data['host']
                   }
        if share_server_id:
            filters['share_server_id'] = share_server_id
        db_api.share_instance_get_all.assert_called_once_with(
            self.context, filters=filters)
        db_api.share_create.assert_called_once_with(self.context, share_data)
        db_api.share_get.assert_called_once_with(self.context, share['id'])
        db_api.export_locations_update.assert_called_once_with(
            self.context, share.instance['id'], export_location)
        self.scheduler_rpcapi.manage_share.assert_called_once_with(
            self.context, share['id'], driver_options, expected_request_spec)
        if dhss:
            db_api.share_server_get.assert_called_once_with(
                self.context, share_data['share_server_id'])

    @ddt.data((True, exception.InvalidInput, True),
              (True, exception.InvalidInput, False),
              (False, exception.InvalidInput, True),
              (True, exception.InvalidInput, True))
    @ddt.unpack
    def test_manage_new_dhss_true_and_false(self, dhss, exception_type,
                                            has_share_server_id):
        share_data = {
            'host': 'fake',
            'export_location_path': 'fake',
            'share_proto': 'fake',
            'share_type_id': 'fake',
        }
        if has_share_server_id:
            share_data['share_server_id'] = 'fake'

        driver_options = {}
        date = datetime.datetime(1, 1, 1, 1, 1, 1)
        timeutils.utcnow.return_value = date
        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': dhss,
            },
        }

        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.mock_object(db_api, 'share_instance_get_all',
                         mock.Mock(return_value=[]))

        self.assertRaises(exception_type,
                          self.api.manage,
                          self.context,
                          share_data=share_data,
                          driver_options=driver_options
                          )
        share_types.get_share_type.assert_called_once_with(
            self.context, share_data['share_type_id']
        )
        filters = {'export_location_path': share_data['export_location_path'],
                   'host': share_data['host']
                   }
        if has_share_server_id:
            filters['share_server_id'] = 'fake'
        db_api.share_instance_get_all.assert_called_once_with(
            self.context, filters=filters)

    def test_manage_new_share_server_not_found(self):
        share_data = {
            'host': 'fake',
            'export_location_path': 'fake',
            'share_proto': 'fake',
            'share_type_id': 'fake',
            'share_server_id': 'fake'

        }
        driver_options = {}
        date = datetime.datetime(1, 1, 1, 1, 1, 1)
        timeutils.utcnow.return_value = date

        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'replication_type': 'dr',
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': True,
            },
        }

        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.mock_object(db_api, 'share_instance_get_all',
                         mock.Mock(return_value=[]))

        self.assertRaises(exception.InvalidInput,
                          self.api.manage,
                          self.context,
                          share_data=share_data,
                          driver_options=driver_options
                          )
        share_types.get_share_type.assert_called_once_with(
            self.context, share_data['share_type_id']
        )
        db_api.share_instance_get_all.assert_called_once_with(
            self.context, filters={
                'export_location_path': share_data['export_location_path'],
                'host': share_data['host'],
                'share_server_id': share_data['share_server_id']
            }
        )

    def test_manage_new_share_server_not_active(self):
        share_data = {
            'host': 'fake',
            'export_location_path': 'fake',
            'share_proto': 'fake',
            'share_type_id': 'fake',
            'share_server_id': 'fake'

        }
        fake_share_data = {
            'id': 'fakeid',
            'status': constants.STATUS_ERROR,
        }
        driver_options = {}
        date = datetime.datetime(1, 1, 1, 1, 1, 1)
        timeutils.utcnow.return_value = date

        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'replication_type': 'dr',
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': True,
            },
        }

        share = db_api.share_create(self.context, fake_share_data)

        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.mock_object(db_api, 'share_instance_get_all',
                         mock.Mock(return_value=[]))
        self.mock_object(db_api, 'share_server_get',
                         mock.Mock(return_value=share))

        self.assertRaises(exception.InvalidShareServer,
                          self.api.manage,
                          self.context,
                          share_data=share_data,
                          driver_options=driver_options
                          )
        share_types.get_share_type.assert_called_once_with(
            self.context, share_data['share_type_id']
        )
        db_api.share_instance_get_all.assert_called_once_with(
            self.context, filters={
                'export_location_path': share_data['export_location_path'],
                'host': share_data['host'],
                'share_server_id': share_data['share_server_id']
            }
        )
        db_api.share_server_get.assert_called_once_with(
            self.context, share_data['share_server_id']
        )

    @ddt.data(constants.STATUS_MANAGE_ERROR, constants.STATUS_AVAILABLE)
    def test_manage_duplicate(self, status):
        share_data = {
            'host': 'fake',
            'export_location_path': 'fake',
            'share_proto': 'fake',
            'share_type_id': 'fake',
        }
        driver_options = {}
        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'driver_handles_share_servers': False,
            },
        }
        already_managed = [{'id': 'fake', 'status': status}]
        self.mock_object(db_api, 'share_instance_get_all',
                         mock.Mock(return_value=already_managed))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.assertRaises(exception.InvalidShare, self.api.manage,
                          self.context, share_data, driver_options)

    def _get_request_spec_dict(self, share, share_type, context, **kwargs):

        if share is None:
            share = {'instance': {}}

        share_instance = share['instance']

        share_properties = {
            'size': kwargs.get('size', share.get('size')),
            'user_id': kwargs.get('user_id', share.get('user_id')),
            'project_id': kwargs.get('project_id', share.get('project_id')),
            'metadata': db_api.share_metadata_get(context, share['id']),
            'snapshot_support': kwargs.get(
                'snapshot_support',
                share_type['extra_specs']['snapshot_support']),
            'create_share_from_snapshot_support': kwargs.get(
                'create_share_from_snapshot_support',
                share_type['extra_specs'].get(
                    'create_share_from_snapshot_support')),
            'revert_to_snapshot_support': kwargs.get(
                'revert_to_snapshot_support',
                share_type['extra_specs'].get('revert_to_snapshot_support')),
            'mount_snapshot_support': kwargs.get(
                'mount_snapshot_support',
                share_type['extra_specs'].get('mount_snapshot_support')),
            'mount_point_name_support': kwargs.get(
                'mount_point_name_support',
                share_type['extra_specs'].get('mount_point_name_support')),
            'share_proto': kwargs.get('share_proto', share.get('share_proto')),
            'share_type_id': share_type['id'],
            'is_public': kwargs.get('is_public', share.get('is_public')),
            'share_group_id': kwargs.get(
                'share_group_id', share.get('share_group_id')),
            'source_share_group_snapshot_member_id': kwargs.get(
                'source_share_group_snapshot_member_id',
                share.get('source_share_group_snapshot_member_id')),
            'snapshot_id': kwargs.get('snapshot_id', share.get('snapshot_id')),
        }
        share_instance_properties = {
            'availability_zone_id': kwargs.get(
                'availability_zone_id',
                share_instance.get('availability_zone_id')),
            'share_network_id': kwargs.get(
                'share_network_id', share_instance.get('share_network_id')),
            'share_server_id': kwargs.get(
                'share_server_id', share_instance.get('share_server_id')),
            'share_id': kwargs.get('share_id', share_instance.get('share_id')),
            'host': kwargs.get('host', share_instance.get('host')),
            'status': kwargs.get('status', share_instance.get('status')),
        }

        request_spec = {
            'share_properties': share_properties,
            'share_instance_properties': share_instance_properties,
            'share_type': share_type,
            'share_id': share.get('id'),
        }
        return request_spec

    def test_unmanage(self):

        share = db_utils.create_share(
            id='fakeid',
            host='fake',
            size='1',
            status=constants.STATUS_AVAILABLE,
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            task_state=None)

        self.mock_object(db_api, 'share_update', mock.Mock())
        self.api.unmanage(self.context, share)

        self.share_rpcapi.unmanage_share.assert_called_once_with(
            self.context, mock.ANY)
        db_api.share_update.assert_called_once_with(
            mock.ANY, share['id'], mock.ANY)

    def test_unmanage_task_state_busy(self):

        share = db_utils.create_share(
            id='fakeid',
            host='fake',
            size='1',
            status=constants.STATUS_AVAILABLE,
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)

        self.assertRaises(exception.ShareBusyException, self.api.unmanage,
                          self.context, share)

    def test_unmanage_locked_share(self):
        self.mock_object(
            self.api.db,
            'resource_lock_get_all',
            mock.Mock(return_value=([{'id': 'l1'}, {'id': 'l2'}], None))
        )
        share = db_utils.create_share(
            id='fakeid',
            host='fake',
            size='1',
            status=constants.STATUS_AVAILABLE,
            user_id=self.context.user_id,
            project_id=self.context.project_id,
            task_state=None)
        self.mock_object(db_api, 'share_update', mock.Mock())

        self.assertRaises(exception.InvalidShare,
                          self.api.unmanage,
                          self.context,
                          share)

        # lock check decorator executed first, nothing else is invoked
        self.share_rpcapi.unmanage_share.assert_not_called()
        db_api.share_update.assert_not_called()

    @mock.patch.object(quota.QUOTAS, 'reserve',
                       mock.Mock(return_value='reservation'))
    @mock.patch.object(quota.QUOTAS, 'commit', mock.Mock())
    def test_create_snapshot(self):
        snapshot = db_utils.create_snapshot(
            with_share=True, status=constants.STATUS_CREATING, size=1)
        share = snapshot['share']

        fake_name = 'fakename'
        fake_desc = 'fakedesc'
        options = {
            'share_id': share['id'],
            'user_id': self.context.user_id,
            'project_id': self.context.project_id,
            'status': constants.STATUS_CREATING,
            'progress': '0%',
            'share_size': share['size'],
            'size': 1,
            'display_name': fake_name,
            'display_description': fake_desc,
            'share_proto': share['share_proto'],
        }
        with mock.patch.object(db_api, 'share_snapshot_create',
                               mock.Mock(return_value=snapshot)):
            self.api.create_snapshot(self.context, share, fake_name,
                                     fake_desc)
            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share', 'create_snapshot', share)
            quota.QUOTAS.reserve.assert_called_once_with(
                self.context, share_type_id=None,
                snapshot_gigabytes=1, snapshots=1)
            quota.QUOTAS.commit.assert_called_once_with(
                self.context, 'reservation', share_type_id=None)
            db_api.share_snapshot_create.assert_called_once_with(
                self.context, options)

    def test_create_snapshot_space_quota_exceeded(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False, status='available')
        usages = {'snapshot_gigabytes': {'reserved': 10, 'in_use': 0}}
        quotas = {'snapshot_gigabytes': 10}
        side_effect = exception.OverQuota(
            overs='snapshot_gigabytes', usages=usages, quotas=quotas)
        self.mock_object(
            quota.QUOTAS, 'reserve', mock.Mock(side_effect=side_effect))
        mock_snap_create = self.mock_object(db_api, 'share_snapshot_create')

        self.assertRaises(exception.SnapshotSizeExceedsAvailableQuota,
                          self.api.create_snapshot,
                          self.context,
                          share,
                          'fake_name',
                          'fake_description')
        mock_snap_create.assert_not_called()

    def test_create_snapshot_count_quota_exceeded(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False, status='available')
        usages = {'snapshots': {'reserved': 10, 'in_use': 0}}
        quotas = {'snapshots': 10}
        side_effect = exception.OverQuota(
            overs='snapshots', usages=usages, quotas=quotas)
        self.mock_object(
            quota.QUOTAS, 'reserve', mock.Mock(side_effect=side_effect))
        mock_snap_create = self.mock_object(db_api, 'share_snapshot_create')

        self.assertRaises(exception.SnapshotLimitExceeded,
                          self.api.create_snapshot,
                          self.context,
                          share,
                          'fake_name',
                          'fake_description')
        mock_snap_create.assert_not_called()

    def test_manage_snapshot_share_not_found(self):
        snapshot = fakes.fake_snapshot(share_id='fake_share',
                                       as_primitive=True)
        mock_share_get_call = self.mock_object(
            db_api, 'share_get', mock.Mock(side_effect=exception.NotFound))
        mock_db_snapshot_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share')

        self.assertRaises(exception.ShareNotFound,
                          self.api.manage_snapshot,
                          self.context,
                          snapshot,
                          {})
        self.assertFalse(mock_db_snapshot_call.called)
        mock_share_get_call.assert_called_once_with(
            self.context, snapshot['share_id'])

    def test_manage_snapshot_share_has_replicas(self):
        share_ref = fakes.fake_share(
            has_replicas=True, status=constants.STATUS_AVAILABLE)
        self.mock_object(
            db_api, 'share_get', mock.Mock(return_value=share_ref))
        snapshot = fakes.fake_snapshot(create_instance=True, as_primitive=True)
        mock_db_snapshot_get_all_for_share_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share')

        self.assertRaises(exception.InvalidShare,
                          self.api.manage_snapshot,
                          context,
                          snapshot,
                          {})
        self.assertFalse(mock_db_snapshot_get_all_for_share_call.called)

    def test_manage_snapshot_already_managed(self):
        share_ref = fakes.fake_share(
            has_replicas=False, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(create_instance=True, as_primitive=True)
        self.mock_object(
            db_api, 'share_get', mock.Mock(return_value=share_ref))
        mock_db_snapshot_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share', mock.Mock(
                return_value=[snapshot]))
        mock_db_snapshot_create_call = self.mock_object(
            db_api, 'share_snapshot_create')

        self.assertRaises(exception.ManageInvalidShareSnapshot,
                          self.api.manage_snapshot,
                          self.context,
                          snapshot,
                          {})
        mock_db_snapshot_call.assert_called_once_with(
            self.context, snapshot['share_id'])
        self.assertFalse(mock_db_snapshot_create_call.called)

    def test_manage_snapshot(self):
        share_ref = fakes.fake_share(
            has_replicas=False, status=constants.STATUS_AVAILABLE,
            host='fake_host')
        existing_snapshot = fakes.fake_snapshot(
            create_instance=True, share_id=share_ref['id'])
        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=[existing_snapshot]))
        snapshot_data = {
            'share_id': share_ref['id'],
            'provider_location': 'someproviderlocation',
        }
        expected_snapshot_data = {
            'user_id': self.context.user_id,
            'project_id': self.context.project_id,
            'status': constants.STATUS_MANAGING,
            'share_size': share_ref['size'],
            'progress': '0%',
            'share_proto': share_ref['share_proto'],
        }
        expected_snapshot_data.update(**snapshot_data)
        snapshot = fakes.fake_snapshot(
            create_instance=True, **expected_snapshot_data)
        self.mock_object(
            db_api, 'share_get', mock.Mock(return_value=share_ref))
        mock_db_snapshot_create_call = self.mock_object(
            db_api, 'share_snapshot_create', mock.Mock(return_value=snapshot))
        mock_rpc_call = self.mock_object(self.share_rpcapi, 'manage_snapshot',
                                         mock.Mock(return_value=snapshot))

        new_snap = self.api.manage_snapshot(
            self.context, snapshot_data, {})

        self.assertEqual(new_snap, snapshot)
        mock_db_snapshot_create_call.assert_called_once_with(
            self.context, expected_snapshot_data)
        mock_rpc_call.assert_called_once_with(
            self.context, snapshot, share_ref['host'], {})

    def test_manage_share_server(self):
        """Tests manage share server"""
        host = 'fake_host'
        fake_share_network = {
            'id': 'fake_net_id'
        }
        fake_share_net_subnet = [{
            'id': 'fake_subnet_id',
            'share_network_id': fake_share_network['id']
        }]
        identifier = 'fake_identifier'
        values = {
            'host': host,
            'share_network_subnets': [fake_share_net_subnet],
            'status': constants.STATUS_MANAGING,
            'is_auto_deletable': False,
            'identifier': identifier,
        }

        server_managing = {
            'id': 'fake_server_id',
            'status': constants.STATUS_MANAGING,
            'host': host,
            'share_network_subnets': [fake_share_net_subnet],
            'is_auto_deletable': False,
            'identifier': identifier,
        }

        mock_share_server_search = self.mock_object(
            db_api, 'share_server_search_by_identifier',
            mock.Mock(side_effect=exception.ShareServerNotFound('fake')))

        mock_share_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(
                return_value=server_managing)
        )
        mock_share_server_create = self.mock_object(
            db_api, 'share_server_create',
            mock.Mock(return_value=server_managing)
        )
        result = self.api.manage_share_server(
            self.context, 'fake_identifier', host, fake_share_net_subnet,
            {'opt1': 'val1', 'opt2': 'val2'}
        )

        mock_share_server_create.assert_called_once_with(
            self.context, values)

        mock_share_server_get.assert_called_once_with(
            self.context, 'fake_server_id')

        mock_share_server_search.assert_called_once_with(
            self.context, 'fake_identifier')

        result_dict = {
            'host': result['host'],
            'share_network_subnets': result['share_network_subnets'],
            'status': result['status'],
            'is_auto_deletable': result['is_auto_deletable'],
            'identifier': result['identifier'],
        }
        self.assertEqual(values, result_dict)

    def test_manage_share_server_invalid(self):

        server = {'identifier': 'fake_server'}

        mock_share_server_search = self.mock_object(
            db_api, 'share_server_search_by_identifier',
            mock.Mock(return_value=[server]))

        self.assertRaises(
            exception.InvalidInput, self.api.manage_share_server,
            self.context, 'invalid_identifier', 'fake_host', 'fake_share_net',
            {})

        mock_share_server_search.assert_called_once_with(
            self.context, 'invalid_identifier')

    def test_unmanage_snapshot(self):
        fake_host = 'fake_host'
        snapshot_data = {
            'status': constants.STATUS_UNMANAGING,
            'terminated_at': timeutils.utcnow(),
        }
        snapshot = fakes.fake_snapshot(
            create_instance=True, share_instance_id='id2', **snapshot_data)
        mock_db_snap_update_call = self.mock_object(
            db_api, 'share_snapshot_update', mock.Mock(return_value=snapshot))
        mock_rpc_call = self.mock_object(
            self.share_rpcapi, 'unmanage_snapshot')

        retval = self.api.unmanage_snapshot(
            self.context, snapshot, fake_host)

        self.assertIsNone(retval)
        mock_db_snap_update_call.assert_called_once_with(
            self.context, snapshot['id'], snapshot_data)
        mock_rpc_call.assert_called_once_with(
            self.context, snapshot, fake_host)

    def test_unmanage_share_server(self):
        shr1 = {}
        share_server = db_utils.create_share_server(**shr1)
        update_data = {'status': constants.STATUS_UNMANAGING,
                       'terminated_at': timeutils.utcnow()}

        mock_share_instance_get_all = self.mock_object(
            db_api, 'share_instance_get_all_by_share_server',
            mock.Mock(return_value={}))
        mock_share_group_get_all = self.mock_object(
            db_api, 'share_group_get_all_by_share_server',
            mock.Mock(return_value={}))
        mock_share_server_update = self.mock_object(
            db_api, 'share_server_update',
            mock.Mock(return_value=share_server))

        mock_rpc = self.mock_object(
            self.api.share_rpcapi, 'unmanage_share_server')

        self.api.unmanage_share_server(self.context, share_server, True)

        mock_share_instance_get_all.assert_called_once_with(
            self.context, share_server['id']
        )
        mock_share_group_get_all.assert_called_once_with(
            self.context, share_server['id']
        )
        mock_share_server_update.assert_called_once_with(
            self.context, share_server['id'], update_data
        )

        mock_rpc.assert_called_once_with(
            self.context, share_server, force=True)

    def test_unmanage_share_server_in_use(self):
        fake_share = db_utils.create_share()
        fake_share_server = db_utils.create_share_server()

        fake_share_instance = db_utils.create_share_instance(
            share_id=fake_share['id'])
        share_instance_get_all_mock = self.mock_object(
            db_api, 'share_instance_get_all_by_share_server',
            mock.Mock(return_value=fake_share_instance)
        )

        self.assertRaises(exception.ShareServerInUse,
                          self.api.unmanage_share_server,
                          self.context,
                          fake_share_server, True)
        share_instance_get_all_mock.assert_called_once_with(
            self.context, fake_share_server['id']
        )

    def test_unmanage_share_server_in_use_share_groups(self):
        fake_share_server = db_utils.create_share_server()
        fake_share_groups = db_utils.create_share_group()

        share_instance_get_all_mock = self.mock_object(
            db_api, 'share_instance_get_all_by_share_server',
            mock.Mock(return_value={})
        )
        group_get_all_mock = self.mock_object(
            db_api, 'share_group_get_all_by_share_server',
            mock.Mock(return_value=fake_share_groups)
        )

        self.assertRaises(exception.ShareServerInUse,
                          self.api.unmanage_share_server,
                          self.context,
                          fake_share_server, True)
        share_instance_get_all_mock.assert_called_once_with(
            self.context, fake_share_server['id']
        )
        group_get_all_mock.assert_called_once_with(
            self.context, fake_share_server['id']
        )

    @ddt.data(True, False)
    def test_revert_to_snapshot(self, has_replicas):

        share = fakes.fake_share(id=uuidutils.generate_uuid(),
                                 has_replicas=has_replicas)
        self.mock_object(db_api, 'share_get', mock.Mock(return_value=share))
        mock_handle_revert_to_snapshot_quotas = self.mock_object(
            self.api, '_handle_revert_to_snapshot_quotas',
            mock.Mock(return_value='fake_reservations'))
        mock_revert_to_replicated_snapshot = self.mock_object(
            self.api, '_revert_to_replicated_snapshot')
        mock_revert_to_snapshot = self.mock_object(
            self.api, '_revert_to_snapshot')
        snapshot = fakes.fake_snapshot(share_id=share['id'])

        self.api.revert_to_snapshot(self.context, share, snapshot)

        mock_handle_revert_to_snapshot_quotas.assert_called_once_with(
            self.context, share, snapshot)
        if not has_replicas:
            self.assertFalse(mock_revert_to_replicated_snapshot.called)
            mock_revert_to_snapshot.assert_called_once_with(
                self.context, share, snapshot, 'fake_reservations')
        else:
            mock_revert_to_replicated_snapshot.assert_called_once_with(
                self.context, share, snapshot, 'fake_reservations')
            self.assertFalse(mock_revert_to_snapshot.called)

    @ddt.data(None, 'fake_reservations')
    def test_revert_to_snapshot_exception(self, reservations):

        share = fakes.fake_share(id=uuidutils.generate_uuid(),
                                 has_replicas=False)
        self.mock_object(db_api, 'share_get', mock.Mock(return_value=share))
        self.mock_object(
            self.api, '_handle_revert_to_snapshot_quotas',
            mock.Mock(return_value=reservations))
        side_effect = exception.ReplicationException(reason='error')
        self.mock_object(
            self.api, '_revert_to_snapshot',
            mock.Mock(side_effect=side_effect))
        mock_quotas_rollback = self.mock_object(quota.QUOTAS, 'rollback')
        snapshot = fakes.fake_snapshot(share_id=share['id'])

        self.assertRaises(exception.ReplicationException,
                          self.api.revert_to_snapshot,
                          self.context,
                          share,
                          snapshot)

        if reservations is not None:
            mock_quotas_rollback.assert_called_once_with(
                self.context, reservations,
                share_type_id=share['instance']['share_type_id'])
        else:
            self.assertFalse(mock_quotas_rollback.called)

    def test_handle_revert_to_snapshot_quotas(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False)
        snapshot = fakes.fake_snapshot(
            id=uuidutils.generate_uuid(), share_id=share['id'], size=1)
        mock_quotas_reserve = self.mock_object(quota.QUOTAS, 'reserve')

        result = self.api._handle_revert_to_snapshot_quotas(
            self.context, share, snapshot)

        self.assertIsNone(result)
        self.assertFalse(mock_quotas_reserve.called)

    def test_handle_revert_to_snapshot_quotas_different_size(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False)
        snapshot = fakes.fake_snapshot(
            id=uuidutils.generate_uuid(), share_id=share['id'], size=2)
        mock_quotas_reserve = self.mock_object(
            quota.QUOTAS, 'reserve',
            mock.Mock(return_value='fake_reservations'))

        result = self.api._handle_revert_to_snapshot_quotas(
            self.context, share, snapshot)

        self.assertEqual('fake_reservations', result)
        mock_quotas_reserve.assert_called_once_with(
            self.context, project_id='fake_project', gigabytes=1,
            share_type_id=share['instance']['share_type_id'],
            user_id='fake_user')

    def test_handle_revert_to_snapshot_quotas_quota_exceeded(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False)
        snapshot = fakes.fake_snapshot(
            id=uuidutils.generate_uuid(), share_id=share['id'], size=2)
        usages = {'gigabytes': {'reserved': 10, 'in_use': 0}}
        quotas = {'gigabytes': 10}
        side_effect = exception.OverQuota(
            overs='fake', usages=usages, quotas=quotas)
        self.mock_object(
            quota.QUOTAS, 'reserve', mock.Mock(side_effect=side_effect))

        self.assertRaises(exception.ShareSizeExceedsAvailableQuota,
                          self.api._handle_revert_to_snapshot_quotas,
                          self.context,
                          share,
                          snapshot)

    def test__revert_to_snapshot(self):

        share = fakes.fake_share(
            id=uuidutils.generate_uuid(), size=1, project_id='fake_project',
            user_id='fake_user', has_replicas=False)
        snapshot = fakes.fake_snapshot(
            id=uuidutils.generate_uuid(), share_id=share['id'], size=2)
        mock_share_update = self.mock_object(db_api, 'share_update')
        mock_share_snapshot_update = self.mock_object(
            db_api, 'share_snapshot_update')
        mock_revert_rpc_call = self.mock_object(
            self.share_rpcapi, 'revert_to_snapshot')

        self.api._revert_to_snapshot(
            self.context, share, snapshot, 'fake_reservations')

        mock_share_update.assert_called_once_with(
            self.context, share['id'], {'status': constants.STATUS_REVERTING})
        mock_share_snapshot_update.assert_called_once_with(
            self.context, snapshot['id'],
            {'status': constants.STATUS_RESTORING})
        mock_revert_rpc_call.assert_called_once_with(
            self.context, share, snapshot, share['instance']['host'],
            'fake_reservations')

    def test_revert_to_replicated_snapshot(self):

        share = fakes.fake_share(
            has_replicas=True, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(share_instance_id='id1')
        snapshot_instance = fakes.fake_snapshot_instance(
            base_snapshot=snapshot, id='sid1')
        replicas = [
            fakes.fake_replica(
                id='rid1', replica_state=constants.REPLICA_STATE_ACTIVE),
            fakes.fake_replica(
                id='rid2', replica_state=constants.REPLICA_STATE_IN_SYNC),
        ]
        self.mock_object(
            db_api, 'share_replicas_get_available_active_replica',
            mock.Mock(return_value=replicas[0]))
        self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=[snapshot_instance]))
        mock_share_replica_update = self.mock_object(
            db_api, 'share_replica_update')
        mock_share_snapshot_instance_update = self.mock_object(
            db_api, 'share_snapshot_instance_update')
        mock_revert_rpc_call = self.mock_object(
            self.share_rpcapi, 'revert_to_snapshot')

        self.api._revert_to_replicated_snapshot(
            self.context, share, snapshot, 'fake_reservations')

        mock_share_replica_update.assert_called_once_with(
            self.context, 'rid1', {'status': constants.STATUS_REVERTING})
        mock_share_snapshot_instance_update.assert_called_once_with(
            self.context, 'sid1', {'status': constants.STATUS_RESTORING})
        mock_revert_rpc_call.assert_called_once_with(
            self.context, share, snapshot, replicas[0]['host'],
            'fake_reservations')

    def test_revert_to_replicated_snapshot_no_active_replica(self):

        share = fakes.fake_share(
            has_replicas=True, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(share_instance_id='id1')
        self.mock_object(
            db_api, 'share_replicas_get_available_active_replica',
            mock.Mock(return_value=None))

        self.assertRaises(exception.ReplicationException,
                          self.api._revert_to_replicated_snapshot,
                          self.context,
                          share,
                          snapshot,
                          'fake_reservations')

    def test_revert_to_replicated_snapshot_no_snapshot_instance(self):

        share = fakes.fake_share(
            has_replicas=True, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(share_instance_id='id1')
        replicas = [
            fakes.fake_replica(
                id='rid1', replica_state=constants.REPLICA_STATE_ACTIVE),
            fakes.fake_replica(
                id='rid2', replica_state=constants.REPLICA_STATE_IN_SYNC),
        ]
        self.mock_object(
            db_api, 'share_replicas_get_available_active_replica',
            mock.Mock(return_value=replicas[0]))
        self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=[None]))

        self.assertRaises(exception.ReplicationException,
                          self.api._revert_to_replicated_snapshot,
                          self.context,
                          share,
                          snapshot,
                          'fake_reservations')

    def test_create_snapshot_for_replicated_share(self):
        share = fakes.fake_share(
            has_replicas=True, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(
            create_instance=True, share_instance_id='id2')
        replicas = [
            fakes.fake_replica(
                id='id1', replica_state=constants.REPLICA_STATE_ACTIVE),
            fakes.fake_replica(
                id='id2', replica_state=constants.REPLICA_STATE_IN_SYNC)
        ]
        self.mock_object(share_api.policy, 'check_policy')
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value='reservation'))
        self.mock_object(
            db_api, 'share_snapshot_create', mock.Mock(return_value=snapshot))
        self.mock_object(db_api, 'share_replicas_get_all_by_share',
                         mock.Mock(return_value=replicas))
        self.mock_object(
            db_api, 'share_snapshot_get', mock.Mock(return_value=snapshot))
        self.mock_object(quota.QUOTAS, 'commit')
        mock_instance_create_call = self.mock_object(
            db_api, 'share_snapshot_instance_create')
        mock_snapshot_rpc_call = self.mock_object(
            self.share_rpcapi, 'create_snapshot')
        mock_replicated_snapshot_rpc_call = self.mock_object(
            self.share_rpcapi, 'create_replicated_snapshot')
        snapshot_instance_args = {
            'status': constants.STATUS_CREATING,
            'progress': '0%',
            'share_instance_id': 'id1',
        }

        retval = self.api.create_snapshot(
            self.context, share, 'fake_name', 'fake_description')

        self.assertEqual(snapshot['id'], retval['id'])
        mock_instance_create_call.assert_called_once_with(
            self.context, snapshot['id'], snapshot_instance_args)
        self.assertFalse(mock_snapshot_rpc_call.called)
        self.assertTrue(mock_replicated_snapshot_rpc_call.called)

    @mock.patch.object(db_api, 'share_instance_get_all_by_share_server',
                       mock.Mock(return_value=[]))
    @mock.patch.object(db_api, 'share_group_get_all_by_share_server',
                       mock.Mock(return_value=[]))
    def test_delete_share_server_no_dependent_shares(self):
        server = {'id': 'fake_share_server_id'}
        server_returned = {
            'id': 'fake_share_server_id',
        }
        self.mock_object(db_api, 'share_server_update',
                         mock.Mock(return_value=server_returned))
        self.api.delete_share_server(self.context, server)
        db_api.share_instance_get_all_by_share_server.assert_called_once_with(
            self.context, server['id'])
        (db_api.share_group_get_all_by_share_server.
            assert_called_once_with(self.context, server['id']))
        self.share_rpcapi.delete_share_server.assert_called_once_with(
            self.context, server_returned)

    @mock.patch.object(db_api, 'share_instance_get_all_by_share_server',
                       mock.Mock(return_value=['fake_share', ]))
    @mock.patch.object(db_api, 'share_group_get_all_by_share_server',
                       mock.Mock(return_value=[]))
    def test_delete_share_server_dependent_share_exists(self):
        server = {'id': 'fake_share_server_id'}
        self.assertRaises(exception.ShareServerInUse,
                          self.api.delete_share_server,
                          self.context,
                          server)
        db_api.share_instance_get_all_by_share_server.assert_called_once_with(
            self.context, server['id'])

    @mock.patch.object(db_api, 'share_instance_get_all_by_share_server',
                       mock.Mock(return_value=[]))
    @mock.patch.object(db_api, 'share_group_get_all_by_share_server',
                       mock.Mock(return_value=['fake_group', ]))
    def test_delete_share_server_dependent_group_exists(self):
        server = {'id': 'fake_share_server_id'}
        self.assertRaises(exception.ShareServerInUse,
                          self.api.delete_share_server,
                          self.context,
                          server)

        db_api.share_instance_get_all_by_share_server.assert_called_once_with(
            self.context, server['id'])
        (db_api.share_group_get_all_by_share_server.
            assert_called_once_with(self.context, server['id']))

    @mock.patch.object(db_api, 'share_snapshot_instance_update', mock.Mock())
    def test_delete_snapshot(self):
        snapshot = db_utils.create_snapshot(
            with_share=True, status=constants.STATUS_AVAILABLE)
        share = snapshot['share']

        with mock.patch.object(db_api, 'share_get',
                               mock.Mock(return_value=share)):
            self.api.delete_snapshot(self.context, snapshot)
            self.share_rpcapi.delete_snapshot.assert_called_once_with(
                self.context, snapshot, share['host'], force=False,
                deferred_delete=False)
            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share', 'delete_snapshot', snapshot)
            db_api.share_snapshot_instance_update.assert_called_once_with(
                self.context,
                snapshot['instance']['id'],
                {'status': constants.STATUS_DELETING})
            db_api.share_get.assert_called_once_with(
                self.context, snapshot['share_id'])

    @ddt.data(True, False)
    def test_delete_snapshot_deferred(self, force):
        CONF.set_default("is_deferred_deletion_enabled", True)
        snapshot = db_utils.create_snapshot(
            with_share=True, status=constants.STATUS_AVAILABLE)
        share = snapshot['share']

        self.mock_object(db_api, 'share_snapshot_instance_update',
                         mock.Mock())

        with mock.patch.object(db_api, 'share_get',
                               mock.Mock(return_value=share)):
            self.api.delete_snapshot(self.context, snapshot, force=force)
            if force:
                self.share_rpcapi.delete_snapshot.assert_called_once_with(
                    self.context, snapshot, share['host'], force=True,
                    deferred_delete=False)
                db_api.share_snapshot_instance_update.assert_called_once_with(
                    self.context,
                    snapshot['instance']['id'],
                    {'status': constants.STATUS_DELETING})
            else:
                self.share_rpcapi.delete_snapshot.assert_called_once_with(
                    self.context, snapshot, share['host'], force=False,
                    deferred_delete=True)
                db_api.share_snapshot_instance_update.assert_called_once_with(
                    self.context,
                    snapshot['instance']['id'],
                    {'status': constants.STATUS_DEFERRED_DELETING})

            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share', 'delete_snapshot', snapshot)
            db_api.share_get.assert_called_once_with(
                self.context, snapshot['share_id'])

    def test_delete_snapshot_wrong_status(self):
        snapshot = db_utils.create_snapshot(
            with_share=True, status=constants.STATUS_CREATING)

        self.assertRaises(exception.InvalidShareSnapshot,
                          self.api.delete_snapshot,
                          self.context,
                          snapshot)
        share_api.policy.check_policy.assert_called_once_with(
            self.context, 'share', 'delete_snapshot', snapshot)

    @ddt.data(constants.STATUS_MANAGING, constants.STATUS_ERROR_DELETING,
              constants.STATUS_CREATING, constants.STATUS_AVAILABLE)
    def test_delete_snapshot_force_delete(self, status):
        share = fakes.fake_share(id=uuidutils.generate_uuid(),
                                 has_replicas=False)
        snapshot = fakes.fake_snapshot(aggregate_status=status, share=share)
        snapshot_instance = fakes.fake_snapshot_instance(
            base_snapshot=snapshot)
        self.mock_object(db_api, 'share_get', mock.Mock(return_value=share))
        self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=[snapshot_instance]))
        mock_instance_update_call = self.mock_object(
            db_api, 'share_snapshot_instance_update')
        mock_rpc_call = self.mock_object(self.share_rpcapi, 'delete_snapshot')

        retval = self.api.delete_snapshot(self.context, snapshot, force=True)

        self.assertIsNone(retval)
        mock_instance_update_call.assert_called_once_with(
            self.context, snapshot_instance['id'],
            {'status': constants.STATUS_DELETING})
        mock_rpc_call.assert_called_once_with(
            self.context, snapshot, share['instance']['host'], force=True,
            deferred_delete=False)

    @ddt.data(True, False)
    def test_delete_snapshot_replicated_snapshot(self, force):
        share = fakes.fake_share(has_replicas=True)
        snapshot = fakes.fake_snapshot(
            create_instance=True, share_id=share['id'],
            status=constants.STATUS_ERROR)
        snapshot_instance = fakes.fake_snapshot_instance(
            base_snapshot=snapshot)
        expected_update_calls = [
            mock.call(self.context, x, {'status': constants.STATUS_DELETING})
            for x in (snapshot['instance']['id'], snapshot_instance['id'])
        ]
        self.mock_object(db_api, 'share_get', mock.Mock(return_value=share))
        self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=[snapshot['instance'], snapshot_instance]))
        mock_db_update_call = self.mock_object(
            db_api, 'share_snapshot_instance_update')
        mock_snapshot_rpc_call = self.mock_object(
            self.share_rpcapi, 'delete_snapshot')
        mock_replicated_snapshot_rpc_call = self.mock_object(
            self.share_rpcapi, 'delete_replicated_snapshot')

        retval = self.api.delete_snapshot(self.context, snapshot, force=force)

        self.assertIsNone(retval)
        self.assertEqual(2, mock_db_update_call.call_count)
        mock_db_update_call.assert_has_calls(expected_update_calls)
        mock_replicated_snapshot_rpc_call.assert_called_once_with(
            self.context, snapshot, share['instance']['host'],
            share_id=share['id'], force=force)
        self.assertFalse(mock_snapshot_rpc_call.called)

    def test_create_snapshot_if_share_not_available(self):
        share = db_utils.create_share(status=constants.STATUS_ERROR)
        self.assertRaises(exception.InvalidShare,
                          self.api.create_snapshot,
                          self.context,
                          share,
                          'fakename',
                          'fakedesc')
        share_api.policy.check_policy.assert_called_once_with(
            self.context, 'share', 'create_snapshot', share)

    def test_create_snapshot_invalid_task_state(self):
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)
        self.assertRaises(exception.ShareBusyException,
                          self.api.create_snapshot,
                          self.context,
                          share,
                          'fakename',
                          'fakedesc')
        share_api.policy.check_policy.assert_called_once_with(
            self.context, 'share', 'create_snapshot', share)

    def test_create_snapshot_fail(self):
        share = fakes.fake_share(
            has_replicas=False, status=constants.STATUS_AVAILABLE)
        mock_db_share_snapshot_create = self.mock_object(
            db_api, 'share_snapshot_create', mock.Mock(
                side_effect=exception.NotFound))

        self.mock_object(quota.QUOTAS, 'rollback')

        self.assertRaises(exception.NotFound,
                          self.api.create_snapshot,
                          self.context, share,
                          'fake_name', 'fake_desc')

        self.assertTrue(mock_db_share_snapshot_create.called)
        quota.QUOTAS.rollback.assert_called_once_with(
            self.context,
            mock.ANY,
            share_type_id=share['instance']['share_type_id'])

    def test_create_snapshot_quota_commit_fail(self):
        share = fakes.fake_share(
            has_replicas=False, status=constants.STATUS_AVAILABLE)
        snapshot = fakes.fake_snapshot(
            create_instance=True, share_instance_id='id2')
        self.mock_object(
            quota.QUOTAS, 'commit', mock.Mock(
                side_effect=exception.QuotaError('fake')))

        self.mock_object(db_api, 'share_snapshot_create', mock.Mock(
            return_value=snapshot))
        self.mock_object(db_api, 'share_snapshot_instance_delete')
        self.mock_object(quota.QUOTAS, 'rollback')

        self.assertRaises(exception.QuotaError,
                          self.api.create_snapshot,
                          self.context, share,
                          'fake_name', 'fake_desc')

        quota.QUOTAS.rollback.assert_called_once_with(
            self.context,
            mock.ANY,
            share_type_id=share['instance']['share_type_id'])

    @ddt.data({'use_scheduler': False, 'valid_host': 'fake',
               'az': None},
              {'use_scheduler': True, 'valid_host': None,
               'az': None},
              {'use_scheduler': True, 'valid_host': None,
               'az': "fakeaz2"})
    @ddt.unpack
    def test_create_from_snapshot(self, use_scheduler, valid_host, az):
        snapshot, share, share_data, request_spec = (
            self._setup_create_from_snapshot_mocks(
                use_scheduler=use_scheduler, host=valid_host)
        )
        share_type = fakes.fake_share_type()

        self.mock_object(db_api, 'share_snapshot_get',
                         mock.Mock(return_value=snapshot))
        mock_get_share_type_call = self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))

        self.api.create(
            self.context,
            share_data['share_proto'],
            None,  # NOTE(u_glide): Get share size from snapshot
            share_data['display_name'],
            share_data['display_description'],
            snapshot_id=snapshot['id'],
            availability_zone=az,
        )

        expected_az = snapshot['share']['availability_zone'] if not az else az
        share_data.pop('availability_zone')

        mock_get_share_type_call.assert_called_once_with(
            self.context, share['share_type_id'])
        self.assertSubDictMatch(share_data,
                                db_api.share_create.call_args[0][1])
        self.api.create_instance.assert_called_once_with(
            self.context, share, share_network_id=share['share_network_id'],
            host=valid_host, share_type_id=share_type['id'],
            availability_zone=expected_az,
            share_group=None, share_group_snapshot_member=None,
            availability_zones=None,
            az_request_multiple_subnet_support_map=None,
            snapshot_host=snapshot['share']['instance']['host'],
            scheduler_hints=None, mount_point_name=None,
            encryption_key_ref=None)
        share_api.policy.check_policy.assert_called_once_with(
            self.context, 'share_snapshot', 'get_snapshot',
            snapshot, do_raise=False)
        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, share_type_id=share_type['id'],
            gigabytes=1, shares=1)
        quota.QUOTAS.commit.assert_called_once_with(
            self.context, 'reservation', share_type_id=share_type['id'])

    def test_create_share_with_share_group(self):
        extra_specs = {'replication_type': constants.REPLICATION_TYPE_READABLE}
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        group = db_utils.create_share_group(
            status=constants.STATUS_AVAILABLE,
            share_types=[share_type['id']])
        share, share_data = self._setup_create_mocks(
            share_type_id=share_type['id'],
            share_group_id=group['id'])

        share_instance = db_utils.create_share_instance(
            share_id=share['id'])
        sg_snap_member = {
            'id': 'fake_sg_snap_member_id',
            'share_instance': share_instance
        }

        az = share_data.pop('availability_zone')

        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value='reservation'))
        self.mock_object(quota.QUOTAS, 'commit')

        self.api.create(
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            availability_zone=az,
            share_type=share_type,
            share_group_id=group['id'],
            share_group_snapshot_member=sg_snap_member,
        )
        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, share_type_id=share_type['id'],
            gigabytes=1, shares=1, share_replicas=1, replica_gigabytes=1)
        quota.QUOTAS.commit.assert_called_once_with(
            self.context, 'reservation', share_type_id=share_type['id']
        )

    def test_create_share_share_type_contains_replication_type(self):
        extra_specs = {'replication_type': constants.REPLICATION_TYPE_READABLE}
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        share, share_data = self._setup_create_mocks(
            share_type_id=share_type['id'])
        az = share_data.pop('availability_zone')

        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value='reservation'))
        self.mock_object(quota.QUOTAS, 'commit')

        self.api.create(
            self.context,
            share_data['share_proto'],
            share_data['size'],
            share_data['display_name'],
            share_data['display_description'],
            availability_zone=az,
            share_type=share_type
        )
        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, share_type_id=share_type['id'],
            gigabytes=1, shares=1, share_replicas=1, replica_gigabytes=1)
        quota.QUOTAS.commit.assert_called_once_with(
            self.context, 'reservation', share_type_id=share_type['id']
        )

    def test_create_from_snapshot_az_different_from_source(self):
        snapshot, share, share_data, request_spec = (
            self._setup_create_from_snapshot_mocks(use_scheduler=False)
        )

        self.assertRaises(exception.InvalidInput, self.api.create,
                          self.context, share_data['share_proto'],
                          share_data['size'],
                          share_data['display_name'],
                          share_data['display_description'],
                          snapshot_id=snapshot['id'],
                          availability_zone='fake_different_az')

    def test_create_from_snapshot_with_different_share_type(self):
        snapshot, share, share_data, request_spec = (
            self._setup_create_from_snapshot_mocks()
        )

        share_type = {'id': 'super_fake_share_type'}

        self.assertRaises(exception.InvalidInput, self.api.create,
                          self.context, share_data['share_proto'],
                          share_data['size'],
                          share_data['display_name'],
                          share_data['display_description'],
                          snapshot_id=snapshot['id'],
                          availability_zone=share_data['availability_zone'],
                          share_type=share_type)

    def test_get_snapshot(self):
        fake_get_snap = {'fake_key': 'fake_val'}
        with mock.patch.object(db_api, 'share_snapshot_get',
                               mock.Mock(return_value=fake_get_snap)):
            rule = self.api.get_snapshot(self.context, 'fakeid')
            self.assertEqual(fake_get_snap, rule)
            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share_snapshot', 'get_snapshot', rule,
                do_raise=False)
            db_api.share_snapshot_get.assert_called_once_with(
                self.context, 'fakeid')

    def test_get_snapshot_non_admin_deferred_state(self):
        fake_get_snap = {
            'fake_key': 'fake_val', 'status': 'deferred_deleting'
        }
        with mock.patch.object(db_api, 'share_snapshot_get',
                               mock.Mock(return_value=fake_get_snap)):
            self.mock_object(
                policy, 'check_policy', mock.Mock(side_effect=[True, False]))
            self.assertRaises(exception.NotFound, self.api.get_snapshot,
                              self.context, 'fakeid')

    def test_get_snapshot_not_authorized(self):
        fake_get_snap = {'fake_key': 'fake_val'}
        share_api.policy.check_policy.return_value = False
        with mock.patch.object(db_api, 'share_snapshot_get',
                               mock.Mock(return_value=fake_get_snap)):
            self.assertRaises(exception.NotFound,
                              self.api.get_snapshot,
                              self.context, 'fakeid')
            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share_snapshot', 'get_snapshot',
                fake_get_snap, do_raise=False)
            db_api.share_snapshot_get.assert_called_once_with(
                self.context, 'fakeid')

    def test_create_from_snapshot_not_available(self):
        snapshot = db_utils.create_snapshot(
            with_share=True, status=constants.STATUS_ERROR)
        self.assertRaises(exception.InvalidShareSnapshot, self.api.create,
                          self.context, 'nfs', '1', 'fakename',
                          'fakedesc', snapshot_id=snapshot['id'],
                          availability_zone='fakeaz')

    def test_create_from_snapshot_larger_size(self):
        snapshot = db_utils.create_snapshot(
            size=100, status=constants.STATUS_AVAILABLE, with_share=True)
        self.assertRaises(exception.InvalidInput, self.api.create,
                          self.context, 'nfs', 1, 'fakename', 'fakedesc',
                          availability_zone='fakeaz',
                          snapshot_id=snapshot['id'])

    def test_create_share_wrong_size_0(self):
        self.assertRaises(exception.InvalidInput, self.api.create,
                          self.context, 'nfs', 0, 'fakename', 'fakedesc',
                          availability_zone='fakeaz')

    def test_create_share_wrong_size_some(self):
        self.assertRaises(exception.InvalidInput, self.api.create,
                          self.context, 'nfs', 'some', 'fakename',
                          'fakedesc', availability_zone='fakeaz')

    @ddt.data(constants.STATUS_AVAILABLE, constants.STATUS_ERROR)
    def test_delete(self, status):
        share = self._setup_delete_mocks(status)

        self.api.delete(self.context, share)

        self.api.delete_instance.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext),
            utils.IsAMatcher(models.ShareInstance), force=False
        )
        db_api.share_snapshot_get_all_for_share.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), share['id'])

    def test_delete_quota_with_different_user(self):
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE)
        diff_user_context = context.RequestContext(
            user_id='fake2',
            project_id='fake',
            is_admin=False
        )

        self.api.delete(diff_user_context, share)

    def test_delete_wrong_status(self):
        share = fakes.fake_share(status='wrongstatus')
        self.mock_object(db_api, 'share_get', mock.Mock(return_value=share))
        self.assertRaises(exception.InvalidShare, self.api.delete,
                          self.context, share)

    def test_delete_share_has_replicas(self):
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE,
                                         replication_type='writable')
        db_utils.create_share_replica(share_id=share['id'],
                                      replica_state='in_sync')
        db_utils.create_share_replica(share_id=share['id'],
                                      replica_state='out_of_sync')

        self.assertRaises(exception.Conflict, self.api.delete,
                          self.context, share)

    @mock.patch.object(db_api, 'count_share_group_snapshot_members_in_share',
                       mock.Mock(return_value=2))
    def test_delete_dependent_share_group_snapshot_members(self):
        share_server_id = 'fake-ss-id'
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE,
                                         share_server_id)

        self.assertRaises(exception.InvalidShare, self.api.delete,
                          self.context, share)

    @mock.patch.object(db_api, 'share_instance_delete', mock.Mock())
    def test_delete_no_host(self):
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE, host=None)

        self.api.delete(self.context, share)
        db_api.share_instance_delete.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), share.instance['id'],
            need_to_update_usages=True)

    def test_delete_share_with_snapshots(self):
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE,
                                         snapshots=['fake'])

        self.assertRaises(
            exception.InvalidShare,
            self.api.delete,
            self.context,
            share
        )

    def test_delete_share_invalid_task_state(self):
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)

        self.assertRaises(exception.ShareBusyException,
                          self.api.delete,
                          self.context, share)

    def test_delete_share_quota_error(self):
        share = self._setup_delete_mocks(constants.STATUS_AVAILABLE)
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(side_effect=exception.QuotaError('fake')))

        self.api.delete(self.context, share)

    def test_delete_locked_share(self):
        self.mock_object(
            self.api.db,
            'resource_lock_get_all',
            mock.Mock(return_value=([{'id': 'l1'}, {'id': 'l2'}], None))
        )
        share = self._setup_delete_mocks('available')

        self.assertRaises(exception.InvalidShare,
                          self.api.delete,
                          self.context,
                          share)

        # lock check decorator executed first, nothing else is invoked
        self.api.delete_instance.assert_not_called()
        db_api.share_snapshot_get_all_for_share.assert_not_called()

    @ddt.data({'status': constants.STATUS_AVAILABLE, 'force': False},
              {'status': constants.STATUS_ERROR, 'force': True})
    @ddt.unpack
    def test_delete_share_instance(self, status, force):
        instance = self._setup_delete_share_instance_mocks(
            status=status, share_server_id='fake')

        self.api.delete_instance(self.context, instance, force=force)

        db_api.share_instance_update.assert_called_once_with(
            self.context,
            instance['id'],
            {'status': constants.STATUS_DELETING,
             'terminated_at': self.dt_utc}
        )
        self.api.share_rpcapi.delete_share_instance.\
            assert_called_once_with(
                self.context,
                instance,
                force=force,
                deferred_delete=False
            )
        db_api.share_server_update(
            self.context,
            instance['share_server_id'],
            {'updated_at': self.dt_utc}
        )

    @ddt.data({'status': constants.STATUS_DEFERRED_DELETING, 'force': True},
              {'status': constants.STATUS_AVAILABLE, 'force': False},
              {'status': constants.STATUS_AVAILABLE, 'force': True})
    @ddt.unpack
    def test_delete_share_instance_deferred(self, status, force):
        CONF.set_default("is_deferred_deletion_enabled", True)
        instance = self._setup_delete_share_instance_mocks(
            status=status, share_server_id='fake')

        self.api.delete_instance(self.context, instance, force=force)
        if force:
            if status != constants.STATUS_DEFERRED_DELETING:
                db_api.share_instance_update.assert_called_once_with(
                    self.context,
                    instance['id'],
                    {'status': constants.STATUS_DELETING,
                     'terminated_at': self.dt_utc}
                )
            self.api.share_rpcapi.delete_share_instance.\
                assert_called_once_with(
                    self.context,
                    instance,
                    force=True,
                    deferred_delete=False
                )
        else:
            db_api.share_instance_update.assert_called_once_with(
                self.context,
                instance['id'],
                {'status': constants.STATUS_DEFERRED_DELETING,
                 'terminated_at': self.dt_utc}
            )
            self.api.share_rpcapi.delete_share_instance.\
                assert_called_once_with(
                    self.context,
                    instance,
                    force=False,
                    deferred_delete=True
                )
        db_api.share_server_update(
            self.context,
            instance['share_server_id'],
            {'updated_at': self.dt_utc}
        )

    def test_delete_share_instance_invalid_status(self):
        instance = self._setup_delete_share_instance_mocks(
            status=constants.STATUS_CREATING, share_server_id='fake')

        self.assertRaises(
            exception.InvalidShareInstance,
            self.api.delete_instance,
            self.context,
            instance
        )

    def test_get(self):
        share = db_utils.create_share()
        with mock.patch.object(db_api, 'share_get',
                               mock.Mock(return_value=share)):
            result = self.api.get(self.context, 'fakeid')
            self.assertEqual(share, result)
            share_api.policy.check_policy.assert_called_once_with(
                self.context, 'share', 'get', share, do_raise=False)
            db_api.share_get.assert_called_once_with(
                self.context, 'fakeid')

    def test_get_admin_deferred_state(self):
        rv = {
            'id': 'fake_id',
            'is_public': False,
            'name': 'bar',
            'status': constants.STATUS_ERROR_DEFERRED_DELETING,
            'project_id': 'fake_pid_2',
            'share_server_id': 'fake_server_3',
        }

        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=rv))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        self.mock_object(
            policy, 'check_policy', mock.Mock(side_effect=[True, True]))
        share = self.api.get(ctx, 'fake_id')
        self.assertEqual(rv, share)

    def test_get_non_admin_deferred_state(self):
        rv = {
            'id': 'fake_id',
            'is_public': False,
            'name': 'bar',
            'status': constants.STATUS_ERROR_DEFERRED_DELETING,
            'project_id': 'fake_pid_2',
            'share_server_id': 'fake_server_3',
        }

        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=rv))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.mock_object(
            policy, 'check_policy', mock.Mock(side_effect=[True, False]))

        self.assertRaises(
            exception.NotFound,
            self.api.get, ctx, 'fake_id')

    def test_get_not_authorized(self):
        share = db_utils.create_share(
            is_public=False,
            project_id='5db325fc4de14fe1a860ff69f190c78c')
        share_api.policy.check_policy.return_value = False
        ctx = context.RequestContext('df6d65cc1f8946ba86be06b8140ec4b3',
                                     'e8133457b853436591a7e4610e7ce679',
                                     is_admin=False)
        with mock.patch.object(db_api, 'share_get',
                               mock.Mock(return_value=share)):

            self.assertRaises(exception.NotFound,
                              self.api.get,
                              ctx,
                              share['id'])
            share_api.policy.check_policy.assert_called_once_with(
                ctx, 'share', 'get', share, do_raise=False)
            db_api.share_get.assert_called_once_with(ctx, share['id'])

    @mock.patch.object(db_api, 'share_snapshot_get_all_by_project',
                       mock.Mock())
    def test_get_all_snapshots_admin_not_all_tenants(self):
        ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=True)
        mock_policy = self.mock_object(share_api.policy, 'check_policy',
                                       mock.Mock(return_value=False))
        self.api.get_all_snapshots(ctx)
        mock_policy.assert_has_calls([
            mock.call(ctx, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                ctx, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])
        db_api.share_snapshot_get_all_by_project.assert_called_once_with(
            ctx, 'fakepid', limit=None, offset=None, sort_dir='desc',
            sort_key='share_id', filters={})

    @mock.patch.object(db_api, 'share_snapshot_get_all', mock.Mock())
    def test_get_all_snapshots_admin_all_tenants(self):
        mock_policy = self.mock_object(
            share_api.policy, 'check_policy',
            mock.Mock(side_effect=[False, True, False]))
        self.api.get_all_snapshots(self.context,
                                   search_opts={'all_tenants': 1})
        mock_policy.assert_has_calls([
            mock.call(self.context, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                self.context, 'share_snapshot',
                'list_all_projects',
                do_raise=False),
            mock.call(
                self.context, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])
        db_api.share_snapshot_get_all.assert_called_once_with(
            self.context, limit=None, offset=None, sort_dir='desc',
            sort_key='share_id', filters={})

    @mock.patch.object(db_api, 'share_snapshot_get_all_by_project',
                       mock.Mock())
    def test_get_all_snapshots_not_admin(self):
        ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=False)
        mock_policy = self.mock_object(share_api.policy, 'check_policy',
                                       mock.Mock(return_value=False))
        self.api.get_all_snapshots(ctx)
        mock_policy.assert_has_calls([
            mock.call(ctx, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                ctx, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])
        db_api.share_snapshot_get_all_by_project.assert_called_once_with(
            ctx, 'fakepid', limit=None, offset=None, sort_dir='desc',
            sort_key='share_id', filters={})

    def test_get_all_snapshots_not_admin_search_opts(self):
        search_opts = {'size': 'fakesize'}
        fake_objs = [{'name': 'fakename1'}, search_opts]
        ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=False)
        self.mock_object(db_api, 'share_snapshot_get_all_by_project',
                         mock.Mock(return_value=fake_objs))
        mock_policy = self.mock_object(share_api.policy, 'check_policy',
                                       mock.Mock(return_value=False))

        result = self.api.get_all_snapshots(ctx, search_opts)

        self.assertEqual(fake_objs, result)
        mock_policy.assert_has_calls([
            mock.call(ctx, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                ctx, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])
        db_api.share_snapshot_get_all_by_project.assert_called_once_with(
            ctx, 'fakepid', limit=None, offset=None, sort_dir='desc',
            sort_key='share_id', filters=search_opts)

    @ddt.data(({'name': 'fo'}, 0, []), ({'description': 'd'}, 0, []),
              ({'name': 'foo', 'description': 'd'}, 0, []),
              ({'name': 'foo'}, 1, [{'name': 'foo', 'description': 'ds'}]),
              ({'description': 'ds'}, 1, [{'name': 'foo',
                                           'description': 'ds'}]),
              ({'name~': 'foo', 'description~': 'ds'}, 2,
               [{'name': 'foo', 'description': 'ds'},
                {'name': 'foo1', 'description': 'ds1'}]),
              ({'name': 'foo', 'description~': 'ds'}, 1,
               [{'name': 'foo', 'description': 'ds'}]),
              ({'name~': 'foo', 'description': 'ds'}, 1,
               [{'name': 'foo', 'description': 'ds'}]))
    @ddt.unpack
    def test_get_all_snapshots_filter_by_name_and_description(
            self, search_opts, get_snapshot_number, res_snapshots):
        fake_objs = [{'name': 'fo2', 'description': 'd2'},
                     {'name': 'foo', 'description': 'ds'},
                     {'name': 'foo1', 'description': 'ds1'}]
        ctx = context.RequestContext('fakeuid', 'fakepid', is_admin=False)
        self.mock_object(db_api, 'share_snapshot_get_all_by_project',
                         mock.Mock(return_value=res_snapshots))
        mock_policy = self.mock_object(share_api.policy, 'check_policy')

        result = self.api.get_all_snapshots(ctx, search_opts)

        self.assertEqual(get_snapshot_number, len(result))
        if get_snapshot_number == 2:
            self.assertEqual(fake_objs[1:], result)
        elif get_snapshot_number == 1:
            self.assertEqual(fake_objs[1:2], result)

        mock_policy.assert_has_calls([
            mock.call(ctx, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                ctx, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])
        db_api.share_snapshot_get_all_by_project.assert_called_once_with(
            ctx, 'fakepid', limit=None, offset=None, sort_dir='desc',
            sort_key='share_id', filters=search_opts)

    def test_get_all_snapshots_with_sorting_valid(self):
        self.mock_object(
            db_api, 'share_snapshot_get_all_by_project',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SNAPSHOTS[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        mock_policy = self.mock_object(share_api.policy, 'check_policy',
                                       mock.Mock(return_value=False))
        snapshots = self.api.get_all_snapshots(
            ctx, sort_key='status', sort_dir='asc')
        mock_policy.assert_has_calls([
            mock.call(ctx, 'share_snapshot', 'get_all_snapshots'),
            mock.call(
                ctx, 'share_snapshot',
                'list_snapshots_in_deferred_deletion_states',
                do_raise=False)])

        db_api.share_snapshot_get_all_by_project.assert_called_once_with(
            ctx, 'fake_pid_1', limit=None, offset=None, sort_dir='asc',
            sort_key='status', filters={})
        self.assertEqual(_FAKE_LIST_OF_ALL_SNAPSHOTS[0], snapshots)

    def test_get_all_snapshots_sort_key_invalid(self):
        self.mock_object(
            db_api, 'share_snapshot_get_all_by_project',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SNAPSHOTS[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.assertRaises(
            exception.InvalidInput,
            self.api.get_all_snapshots,
            ctx,
            sort_key=1,
        )
        share_api.policy.check_policy.assert_called_once_with(
            ctx, 'share_snapshot', 'get_all_snapshots')

    def test_get_all_snapshots_sort_dir_invalid(self):
        self.mock_object(
            db_api, 'share_snapshot_get_all_by_project',
            mock.Mock(return_value=_FAKE_LIST_OF_ALL_SNAPSHOTS[0]))
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        self.assertRaises(
            exception.InvalidInput,
            self.api.get_all_snapshots,
            ctx,
            sort_dir=1,
        )
        share_api.policy.check_policy.assert_called_once_with(
            ctx, 'share_snapshot', 'get_all_snapshots')

    def test_allow_access_rule_already_exists(self):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE)
        fake_access = db_utils.create_access(share_id=share['id'])
        self.mock_object(self.api.db, 'share_access_create')

        self.assertRaises(
            exception.ShareAccessExists, self.api.allow_access,
            self.context, share, fake_access['access_type'],
            fake_access['access_to'], fake_access['access_level'])
        self.assertFalse(self.api.db.share_access_create.called)

    def test_allow_access_invalid_access_level(self):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE)
        self.mock_object(self.api.db, 'share_access_create')

        self.assertRaises(
            exception.InvalidShareAccess, self.api.allow_access,
            self.context, share, 'user', 'alice', access_level='execute')
        self.assertFalse(self.api.db.share_access_create.called)

    @ddt.data({'host': None},
              {'status': constants.STATUS_ERROR_DELETING,
               'access_rules_status': constants.STATUS_ACTIVE},
              {'host': None, 'access_rules_status': constants.STATUS_ERROR},
              {'access_rules_status': constants.STATUS_ERROR})
    def test_allow_access_invalid_instance(self, params):
        share = db_utils.create_share(host='fake')
        db_utils.create_share_instance(share_id=share['id'])
        db_utils.create_share_instance(share_id=share['id'], **params)
        self.mock_object(self.api.db, 'share_access_create')

        self.assertRaises(exception.InvalidShare, self.api.allow_access,
                          self.context, share, 'ip', '10.0.0.1')
        self.assertFalse(self.api.db.share_access_create.called)

    @ddt.data(*(constants.ACCESS_LEVELS + (None,)))
    def test_allow_access(self, level):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE)
        values = {
            'share_id': share['id'],
            'access_type': 'fake_access_type',
            'access_to': 'fake_access_to',
            'access_level': level,
            'metadata': None,
        }
        fake_access = copy.deepcopy(values)
        fake_access.update({
            'id': 'fake_access_id',
            'state': constants.STATUS_ACTIVE,
            'deleted': 'fake_deleted',
            'deleted_at': 'fake_deleted_at',
            'instance_mappings': ['foo', 'bar'],
        })
        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=share))
        self.mock_object(db_api, 'share_access_create',
                         mock.Mock(return_value=fake_access))
        self.mock_object(db_api, 'share_access_get',
                         mock.Mock(return_value=fake_access))
        self.mock_object(db_api, 'share_access_get_all_by_type_and_access',
                         mock.Mock(return_value=[]))
        self.mock_object(self.api, 'allow_access_to_instance')

        access = self.api.allow_access(
            self.context, share, fake_access['access_type'],
            fake_access['access_to'], level)

        self.assertEqual(fake_access, access)
        db_api.share_access_create.assert_called_once_with(
            self.context, values)
        self.api.allow_access_to_instance.assert_called_once_with(
            self.context, share.instance)

    def test_allow_access_to_instance(self):
        share = db_utils.create_share(host='fake')
        rpc_method = self.mock_object(self.api.share_rpcapi, 'update_access')

        self.api.allow_access_to_instance(self.context, share.instance)

        rpc_method.assert_called_once_with(self.context, share.instance)

    @ddt.data({'host': None},
              {'status': constants.STATUS_ERROR_DELETING,
               'access_rules_status': constants.STATUS_ACTIVE},
              {'host': None, 'access_rules_status': constants.STATUS_ERROR},
              {'access_rules_status': constants.STATUS_ERROR})
    def test_deny_access_invalid_instance(self, params):
        share = db_utils.create_share(host='fake')
        db_utils.create_share_instance(share_id=share['id'])
        db_utils.create_share_instance(share_id=share['id'], **params)
        access_rule = db_utils.create_access(share_id=share['id'])
        self.mock_object(self.api, 'deny_access_to_instance')

        self.assertRaises(exception.InvalidShare, self.api.deny_access,
                          self.context, share, access_rule)
        self.assertFalse(self.api.deny_access_to_instance.called)

    def test_deny_access(self):
        share = db_utils.create_share(
            host='fake', status=constants.STATUS_AVAILABLE,
            access_rules_status=constants.STATUS_ACTIVE)
        access_rule = db_utils.create_access(share_id=share['id'])
        self.mock_object(self.api, 'deny_access_to_instance')

        retval = self.api.deny_access(self.context, share, access_rule)

        self.assertIsNone(retval)
        self.api.deny_access_to_instance.assert_called_once_with(
            self.context, share.instance, access_rule)

    def test_deny_access_to_instance(self):
        share = db_utils.create_share(host='fake')
        share_instance = db_utils.create_share_instance(
            share_id=share['id'], host='fake')
        access = db_utils.create_access(share_id=share['id'])
        rpc_method = self.mock_object(self.api.share_rpcapi, 'update_access')
        self.mock_object(db_api, 'share_instance_access_get',
                         mock.Mock(return_value=access.instance_mappings[0]))
        mock_share_instance_rules_status_update = self.mock_object(
            self.api.access_helper,
            'get_and_update_share_instance_access_rules_status')
        mock_access_rule_state_update = self.mock_object(
            self.api.access_helper,
            'get_and_update_share_instance_access_rule')

        self.api.deny_access_to_instance(self.context, share_instance, access)

        rpc_method.assert_called_once_with(self.context, share_instance)
        mock_access_rule_state_update.assert_called_once_with(
            self.context, access['id'],
            updates={'state': constants.ACCESS_STATE_QUEUED_TO_DENY},
            share_instance_id=share_instance['id'])
        expected_conditional_change = {
            constants.STATUS_ACTIVE: constants.SHARE_INSTANCE_RULES_SYNCING,
        }
        mock_share_instance_rules_status_update.assert_called_once_with(
            self.context, share_instance_id=share_instance['id'],
            conditionally_change=expected_conditional_change)

    def test_access_get(self):
        with mock.patch.object(db_api, 'share_access_get',
                               mock.Mock(return_value={'share_id': 'fake'})):
            self.mock_object(self.api, 'get')
            rule = self.api.access_get(self.context, 'fakeid')
            self.assertEqual({'share_id': 'fake'}, rule)
            db_api.share_access_get.assert_called_once_with(
                self.context, 'fakeid')
            self.api.get.assert_called_once_with(self.context, 'fake')

    def test_access_get_all(self):
        share = db_utils.create_share(id='fakeid')

        values = {
            'fakeacc0id': {
                'id': 'fakeacc0id',
                'access_type': 'fakeacctype',
                'access_to': 'fakeaccto',
                'access_level': 'rw',
                'share_id': share['id'],
            },
            'fakeacc1id': {
                'id': 'fakeacc1id',
                'access_type': 'fakeacctype',
                'access_to': 'fakeaccto',
                'access_level': 'rw',
                'share_id': share['id'],
            },
        }
        rules = [
            db_utils.create_access(**values['fakeacc0id']),
            db_utils.create_access(**values['fakeacc1id']),
        ]

        # add state property
        values['fakeacc0id']['state'] = constants.STATUS_ACTIVE
        values['fakeacc1id']['state'] = constants.STATUS_ACTIVE

        self.mock_object(db_api, 'share_access_get_all_for_share',
                         mock.Mock(return_value=rules))
        actual = self.api.access_get_all(self.context, share)

        self.assertEqual(rules, actual)
        share_api.policy.check_policy.assert_called_once_with(
            self.context, 'share', 'access_get_all')
        db_api.share_access_get_all_for_share.assert_called_once_with(
            self.context, 'fakeid', filters=None)

    def test_share_metadata_get(self):
        metadata = {'a': 'b', 'c': 'd'}
        share_id = uuidutils.generate_uuid()
        db_api.share_create(self.context,
                            {'id': share_id, 'metadata': metadata})
        self.assertEqual(metadata,
                         db_api.share_metadata_get(self.context, share_id))

    def test_share_metadata_update(self):
        metadata1 = {'a': '1', 'c': '2'}
        metadata2 = {'a': '3', 'd': '5'}
        should_be = {'a': '3', 'c': '2', 'd': '5'}
        share_id = uuidutils.generate_uuid()
        db_api.share_create(self.context,
                            {'id': share_id, 'metadata': metadata1})
        db_api.share_metadata_update(self.context, share_id, metadata2, False)
        self.assertEqual(should_be,
                         db_api.share_metadata_get(self.context, share_id))

    def test_share_metadata_update_delete(self):
        metadata1 = {'a': '1', 'c': '2'}
        metadata2 = {'a': '3', 'd': '4'}
        should_be = metadata2
        share_id = uuidutils.generate_uuid()
        db_api.share_create(self.context,
                            {'id': share_id, 'metadata': metadata1})
        db_api.share_metadata_update(self.context, share_id, metadata2, True)
        self.assertEqual(should_be,
                         db_api.share_metadata_get(self.context, share_id))

    def test_extend_invalid_status(self):
        invalid_status = 'fake'
        share = db_utils.create_share(status=invalid_status)
        new_size = 123

        self.assertRaises(exception.InvalidShare,
                          self.api.extend, self.context, share, new_size)

    def test_extend_invalid_task_state(self):
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)
        new_size = 123

        self.assertRaises(exception.ShareBusyException,
                          self.api.extend, self.context, share, new_size)

    def test_extend_invalid_size(self):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=200)
        new_size = 123

        self.assertRaises(exception.InvalidInput,
                          self.api.extend, self.context, share, new_size)

    def test_extend_share_over_per_share_quota(self):
        quota.CONF.set_default("per_share_gigabytes", 5, 'quota')
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=4)
        new_size = 6
        self.assertRaises(exception.ShareSizeExceedsLimit,
                          self.api.extend, self.context, share, new_size)

    def test_extend_with_share_type_size_limit(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=False)
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=3)
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=self.sized_sha_type))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=False))

        new_size = 5

        self.assertRaises(exception.InvalidInput,
                          self.api.extend, ctx,
                          share, new_size)

    def test_extend_with_share_type_size_limit_admin(self):
        ctx = context.RequestContext('fake_uid', 'fake_pid_1', is_admin=True)
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=3)
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=self.sized_sha_type))
        self.mock_policy_check = self.mock_object(
            policy, 'check_policy', mock.Mock(return_value=True))

        new_size = 7

        self.assertRaises(exception.InvalidInput,
                          self.api.extend, ctx,
                          share, new_size)

    def _setup_extend_mocks(self, supports_replication):
        replica_list = []
        if supports_replication:
            replica_list.append({'id': 'fake_replica_id'})
            replica_list.append({'id': 'fake_replica_id_2'})
        self.mock_object(db_api, 'share_replicas_get_all_by_share',
                         mock.Mock(return_value=replica_list))

    @ddt.data(
        (False, 'gigabytes', exception.ShareSizeExceedsAvailableQuota),
        (True, 'replica_gigabytes',
         exception.ShareReplicaSizeExceedsAvailableQuota)
    )
    @ddt.unpack
    def test_extend_quota_error(self, supports_replication, quota_key,
                                expected_exception):
        self._setup_extend_mocks(supports_replication)
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=100)
        new_size = 123
        replica_amount = len(
            db_api.share_replicas_get_all_by_share.return_value)
        value_to_be_extended = new_size - share['size']
        usages = {quota_key: {'reserved': 11, 'in_use': 12}}
        quotas = {quota_key: 13}
        overs = {quota_key: new_size}
        exc = exception.OverQuota(usages=usages, quotas=quotas, overs=overs)
        expected_deltas = {
            'project_id': share['project_id'],
            'gigabytes': value_to_be_extended,
            'user_id': share['user_id'],
            'share_type_id': share['instance']['share_type_id']
        }
        if supports_replication:
            expected_deltas.update(
                {'replica_gigabytes': value_to_be_extended * replica_amount})
        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))

        self.assertRaises(expected_exception,
                          self.api.extend, self.context, share, new_size)
        quota.QUOTAS.reserve.assert_called_once_with(
            mock.ANY, **expected_deltas
        )

    def test_extend_quota_user(self):
        self._setup_extend_mocks(False)
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=100)
        diff_user_context = context.RequestContext(
            user_id='fake2',
            project_id='fake',
            is_admin=False
        )
        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'driver_handles_share_servers': False,
            },
        }
        new_size = 123
        size_increase = int(new_size) - share['size']
        self.mock_object(quota.QUOTAS, 'reserve')
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))

        self.api.extend(diff_user_context, share, new_size)

        quota.QUOTAS.reserve.assert_called_once_with(
            diff_user_context,
            project_id=share['project_id'],
            gigabytes=size_increase,
            share_type_id=None,
            user_id=share['user_id']
        )

    @ddt.data(True, False)
    def test_extend_valid(self, supports_replication):
        self._setup_extend_mocks(supports_replication)
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=100)
        new_size = 123
        size_increase = int(new_size) - share['size']
        replica_amount = len(
            db_api.share_replicas_get_all_by_share.return_value)

        expected_deltas = {
            'project_id': share['project_id'],
            'gigabytes': size_increase,
            'user_id': share['user_id'],
            'share_type_id': share['instance']['share_type_id']
        }
        if supports_replication:
            new_replica_size = size_increase * replica_amount
            expected_deltas.update({'replica_gigabytes': new_replica_size})
        self.mock_object(self.api, 'update')
        self.mock_object(self.api.scheduler_rpcapi, 'extend_share')
        self.mock_object(quota.QUOTAS, 'reserve')
        self.mock_object(share_types, 'get_share_type')
        self.mock_object(share_types, 'provision_filter_on_size')
        self.mock_object(self.api, '_get_request_spec_dict')

        self.api.extend(self.context, share, new_size)

        self.api.update.assert_called_once_with(
            self.context, share, {'status': constants.STATUS_EXTENDING})

        self.api.scheduler_rpcapi.extend_share.assert_called_once_with(
            self.context, share['id'], new_size, mock.ANY, mock.ANY
        )
        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, **expected_deltas)

    def test_shrink_invalid_status(self):
        invalid_status = 'fake'
        share = db_utils.create_share(status=invalid_status)

        self.assertRaises(exception.InvalidShare,
                          self.api.shrink, self.context, share, 123)

    def test_shrink_invalid_task_state(self):
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)

        self.assertRaises(exception.ShareBusyException,
                          self.api.shrink, self.context, share, 123)

    @ddt.data(300, 0, -1)
    def test_shrink_invalid_size(self, new_size):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=200)

        self.assertRaises(exception.InvalidInput,
                          self.api.shrink, self.context, share, new_size)

    @ddt.data(constants.STATUS_AVAILABLE,
              constants.STATUS_SHRINKING_POSSIBLE_DATA_LOSS_ERROR)
    def test_shrink_valid(self, share_status):
        share = db_utils.create_share(status=share_status, size=100)
        new_size = 50
        self.mock_object(self.api, 'update')
        self.mock_object(self.api.share_rpcapi, 'shrink_share')

        self.api.shrink(self.context, share, new_size)

        self.api.update.assert_called_once_with(
            self.context, share, {'status': constants.STATUS_SHRINKING})
        self.api.share_rpcapi.shrink_share.assert_called_once_with(
            self.context, share, new_size
        )

    def test_shrink_with_share_type_size_limit(self):
        share = db_utils.create_share(status=constants.STATUS_AVAILABLE,
                                      size=3)
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=self.sized_sha_type))
        new_size = 1

        self.assertRaises(exception.InvalidInput,
                          self.api.shrink, self.context,
                          share, new_size)

    def test_snapshot_allow_access(self):
        access_to = '1.1.1.1'
        access_type = 'ip'
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'],
                                            status=constants.STATUS_AVAILABLE)
        access = db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id'])
        values = {'share_snapshot_id': snapshot['id'],
                  'access_type': access_type,
                  'access_to': access_to}

        existing_access_check = self.mock_object(
            db_api, 'share_snapshot_check_for_existing_access',
            mock.Mock(return_value=False))
        access_create = self.mock_object(
            db_api, 'share_snapshot_access_create',
            mock.Mock(return_value=access))
        self.mock_object(self.api.share_rpcapi, 'snapshot_update_access')

        out = self.api.snapshot_allow_access(self.context, snapshot,
                                             access_type, access_to)

        self.assertEqual(access, out)
        existing_access_check.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), snapshot['id'],
            access_type, access_to)
        access_create.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), values)

    def test_snapshot_allow_access_instance_exception(self):
        access_to = '1.1.1.1'
        access_type = 'ip'
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])
        existing_access_check = self.mock_object(
            db_api, 'share_snapshot_check_for_existing_access',
            mock.Mock(return_value=False))

        self.assertRaises(exception.InvalidShareSnapshotInstance,
                          self.api.snapshot_allow_access, self.context,
                          snapshot, access_type, access_to)

        existing_access_check.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), snapshot['id'],
            access_type, access_to)

    def test_snapshot_allow_access_access_exists_exception(self):
        access_to = '1.1.1.1'
        access_type = 'ip'
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])
        db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id'], access_to=access_to,
            access_type=access_type)

        existing_access_check = self.mock_object(
            db_api, 'share_snapshot_check_for_existing_access',
            mock.Mock(return_value=True))

        self.assertRaises(exception.ShareSnapshotAccessExists,
                          self.api.snapshot_allow_access, self.context,
                          snapshot, access_type, access_to)

        existing_access_check.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), snapshot['id'],
            access_type, access_to)

    def test_snapshot_deny_access(self):
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'],
                                            status=constants.STATUS_AVAILABLE)
        access = db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id'])
        mapping = {'id': 'fake_id',
                   'state': constants.STATUS_ACTIVE,
                   'access_id': access['id']}

        access_get = self.mock_object(
            db_api, 'share_snapshot_instance_access_get',
            mock.Mock(return_value=mapping))
        access_update_state = self.mock_object(
            db_api, 'share_snapshot_instance_access_update')
        update_access = self.mock_object(self.api.share_rpcapi,
                                         'snapshot_update_access')

        self.api.snapshot_deny_access(self.context, snapshot, access)

        access_get.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), access['id'],
            snapshot['instance']['id'])
        access_update_state.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), access['id'],
            snapshot.instance['id'],
            {'state': constants.ACCESS_STATE_QUEUED_TO_DENY})
        update_access.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), snapshot['instance'])

    def test_snapshot_deny_access_exception(self):
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])
        access = db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id'])

        self.assertRaises(exception.InvalidShareSnapshotInstance,
                          self.api.snapshot_deny_access, self.context,
                          snapshot, access)

    def test_snapshot_access_get_all(self):
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])
        access = []
        access.append(db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id']))

        self.mock_object(
            db_api, 'share_snapshot_access_get_all_for_share_snapshot',
            mock.Mock(return_value=access))

        out = self.api.snapshot_access_get_all(self.context, snapshot)

        self.assertEqual(access, out)

    def test_snapshot_access_get(self):
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])
        access = db_utils.create_snapshot_access(
            share_snapshot_id=snapshot['id'])

        self.mock_object(
            db_api, 'share_snapshot_access_get',
            mock.Mock(return_value=access))

        out = self.api.snapshot_access_get(self.context, access['id'])

        self.assertEqual(access, out)

    def test_snapshot_export_locations_get(self):
        share = db_utils.create_share()
        snapshot = db_utils.create_snapshot(share_id=share['id'])

        self.mock_object(
            db_api, 'share_snapshot_export_locations_get',
            mock.Mock(return_value=''))

        out = self.api.snapshot_export_locations_get(self.context, snapshot)

        self.assertEqual('', out)

    def test_snapshot_export_location_get(self):
        fake_el = '/fake_export_location'

        self.mock_object(
            db_api, 'share_snapshot_instance_export_location_get',
            mock.Mock(return_value=fake_el))

        out = self.api.snapshot_export_location_get(self.context, 'fake_id')

        self.assertEqual(fake_el, out)

    @ddt.data(True, False)
    def test__modify_quotas_for_share_migration(self, new_replication_type):
        extra_specs = (
            {'replication_type': 'readable'} if new_replication_type else {})
        share = db_utils.create_share()
        share_type = db_utils.create_share_type(extra_specs=extra_specs)

        expected_deltas = {
            'project_id': share['project_id'],
            'user_id': share['user_id'],
            'shares': 1,
            'gigabytes': share['size'],
            'share_type_id': share_type['id']
        }

        if new_replication_type:
            expected_deltas.update({
                'share_replicas': 1,
                'replica_gigabytes': share['size'],
            })
        reservations = 'reservations'

        mock_specs_get = self.mock_object(
            self.api, 'get_share_attributes_from_share_type',
            mock.Mock(return_value=extra_specs))
        mock_reserve = self.mock_object(
            quota.QUOTAS, 'reserve', mock.Mock(return_value=reservations))
        mock_commit = self.mock_object(quota.QUOTAS, 'commit')

        self.api._modify_quotas_for_share_migration(
            self.context, share, share_type)

        mock_specs_get.assert_called_once_with(share_type)
        mock_reserve.assert_called_once_with(
            self.context, **expected_deltas)
        mock_commit.assert_called_once_with(
            self.context, reservations, project_id=share['project_id'],
            user_id=share['user_id'], share_type_id=share_type['id'])

    @ddt.data(
        ('replica_gigabytes', exception.ShareReplicaSizeExceedsAvailableQuota),
        ('share_replicas', exception.ShareReplicasLimitExceeded),
        ('gigabytes', exception.ShareSizeExceedsAvailableQuota)
    )
    @ddt.unpack
    def test__modify_quotas_for_share_migration_reservation_failed(
            self, over_resource, expected_exception):
        extra_specs = {'replication_type': 'readable'}
        share = db_utils.create_share()
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        expected_deltas = {
            'project_id': share['project_id'],
            'user_id': share['user_id'],
            'share_replicas': 1,
            'shares': 1,
            'gigabytes': share['size'],
            'replica_gigabytes': share['size'],
            'share_type_id': share_type['id']
        }
        usages = {
            over_resource: {
                'reserved': 'fake',
                'in_use': 'fake'
            }
        }
        quotas = {
            over_resource: 'fake'
        }

        effect_exc = exception.OverQuota(
            overs=[over_resource], usages=usages, quotas=quotas)
        mock_specs_get = self.mock_object(
            self.api, 'get_share_attributes_from_share_type',
            mock.Mock(return_value=extra_specs))
        mock_reserve = self.mock_object(
            quota.QUOTAS, 'reserve', mock.Mock(side_effect=effect_exc))

        self.assertRaises(
            expected_exception,
            self.api._modify_quotas_for_share_migration,
            self.context, share, share_type
        )

        mock_specs_get.assert_called_once_with(share_type)
        mock_reserve.assert_called_once_with(self.context, **expected_deltas)

    @ddt.data({'share_type': True, 'share_net': True, 'dhss': True},
              {'share_type': False, 'share_net': True, 'dhss': True},
              {'share_type': False, 'share_net': False, 'dhss': True},
              {'share_type': True, 'share_net': False, 'dhss': False},
              {'share_type': False, 'share_net': False, 'dhss': False})
    @ddt.unpack
    def test_migration_start(self, share_type, share_net, dhss):
        host = 'fake2@backend#pool'
        service = {'availability_zone_id': 'fake_az_id',
                   'availability_zone': {'name': 'fake_az1'}}
        share_network = None
        share_network_id = None
        if share_net:
            share_network = db_utils.create_share_network(id='fake_net_id')
            share_network_id = share_network['id']

        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': dhss,
            },
        }

        if share_type:
            fake_type_2 = {
                'id': 'fake_type_2_id',
                'extra_specs': {
                    'snapshot_support': False,
                    'create_share_from_snapshot_support': False,
                    'revert_to_snapshot_support': False,
                    'mount_snapshot_support': False,
                    'mount_point_name_support': False,
                    'driver_handles_share_servers': dhss,
                    'availability_zones': 'fake_az1,fake_az2',
                },
            }
        else:
            fake_type_2 = fake_type

        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            host='fake@backend#pool', share_type_id=fake_type['id'],
            share_network_id=share_network_id)

        request_spec = self._get_request_spec_dict(
            share, fake_type_2, self.context, size=0,
            availability_zone_id='fake_az_id',
            share_network_id=share_network_id)

        self.mock_object(self.scheduler_rpcapi, 'migrate_share_to_host')
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))
        self.mock_object(utils, 'validate_service_host')
        self.mock_object(db_api, 'share_instance_update')
        self.mock_object(db_api, 'share_update')
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(share_api.API, '_modify_quotas_for_share_migration')

        if share_type:
            self.api.migration_start(self.context, share, host, False, True,
                                     True, True, True, share_network,
                                     fake_type_2)
        else:
            self.api.migration_start(self.context, share, host, False, True,
                                     True, True, True, share_network, None)

        self.scheduler_rpcapi.migrate_share_to_host.assert_called_once_with(
            self.context, share['id'], host, False, True, True, True, True,
            share_network_id, fake_type_2['id'], request_spec)
        if not share_type:
            share_types.get_share_type.assert_called_once_with(
                self.context, fake_type['id'])
        utils.validate_service_host.assert_called_once_with(
            self.context, 'fake2@backend')
        db_api.service_get_by_args.assert_called_once_with(
            self.context, 'fake2@backend', 'manila-share')
        db_api.share_update.assert_called_once_with(
            self.context, share['id'],
            {'task_state': constants.TASK_STATE_MIGRATION_STARTING})
        db_api.share_instance_update.assert_called_once_with(
            self.context, share.instance['id'],
            {'status': constants.STATUS_MIGRATING})
        if share_type:
            (share_api.API._modify_quotas_for_share_migration.
                assert_called_once_with(self.context, share, fake_type_2))

    def test_migration_start_with_new_share_type_limit(self):
        host = 'fake2@backend#pool'
        self.mock_object(utils, 'validate_service_host')
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            size=1)
        self.assertRaises(exception.InvalidInput,
                          self.api.migration_start,
                          self.context,
                          share, host, False, True,
                          True, True, True, None,
                          self.sized_sha_type)

    def test_migration_start_destination_az_unsupported(self):
        host = 'fake2@backend#pool'
        host_without_pool = host.split('#')[0]
        service = {'availability_zone_id': 'fake_az_id',
                   'availability_zone': {'name': 'fake_az3'}}
        share_network = db_utils.create_share_network(id='fake_net_id')
        share_network_id = share_network['id']
        existing_share_type = {
            'id': '4b5b0920-a294-401b-bb7d-c55b425e1cad',
            'name': 'fake_type_1',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': 'true',
                'availability_zones': 'fake_az3'
            },
        }
        new_share_type = {
            'id': 'fa844ae2-494d-4da9-95e7-37ac6a26f635',
            'name': 'fake_type_2',
            'extra_specs': {
                'snapshot_support': False,
                'create_share_from_snapshot_support': False,
                'revert_to_snapshot_support': False,
                'mount_snapshot_support': False,
                'mount_point_name_support': False,
                'driver_handles_share_servers': 'true',
                'availability_zones': 'fake_az1,fake_az2',
            },
        }
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            host='fake@backend#pool', share_type_id=existing_share_type['id'],
            share_network_id=share_network_id)
        self.mock_object(self.api, '_get_request_spec_dict')
        self.mock_object(self.scheduler_rpcapi, 'migrate_share_to_host')
        self.mock_object(share_types, 'get_share_type')
        self.mock_object(utils, 'validate_service_host')
        self.mock_object(db_api, 'share_instance_update')
        self.mock_object(db_api, 'share_update')
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(share_api.API, '_modify_quotas_for_share_migration')

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_start,
                          self.context, share, host, False, True, True,
                          True, False, new_share_network=share_network,
                          new_share_type=new_share_type)
        utils.validate_service_host.assert_called_once_with(
            self.context, host_without_pool)
        share_types.get_share_type.assert_not_called()
        db_api.share_update.assert_not_called()
        db_api.service_get_by_args.assert_called_once_with(
            self.context, host_without_pool, 'manila-share')
        self.api._get_request_spec_dict.assert_not_called()
        db_api.share_instance_update.assert_not_called()
        self.scheduler_rpcapi.migrate_share_to_host.assert_not_called()

    @ddt.data({'force_host_assisted': True, 'writable': True,
               'preserve_metadata': False, 'preserve_snapshots': False,
               'nondisruptive': False},
              {'force_host_assisted': True, 'writable': False,
               'preserve_metadata': True, 'preserve_snapshots': False,
               'nondisruptive': False},
              {'force_host_assisted': True, 'writable': False,
               'preserve_metadata': False, 'preserve_snapshots': True,
               'nondisruptive': False},
              {'force_host_assisted': True, 'writable': False,
               'preserve_metadata': False, 'preserve_snapshots': False,
               'nondisruptive': True})
    @ddt.unpack
    def test_migration_start_invalid_host_and_driver_assisted_params(
            self, force_host_assisted, writable, preserve_metadata,
            preserve_snapshots, nondisruptive):

        self.assertRaises(
            exception.InvalidInput, self.api.migration_start, self.context,
            'some_share', 'some_host', force_host_assisted, preserve_metadata,
            writable, preserve_snapshots, nondisruptive)

    @ddt.data(True, False)
    def test_migration_start_invalid_share_network_type_combo(self, dhss):
        host = 'fake2@backend#pool'
        share_network = None
        if not dhss:
            share_network = db_utils.create_share_network(id='fake_net_id')

        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': False,
                'driver_handles_share_servers': not dhss,
            },
        }

        fake_type_2 = {
            'id': 'fake_type_2_id',
            'extra_specs': {
                'snapshot_support': False,
                'driver_handles_share_servers': dhss,
            },
        }

        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            host='fake@backend#pool', share_type_id=fake_type['id'])

        self.mock_object(utils, 'validate_service_host')
        self.mock_object(share_api.API, '_modify_quotas_for_share_migration')

        self.assertRaises(
            exception.InvalidInput, self.api.migration_start, self.context,
            share, host, False, True, True, True, True, share_network,
            fake_type_2)

        utils.validate_service_host.assert_called_once_with(
            self.context, 'fake2@backend')

    def test_migration_start_status_unavailable(self):
        host = 'fake2@backend#pool'
        share = db_utils.create_share(
            status=constants.STATUS_ERROR)

        self.assertRaises(exception.InvalidShare, self.api.migration_start,
                          self.context, share, host, False, True, True, True,
                          True)

    def test_migration_start_access_rules_status_error(self):
        host = 'fake2@backend#pool'
        instance = db_utils.create_share_instance(
            share_id='fake_share_id',
            access_rules_status=constants.STATUS_ERROR,
            status=constants.STATUS_AVAILABLE)
        share = db_utils.create_share(
            id='fake_share_id',
            instances=[instance])

        self.assertRaises(exception.InvalidShare, self.api.migration_start,
                          self.context, share, host, False, True, True, True,
                          True)

    def test_migration_start_task_state_invalid(self):
        host = 'fake2@backend#pool'
        share = db_utils.create_share(
            status=constants.STATUS_AVAILABLE,
            task_state=constants.TASK_STATE_MIGRATION_IN_PROGRESS)

        self.assertRaises(exception.ShareBusyException,
                          self.api.migration_start,
                          self.context, share, host, False, True, True, True,
                          True)

    def test_migration_start_host_assisted_with_snapshots(self):
        host = 'fake2@backend#pool'
        share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE)
        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=True))

        self.assertRaises(exception.Conflict, self.api.migration_start,
                          self.context, share, host, True, False, False, False,
                          False)

    def test_migration_start_with_snapshots(self):
        host = 'fake2@backend#pool'

        fake_type = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': True,
                'driver_handles_share_servers': False,
            },
        }

        service = {'availability_zone_id': 'fake_az_id',
                   'availability_zone': {'name': 'fake_az'}}
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(utils, 'validate_service_host')
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type))

        share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=fake_type['id'])

        request_spec = self._get_request_spec_dict(
            share, fake_type, self.context, availability_zone_id='fake_az_id')

        self.api.migration_start(self.context, share, host, False, True, True,
                                 True, True)

        self.scheduler_rpcapi.migrate_share_to_host.assert_called_once_with(
            self.context, share['id'], host, False, True, True, True, True,
            None, 'fake_type_id', request_spec)

    def test_migration_start_has_replicas(self):
        host = 'fake2@backend#pool'
        share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            replication_type='dr')
        for i in range(1, 4):
            db_utils.create_share_replica(
                share_id=share['id'], replica_state='in_sync')
        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=True))
        mock_log = self.mock_object(share_api, 'LOG')
        mock_snapshot_get_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share')
        # Share was updated after adding replicas, grabbing it again.
        share = db_api.share_get(self.context, share['id'])

        self.assertRaises(exception.Conflict, self.api.migration_start,
                          self.context, share, host, False, True, True, True,
                          True)
        self.assertTrue(mock_log.error.called)
        self.assertFalse(mock_snapshot_get_call.called)

    def test_migration_start_is_member_of_group(self):
        group = db_utils.create_share_group()
        share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_group_id=group['id'])
        mock_log = self.mock_object(share_api, 'LOG')

        self.assertRaises(exception.InvalidShare, self.api.migration_start,
                          self.context, share, 'fake_host', False, True, True,
                          True, True)
        self.assertTrue(mock_log.error.called)

    def test_migration_start_invalid_host(self):
        host = 'fake@backend#pool'
        share = db_utils.create_share(
            host='fake2@backend', status=constants.STATUS_AVAILABLE)

        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=False))

        self.assertRaises(exception.ServiceNotFound,
                          self.api.migration_start,
                          self.context, share, host, False, True, True, True,
                          True)

    @ddt.data({'dhss': True, 'new_share_network_id': 'fake_net_id',
               'new_share_type_id': 'fake_type_id'},
              {'dhss': False, 'new_share_network_id': None,
               'new_share_type_id': 'fake_type_id'},
              {'dhss': True, 'new_share_network_id': 'fake_net_id',
               'new_share_type_id': None})
    @ddt. unpack
    def test_migration_start_same_data_as_source(
            self, dhss, new_share_network_id, new_share_type_id):
        host = 'fake@backend#pool'

        fake_type_src = {
            'id': 'fake_type_id',
            'extra_specs': {
                'snapshot_support': True,
                'driver_handles_share_servers': True,
            },
        }

        new_share_type_param = None
        if new_share_type_id:
            new_share_type_param = {
                'id': new_share_type_id,
                'extra_specs': {
                    'snapshot_support': True,
                    'driver_handles_share_servers': dhss,
                },
            }

        new_share_net_param = None
        if new_share_network_id:
            new_share_net_param = db_utils.create_share_network(
                id=new_share_network_id)

        share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=fake_type_src['id'],
            share_network_id=new_share_network_id)

        self.mock_object(utils, 'validate_service_host')
        self.mock_object(db_api, 'share_update')
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=fake_type_src))

        result = self.api.migration_start(
            self.context, share, host, False, True, True, True, True,
            new_share_net_param, new_share_type_param)

        self.assertEqual(200, result)

        db_api.share_update.assert_called_once_with(
            self.context, share['id'],
            {'task_state': constants.TASK_STATE_MIGRATION_SUCCESS})

    @ddt.data({}, {'replication_type': None})
    def test_create_share_replica_invalid_share_type(self, attributes):
        share = fakes.fake_share(id='FAKE_SHARE_ID', **attributes)
        mock_request_spec_call = self.mock_object(
            self.api, 'create_share_instance_and_get_request_spec')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')
        mock_scheduler_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')

        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_replica,
                          self.context, share)
        self.assertFalse(mock_request_spec_call.called)
        self.assertFalse(mock_db_update_call.called)
        self.assertFalse(mock_scheduler_rpcapi_call.called)

    def test_create_share_replica_busy_share(self):
        share = fakes.fake_share(
            id='FAKE_SHARE_ID',
            task_state='doing_something_real_important',
            is_busy=True,
            replication_type='dr')
        mock_request_spec_call = self.mock_object(
            self.api, 'create_share_instance_and_get_request_spec')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')
        mock_scheduler_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')

        self.assertRaises(exception.ShareBusyException,
                          self.api.create_share_replica,
                          self.context, share)
        self.assertFalse(mock_request_spec_call.called)
        self.assertFalse(mock_db_update_call.called)
        self.assertFalse(mock_scheduler_rpcapi_call.called)

    @ddt.data(None, [])
    def test_create_share_replica_no_active_replica(self, active_replicas):
        share = fakes.fake_share(
            id='FAKE_SHARE_ID', replication_type='dr')
        mock_request_spec_call = self.mock_object(
            self.api, 'create_share_instance_and_get_request_spec')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')
        mock_scheduler_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value=active_replicas))

        self.assertRaises(exception.ReplicationException,
                          self.api.create_share_replica,
                          self.context, share)
        self.assertFalse(mock_request_spec_call.called)
        self.assertFalse(mock_db_update_call.called)
        self.assertFalse(mock_scheduler_rpcapi_call.called)

    @ddt.data(None, 'fake-share-type')
    def test_create_share_replica_type_doesnt_support_AZ(self, st_name):
        share_type = fakes.fake_share_type(
            name=st_name,
            extra_specs={'availability_zones': 'zone 1,zone 3'})
        share = fakes.fake_share(
            id='FAKE_SHARE_ID', replication_type='dr',
            availability_zone='zone 2')
        share['instance'].update({
            'share_type': share_type,
            'share_type_id': '359b9851-2bd5-4404-89a9-5cd22bbc5fb9',
        })
        mock_request_spec_call = self.mock_object(
            self.api, 'create_share_instance_and_get_request_spec')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')
        mock_scheduler_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value=mock.Mock(
                             return_value={'host': 'fake_ar_host'})))

        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_replica,
                          self.context, share, availability_zone='zone 2')
        share_types.get_share_type.assert_called_once_with(
            self.context, '359b9851-2bd5-4404-89a9-5cd22bbc5fb9')
        self.assertFalse(mock_request_spec_call.called)
        self.assertFalse(mock_db_update_call.called)
        self.assertFalse(mock_scheduler_rpcapi_call.called)

    def test_create_share_replica_subnet_not_found(self):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        extra_specs = {
            'availability_zones': 'FAKE_AZ,FAKE_AZ2',
            'replication_type': constants.REPLICATION_TYPE_DR
        }
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        az_name = 'FAKE_AZ'
        share = db_utils.create_share(
            id=replica['share_id'], replication_type='dr')
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value=mock.Mock(
                             return_value={'host': 'fake_ar_host'})))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        self.mock_object(db_api, 'availability_zone_get')
        self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(return_value=None))

        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_replica,
                          self.context,
                          share,
                          availability_zone=az_name,
                          share_network_id='fake_id')
        (db_api.share_replicas_get_available_active_replica
            .assert_called_once_with(self.context, share['id']))
        self.assertTrue(share_types.get_share_type.called)
        db_api.availability_zone_get.assert_called_once_with(
            self.context, az_name)
        self.assertTrue(
            (db_api.share_network_subnets_get_all_by_availability_zone_id.
             called))

    def test_create_share_replica_az_not_found(self):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        extra_specs = {
            'availability_zones': 'FAKE_AZ,FAKE_AZ2',
            'replication_type': constants.REPLICATION_TYPE_DR
        }
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        az_name = 'FAKE_AZ'
        share = db_utils.create_share(
            id=replica['share_id'], replication_type='dr')
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value=mock.Mock(
                             return_value={'host': 'fake_ar_host'})))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        side_effect = exception.AvailabilityZoneNotFound(id=az_name)
        self.mock_object(db_api, 'availability_zone_get',
                         mock.Mock(side_effect=side_effect))

        self.assertRaises(exception.InvalidInput,
                          self.api.create_share_replica,
                          self.context,
                          share,
                          availability_zone=az_name,
                          share_network_id='fake_id')
        (db_api.share_replicas_get_available_active_replica
            .assert_called_once_with(self.context, share['id']))
        self.assertTrue(share_types.get_share_type.called)
        db_api.availability_zone_get.assert_called_once_with(
            self.context, az_name)

    @ddt.data(
        {'availability_zones': '', 'compatible_azs_name': ['fake_az_1'],
         'compatible_azs_multiple': []},
        {'availability_zones': 'fake_az_1,fake_az_2',
         'compatible_azs_name': ['fake_az_2'], 'compatible_azs_multiple': []}
    )
    @ddt.unpack
    def test_create_share_replica_azs_with_subnets(self, availability_zones,
                                                   compatible_azs_name,
                                                   compatible_azs_multiple):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        share_network_id = 'fake_share_network_id'
        extra_specs = {
            'availability_zones': availability_zones,
            'replication_type': constants.REPLICATION_TYPE_DR
        }
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        share = db_utils.create_share(
            id=replica['share_id'], replication_type='dr',
            share_type_id=share_type['id'])
        cast_rules_to_readonly = (
            share['replication_type'] == constants.REPLICATION_TYPE_READABLE)
        fake_replica = fakes.fake_replica(id=replica['id'])
        fake_request_spec = fakes.fake_replica_request_spec()

        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value={'host': 'fake_ar_host'}))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        mock_get_all_az_subnet = self.mock_object(
            self.api, '_get_all_availability_zones_with_subnets',
            mock.Mock(return_value=[compatible_azs_name,
                                    compatible_azs_multiple]))

        if availability_zones == '':
            expected_azs = compatible_azs_name
        else:
            availability_zones = [
                t for t in availability_zones.split(',') if availability_zones]
            expected_azs = (
                [az for az in availability_zones if az in compatible_azs_name])

        self.mock_object(
            self.api, 'create_share_instance_and_get_request_spec',
            mock.Mock(return_value=(fake_request_spec, fake_replica)))
        self.mock_object(db_api, 'share_replica_update')
        mock_snapshot_get_all_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share',
            mock.Mock(return_value=[]))
        mock_sched_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')

        self.api.create_share_replica(
            self.context, share, share_network_id=share_network_id)

        (db_api.share_replicas_get_available_active_replica
            .assert_called_once_with(self.context, share['id']))
        self.assertTrue(share_types.get_share_type.called)
        mock_get_all_az_subnet.assert_called_once_with(
            self.context, share_network_id
        )
        (self.api.create_share_instance_and_get_request_spec.
         assert_called_once_with(
             self.context, share, availability_zone=None,
             share_network_id=share_network_id, share_type_id=share_type['id'],
             availability_zones=expected_azs,
             az_request_multiple_subnet_support_map={},
             cast_rules_to_readonly=cast_rules_to_readonly))
        db_api.share_replica_update.assert_called_once()
        mock_snapshot_get_all_call.assert_called_once()
        mock_sched_rpcapi_call.assert_called_once()

    @ddt.data(
        {'availability_zones': '', 'compatible_azs_name': [],
         'compatible_azs_multiple': []},
        {'availability_zones': 'fake_az_1,fake_az_2',
         'compatible_azs_name': ['fake_az_3'], 'compatible_azs_multiple': []}
    )
    @ddt.unpack
    def test_create_share_replica_azs_with_subnets_invalid_input(
            self, availability_zones,
            compatible_azs_name, compatible_azs_multiple):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        share_network_id = 'fake_share_network_id'
        extra_specs = {
            'availability_zones': availability_zones,
            'replication_type': constants.REPLICATION_TYPE_DR
        }
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        share = db_utils.create_share(
            id=replica['share_id'], replication_type='dr',
            share_type_id=share_type['id'])

        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value={'host': 'fake_ar_host'}))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        mock_get_all_az_subnet = self.mock_object(
            self.api, '_get_all_availability_zones_with_subnets',
            mock.Mock(return_value=[compatible_azs_name,
                                    compatible_azs_multiple]))

        self.assertRaises(
            exception.InvalidInput,
            self.api.create_share_replica,
            self.context, share, share_network_id=share_network_id)

        (db_api.share_replicas_get_available_active_replica
         .assert_called_once_with(self.context, share['id']))
        self.assertTrue(share_types.get_share_type.called)
        mock_get_all_az_subnet.assert_called_once_with(
            self.context, share_network_id
        )

    @ddt.data({'has_snapshots': True,
               'az_id': {},
               'extra_specs': {
                   'replication_type': constants.REPLICATION_TYPE_DR,
               },
               'share_network_id': None},
              {'has_snapshots': False,
               'az_id': {},
               'extra_specs': {
                   'availability_zones': 'FAKE_AZ,FAKE_AZ2',
                   'replication_type': constants.REPLICATION_TYPE_DR,
               },
               'share_network_id': None},
              {'has_snapshots': True,
               'az_id': {},
               'extra_specs': {
                   'availability_zones': 'FAKE_AZ,FAKE_AZ2',
                   'replication_type': constants.REPLICATION_TYPE_READABLE,
               },
               'share_network_id': None},
              {'has_snapshots': False,
               'az_id': {'fake_zone_id': False},
               'extra_specs': {
                   'replication_type': constants.REPLICATION_TYPE_READABLE,
               },
               'share_network_id': 'fake_sn_id'})
    @ddt.unpack
    def test_create_share_replica(self, has_snapshots, extra_specs,
                                  share_network_id, az_id):
        subnets = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_network_id')
        az = {'id': 'fake_zone_id'}
        request_spec = fakes.fake_replica_request_spec()
        replication_type = extra_specs['replication_type']
        replica = request_spec['share_instance_properties']
        share_type = db_utils.create_share_type(extra_specs=extra_specs)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        share = db_utils.create_share(
            id=replica['share_id'], replication_type=replication_type,
            share_type_id=share_type['id'])
        snapshots = (
            [fakes.fake_snapshot(), fakes.fake_snapshot()]
            if has_snapshots else []
        )
        cast_rules_to_readonly = (
            replication_type == constants.REPLICATION_TYPE_READABLE)

        fake_replica = fakes.fake_replica(id=replica['id'])
        fake_request_spec = fakes.fake_replica_request_spec()
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value={'host': 'fake_ar_host'}))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        self.mock_object(db_api, 'availability_zone_get',
                         mock.Mock(return_value=az))
        self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(return_value=[subnets]))
        self.mock_object(
            share_api.API, 'create_share_instance_and_get_request_spec',
            mock.Mock(return_value=(fake_request_spec, fake_replica)))
        self.mock_object(db_api, 'share_replica_update')
        mock_sched_rpcapi_call = self.mock_object(
            self.api.scheduler_rpcapi, 'create_share_replica')
        mock_snapshot_get_all_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share',
            mock.Mock(return_value=snapshots))
        mock_snapshot_instance_create_call = self.mock_object(
            db_api, 'share_snapshot_instance_create')
        expected_snap_instance_create_call_count = 2 if has_snapshots else 0

        result = self.api.create_share_replica(
            self.context, share, availability_zone='FAKE_AZ',
            share_network_id=share_network_id)

        self.assertTrue(mock_sched_rpcapi_call.called)
        self.assertEqual(replica, result)
        share_types.get_share_type.assert_called_once_with(
            self.context, share_type['id'])
        mock_snapshot_get_all_call.assert_called_once_with(
            self.context, fake_replica['share_id'])
        self.assertEqual(expected_snap_instance_create_call_count,
                         mock_snapshot_instance_create_call.call_count)
        expected_azs = extra_specs.get('availability_zones', '')
        expected_azs = expected_azs.split(',') if expected_azs else []
        (share_api.API.create_share_instance_and_get_request_spec.
         assert_called_once_with(
             self.context, share, availability_zone='FAKE_AZ',
             share_network_id=share_network_id, share_type_id=share_type['id'],
             availability_zones=expected_azs,
             az_request_multiple_subnet_support_map=az_id,
             cast_rules_to_readonly=cast_rules_to_readonly))

    def test_delete_last_active_replica(self):
        fake_replica = fakes.fake_replica(
            share_id='FAKE_SHARE_ID',
            replica_state=constants.REPLICA_STATE_ACTIVE)
        self.mock_object(db_api, 'share_replicas_get_all_by_share',
                         mock.Mock(return_value=[fake_replica]))
        mock_log = self.mock_object(share_api.LOG, 'info')

        self.assertRaises(
            exception.ReplicationException, self.api.delete_share_replica,
            self.context, fake_replica)
        self.assertFalse(mock_log.called)

    @ddt.data(True, False)
    def test_delete_share_replica_no_host(self, has_snapshots):
        snapshots = [{'id': 'xyz'}, {'id': 'abc'}, {'id': 'pqr'}]
        snapshots = snapshots if has_snapshots else []
        replica = fakes.fake_replica('FAKE_ID', host='')
        mock_sched_rpcapi_call = self.mock_object(
            self.share_rpcapi, 'delete_share_replica')
        mock_db_replica_delete_call = self.mock_object(
            db_api, 'share_replica_delete')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')
        mock_snapshot_get_call = self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=snapshots))
        mock_snapshot_instance_delete_call = self.mock_object(
            db_api, 'share_snapshot_instance_delete')

        self.api.delete_share_replica(self.context, replica)

        self.assertFalse(mock_sched_rpcapi_call.called)
        mock_db_replica_delete_call.assert_called_once_with(
            self.context, replica['id'])
        mock_db_update_call.assert_called_once_with(
            self.context, replica['id'],
            {'status': constants.STATUS_DELETING, 'terminated_at': mock.ANY})
        mock_snapshot_get_call.assert_called_once_with(
            self.context, {'share_instance_ids': replica['id']})
        self.assertEqual(
            len(snapshots), mock_snapshot_instance_delete_call.call_count)

    @ddt.data(True, False)
    def test_delete_share_replica(self, force):
        replica = fakes.fake_replica('FAKE_ID', host='HOSTA@BackendB#PoolC')
        mock_sched_rpcapi_call = self.mock_object(
            self.share_rpcapi, 'delete_share_replica')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')

        self.api.delete_share_replica(self.context, replica, force=force)

        mock_sched_rpcapi_call.assert_called_once_with(
            self.context, replica, force=force)
        mock_db_update_call.assert_called_once_with(
            self.context, replica['id'],
            {'status': constants.STATUS_DELETING,
             'terminated_at': mock.ANY})

    @ddt.data(constants.STATUS_CREATING, constants.STATUS_DELETING,
              constants.STATUS_ERROR, constants.STATUS_EXTENDING,
              constants.STATUS_REPLICATION_CHANGE, constants.STATUS_MANAGING,
              constants.STATUS_ERROR_DELETING)
    def test_promote_share_replica_non_available_status(self, status):
        replica = fakes.fake_replica(
            status=status, replica_state=constants.REPLICA_STATE_IN_SYNC)
        mock_rpcapi_promote_share_replica_call = self.mock_object(
            self.share_rpcapi, 'promote_share_replica')

        self.assertRaises(exception.ReplicationException,
                          self.api.promote_share_replica,
                          self.context,
                          replica)
        self.assertFalse(mock_rpcapi_promote_share_replica_call.called)

    @ddt.data(constants.REPLICA_STATE_OUT_OF_SYNC, constants.STATUS_ERROR)
    def test_promote_share_replica_out_of_sync_non_admin(self, replica_state):
        fake_user_context = context.RequestContext(
            user_id=None, project_id=None, is_admin=False,
            read_deleted='no', overwrite=False)
        replica = fakes.fake_replica(
            status=constants.STATUS_AVAILABLE,
            replica_state=replica_state)
        mock_rpcapi_promote_share_replica_call = self.mock_object(
            self.share_rpcapi, 'promote_share_replica')

        self.assertRaises(exception.AdminRequired,
                          self.api.promote_share_replica,
                          fake_user_context,
                          replica)
        self.assertFalse(mock_rpcapi_promote_share_replica_call.called)

    @ddt.data(constants.REPLICA_STATE_OUT_OF_SYNC, constants.STATUS_ERROR)
    def test_promote_share_replica_admin_authorized(self, replica_state):
        replica = fakes.fake_replica(
            status=constants.STATUS_AVAILABLE,
            replica_state=replica_state, host='HOSTA@BackendB#PoolC')
        self.mock_object(db_api, 'share_replica_get',
                         mock.Mock(return_value=replica))
        mock_rpcapi_promote_share_replica_call = self.mock_object(
            self.share_rpcapi, 'promote_share_replica')
        mock_db_update_call = self.mock_object(db_api, 'share_replica_update')

        retval = self.api.promote_share_replica(
            self.context, replica)

        self.assertEqual(replica, retval)
        mock_db_update_call.assert_called_once_with(
            self.context, replica['id'],
            {'status': constants.STATUS_REPLICATION_CHANGE})
        mock_rpcapi_promote_share_replica_call.assert_called_once_with(
            self.context, replica, quiesce_wait_time=None)

    def test_promote_share_replica(self):
        replica = fakes.fake_replica('FAKE_ID', host='HOSTA@BackendB#PoolC')
        self.mock_object(db_api, 'share_replica_get',
                         mock.Mock(return_value=replica))
        self.mock_object(db_api, 'share_replica_update')
        mock_sched_rpcapi_call = self.mock_object(
            self.share_rpcapi, 'promote_share_replica')

        result = self.api.promote_share_replica(self.context, replica)

        mock_sched_rpcapi_call.assert_called_once_with(
            self.context, replica, quiesce_wait_time=None)
        self.assertEqual(replica, result)

    def test_update_share_replica_no_host(self):
        replica = fakes.fake_replica('FAKE_ID')
        replica['host'] = None
        mock_rpcapi_update_share_replica_call = self.mock_object(
            self.share_rpcapi, 'update_share_replica')

        self.assertRaises(exception.InvalidHost,
                          self.api.update_share_replica,
                          self.context,
                          replica)
        self.assertFalse(mock_rpcapi_update_share_replica_call.called)

    def test_update_share_replica(self):
        replica = fakes.fake_replica('FAKE_ID', host='HOSTA@BackendB#PoolC')
        mock_rpcapi_update_share_replica_call = self.mock_object(
            self.share_rpcapi, 'update_share_replica')

        retval = self.api.update_share_replica(self.context, replica)

        self.assertTrue(mock_rpcapi_update_share_replica_call.called)
        self.assertIsNone(retval)

    @ddt.data({'overs': {'replica_gigabytes': 'fake'},
               'expected_exception':
                   exception.ShareReplicaSizeExceedsAvailableQuota},
              {'overs': {'share_replicas': 'fake'},
               'expected_exception': exception.ShareReplicasLimitExceeded})
    @ddt.unpack
    def test_create_share_replica_over_quota(self, overs, expected_exception):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        share = db_utils.create_share(replication_type='dr',
                                      id=replica['share_id'])
        share_type = db_utils.create_share_type()
        share_type = db_api.share_type_get(self.context, share_type['id'])
        usages = {'replica_gigabytes': {'reserved': 5, 'in_use': 5},
                  'share_replicas': {'reserved': 5, 'in_use': 5}}
        quotas = {'share_replicas': 5, 'replica_gigabytes': 5}
        exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas)

        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value={'host': 'fake_ar_host'}))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))

        self.assertRaises(
            expected_exception,
            self.api.create_share_replica,
            self.context,
            share
        )
        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, share_type_id=share_type['id'],
            share_replicas=1, replica_gigabytes=share['size'])
        (db_api.share_replicas_get_available_active_replica
         .assert_called_once_with(self.context, share['id']))
        share_types.get_share_type.assert_called_once_with(
            self.context, share['instance']['share_type_id'])

    def test_create_share_replica_error_on_quota_commit(self):
        request_spec = fakes.fake_replica_request_spec()
        replica = request_spec['share_instance_properties']
        share_type = db_utils.create_share_type()
        fake_replica = fakes.fake_replica(id=replica['id'])
        share = db_utils.create_share(replication_type='dr',
                                      id=fake_replica['share_id'],
                                      share_type_id=share_type['id'])
        share_network_id = None
        share_type = db_api.share_type_get(self.context, share_type['id'])
        expected_azs = share_type['extra_specs'].get('availability_zones', '')
        expected_azs = expected_azs.split(',') if expected_azs else []

        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(quota.QUOTAS, 'commit',
                         mock.Mock(side_effect=exception.QuotaError('fake')))
        self.mock_object(db_api, 'share_replica_delete')
        self.mock_object(quota.QUOTAS, 'rollback')
        self.mock_object(db_api, 'share_replicas_get_available_active_replica',
                         mock.Mock(return_value={'host': 'fake_ar_host'}))
        self.mock_object(share_types, 'get_share_type',
                         mock.Mock(return_value=share_type))
        self.mock_object(
            share_api.API, 'create_share_instance_and_get_request_spec',
            mock.Mock(return_value=(request_spec, fake_replica)))

        self.assertRaises(
            exception.QuotaError,
            self.api.create_share_replica,
            self.context,
            share
        )

        db_api.share_replica_delete.assert_called_once_with(
            self.context, replica['id'], need_to_update_usages=False)
        quota.QUOTAS.rollback.assert_called_once_with(
            self.context, reservation,
            share_type_id=share['instance']['share_type_id'])
        (db_api.share_replicas_get_available_active_replica.
         assert_called_once_with(self.context, share['id']))
        share_types.get_share_type.assert_called_once_with(
            self.context, share['instance']['share_type_id'])
        (share_api.API.create_share_instance_and_get_request_spec.
         assert_called_once_with(self.context, share, availability_zone=None,
                                 share_network_id=share_network_id,
                                 share_type_id=share_type['id'],
                                 availability_zones=expected_azs,
                                 az_request_multiple_subnet_support_map={},
                                 cast_rules_to_readonly=False))

    def test_migration_complete(self):

        instance1 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING)
        instance2 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_COMPLETED,
            instances=[instance1, instance2])

        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(self.api.share_rpcapi, 'migration_complete')

        self.api.migration_complete(self.context, share)

        self.api.share_rpcapi.migration_complete.assert_called_once_with(
            self.context, instance1, instance2['id'])

    @ddt.data(constants.TASK_STATE_DATA_COPYING_STARTING,
              constants.TASK_STATE_MIGRATION_SUCCESS,
              constants.TASK_STATE_DATA_COPYING_IN_PROGRESS,
              constants.TASK_STATE_MIGRATION_ERROR,
              constants.TASK_STATE_MIGRATION_CANCELLED,
              None)
    def test_migration_complete_task_state_invalid(self, task_state):

        share = db_utils.create_share(
            id='fake_id',
            task_state=task_state)

        self.assertRaises(exception.InvalidShare, self.api.migration_complete,
                          self.context, share)

    def test_migration_complete_status_invalid(self):

        instance1 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_ERROR)
        instance2 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_ERROR)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_COMPLETED,
            instances=[instance1, instance2])

        self.assertRaises(exception.ShareMigrationFailed,
                          self.api.migration_complete, self.context,
                          share)

    @ddt.data(None, Exception('fake'))
    def test_migration_cancel(self, exc):

        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_IN_PROGRESS)
        services = ['fake_service']

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_all_by_topic',
                         mock.Mock(return_value=services))
        self.mock_object(data_rpc.DataAPI, 'data_copy_cancel',
                         mock.Mock(side_effect=[exc]))

        if exc:
            self.assertRaises(
                exception.ShareMigrationError, self.api.migration_cancel,
                self.context, share)
        else:
            self.api.migration_cancel(self.context, share)

        data_rpc.DataAPI.data_copy_cancel.assert_called_once_with(
            self.context, share['id'])
        db_api.service_get_all_by_topic.assert_called_once_with(
            self.context, 'manila-data')

    def test_migration_cancel_service_down(self):
        service = 'fake_service'
        instance1 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING)
        instance2 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_IN_PROGRESS,
            instances=[instance1, instance2])

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=False))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(db_api, 'service_get_all_by_topic',
                         mock.Mock(return_value=service))

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_cancel, self.context, share)

    def test_migration_cancel_driver(self):

        service = 'fake_service'
        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])

        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(self.api.share_rpcapi, 'migration_cancel')
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))

        self.api.migration_cancel(self.context, share)

        self.api.share_rpcapi.migration_cancel.assert_called_once_with(
            self.context, instance1, instance2['id'])
        db_api.service_get_by_args.assert_called_once_with(
            self.context, instance1['host'], 'manila-share')

    @ddt.data(constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
              constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
              constants.TASK_STATE_DATA_COPYING_COMPLETED)
    def test_migration_cancel_driver_service_down(self, task_state):

        service = 'fake_service'
        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=task_state,
            instances=[instance1, instance2])

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=False))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_cancel, self.context, share)

    @ddt.data(constants.TASK_STATE_DATA_COPYING_STARTING,
              constants.TASK_STATE_MIGRATION_SUCCESS,
              constants.TASK_STATE_MIGRATION_ERROR,
              constants.TASK_STATE_MIGRATION_CANCELLED,
              None)
    def test_migration_cancel_task_state_invalid(self, task_state):

        share = db_utils.create_share(
            id='fake_id',
            task_state=task_state)

        self.assertRaises(exception.InvalidShare, self.api.migration_cancel,
                          self.context, share)

    @ddt.data({'total_progress': 50}, Exception('fake'))
    def test_migration_get_progress(self, expected):

        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_IN_PROGRESS)
        services = ['fake_service']

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_all_by_topic',
                         mock.Mock(return_value=services))
        self.mock_object(data_rpc.DataAPI, 'data_copy_get_progress',
                         mock.Mock(side_effect=[expected]))

        if not isinstance(expected, Exception):
            result = self.api.migration_get_progress(self.context, share)
            self.assertEqual(expected, result)
        else:
            self.assertRaises(
                exception.ShareMigrationError, self.api.migration_get_progress,
                self.context, share)

        data_rpc.DataAPI.data_copy_get_progress.assert_called_once_with(
            self.context, share['id'])
        db_api.service_get_all_by_topic.assert_called_once_with(
            self.context, 'manila-data')

    def test_migration_get_progress_service_down(self):
        instance1 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING)
        instance2 = db_utils.create_share_instance(
            share_id='fake_id', status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_DATA_COPYING_IN_PROGRESS,
            instances=[instance1, instance2])
        services = ['fake_service']

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=False))
        self.mock_object(db_api, 'service_get_all_by_topic',
                         mock.Mock(return_value=services))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_get_progress, self.context, share)

    def test_migration_get_progress_driver(self):

        expected = {'total_progress': 50}
        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])
        service = 'fake_service'

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(self.api.share_rpcapi, 'migration_get_progress',
                         mock.Mock(return_value=expected))

        result = self.api.migration_get_progress(self.context, share)

        self.assertEqual(expected, result)

        self.api.share_rpcapi.migration_get_progress.assert_called_once_with(
            self.context, instance1, instance2['id'])
        db_api.service_get_by_args.assert_called_once_with(
            self.context, instance1['host'], 'manila-share')

    def test_migration_get_progress_driver_error(self):

        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])
        service = 'fake_service'

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(self.api.share_rpcapi, 'migration_get_progress',
                         mock.Mock(side_effect=Exception('fake')))

        self.assertRaises(exception.ShareMigrationError,
                          self.api.migration_get_progress, self.context, share)

        self.api.share_rpcapi.migration_get_progress.assert_called_once_with(
            self.context, instance1, instance2['id'])

    def test_migration_get_progress_driver_service_down(self):
        service = 'fake_service'
        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=False))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_get_progress, self.context, share)

    @ddt.data(constants.TASK_STATE_MIGRATION_STARTING,
              constants.TASK_STATE_MIGRATION_DRIVER_STARTING,
              constants.TASK_STATE_DATA_COPYING_STARTING,
              constants.TASK_STATE_MIGRATION_IN_PROGRESS)
    def test_migration_get_progress_task_state_progress_0(self, task_state):

        share = db_utils.create_share(
            id='fake_id',
            task_state=task_state)
        expected = {'total_progress': 0}

        result = self.api.migration_get_progress(self.context, share)

        self.assertEqual(expected, result)

    @ddt.data(constants.TASK_STATE_MIGRATION_SUCCESS,
              constants.TASK_STATE_DATA_COPYING_ERROR,
              constants.TASK_STATE_MIGRATION_CANCELLED,
              constants.TASK_STATE_MIGRATION_COMPLETING,
              constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
              constants.TASK_STATE_DATA_COPYING_COMPLETED,
              constants.TASK_STATE_DATA_COPYING_COMPLETING,
              constants.TASK_STATE_DATA_COPYING_CANCELLED,
              constants.TASK_STATE_MIGRATION_ERROR)
    def test_migration_get_progress_task_state_progress_100(self, task_state):

        share = db_utils.create_share(
            id='fake_id',
            task_state=task_state)
        expected = {'total_progress': 100}

        result = self.api.migration_get_progress(self.context, share)

        self.assertEqual(expected, result)

    def test_migration_get_progress_task_state_None(self):

        share = db_utils.create_share(id='fake_id', task_state=None)

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_get_progress, self.context, share)

    @ddt.data(None, {'invalid_progress': None}, {})
    def test_migration_get_progress_invalid(self, progress):

        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])
        service = 'fake_service'

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))

        self.mock_object(self.api.share_rpcapi, 'migration_get_progress',
                         mock.Mock(return_value=progress))

        self.assertRaises(exception.InvalidShare,
                          self.api.migration_get_progress, self.context, share)

        self.api.share_rpcapi.migration_get_progress.assert_called_once_with(
            self.context, instance1, instance2['id'])

    @ddt.data(True, False)
    def test__migration_initial_checks(self, create_share_network):
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'])
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        fake_share_network = (
            db_utils.create_share_network() if create_share_network else None)
        expected_network_change = create_share_network is True

        fake_share_network_id = (
            fake_share_network['id']
            if create_share_network else fake_share['share_network_id'])
        fake_subnet = db_utils.create_share_network_subnet(
            availability_zone_id=fake_az['id'])

        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        mock_shares_in_recycle_bin_get_all = self.mock_object(
            db_api, 'share_get_all_soft_deleted',
            mock.Mock(return_value=[]))
        mock_get_type = self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))
        mock_validate_service = self.mock_object(
            utils, 'validate_service_host')
        mock_service_get = self.mock_object(
            db_api, 'service_get_by_args', mock.Mock(return_value=service))
        mock_az_get = self.mock_object(
            db_api, 'availability_zone_get', mock.Mock(return_value=fake_az))
        mock_get_subnet = self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(return_value=[fake_subnet]))

        exp_shares, exp_types, exp_service, exp_network_id, net_change = (
            self.api._migration_initial_checks(
                self.context, fake_share_server, fake_host,
                fake_share_network))

        self.assertEqual(exp_shares, [fake_share])
        self.assertEqual(exp_types, [share_type])
        self.assertEqual(exp_service, service)
        self.assertEqual(exp_network_id, fake_share_network_id)
        self.assertIs(expected_network_change, net_change)
        mock_shares_get_all.assert_has_calls([
            mock.call(self.context, fake_share_server['id']),
            mock.call(self.context, fake_share_server['id'])])
        mock_shares_in_recycle_bin_get_all.assert_has_calls([
            mock.call(self.context, fake_share_server['id'])])
        mock_get_type.assert_called_once_with(self.context, share_type['id'])
        mock_validate_service.assert_called_once_with(self.context, fake_host)
        mock_service_get.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        mock_get_subnet.assert_called_once_with(
            self.context, fake_share_network_id, fake_az['id'])
        mock_az_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )

    def test_share_server_migration_get_destination(self):
        fake_source_server_id = 'fake_source_id'
        server_data = {
            'id': 'fake',
            'source_share_server_id': fake_source_server_id,
            'status': constants.STATUS_SERVER_MIGRATING_TO,
        }
        server = db_utils.create_share_server(**server_data)
        mock_get_all = self.mock_object(
            db_api, 'share_server_get_all_with_filters',
            mock.Mock(return_value=[server]))
        filters = {
            'status': constants.STATUS_SERVER_MIGRATING_TO,
            'source_share_server_id': fake_source_server_id,
        }

        filtered_server = self.api.share_server_migration_get_destination(
            self.context, fake_source_server_id,
            status=constants.STATUS_SERVER_MIGRATING_TO
        )
        self.assertEqual(filtered_server['id'], server['id'])
        mock_get_all.assert_called_once_with(self.context, filters=filters)

    def test_share_server_migration_get_destination_no_share_server(
            self):

        fake_source_server_id = 'fake_source_id'
        server_data = {
            'id': 'fake',
            'source_share_server_id': fake_source_server_id,
            'status': constants.STATUS_SERVER_MIGRATING_TO,
        }
        db_utils.create_share_server(**server_data)
        mock_get_all = self.mock_object(
            db_api, 'share_server_get_all_with_filters',
            mock.Mock(return_value=[]))
        filters = {
            'status': constants.STATUS_SERVER_MIGRATING_TO,
            'source_share_server_id': fake_source_server_id,
        }

        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_get_destination,
            self.context, fake_source_server_id,
            status=constants.STATUS_SERVER_MIGRATING_TO
        )
        mock_get_all.assert_called_once_with(self.context, filters=filters)

    def test_share_server_migration_get_destination_multiple_servers(self):
        fake_source_server_id = 'fake_source_id'
        server_data = {
            'id': 'fake',
            'source_share_server_id': fake_source_server_id,
            'status': constants.STATUS_SERVER_MIGRATING_TO,
        }
        server_1 = db_utils.create_share_server(**server_data)
        server_data['id'] = 'fake_id_2'
        server_2 = db_utils.create_share_server(**server_data)
        mock_get_all = self.mock_object(
            db_api, 'share_server_get_all_with_filters',
            mock.Mock(return_value=[server_1, server_2]))
        filters = {
            'status': constants.STATUS_SERVER_MIGRATING_TO,
            'source_share_server_id': fake_source_server_id,
        }

        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_get_destination,
            self.context, fake_source_server_id,
            status=constants.STATUS_SERVER_MIGRATING_TO
        )
        mock_get_all.assert_called_once_with(self.context, filters=filters)

    def test__migration_initial_checks_no_shares(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_network = {}
        fake_host = 'test@fake'
        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_server_not_active(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_server['status'] = 'error'
        fake_share = fakes.fake_share()
        fake_share_network = {}
        fake_host = 'test@fake'

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_share_group_related_to_server(self):
        fake_share_server = db_utils.create_share_server()
        fake_share = db_utils.create_share()
        fake_share_group = db_utils.create_share_group()
        fake_share_network = {}
        fake_host = 'test@fake'

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        mock_get_groups = self.mock_object(
            db_api, 'share_group_get_all_by_share_server',
            mock.Mock(return_value=[fake_share_group]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])
        mock_get_groups.assert_called_once_with(self.context,
                                                fake_share_server['id'])

    def _setup_mocks_for_initial_checks(self, fake_share, share_type, service,
                                        fake_az, fake_subnet):
        self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        self.mock_object(
            db_api, 'share_group_get_all_by_share_server',
            mock.Mock(return_value=[]))
        self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))
        self.mock_object(
            utils, 'validate_service_host')
        self.mock_object(
            db_api, 'service_get_by_args', mock.Mock(return_value=service))
        self.mock_object(
            db_api, 'availability_zone_get', mock.Mock(return_value=fake_az))
        self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(return_value=fake_subnet))

    def test__migration_initial_checks_share_not_available(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_server['host'] = 'fake@backend'
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_ERROR,
            share_type_id=share_type['id'])
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        fake_share_network = None
        fake_share_network_id = fake_share['share_network_id']
        fake_subnet = db_utils.create_share_network_subnet(
            availability_zone_id=fake_az['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}
        self._setup_mocks_for_initial_checks(fake_share, share_type, service,
                                             fake_az, fake_subnet)

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        db_api.share_get_all_by_share_server.assert_has_calls([
            mock.call(self.context, fake_share_server['id']),
            mock.call(self.context, fake_share_server['id'])])
        share_types.get_share_type.assert_called_once_with(
            self.context, share_type['id'])
        utils.validate_service_host.assert_called_once_with(
            self.context, fake_host)
        db_api.service_get_by_args.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        db_api.availability_zone_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )
        (db_api.share_network_subnets_get_all_by_availability_zone_id.
            assert_called_once_with(
                self.context, fake_share_network_id, fake_az['id']))
        db_api.share_group_get_all_by_share_server.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_share_with_replicas(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_server['host'] = 'fake@backend'
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            replication_type='dr', share_type_id=share_type['id'])
        for i in range(1, 4):
            db_utils.create_share_replica(
                share_id=fake_share['id'], replica_state='in_sync')
        fake_share = db_api.share_get(self.context, fake_share['id'])
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        fake_share_network = None
        fake_share_network_id = fake_share['share_network_id']
        fake_subnet = db_utils.create_share_network_subnet(
            availability_zone_id=fake_az['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}
        self._setup_mocks_for_initial_checks(fake_share, share_type, service,
                                             fake_az, fake_subnet)

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        db_api.share_get_all_by_share_server.assert_has_calls([
            mock.call(self.context, fake_share_server['id']),
            mock.call(self.context, fake_share_server['id'])])
        share_types.get_share_type.assert_called_once_with(
            self.context, share_type['id'])
        utils.validate_service_host.assert_called_once_with(
            self.context, fake_host)
        db_api.service_get_by_args.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        db_api.availability_zone_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )
        (db_api.share_network_subnets_get_all_by_availability_zone_id.
            assert_called_once_with(
                self.context, fake_share_network_id, fake_az['id']))
        db_api.share_group_get_all_by_share_server.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_share_in_share_group(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_server['host'] = 'fake@backend'
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'], share_group_id='fake_group_id')
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        fake_share_network = None
        fake_share_network_id = fake_share['share_network_id']
        fake_subnet = db_utils.create_share_network_subnet(
            availability_zone_id=fake_az['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}
        self._setup_mocks_for_initial_checks(fake_share, share_type, service,
                                             fake_az, fake_subnet)
        mock_snapshots_get = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share',
            mock.Mock(return_value=[]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        db_api.share_get_all_by_share_server.assert_has_calls([
            mock.call(self.context, fake_share_server['id']),
            mock.call(self.context, fake_share_server['id'])])
        share_types.get_share_type.assert_called_once_with(
            self.context, share_type['id'])
        utils.validate_service_host.assert_called_once_with(
            self.context, fake_host)
        db_api.service_get_by_args.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        db_api.availability_zone_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )
        (db_api.share_network_subnets_get_all_by_availability_zone_id.
            assert_called_once_with(
                self.context, fake_share_network_id, fake_az['id']))
        mock_snapshots_get.assert_called_once_with(
            self.context, fake_share['id'])
        db_api.share_group_get_all_by_share_server.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_same_backend_and_network(self):
        fake_server_host = 'fake@backend'
        fake_share_network = {'id': 'fake_share_network_id'}
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        fake_share = db_utils.create_share(
            host=fake_server_host, status=constants.STATUS_AVAILABLE,
            share_group_id='fake_group_id',
            share_network_id=fake_share_network['id'])

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_server_host,
            fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_another_migration_found(self):
        fake_server_host = 'fake@backend2'
        fake_share_network = {'id': 'fake_share_network_id'}
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_group_id='fake_group_id', share_network=fake_share_network)

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        mock_shares_get_servers_filters = self.mock_object(
            db_api, 'share_server_get_all_with_filters',
            mock.Mock(return_value=['fake_share_server']))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_server_host,
            fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])
        filters = {'source_share_server_id': fake_share_server['id'],
                   'status': constants.STATUS_SERVER_MIGRATING_TO}
        mock_shares_get_servers_filters.assert_called_once_with(
            self.context, filters=filters)

    def test_share_server_migration_get_request_spec_dict(self):
        share_instances = [
            db_utils.create_share_instance(share_id='fake_id')
            for i in range(1, 3)]
        snapshot_instances = [
            db_utils.create_snapshot_instance(
                snapshot_id='fake_' + str(i), share_instance_id='fake')
            for i in range(1, 3)]
        shares_req_spec = [{} for instance in share_instances]
        total_shares_size = sum(
            [instance.get('size', 0) for instance in share_instances])
        total_snapshots_size = sum(
            [instance.get('size', 0) for instance in snapshot_instances])
        expected_result = {
            'shares_size': total_shares_size,
            'snapshots_size': total_snapshots_size,
            'shares_req_spec': shares_req_spec,
        }
        fake_share_type = db_utils.create_share_type()
        get_type_calls = []
        get_request_spec_calls = []
        for instance in share_instances:
            get_type_calls.append(
                mock.call(self.context, instance['share_type_id']))
            get_request_spec_calls.append(
                mock.call(self.context, instance, fake_share_type))

        mock_get_type = self.mock_object(
            share_types, 'get_share_type',
            mock.Mock(return_value=fake_share_type))
        mock_get_request_spec = self.mock_object(
            self.api, '_get_request_spec_dict', mock.Mock(return_value={}))

        result = self.api.get_share_server_migration_request_spec_dict(
            self.context, share_instances, snapshot_instances)

        self.assertEqual(result, expected_result)
        mock_get_type.assert_has_calls(get_type_calls)
        mock_get_request_spec.assert_has_calls(get_request_spec_calls)

    def test__migration_initial_checks_instance_rules_error_status(self):
        fake_share_server = fakes.fake_share_server_get()
        fake_share_server['host'] = 'fake@backend'
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'], share_group_id='fake_group_id')
        fake_share['instance']['access_rules_status'] = constants.STATUS_ERROR
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        fake_share_network = None
        fake_share_network_id = fake_share['share_network_id']
        fake_subnet = db_utils.create_share_network_subnet(
            availability_zone_id=fake_az['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}
        self._setup_mocks_for_initial_checks(fake_share, share_type, service,
                                             fake_az, fake_subnet)

        mock_snapshots_get = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share',
            mock.Mock(return_value=[]))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )

        db_api.share_get_all_by_share_server.assert_has_calls([
            mock.call(self.context, fake_share_server['id']),
            mock.call(self.context, fake_share_server['id'])])
        share_types.get_share_type.assert_called_once_with(
            self.context, share_type['id'])
        utils.validate_service_host.assert_called_once_with(
            self.context, fake_host)
        db_api.service_get_by_args.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        db_api.availability_zone_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )
        (db_api.share_network_subnets_get_all_by_availability_zone_id.
            assert_called_once_with(
                self.context, fake_share_network_id, fake_az['id']))
        mock_snapshots_get.assert_called_once_with(
            self.context, fake_share['id'])
        db_api.share_group_get_all_by_share_server.assert_called_once_with(
            self.context, fake_share_server['id'])

    def test__migration_initial_checks_dest_az_not_match_host_az(self):
        type_data = {
            'extra_specs': {
                'availability_zones': 'zone1,zone2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'])
        fake_share_network = {}
        fake_host = 'test@fake'
        service = {'availability_zone_id': 'fake_az_id',
                   'availability_zone': {'name': 'fake_az1'}}

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        mock_get_type = self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))
        mock_validate_service = self.mock_object(
            utils, 'validate_service_host')
        mock_service_get = self.mock_object(
            db_api, 'service_get_by_args', mock.Mock(return_value=service))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])
        mock_get_type.assert_called_once_with(self.context, share_type['id'])
        mock_validate_service.assert_called_once_with(self.context, fake_host)
        mock_service_get.assert_called_once_with(
            self.context, fake_host, 'manila-share')

    def test__migration_initial_checks_no_matching_subnet(self):
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_server_host = 'fake@backend'
        fake_share_server = db_utils.create_share_server(host=fake_server_host)
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'])
        fake_share_network = db_utils.create_share_network()
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }

        db_utils.create_share_network_subnet(
            availability_zone_id='fake',
            share_network_id=fake_share_network['id'])
        fake_share_network = db_api.share_network_get(
            self.context, fake_share_network['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}

        mock_shares_get_all = self.mock_object(
            db_api, 'share_get_all_by_share_server',
            mock.Mock(return_value=[fake_share]))
        mock_get_type = self.mock_object(
            share_types, 'get_share_type', mock.Mock(return_value=share_type))
        mock_validate_service = self.mock_object(
            utils, 'validate_service_host')
        mock_service_get = self.mock_object(
            db_api, 'service_get_by_args', mock.Mock(return_value=service))
        mock_az_get = self.mock_object(
            db_api, 'availability_zone_get', mock.Mock(return_value=fake_az))
        mock_get_subnet = self.mock_object(
            db_api, 'share_network_subnets_get_all_by_availability_zone_id',
            mock.Mock(return_value=None))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api._migration_initial_checks,
            self.context, fake_share_server, fake_host, fake_share_network,
        )
        mock_shares_get_all.assert_called_once_with(
            self.context, fake_share_server['id'])
        mock_get_type.assert_called_once_with(self.context, share_type['id'])
        mock_validate_service.assert_called_once_with(self.context, fake_host)
        mock_service_get.assert_called_once_with(
            self.context, fake_host, 'manila-share')
        mock_get_subnet.assert_called_once_with(
            self.context, fake_share_network['id'], fake_az['id'])
        mock_az_get.assert_called_once_with(
            self.context, service['availability_zone']['name']
        )

    def test_server_migration_check_nondisruptive_and_network_change(self):
        fake_shares = [db_utils.create_share() for i in range(2)]
        fake_types = [{'id': 'fake_type_id'}]
        fake_share_server = db_utils.create_share_server()
        dest_host = fake_share_server['host']
        service = {
            'availability_zone_id': 'fake_az_id',
            'availability_zone': {'name': 'fake_az_name'}
        }
        fake_share_network = db_utils.create_share_network()
        network_has_changed = True
        writable = preserve_snapshots = False
        nondisruptive = True
        expected_result = {
            'compatible': False,
            'writable': writable,
            'nondisruptive': False,
            'preserve_snapshots': preserve_snapshots,
            'share_network_id': fake_share_network['id'],
            'migration_cancel': False,
            'migration_get_progress': False
        }

        mock_initial_checks = self.mock_object(
            self.api, '_migration_initial_checks',
            mock.Mock(
                return_value=[fake_shares, fake_types, service,
                              fake_share_network['id'], network_has_changed]))

        check_result = self.api.share_server_migration_check(
            self.context, fake_share_server, dest_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network
        )

        self.assertEqual(expected_result, check_result)
        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_server, dest_host, fake_share_network)

    def test_server_migration_start_nondisruptive_and_network_change(self):
        fake_shares = [db_utils.create_share() for i in range(2)]
        fake_types = [{'id': 'fake_type_id'}]
        fake_share_server = db_utils.create_share_server()
        dest_host = fake_share_server['host']
        service = {
            'availability_zone_id': 'fake_az_id',
            'availability_zone': {'name': 'fake_az_name'}
        }
        fake_share_network = db_utils.create_share_network()
        network_has_changed = True
        writable = preserve_snapshots = False
        nondisruptive = True

        mock_initial_checks = self.mock_object(
            self.api, '_migration_initial_checks',
            mock.Mock(
                return_value=[fake_shares, fake_types, service,
                              fake_share_network['id'], network_has_changed]))

        self.assertRaises(
            exception.InvalidInput,
            self.api.share_server_migration_start,
            self.context, fake_share_server, dest_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network
        )

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_server, dest_host, fake_share_network)

    def test_share_server_migration_check(self):
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_share_server = db_utils.create_share_server()
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_share = db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'])
        fake_shares = [fake_share]
        fake_types = [share_type]
        fake_share_network = db_utils.create_share_network()
        fake_az = {
            'id': 'fake_az_id',
            'name': 'fake_az1'
        }
        writable = True
        nondisruptive = True
        preserve_snapshots = True
        fake_share_network = db_api.share_network_get(
            self.context, fake_share_network['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': fake_az['id'],
                   'availability_zone': {'name': fake_az['name']}}
        expected_result = {
            'requested_capabilities': {},
            'supported_capabilities': {}
        }

        mock_initial_checks = self.mock_object(
            self.api, '_migration_initial_checks',
            mock.Mock(return_value=[fake_shares, fake_types, service,
                                    fake_share_network['id'], False]))
        # NOTE(carloss): Returning an "empty" dictionary should be enough for
        # this test case. The unit test to check the values being returned to
        # the user should be placed in the share manager, where the dict is
        # populated with the real info. At this level we only forward the
        # received response to the user.
        mock_migration_check = self.mock_object(
            self.share_rpcapi, 'share_server_migration_check',
            mock.Mock(return_value=expected_result))

        result = self.api.share_server_migration_check(
            self.context, fake_share_server, fake_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network
        )

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_server, fake_host, fake_share_network)
        mock_migration_check.assert_called_once_with(
            self.context, fake_share_server['id'], fake_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network['id']
        )
        self.assertEqual(result, expected_result)

    def test_share_server_migration_start(self):
        type_data = {
            'extra_specs': {
                'availability_zones': 'fake_az1,fake_az2'
            }
        }
        fake_share_server = db_utils.create_share_server()
        share_type = db_utils.create_share_type(**type_data)
        share_type = db_api.share_type_get(self.context, share_type['id'])
        fake_shares = [db_utils.create_share(
            host='fake@backend#pool', status=constants.STATUS_AVAILABLE,
            share_type_id=share_type['id'],
            share_server_id=fake_share_server['id']) for x in range(4)]
        fake_snapshots = [
            db_utils.create_snapshot(share_id=fake_shares[0]['id'])]
        instance_ids = [share['instance']['id'] for share in fake_shares]
        snap_instances = []
        snap_instance_ids = []
        for fake_share in fake_shares:
            for snapshot in fake_snapshots:
                snap_instances.append({'id': snapshot['instance']['id']})
                snap_instance_ids.append(snapshot['instance']['id'])
        fake_types = [share_type]
        fake_share_network = db_utils.create_share_network()
        writable = True
        nondisruptive = True
        preserve_snapshots = True
        fake_share_network = db_api.share_network_get(
            self.context, fake_share_network['id'])
        fake_host = 'test@fake'
        service = {'availability_zone_id': 'fake_az_id',
                   'availability_zone': {'name': 'fake_az1'}}
        server_expected_update = {
            'task_state': constants.TASK_STATE_MIGRATION_STARTING,
            'status': constants.STATUS_SERVER_MIGRATING
        }
        share_expected_update = {
            'status': constants.STATUS_SERVER_MIGRATING
        }
        mock_initial_checks = self.mock_object(
            self.api, '_migration_initial_checks',
            mock.Mock(return_value=[fake_shares, fake_types, service,
                                    fake_share_network['id'], False]))
        mock_migration_start = self.mock_object(
            self.share_rpcapi, 'share_server_migration_start')
        mock_server_update = self.mock_object(db_api, 'share_server_update')
        mock_snapshots_get = self.mock_object(
            db_api, 'share_snapshot_instance_get_all_with_filters',
            mock.Mock(return_value=snap_instances))
        mock_update_instances = self.mock_object(
            db_api, 'share_and_snapshot_instances_status_update')

        self.api.share_server_migration_start(
            self.context, fake_share_server, fake_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network
        )

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_server, fake_host, fake_share_network)
        mock_migration_start.assert_called_once_with(
            self.context, fake_share_server, fake_host, writable,
            nondisruptive, preserve_snapshots, fake_share_network['id']
        )
        mock_server_update.assert_called_once_with(
            self.context, fake_share_server['id'], server_expected_update)
        mock_snapshots_get.assert_called()
        mock_update_instances.assert_called_once_with(
            self.context, share_expected_update,
            current_expected_status=constants.STATUS_AVAILABLE,
            share_instance_ids=instance_ids,
            snapshot_instance_ids=snap_instance_ids)

    @ddt.data(
        (constants.STATUS_ACTIVE, None),
        (constants.STATUS_SERVER_MIGRATING,
         constants.TASK_STATE_MIGRATION_STARTING)
    )
    @ddt.unpack
    def test_share_server_migration_complete_invalid_status(self, status,
                                                            task_state):
        fake_host = 'fakehost@fakebackend'
        fake_share_server = db_utils.create_share_server(
            status=status, task_state=task_state, host=fake_host)
        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_complete,
            self.context, fake_share_server)

    def test_share_server_migration_complete(self):
        fake_service_host = 'fakehost@fakebackend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
            host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)
        fake_service = {'availability_zone_id': 'fake_az_id',
                        'availability_zone': {'name': 'fake_az1'}}

        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(return_value=fake_service))
        mock_migration_complete = self.mock_object(
            self.share_rpcapi, 'share_server_migration_complete')

        result = self.api.share_server_migration_complete(
            self.context, fake_share_server)

        expected = {
            'destination_share_server_id': fake_share_server_dest['id']
        }
        self.assertEqual(expected, result)
        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_validate_service_host.assert_called_once_with(
            self.context, fake_service_host)
        mock_migration_complete.assert_called_once_with(
            self.context, fake_share_server['host'], fake_share_server,
            fake_share_server_dest
        )

    @ddt.data(
        (constants.STATUS_ACTIVE, None),
        (constants.STATUS_SERVER_MIGRATING,
         constants.TASK_STATE_MIGRATION_STARTING)
    )
    @ddt.unpack
    def test_share_server_migration_cancel_server_not_migrating(
            self, status, task_state):
        fake_share_server = db_utils.create_share_server(
            status=status, task_state=task_state)

        self.mock_object(self.api, '_migration_validate_error_message',
                         mock.Mock(return_value=None))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_cancel,
            self.context,
            fake_share_server
        )

    @ddt.data(constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
              constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS)
    def test_share_server_migration_cancel_service_not_up(self, task_state):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=task_state,
            host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)

        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(side_effect=exception.ServiceIsDown(
                service="fake_service")))

        self.assertRaises(
            exception.ServiceIsDown,
            self.api.share_server_migration_cancel,
            self.context,
            fake_share_server
        )
        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_validate_service_host.assert_called_once_with(
            self.context, fake_service_host)

    @ddt.data(constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE,
              constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS)
    def test_share_server_migration_cancel(self, task_state):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=task_state,
            host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)
        fake_service = {'availability_zone_id': 'fake_az_id',
                        'availability_zone': {'name': 'fake_az1'}}

        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(return_value=fake_service))

        self.api.share_server_migration_cancel(
            self.context, fake_share_server)

        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_validate_service_host.assert_called_once_with(
            self.context, fake_service_host)

    def test_share_server_migration_get_progress_not_migrating(self):
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_ACTIVE)
        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_get_progress,
            self.context, fake_share_server['id']
        )

    def test_share_server_migration_get_progress_service_not_up(self):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)

        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(side_effect=exception.ServiceIsDown(
                service="fake_service")))

        self.assertRaises(
            exception.ServiceIsDown,
            self.api.share_server_migration_get_progress,
            self.context, fake_share_server['id']
        )

        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_validate_service_host.assert_called_once_with(
            self.context, fake_service_host)

    def test_share_server_migration_get_progress_rpcapi_exception(self):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)
        fake_service = {'availability_zone_id': 'fake_az_id',
                        'availability_zone': {'name': 'fake_az1'}}

        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(return_value=fake_share_server))
        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(return_value=fake_service))
        mock_migration_get_progress = self.mock_object(
            self.share_rpcapi, 'share_server_migration_get_progress',
            mock.Mock(side_effect=Exception))

        self.assertRaises(
            exception.ShareServerMigrationError,
            self.api.share_server_migration_get_progress,
            self.context,
            fake_share_server['id']
        )

        mock_server_get.assert_called_once_with(self.context,
                                                fake_share_server['id'])
        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_validate_service_host.assert_called_once_with(
            self.context, fake_service_host)
        mock_migration_get_progress.assert_called_once_with(
            self.context, fake_share_server_dest['host'], fake_share_server,
            fake_share_server_dest)

    @ddt.data(constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
              constants.TASK_STATE_MIGRATION_SUCCESS)
    def test_share_server_migration_get_progress(self, task_state):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=task_state, host=fake_service_host)
        fake_share_server_dest = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING_TO,
            host=fake_service_host)
        fake_service = {'availability_zone_id': 'fake_az_id',
                        'availability_zone': {'name': 'fake_az1'}}

        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(return_value=fake_share_server))
        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_share_server_dest))
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host',
            mock.Mock(return_value=fake_service))
        mock_migration_get_progress = self.mock_object(
            self.share_rpcapi, 'share_server_migration_get_progress',
            mock.Mock(return_value={'total_progress': 50}))
        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))

        result = self.api.share_server_migration_get_progress(
            self.context, fake_share_server['id'])

        self.assertIn('total_progress', result)
        mock_server_get.assert_called_once_with(self.context,
                                                fake_share_server['id'])
        if task_state == constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS:
            mock_get_destination.assert_called_once_with(
                self.context, fake_share_server['id'],
                status=constants.STATUS_SERVER_MIGRATING_TO)
            mock_validate_service_host.assert_called_once_with(
                self.context, fake_service_host)
            mock_migration_get_progress.assert_called_once_with(
                self.context, fake_share_server_dest['host'],
                fake_share_server, fake_share_server_dest)

    @ddt.data(constants.STATUS_SERVER_MIGRATING_TO,
              constants.STATUS_SERVER_MIGRATING)
    def test_share_server_migration_get_progress_invalid_share_server(self,
                                                                      status):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=status,
            task_state=None,
            host=fake_service_host)
        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(return_value=fake_share_server))
        mock_get_progress_state = self.mock_object(
            self.api, '_migration_get_progress_state',
            mock.Mock(return_value=None))
        self.mock_object(self.api, 'share_server_migration_get_destination')

        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_get_progress,
            self.context, fake_share_server['id'])

        mock_server_get.assert_called_once_with(self.context,
                                                fake_share_server['id'])
        if status == constants.STATUS_SERVER_MIGRATING:
            mock_get_progress_state.assert_called_once_with(fake_share_server)

    def test_share_server_migration_get_progress_source_not_found(self):
        fake_dest_hare_server = db_utils.create_share_server(
            status=constants.STATUS_ACTIVE,
            task_state=constants.TASK_STATE_MIGRATION_SUCCESS)
        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(side_effect=exception.ShareServerNotFound(
                share_server_id='fake_id')))
        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(return_value=fake_dest_hare_server))

        result = self.api.share_server_migration_get_progress(
            self.context, 'fake_source_server_id')
        expected = {
            'total_progress': 100,
            'destination_share_server_id': fake_dest_hare_server['id'],
            'task_state': constants.TASK_STATE_MIGRATION_SUCCESS,
        }

        self.assertEqual(expected, result)
        mock_server_get.assert_called_once_with(self.context,
                                                'fake_source_server_id')
        mock_get_destination.assert_called_once_with(
            self.context, 'fake_source_server_id',
            status=constants.STATUS_ACTIVE)

    def test_share_server_migration_get_progress_has_destination_only(self):
        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(side_effect=exception.ShareServerNotFound(
                share_server_id='fake_id')))
        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(side_effect=exception.InvalidShareServer(reason='')))

        self.assertRaises(
            exception.InvalidShareServer,
            self.api.share_server_migration_get_progress,
            self.context, 'fake_src_server_id')

        mock_server_get.assert_called_once_with(self.context,
                                                'fake_src_server_id')
        mock_get_destination.assert_called_once_with(
            self.context, 'fake_src_server_id', status=constants.STATUS_ACTIVE)

    def test_share_server_migration_get_progress_not_determinated(self):
        fake_service_host = 'host@backend'
        fake_share_server = db_utils.create_share_server(
            status=constants.STATUS_SERVER_MIGRATING,
            task_state=None,
            host=fake_service_host)
        mock_server_get = self.mock_object(
            db_api, 'share_server_get',
            mock.Mock(return_value=fake_share_server))
        mock_get_destination = self.mock_object(
            self.api, 'share_server_migration_get_destination',
            mock.Mock(side_effect=exception.InvalidShareServer(reason='')))
        mock_get_progress_state = self.mock_object(
            self.api, '_migration_get_progress_state',
            mock.Mock(return_value={'total_progress': 0}))

        result = self.api.share_server_migration_get_progress(
            self.context, 'fake_source_server_id')

        expected = {
            'total_progress': 0,
            'destination_share_server_id': '',
            'task_state': '',
        }

        self.assertEqual(expected, result)
        mock_server_get.assert_called_once_with(self.context,
                                                'fake_source_server_id')
        mock_get_destination.assert_called_once_with(
            self.context, fake_share_server['id'],
            status=constants.STATUS_SERVER_MIGRATING_TO)
        mock_get_progress_state.assert_called_once_with(fake_share_server)

    def test_migration_get_progress_race(self):

        instance1 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING,
            host='some_host')
        instance2 = db_utils.create_share_instance(
            share_id='fake_id',
            status=constants.STATUS_MIGRATING_TO)
        share = db_utils.create_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_IN_PROGRESS,
            instances=[instance1, instance2])
        share_ref = fakes.fake_share(
            id='fake_id',
            task_state=constants.TASK_STATE_MIGRATION_DRIVER_PHASE1_DONE)
        service = 'fake_service'
        expected = {'total_progress': 100}

        self.mock_object(utils, 'service_is_up', mock.Mock(return_value=True))
        self.mock_object(db_api, 'service_get_by_args',
                         mock.Mock(return_value=service))
        self.mock_object(db_api, 'share_instance_get',
                         mock.Mock(return_value=instance1))
        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=share_ref))
        self.mock_object(self.api.share_rpcapi, 'migration_get_progress',
                         mock.Mock(side_effect=exception.InvalidShare('fake')))

        result = self.api.migration_get_progress(self.context, share)
        self.assertEqual(expected, result)

        self.api.share_rpcapi.migration_get_progress.assert_called_once_with(
            self.context, instance1, instance2['id'])

    def test__share_network_update_initial_checks_network_not_active(self):
        share_network = db_utils.create_share_network(
            status=constants.STATUS_NETWORK_CHANGE)
        new_sec_service = db_utils.create_security_service(
            share_network_id=share_network['id'], type='ldap')

        self.assertRaises(
            webob_exc.HTTPBadRequest,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service
        )

    def test__share_network_update_initial_checks_server_not_active(self):
        share_subnet = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fakenetid')
        db_utils.create_share_server(
            share_network_subnets=[share_subnet],
            status=constants.STATUS_ERROR,
            security_service_update_support=True)
        share_network = db_utils.create_share_network(id='fakenetid')
        new_sec_service = db_utils.create_security_service(
            share_network_id='fakenetid', type='ldap')

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service,
        )

    def test__share_network_update_initial_checks_shares_not_available(self):
        share_subnet = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_network_id')
        db_utils.create_share_server(share_network_subnets=[share_subnet],
                                     security_service_update_support=True)
        share_network = db_utils.create_share_network(
            id='fake_network_id')
        new_sec_service = db_utils.create_security_service(
            share_network_id='fake_network_id', type='ldap')
        shares = [db_utils.create_share(status=constants.STATUS_ERROR)]

        self.mock_object(utils, 'validate_service_host')
        self.mock_object(
            self.api, 'get_all', mock.Mock(return_value=shares))

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service
        )
        utils.validate_service_host.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), 'host1')
        self.api.get_all.assert_called_once_with(
            self.context,
            search_opts={'share_network_id': share_network['id']})

    def test__share_network_update_initial_checks_rules_in_error(self):
        share_subnet = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_network_id')
        db_utils.create_share_server(share_network_subnets=[share_subnet],
                                     security_service_update_support=True)
        share_network = db_utils.create_share_network(
            id='fake_network_id')
        new_sec_service = db_utils.create_security_service(
            share_network_id='fake_network_id', type='ldap')
        shares = [db_utils.create_share(status=constants.STATUS_AVAILABLE)]
        shares[0]['instance']['access_rules_status'] = (
            constants.ACCESS_STATE_ERROR)

        self.mock_object(utils, 'validate_service_host')
        self.mock_object(
            self.api, 'get_all', mock.Mock(return_value=shares))

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service
        )
        utils.validate_service_host.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), 'host1')
        self.api.get_all.assert_called_once_with(
            self.context,
            search_opts={'share_network_id': share_network['id']})

    def test__share_network_update_initial_checks_share_is_busy(self):
        share_subnet = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_net_id')
        db_utils.create_share_server(share_network_subnets=[share_subnet],
                                     security_service_update_support=True)
        share_network = db_utils.create_share_network(id='fake_net_id')
        new_sec_service = db_utils.create_security_service(
            share_network_id='fake_net_id', type='ldap')
        shares = [db_utils.create_share(status=constants.STATUS_AVAILABLE)]

        self.mock_object(utils, 'validate_service_host')
        self.mock_object(
            self.api, 'get_all', mock.Mock(return_value=shares))
        self.mock_object(
            self.api, '_check_is_share_busy',
            mock.Mock(side_effect=exception.ShareBusyException(message='fake'))
        )

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service
        )
        utils.validate_service_host.assert_called_once_with(
            utils.IsAMatcher(context.RequestContext), 'host1')
        self.api.get_all.assert_called_once_with(
            self.context,
            search_opts={'share_network_id': share_network['id']})
        self.api._check_is_share_busy.assert_called_once_with(shares[0])

    def test__share_network_update_initial_checks_unsupported_server(self):
        share_subnet = db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_net_id')
        db_utils.create_share_server(share_network_subnets=[share_subnet],
                                     security_service_update_support=False)
        share_network = db_utils.create_share_network(id='fake_net_id')

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_network_update_initial_checks,
            self.context, share_network, None
        )

    def test__share_network_update_initial_checks_update_different_types(self):
        db_utils.create_share_server(share_network_subnet_id='fakeid',
                                     security_service_update_support=True)
        db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_net_id')
        share_network = db_utils.create_share_network(id='fake_net_id')
        new_sec_service = db_utils.create_security_service(
            share_network_id='fake_net_id', type='ldap')
        curr_sec_service = db_utils.create_security_service(
            share_network_id='fake_net_id', type='kerberos')

        self.assertRaises(
            exception.InvalidSecurityService,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service,
            current_security_service=curr_sec_service
        )

    def test__share_network_update_initial_checks_add_type_conflict(self):
        db_utils.create_share_server(share_network_subnet_id='fakeid',
                                     security_service_update_support=True)
        db_utils.create_share_network_subnet(
            id='fakeid', share_network_id='fake_net_id')
        share_network = db_utils.create_share_network(id='fake_net_id')
        db_utils.create_security_service(
            share_network_id='fake_net_id', type='ldap')
        share_network = db_api.share_network_get(self.context,
                                                 share_network['id'])
        new_sec_service = db_utils.create_security_service(
            share_network_id='fake_net_id', type='ldap')

        self.assertRaises(
            exception.InvalidSecurityService,
            self.api._share_network_update_initial_checks,
            self.context, share_network, new_sec_service,
        )

    def test_update_share_network_security_service_no_share_servers(self):
        mock_initial_checks = self.mock_object(
            self.api, '_share_network_update_initial_checks',
            mock.Mock(return_value=([], [])))
        mock_get_key = self.mock_object(
            self.api, 'get_security_service_update_key')
        fake_share_network = {'id': 'fake_share_net_id'}
        fake_sec_service = {'id': 'fake_sec_serv_id'}

        self.api.update_share_network_security_service(
            self.context, fake_share_network, fake_sec_service,
            current_security_service=None)

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_network, fake_sec_service,
            current_security_service=None)
        mock_get_key.assert_not_called()

    def test_update_share_network_security_service_without_check(self):
        mock_initial_checks = self.mock_object(
            self.api, '_share_network_update_initial_checks',
            mock.Mock(return_value=(['fake_server'], ['fake_host'])))
        mock_get_key = self.mock_object(
            self.api, 'get_security_service_update_key',
            mock.Mock(return_value=None))
        fake_share_network = {'id': 'fake_share_net_id'}
        fake_sec_service = {'id': 'fake_sec_serv_id'}

        self.assertRaises(exception.InvalidShareNetwork,
                          self.api.update_share_network_security_service,
                          self.context, fake_share_network, fake_sec_service)

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_network, fake_sec_service,
            current_security_service=None)
        mock_get_key.assert_called_once_with(
            'hosts_check', fake_sec_service['id'],
            current_security_service_id=None)

    def test_update_share_network_security_service_update_hosts_failure(self):
        mock_initial_checks = self.mock_object(
            self.api, '_share_network_update_initial_checks',
            mock.Mock(return_value=(['fake_server'], ['fake_host'])))
        mock_get_key = self.mock_object(
            self.api, 'get_security_service_update_key',
            mock.Mock(return_value='fake_key'))
        mock_async_db_get = self.mock_object(
            db_api, 'async_operation_data_get',
            mock.Mock(return_value='fake_value'))
        mock_validate_host = self.mock_object(
            self.api, '_security_service_update_validate_hosts',
            mock.Mock(side_effect=Exception))
        mock_async_db_delete = self.mock_object(
            db_api, 'async_operation_data_delete')
        fake_share_network = {'id': 'fake_share_net_id'}
        fake_sec_service = {'id': 'fake_sec_serv_id'}

        self.assertRaises(exception.InvalidShareNetwork,
                          self.api.update_share_network_security_service,
                          self.context, fake_share_network, fake_sec_service)

        mock_initial_checks.assert_called_once_with(
            self.context, fake_share_network, fake_sec_service,
            current_security_service=None)
        mock_get_key.assert_called_once_with(
            'hosts_check', fake_sec_service['id'],
            current_security_service_id=None)
        mock_async_db_get.assert_called_once_with(
            self.context, fake_share_network['id'], 'fake_key')
        mock_validate_host.assert_called_once_with(
            self.context, fake_share_network, ['fake_host'], ['fake_server'],
            new_security_service_id=fake_sec_service['id'],
            current_security_service_id=None)
        mock_async_db_delete.assert_called_once_with(
            self.context, fake_share_network['id'], 'fake_key')

    def test_update_share_network_security_service_backend_host_failure(self):
        share_network = db_utils.create_share_network()
        security_service = db_utils.create_security_service()
        backend_host = 'fakehost'

        mock_initial_checks = self.mock_object(
            self.api, '_share_network_update_initial_checks',
            mock.Mock(return_value=(['fake_server'], [backend_host])))
        mock_get_update_key = self.mock_object(
            self.api, 'get_security_service_update_key',
            mock.Mock(return_value='fake_key'))
        mock_db_async_op = self.mock_object(
            db_api, 'async_operation_data_get',
            mock.Mock(return_value='fake_update_value'))
        mock_validate_host = self.mock_object(
            self.api, '_security_service_update_validate_hosts',
            mock.Mock(return_value=(False, None)))

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api.update_share_network_security_service,
            self.context, share_network, security_service)

        mock_initial_checks.assert_called_once_with(
            self.context, share_network, security_service,
            current_security_service=None)
        mock_db_async_op.assert_called_once_with(
            self.context, share_network['id'], 'fake_key')
        mock_get_update_key.assert_called_once_with(
            'hosts_check', security_service['id'],
            current_security_service_id=None)
        mock_validate_host.assert_called_once_with(
            self.context, share_network, [backend_host], ['fake_server'],
            new_security_service_id=security_service['id'],
            current_security_service_id=None)

    def test_update_share_network_security_service(self):
        share_network = db_utils.create_share_network()
        security_service = db_utils.create_security_service()
        backend_hosts = ['fakehost']
        fake_update_key = 'fake_key'
        servers = [
            db_utils.create_share_server() for i in range(2)]
        server_ids = [server['id'] for server in servers]

        mock_initial_checks = self.mock_object(
            self.api, '_share_network_update_initial_checks',
            mock.Mock(return_value=(servers, backend_hosts)))
        mock_get_update_key = self.mock_object(
            self.api, 'get_security_service_update_key',
            mock.Mock(return_value=fake_update_key))
        mock_db_async_op = self.mock_object(
            db_api, 'async_operation_data_get',
            mock.Mock(return_value='fake_update_value'))
        mock_validate_host = self.mock_object(
            self.api, '_security_service_update_validate_hosts',
            mock.Mock(return_value=(True, None)))
        mock_network_update = self.mock_object(
            db_api, 'share_network_update')
        mock_servers_update = self.mock_object(
            db_api, 'share_servers_update')
        mock_update_security_services = self.mock_object(
            self.share_rpcapi, 'update_share_network_security_service')
        mock_db_async_op_del = self.mock_object(
            db_api, 'async_operation_data_delete',)

        self.api.update_share_network_security_service(
            self.context, share_network, security_service)

        mock_initial_checks.assert_called_once_with(
            self.context, share_network, security_service,
            current_security_service=None)
        mock_db_async_op.assert_called_once_with(
            self.context, share_network['id'], fake_update_key)
        mock_get_update_key.assert_called_once_with(
            'hosts_check', security_service['id'],
            current_security_service_id=None)
        mock_validate_host.assert_called_once_with(
            self.context, share_network, backend_hosts, servers,
            new_security_service_id=security_service['id'],
            current_security_service_id=None)
        mock_network_update.assert_called_once_with(
            self.context, share_network['id'],
            {'status': constants.STATUS_NETWORK_CHANGE})
        mock_servers_update.assert_called_once_with(
            self.context, server_ids,
            {'status': constants.STATUS_SERVER_NETWORK_CHANGE}
        )
        mock_update_security_services.assert_called_once_with(
            self.context, backend_hosts[0], share_network['id'],
            security_service['id'], current_security_service_id=None)
        mock_db_async_op_del.assert_called_once_with(
            self.context, share_network['id'], fake_update_key)

    def test__security_service_update_validate_hosts_new_check(self):
        curr_sec_service_id = "fake_curr_sec_serv_id"
        new_sec_service_id = "fake_new_sec_serv_id"
        fake_key = (curr_sec_service_id + '_' + new_sec_service_id +
                    '_' + 'hosts_check')
        fake_share_network = {'id': 'fake_network_id'}
        backend_hosts = {'hostA', 'hostB'}
        fake_return = 'fake_update'
        mock_get_key = self.mock_object(
            self.api, 'get_security_service_update_key',
            mock.Mock(return_value=fake_key))
        mock_do_update_validate = self.mock_object(
            self.api, '_do_update_validate_hosts',
            mock.Mock(return_value=fake_return))

        res = self.api._security_service_update_validate_hosts(
            self.context, fake_share_network, backend_hosts, None,
            new_security_service_id=new_sec_service_id,
            current_security_service_id=curr_sec_service_id)

        self.assertEqual(fake_return, res)
        mock_get_key.assert_called_once_with(
            'hosts_check', new_sec_service_id,
            current_security_service_id=curr_sec_service_id)
        mock_do_update_validate.assert_called_once_with(
            self.context, fake_share_network['id'], backend_hosts, fake_key,
            new_security_service_id=new_sec_service_id,
            current_security_service_id=curr_sec_service_id)

    @ddt.data(True, False)
    def test__do_update_validate_hosts(self, update_security_service):
        curr_sec_service_id = None
        new_sec_service_id = None
        new_share_network_subnet = 'fake_new_share_network_subnet'
        if update_security_service:
            curr_sec_service_id = "fake_curr_sec_serv_id"
            new_sec_service_id = "fake_new_sec_serv_id"
            new_share_network_subnet = None

        fake_key = 'fake_key'
        fake_share_network_id = 'fake_network_id'
        backend_hosts = {'hostA', 'hostB'}
        hosts_to_validate = {}
        for bh in backend_hosts:
            hosts_to_validate[bh] = None
        mock_async_data_get = self.mock_object(
            db_api, 'async_operation_data_get', mock.Mock(return_value=None))
        mock_async_data_update = self.mock_object(
            db_api, 'async_operation_data_update')
        mock_check_update_allocations = self.mock_object(
            self.share_rpcapi, 'check_update_share_server_network_allocations')
        mock_check_update_services = self.mock_object(
            self.share_rpcapi, 'check_update_share_network_security_service')

        compatible, hosts_info = self.api._do_update_validate_hosts(
            self.context, fake_share_network_id, backend_hosts, fake_key,
            new_share_network_subnet=new_share_network_subnet,
            new_security_service_id=new_sec_service_id,
            current_security_service_id=curr_sec_service_id)

        self.assertIsNone(compatible)
        self.assertEqual(hosts_to_validate, hosts_info)
        mock_async_data_get.assert_called_once_with(
            self.context, fake_share_network_id, fake_key)
        mock_async_data_update.assert_called_once_with(
            self.context, fake_share_network_id,
            {fake_key: json.dumps(hosts_to_validate)})
        mock_share_api_check_calls = []
        for host in backend_hosts:
            if update_security_service:
                mock_share_api_check_calls.append(
                    mock.call(self.context, host, fake_share_network_id,
                              new_sec_service_id,
                              current_security_service_id=curr_sec_service_id))
            else:
                mock_share_api_check_calls.append(
                    mock.call(self.context, host, fake_share_network_id,
                              new_share_network_subnet))
        if update_security_service:
            mock_check_update_services.assert_has_calls(
                mock_share_api_check_calls)
            mock_check_update_allocations.assert_not_called()
        else:
            mock_check_update_allocations.assert_has_calls(
                mock_share_api_check_calls)
            mock_check_update_services.assert_not_called()

    @ddt.data(
        {'update_security_service': True, 'new_host': None,
         'host_support': None, 'exp_result': None},
        {'update_security_service': True, 'new_host': None,
         'host_support': False, 'exp_result': False},
        {'update_security_service': True, 'new_host': None,
         'host_support': True, 'exp_result': True},
        {'update_security_service': True, 'new_host': 'hostC',
         'host_support': None, 'exp_result': None},
        {'update_security_service': True, 'new_host': 'hostC',
         'host_support': False, 'exp_result': False},
        {'update_security_service': True, 'new_host': 'hostC',
         'host_support': True, 'exp_result': None},
        {'update_security_service': False, 'new_host': None,
         'host_support': None, 'exp_result': None},
        {'update_security_service': False, 'new_host': None,
         'host_support': False, 'exp_result': False},
        {'update_security_service': False, 'new_host': None,
         'host_support': True, 'exp_result': True},
        {'update_security_service': False, 'new_host': 'hostC',
         'host_support': None, 'exp_result': None},
        {'update_security_service': False, 'new_host': 'hostC',
         'host_support': False, 'exp_result': False},
        {'update_security_service': False, 'new_host': 'hostC',
         'host_support': True, 'exp_result': None},
    )
    @ddt.unpack
    def test__do_update_validate_hosts_all(
            self, update_security_service, new_host, host_support, exp_result):
        curr_sec_service_id = None
        new_sec_service_id = None
        new_share_network_subnet = 'fake_new_share_network_subnet'
        if update_security_service:
            curr_sec_service_id = "fake_curr_sec_serv_id"
            new_sec_service_id = "fake_new_sec_serv_id"
            new_share_network_subnet = None

        fake_key = 'fake_key'
        fake_share_network_id = 'fake_network_id'
        backend_hosts = ['hostA', 'hostB']
        hosts_to_validate = {}
        for bh in backend_hosts:
            hosts_to_validate[bh] = host_support
        json_orig_hosts = json.dumps(hosts_to_validate)

        if new_host:
            backend_hosts.append(new_host)
            hosts_to_validate[new_host] = None

        mock_async_data_get = self.mock_object(
            db_api, 'async_operation_data_get',
            mock.Mock(return_value=json_orig_hosts))
        mock_async_data_update = self.mock_object(
            db_api, 'async_operation_data_update')
        mock_check_update_allocations = self.mock_object(
            self.share_rpcapi, 'check_update_share_server_network_allocations')
        mock_check_update_services = self.mock_object(
            self.share_rpcapi, 'check_update_share_network_security_service')

        result, hosts_info = self.api._do_update_validate_hosts(
            self.context, fake_share_network_id, backend_hosts, fake_key,
            new_share_network_subnet=new_share_network_subnet,
            new_security_service_id=new_sec_service_id,
            current_security_service_id=curr_sec_service_id)

        self.assertEqual(exp_result, result)
        self.assertEqual(hosts_to_validate, hosts_info)
        mock_async_data_get.assert_called_once_with(
            self.context, fake_share_network_id, fake_key)

        # we fail earlier if one one the host answer False.
        if new_host and host_support is not False:
            mock_async_data_update.assert_called_once_with(
                self.context, fake_share_network_id,
                {fake_key: json.dumps(hosts_to_validate)})
            if update_security_service:
                mock_check_update_services.assert_called_once_with(
                    self.context, new_host, fake_share_network_id,
                    new_sec_service_id,
                    current_security_service_id=curr_sec_service_id)
            else:
                mock_check_update_allocations.assert_called_once_with(
                    self.context, new_host, fake_share_network_id,
                    new_share_network_subnet)

    def test_soft_delete_share_already_soft_deleted(self):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 is_soft_deleted=True)
        self.assertRaises(exception.InvalidShare,
                          self.api.soft_delete, self.context, share)

    def test_soft_delete_invalid_status(self):
        invalid_status = 'fake'
        share = fakes.fake_share(id='fake_id',
                                 status=invalid_status,
                                 is_soft_deleted=False)

        self.assertRaises(exception.InvalidShare,
                          self.api.soft_delete, self.context, share)

    def test_soft_delete_share_with_replicas(self):
        share = fakes.fake_share(id='fake_id',
                                 has_replicas=True,
                                 status=constants.STATUS_AVAILABLE,
                                 is_soft_deleted=False)

        self.assertRaises(exception.Conflict,
                          self.api.soft_delete, self.context, share)

    def test_soft_delete_share_with_snapshot(self):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 has_replicas=False,
                                 is_soft_deleted=False)
        snapshot = fakes.fake_snapshot(create_instance=True, as_primitive=True)
        mock_db_snapshot_call = self.mock_object(
            db_api, 'share_snapshot_get_all_for_share', mock.Mock(
                return_value=[snapshot]))

        self.assertRaises(exception.InvalidShare,
                          self.api.soft_delete, self.context, share)

        mock_db_snapshot_call.assert_called_once_with(
            self.context, share['id'])

    @mock.patch.object(db_api, 'count_share_group_snapshot_members_in_share',
                       mock.Mock(return_value=2))
    def test_soft_delete_share_with_group_snapshot_members(self):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 has_replicas=False,
                                 is_soft_deleted=False)
        self.mock_object(db_api, 'share_backups_get_all',
                         mock.Mock(return_value=[]))

        self.assertRaises(exception.InvalidShare,
                          self.api.soft_delete, self.context, share)

    def test_soft_delete_locked_share(self):
        self.mock_object(
            self.api.db,
            'resource_lock_get_all',
            mock.Mock(return_value=([{'id': 'l1'}, {'id': 'l2'}], None))
        )
        share = self._setup_delete_mocks('available')
        self.mock_object(db_api, 'share_soft_delete')

        self.assertRaises(exception.InvalidShare,
                          self.api.soft_delete,
                          self.context,
                          share)

        # lock check decorator executed first, nothing else is invoked
        db_api.share_soft_delete.assert_not_called()
        db_api.share_snapshot_get_all_for_share.assert_not_called()

    def test_soft_delete_share(self):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 has_replicas=False,
                                 is_soft_deleted=False)
        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=[]))
        self.mock_object(db_api, 'share_backups_get_all',
                         mock.Mock(return_value=[]))
        self.mock_object(db_api, 'count_share_group_snapshot_members_in_share',
                         mock.Mock(return_value=0))
        self.mock_object(db_api, 'share_soft_delete')
        self.mock_object(self.api, '_check_is_share_busy')
        self.api.soft_delete(self.context, share)
        self.api._check_is_share_busy.assert_called_once_with(share)

    def test_restore_share(self):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 is_soft_deleted=True)
        self.mock_object(db_api, 'share_restore')
        self.api.restore(self.context, share)

    def test__share_server_update_allocations_validate_hosts(self):
        update_return = 'fake_return'
        mock_do_update = self.mock_object(
            self.api, '_do_update_validate_hosts',
            mock.Mock(return_value=update_return))

        backend_hosts = 'fake_hosts'
        update_key = 'fake_key'
        share_network_id = 'fake_net_id'
        subnet = {
            'neutron_net_id': 'fake_net_id',
            'neutron_subnet_id': 'fake_subnet_id',
            'availability_zone_id': 'fake_availability_zone_id',
        }
        res = self.api._share_server_update_allocations_validate_hosts(
            self.context, backend_hosts, update_key,
            share_network_id=share_network_id,
            neutron_net_id=subnet['neutron_net_id'],
            neutron_subnet_id=subnet['neutron_subnet_id'],
            availability_zone_id=subnet['availability_zone_id'])

        self.assertEqual(update_return, res)
        mock_do_update.assert_called_once_with(
            self.context, share_network_id, backend_hosts, update_key,
            new_share_network_subnet=subnet)

    def test_get_share_server_update_allocations_key(self):
        availability_zone_id = None
        share_network_id = 'fake_share_network_id'
        expected_key = ('share_server_update_allocations_' +
                        share_network_id + '_' + str(availability_zone_id) +
                        '_' + 'hosts_check')
        res = self.api.get_share_server_update_allocations_key(
            share_network_id, availability_zone_id)

        self.assertEqual(expected_key, res)

    def test__share_server_update_allocations_initial_checks(self):
        share_network = db_utils.create_share_network()
        share1 = db_utils.create_share(
            share_network_id=share_network['id'],
            status=constants.STATUS_AVAILABLE)
        server_host = 'fake_host'
        share_server = db_utils.create_share_server(host=server_host)
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host')
        mock_share_get_all_by_share_server = self.mock_object(
            self.api.db, 'share_get_all_by_share_server',
            mock.Mock(return_value=[share1]))
        mock_share_is_busy = self.mock_object(
            self.api, '_check_is_share_busy')

        res_hosts = self.api._share_server_update_allocations_initial_checks(
            self.context, share_network, [share_server])

        self.assertEqual(set([server_host]), res_hosts)
        mock_validate_service_host.assert_called_once()
        mock_share_get_all_by_share_server.assert_called_once_with(
            self.context, share_server['id'])
        mock_share_is_busy.assert_called_once_with(share1)

    def test__share_server_update_allocations_initial_checks_no_support(self):
        fake_share_network = {
            'id': 'fake_sn_id',
            'network_allocation_update_support': False,
            'status': constants.STATUS_NETWORK_ACTIVE,
        }
        sn_subnet = db_utils.create_share_network_subnet()

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_server_update_allocations_initial_checks,
            self.context,
            fake_share_network,
            sn_subnet)

    def test__share_server_update_allocations_initial_checks_inactive(self):
        share_network = db_utils.create_share_network()
        share_server = db_utils.create_share_server(
            status=constants.STATUS_INACTIVE)

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_server_update_allocations_initial_checks,
            self.context,
            share_network,
            [share_server])

    def test__share_server_update_allocations_initial_checks_shares_na(self):
        share_network = db_utils.create_share_network()
        share1 = db_utils.create_share(
            share_network_id=share_network['id'],
            status=constants.STATUS_ERROR)
        share_server = db_utils.create_share_server()
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host')
        mock_share_get_all_by_share_server = self.mock_object(
            self.api.db, 'share_get_all_by_share_server',
            mock.Mock(return_value=[share1]))

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_server_update_allocations_initial_checks,
            self.context,
            share_network,
            [share_server])

        mock_validate_service_host.assert_called_once()
        mock_share_get_all_by_share_server.assert_called_once_with(
            self.context, share_server['id'])

    def test__share_server_update_allocations_initial_checks_rules_na(self):
        share_network = db_utils.create_share_network()
        share1 = db_utils.create_share(
            share_network_id=share_network['id'],
            status=constants.STATUS_AVAILABLE)
        share_server = db_utils.create_share_server()
        share1['instance']['access_rules_status'] = constants.STATUS_INACTIVE
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host')
        mock_share_get_all_by_share_server = self.mock_object(
            self.api.db, 'share_get_all_by_share_server',
            mock.Mock(return_value=[share1]))

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_server_update_allocations_initial_checks,
            self.context,
            share_network,
            [share_server])

        mock_validate_service_host.assert_called_once()
        mock_share_get_all_by_share_server.assert_called_once_with(
            self.context, share_server['id'])

    def test__share_server_update_allocations_initial_checks_share_busy(self):
        share_network = db_utils.create_share_network()
        share1 = db_utils.create_share(
            share_network_id=share_network['id'],
            status=constants.STATUS_AVAILABLE)
        share_server = db_utils.create_share_server()
        mock_validate_service_host = self.mock_object(
            utils, 'validate_service_host')
        mock_share_get_all_by_share_server = self.mock_object(
            self.api.db, 'share_get_all_by_share_server',
            mock.Mock(return_value=[share1]))
        mock_share_is_busy = self.mock_object(
            self.api, '_check_is_share_busy',
            mock.Mock(side_effect=exception.ShareBusyException(message='fake'))
        )

        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api._share_server_update_allocations_initial_checks,
            self.context,
            share_network,
            [share_server])

        mock_validate_service_host.assert_called_once()
        mock_share_get_all_by_share_server.assert_called_once_with(
            self.context, share_server['id'])
        mock_share_is_busy.assert_called_once_with(share1)

    def test_check_update_share_server_network_allocations(self):
        backend_hosts = 'fake_hosts'
        mock_initial_check = self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        mock_get_key = self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        mock_reset_data = self.mock_object(
            self.api.db, 'async_operation_data_delete')
        compatible = True
        hosts_info = {'fake_host': True}
        mock_validate_hosts = self.mock_object(
            self.api, '_share_server_update_allocations_validate_hosts',
            mock.Mock(return_value=(compatible, hosts_info)))

        share_network = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': 'fake_servers',
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        res = self.api.check_update_share_server_network_allocations(
            self.context, share_network, new_share_network_subnet, True)

        self.assertEqual(
            {'compatible': compatible, 'hosts_check_result': hosts_info}, res)
        mock_initial_check.assert_called_once_with(
            self.context, share_network,
            new_share_network_subnet['share_servers'])
        mock_get_key.assert_called_once_with(
            share_network['id'],
            new_share_network_subnet['availability_zone_id'])
        mock_reset_data.assert_called_once_with(
            self.context, share_network['id'], update_key)
        mock_validate_hosts.assert_called_once_with(
            self.context, backend_hosts, update_key,
            share_network_id=share_network['id'],
            neutron_net_id=new_share_network_subnet['neutron_net_id'],
            neutron_subnet_id=new_share_network_subnet['neutron_subnet_id'],
            availability_zone_id=(
                new_share_network_subnet["availability_zone_id"]))

    def test_check_update_share_server_network_allocations_failed(self):
        backend_hosts = 'fake_hosts'
        self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        self.mock_object(self.api.db, 'async_operation_data_delete')
        self.mock_object(
            self.api, '_share_server_update_allocations_validate_hosts',
            mock.Mock(side_effect=exception.InvalidShareNetwork(reason="msg")))

        share_network = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': 'fake_servers',
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api.check_update_share_server_network_allocations,
            self.context, share_network, new_share_network_subnet, True)

    def test_update_share_server_network_allocations(self):
        backend_host = 'fake_host'
        backend_hosts = [backend_host]
        mock_initial_check = self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        mock_get_key = self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        mock_get_data = self.mock_object(
            self.api.db, 'async_operation_data_get',
            mock.Mock(return_value='fake_update_value'))
        mock_validate_hosts = self.mock_object(
            self.api, '_share_server_update_allocations_validate_hosts',
            mock.Mock(return_value=(True, 'fake_host')))
        mock_net_update = self.mock_object(self.api.db, 'share_network_update')
        mock_server_update = self.mock_object(self.api.db,
                                              'share_servers_update')
        new_share_network_subnet_db = {'id': 'fake_subnet_id'}
        mock_subnet_create = self.mock_object(
            self.api.db, 'share_network_subnet_create',
            mock.Mock(return_value=new_share_network_subnet_db))
        mock_update_allocations = self.mock_object(
            self.api.share_rpcapi, 'update_share_server_network_allocations')
        mock_delete_data = self.mock_object(self.api.db,
                                            'async_operation_data_delete')

        share_network = {'id': 'fake_id'}
        server1 = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': [server1],
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        res_subnet = self.api.update_share_server_network_allocations(
            self.context, share_network, new_share_network_subnet)

        self.assertEqual(new_share_network_subnet_db, res_subnet)
        mock_initial_check.assert_called_once_with(
            self.context, share_network,
            new_share_network_subnet['share_servers'])
        mock_get_key.assert_called_once_with(
            share_network['id'],
            new_share_network_subnet['availability_zone_id'])
        mock_get_data.assert_called_once_with(
            self.context, share_network['id'], update_key)
        mock_validate_hosts.assert_called_once_with(
            self.context, backend_hosts, update_key,
            share_network_id=share_network['id'],
            neutron_net_id=new_share_network_subnet['neutron_net_id'],
            neutron_subnet_id=new_share_network_subnet['neutron_subnet_id'],
            availability_zone_id=(
                new_share_network_subnet["availability_zone_id"]))
        mock_net_update.assert_called_once_with(
            self.context, share_network['id'],
            {'status': constants.STATUS_NETWORK_CHANGE})
        mock_server_update.assert_called_once_with(
            self.context, [server1['id']],
            {'status': constants.STATUS_SERVER_NETWORK_CHANGE})
        mock_subnet_create.assert_called_once_with(
            self.context, new_share_network_subnet)
        mock_update_allocations.assert_called_once_with(
            self.context, backend_host, share_network['id'],
            new_share_network_subnet_db['id'])
        mock_delete_data.assert_called_once_with(
            self.context, share_network['id'], update_key)

    def test_update_share_server_network_allocations_no_check(self):
        backend_host = 'fake_host'
        backend_hosts = [backend_host]
        self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        self.mock_object(
            self.api.db, 'async_operation_data_get',
            mock.Mock(return_value=None))

        share_network = {'id': 'fake_id'}
        server1 = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': [server1],
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api.update_share_server_network_allocations,
            self.context,
            share_network,
            new_share_network_subnet)

    def test_update_share_server_network_allocations_fail_validation(self):
        backend_host = 'fake_host'
        backend_hosts = [backend_host]
        self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        self.mock_object(
            self.api.db, 'async_operation_data_get',
            mock.Mock(return_value='fake_update_value'))
        self.mock_object(
            self.api, '_share_server_update_allocations_validate_hosts',
            mock.Mock(side_effect=exception.InvalidShareNetwork(
                reason='fake_reason')))
        mock_delete_data = self.mock_object(self.api.db,
                                            'async_operation_data_delete')

        share_network = {'id': 'fake_id'}
        server1 = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': [server1],
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api.update_share_server_network_allocations,
            self.context,
            share_network,
            new_share_network_subnet)

        mock_delete_data.assert_called_once_with(
            self.context, share_network['id'], update_key)

    @ddt.data(False, None)
    def test_update_share_server_network_allocations_check_fail(self, result):
        backend_host = 'fake_host'
        backend_hosts = [backend_host]
        self.mock_object(
            self.api, '_share_server_update_allocations_initial_checks',
            mock.Mock(return_value=backend_hosts))
        update_key = 'fake_key'
        self.mock_object(
            self.api, 'get_share_server_update_allocations_key',
            mock.Mock(return_value=update_key))
        self.mock_object(
            self.api.db, 'async_operation_data_get',
            mock.Mock(return_value='fake_update_value'))
        self.mock_object(
            self.api, '_share_server_update_allocations_validate_hosts',
            mock.Mock(return_value=(result, 'fake_host')))

        share_network = {'id': 'fake_id'}
        server1 = {'id': 'fake_id'}
        new_share_network_subnet = {
            'share_servers': [server1],
            'availability_zone_id': 'fake_availability_zone_id',
            'neutron_net_id': 'fake_neutron_net_id',
            'neutron_subnet_id': 'fake_neutron_subnet_id',
        }
        self.assertRaises(
            exception.InvalidShareNetwork,
            self.api.update_share_server_network_allocations,
            self.context,
            share_network,
            new_share_network_subnet)

    @ddt.data(None, {'driver': test})
    def test_create_share_backup(self, backup_opts):
        share = db_utils.create_share(is_public=True, status='available')
        backup_ref = db_utils.create_backup(share['id'], status='available')

        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(quota.QUOTAS, 'commit')
        self.mock_object(db_api, 'share_backup_create',
                         mock.Mock(return_value=backup_ref))
        self.mock_object(db_api, 'share_backup_update', mock.Mock())
        self.mock_object(data_rpc.DataAPI, 'create_backup', mock.Mock())
        self.mock_object(self.share_rpcapi, 'create_backup', mock.Mock())

        backup = {
            'display_name': 'tmp_backup',
            'backup_options': backup_opts,
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }
        self.api.create_share_backup(self.context, share, backup)

        quota.QUOTAS.reserve.assert_called_once()
        db_api.share_backup_create.assert_called_once()
        quota.QUOTAS.commit.assert_called_once()
        db_api.share_backup_update.assert_called_once()
        if backup_opts:
            self.share_rpcapi.create_backup.assert_called_once_with(
                self.context, backup_ref)
        else:
            data_rpc.DataAPI.create_backup.assert_called_once_with(
                self.context, backup_ref)

    def test_create_share_backup_share_error_state(self):
        share = db_utils.create_share(is_public=True, status='error')
        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }
        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_backup,
                          self.context, share, backup)

    def test_create_share_backup_share_busy_task_state(self):
        share = db_utils.create_share(
            is_public=True, task_state='data_copying_in_progress')
        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }

        self.assertRaises(exception.ShareBusyException,
                          self.api.create_share_backup,
                          self.context, share, backup)

    def test_create_share_backup_share_has_snapshots(self):
        share = db_utils.create_share(
            is_public=True, state='available')
        snapshot = db_utils.create_snapshot(
            share_id=share['id'], status='available', size=1)

        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }

        self.mock_object(db_api, 'share_snapshot_get_all_for_share',
                         mock.Mock(return_value=[snapshot]))

        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_backup,
                          self.context, share, backup)

    def test_create_share_backup_share_has_replicas(self):
        share = fakes.fake_share(id='fake_id',
                                 has_replicas=True,
                                 status=constants.STATUS_AVAILABLE,
                                 is_soft_deleted=False)
        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }

        self.assertRaises(exception.InvalidShare,
                          self.api.create_share_backup,
                          self.context, share, backup)

    @ddt.data({'overs': {'backup_gigabytes': 'fake'},
               'expected_exception':
                   exception.ShareBackupSizeExceedsAvailableQuota},
              {'overs': {'backups': 'fake'},
               'expected_exception': exception.BackupLimitExceeded},)
    @ddt.unpack
    def test_create_share_backup_over_quota(self, overs, expected_exception):
        share = fakes.fake_share(id='fake_id',
                                 status=constants.STATUS_AVAILABLE,
                                 is_soft_deleted=False, size=5)
        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }

        usages = {'backup_gigabytes': {'reserved': 5, 'in_use': 5},
                  'backups': {'reserved': 5, 'in_use': 5}}

        quotas = {'backup_gigabytes': 5, 'backups': 5}

        exc = exception.OverQuota(overs=overs, usages=usages, quotas=quotas)
        self.mock_object(quota.QUOTAS, 'reserve', mock.Mock(side_effect=exc))

        self.assertRaises(expected_exception, self.api.create_share_backup,
                          self.context, share, backup)

        quota.QUOTAS.reserve.assert_called_once_with(
            self.context, backups=1, backup_gigabytes=share['size'])

    def test_create_share_backup_rollback_quota(self):
        share = db_utils.create_share(is_public=True, status='available')

        reservation = 'fake'
        self.mock_object(quota.QUOTAS, 'reserve',
                         mock.Mock(return_value=reservation))
        self.mock_object(quota.QUOTAS, 'rollback')
        self.mock_object(db_api, 'share_backup_create',
                         mock.Mock(side_effect=exception.ManilaException))
        self.mock_object(data_rpc.DataAPI, 'create_backup', mock.Mock())
        self.mock_object(self.share_rpcapi, 'create_backup', mock.Mock())

        backup = {
            'display_name': 'tmp_backup',
            'id': 'dbe28dff-ce7d-4c3d-a795-c31f6fee31ab',
        }
        self.assertRaises(exception.ManilaException,
                          self.api.create_share_backup,
                          self.context, share, backup)

        quota.QUOTAS.reserve.assert_called_once()
        db_api.share_backup_create.assert_called_once()
        quota.QUOTAS.rollback.assert_called_once_with(
            self.context, reservation)

    @ddt.data(CONF.share_topic, CONF.data_topic)
    def test_delete_share_backup(self, topic):
        share = db_utils.create_share(is_public=True, status='available')
        backup = db_utils.create_backup(share['id'], status='available')

        self.mock_object(db_api, 'share_backup_update', mock.Mock())
        self.mock_object(data_rpc.DataAPI, 'delete_backup', mock.Mock())
        self.mock_object(self.share_rpcapi, 'delete_backup', mock.Mock())

        backup.update({'topic': topic})
        self.api.delete_share_backup(self.context, backup)

        db_api.share_backup_update.assert_called_once()
        if topic == CONF.share_topic:
            self.share_rpcapi.delete_backup.assert_called_once_with(
                self.context, backup)
        else:
            data_rpc.DataAPI.delete_backup.assert_called_once_with(
                self.context, backup)

    @ddt.data(constants.STATUS_DELETING, constants.STATUS_CREATING)
    def test_delete_share_backup_invalid_state(self, state):
        share = db_utils.create_share(is_public=True, status='available')
        backup = db_utils.create_backup(share['id'], status=state)
        self.assertRaises(exception.InvalidBackup,
                          self.api.delete_share_backup,
                          self.context, backup)

    @ddt.data(CONF.share_topic, CONF.data_topic)
    def test_restore_share_backup(self, topic):
        share = db_utils.create_share(
            is_public=True, status='available', size=1)
        backup = db_utils.create_backup(
            share['id'], status='available', size=1)

        self.mock_object(self.api, 'get', mock.Mock(return_value=share))
        self.mock_object(db_api, 'share_backup_update', mock.Mock())
        self.mock_object(db_api, 'share_update', mock.Mock())
        self.mock_object(data_rpc.DataAPI, 'restore_backup', mock.Mock())
        self.mock_object(self.share_rpcapi, 'restore_backup', mock.Mock())

        backup.update({'topic': topic})
        self.api.restore_share_backup(self.context, backup)

        self.api.get.assert_called_once()
        db_api.share_update.assert_called_once()
        db_api.share_backup_update.assert_called_once()
        if topic == CONF.share_topic:
            self.share_rpcapi.restore_backup.assert_called_once_with(
                self.context, backup, share['id'])
        else:
            data_rpc.DataAPI.restore_backup.assert_called_once_with(
                self.context, backup, share['id'])

    def test_restore_share_backup_invalid_share_sizee(self):
        share = db_utils.create_share(
            is_public=True, status='available', size=1)
        backup = db_utils.create_backup(
            share['id'], status='available', size=2)
        self.assertRaises(exception.InvalidShare,
                          self.api.restore_share_backup,
                          self.context, backup)

    def test_restore_share_backup_invalid_share_state(self):
        share = db_utils.create_share(is_public=True, status='deleting')
        backup = db_utils.create_backup(share['id'], status='available')
        self.assertRaises(exception.InvalidShare,
                          self.api.restore_share_backup,
                          self.context, backup)

    def test_restore_share_backup_invalid_backup_state(self):
        share = db_utils.create_share(is_public=True, status='available')
        backup = db_utils.create_backup(share['id'], status='deleting')
        self.assertRaises(exception.InvalidBackup,
                          self.api.restore_share_backup,
                          self.context, backup)

    def test_update_share_backup(self):
        share = db_utils.create_share(is_public=True, status='available')
        backup = db_utils.create_backup(share['id'], status='available')
        self.mock_object(db_api, 'share_backup_update', mock.Mock())

        self.api.update_share_backup(self.context, backup,
                                     {'display_name': 'new_name'})

        db_api.share_backup_update.assert_called_once()


class OtherTenantsShareActionsTestCase(test.TestCase):
    def setUp(self):
        super(OtherTenantsShareActionsTestCase, self).setUp()
        self.api = share.API()

    def test_delete_other_tenants_public_share(self):
        share = db_utils.create_share(is_public=True)
        ctx = context.RequestContext(user_id='1111', project_id='2222')
        self.assertRaises(exception.PolicyNotAuthorized, self.api.delete, ctx,
                          share)

    def test_update_other_tenants_public_share(self):
        share = db_utils.create_share(is_public=True)
        ctx = context.RequestContext(user_id='1111', project_id='2222')
        self.assertRaises(exception.PolicyNotAuthorized, self.api.update, ctx,
                          share, {'display_name': 'newname'})

    def test_get_other_tenants_public_share(self):
        share = db_utils.create_share(is_public=True)
        ctx = context.RequestContext(user_id='1111', project_id='2222')
        self.mock_object(db_api, 'share_get',
                         mock.Mock(return_value=share))
        result = self.api.get(ctx, 'fakeid')
        self.assertEqual(share, result)
        db_api.share_get.assert_called_once_with(ctx, 'fakeid')
