Merge "NetApp cDOT: Fix status updates for replicas"
This commit is contained in:
@@ -774,3 +774,7 @@ class ShareReplicaNotFound(NotFound):
|
||||
class TegileAPIException(ShareBackendException):
|
||||
message = _("Unexpected response from Tegile IntelliFlash API: "
|
||||
"%(response)s")
|
||||
|
||||
|
||||
class StorageCommunicationException(ShareBackendException):
|
||||
message = _("Could not communicate with storage array.")
|
||||
|
||||
@@ -28,7 +28,6 @@ from six.moves import urllib
|
||||
from manila import exception
|
||||
from manila.i18n import _
|
||||
|
||||
|
||||
LOG = log.getLogger(__name__)
|
||||
|
||||
EONTAPI_EINVAL = '22'
|
||||
@@ -237,8 +236,10 @@ class NaServer(object):
|
||||
response = self._opener.open(request)
|
||||
except urllib.error.HTTPError as e:
|
||||
raise NaApiError(e.code, e.msg)
|
||||
except urllib.error.URLError as e:
|
||||
raise exception.StorageCommunicationException(six.text_type(e))
|
||||
except Exception as e:
|
||||
raise NaApiError('Unexpected error', e)
|
||||
raise NaApiError(message=e)
|
||||
|
||||
response_xml = response.read()
|
||||
response_element = self._get_result(response_xml)
|
||||
@@ -380,7 +381,7 @@ class NaElement(object):
|
||||
if isinstance(na_element, NaElement):
|
||||
self._element.append(na_element._element)
|
||||
return
|
||||
raise
|
||||
raise ValueError(_("Can only add elements of type NaElement."))
|
||||
|
||||
def get_child_by_name(self, name):
|
||||
"""Get the child element by the tag name."""
|
||||
|
||||
@@ -1159,6 +1159,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
vserver_client = data_motion.get_client_for_backend(
|
||||
dest_backend, vserver_name=vserver)
|
||||
share_name = self._get_backend_share_name(replica['id'])
|
||||
if self._share_exists(share_name, vserver_client):
|
||||
self._deallocate_container(share_name, vserver_client)
|
||||
|
||||
def update_replica_state(self, context, replica_list, replica,
|
||||
@@ -1168,6 +1169,13 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
|
||||
share_name = self._get_backend_share_name(replica['id'])
|
||||
vserver, vserver_client = self._get_vserver(share_server=share_server)
|
||||
|
||||
if not vserver_client.volume_exists(share_name):
|
||||
msg = _("Volume %(share_name)s does not exist on vserver "
|
||||
"%(vserver)s.")
|
||||
msg_args = {'share_name': share_name, 'vserver': vserver}
|
||||
raise exception.ShareResourceNotFound(msg % msg_args)
|
||||
|
||||
dm_session = data_motion.DataMotionSession()
|
||||
try:
|
||||
snapmirrors = dm_session.get_snapmirrors(active_replica, replica)
|
||||
@@ -1242,10 +1250,21 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
new_replica_list = []
|
||||
|
||||
# Setup the new active replica
|
||||
try:
|
||||
new_active_replica = (
|
||||
self._convert_destination_replica_to_independent(
|
||||
context, dm_session, orig_active_replica, replica,
|
||||
access_rules, share_server=share_server))
|
||||
except exception.StorageCommunicationException:
|
||||
LOG.exception(_LE("Could not communicate with the backend "
|
||||
"for replica %s during promotion."),
|
||||
replica['id'])
|
||||
new_active_replica = copy.deepcopy(replica)
|
||||
new_active_replica['replica_state'] = (
|
||||
constants.STATUS_ERROR)
|
||||
new_active_replica['status'] = constants.STATUS_ERROR
|
||||
return [new_active_replica]
|
||||
|
||||
new_replica_list.append(new_active_replica)
|
||||
|
||||
# Change the source replica for all destinations to the new
|
||||
@@ -1283,7 +1302,7 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
# 1. Start an update to try to get a last minute transfer before we
|
||||
# quiesce and break
|
||||
dm_session.update_snapmirror(orig_active_replica, replica)
|
||||
except netapp_api.NaApiError:
|
||||
except exception.StorageCommunicationException:
|
||||
# Ignore any errors since the current source replica may be
|
||||
# unreachable
|
||||
pass
|
||||
@@ -1324,12 +1343,21 @@ class NetAppCmodeFileStorageLibrary(object):
|
||||
orig_source_replica,
|
||||
new_source_replica,
|
||||
replica_list)
|
||||
except Exception:
|
||||
except exception.StorageCommunicationException:
|
||||
replica['status'] = constants.STATUS_ERROR
|
||||
replica['replica_state'] = constants.STATUS_ERROR
|
||||
replica['export_locations'] = []
|
||||
msg = _LE("Failed to change replica (%s) to a SnapMirror "
|
||||
"destination."), replica['id']
|
||||
LOG.exception(msg)
|
||||
"destination. Replica backend is unreachable.")
|
||||
|
||||
LOG.exception(msg, replica['id'])
|
||||
return replica
|
||||
except netapp_api.NaApiError:
|
||||
replica['replica_state'] = constants.STATUS_ERROR
|
||||
replica['export_locations'] = []
|
||||
msg = _LE("Failed to change replica (%s) to a SnapMirror "
|
||||
"destination.")
|
||||
LOG.exception(msg, replica['id'])
|
||||
return replica
|
||||
|
||||
replica['replica_state'] = constants.REPLICA_STATE_OUT_OF_SYNC
|
||||
|
||||
@@ -13,6 +13,10 @@
|
||||
# under the License.
|
||||
|
||||
from lxml import etree
|
||||
import mock
|
||||
from six.moves import urllib
|
||||
|
||||
from manila.share.drivers.netapp.dataontap.client import api
|
||||
|
||||
|
||||
CONNECTION_INFO = {
|
||||
@@ -1879,3 +1883,75 @@ SNAPMIRROR_INITIALIZE_RESULT = etree.XML("""
|
||||
<result-status>succeeded</result-status>
|
||||
</results>
|
||||
""")
|
||||
|
||||
FAKE_VOL_XML = """<volume-info xmlns='http://www.netapp.com/filer/admin'>
|
||||
<name>open123</name>
|
||||
<state>online</state>
|
||||
<size-total>0</size-total>
|
||||
<size-used>0</size-used>
|
||||
<size-available>0</size-available>
|
||||
<is-inconsistent>false</is-inconsistent>
|
||||
<is-invalid>false</is-invalid>
|
||||
</volume-info>"""
|
||||
|
||||
FAKE_XML1 = """<options>\
|
||||
<test1>abc</test1>\
|
||||
<test2>abc</test2>\
|
||||
</options>"""
|
||||
|
||||
FAKE_XML2 = """<root><options>somecontent</options></root>"""
|
||||
|
||||
FAKE_NA_ELEMENT = api.NaElement(etree.XML(FAKE_VOL_XML))
|
||||
|
||||
FAKE_INVOKE_DATA = 'somecontent'
|
||||
|
||||
FAKE_XML_STR = 'abc'
|
||||
|
||||
FAKE_API_NAME = 'volume-get-iter'
|
||||
|
||||
FAKE_API_NAME_ELEMENT = api.NaElement(FAKE_API_NAME)
|
||||
|
||||
FAKE_NA_SERVER_STR = '127.0.0.1'
|
||||
|
||||
FAKE_NA_SERVER = api.NaServer(FAKE_NA_SERVER_STR)
|
||||
|
||||
FAKE_NA_SERVER_API_1_5 = api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_5.set_vfiler('filer')
|
||||
FAKE_NA_SERVER_API_1_5.set_api_version(1, 5)
|
||||
|
||||
|
||||
FAKE_NA_SERVER_API_1_14 = api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_14.set_vserver('server')
|
||||
FAKE_NA_SERVER_API_1_14.set_api_version(1, 14)
|
||||
|
||||
|
||||
FAKE_NA_SERVER_API_1_20 = api.NaServer(FAKE_NA_SERVER_STR)
|
||||
FAKE_NA_SERVER_API_1_20.set_vfiler('filer')
|
||||
FAKE_NA_SERVER_API_1_20.set_vserver('server')
|
||||
FAKE_NA_SERVER_API_1_20.set_api_version(1, 20)
|
||||
|
||||
|
||||
FAKE_QUERY = {'volume-attributes': None}
|
||||
|
||||
FAKE_DES_ATTR = {'volume-attributes': ['volume-id-attributes',
|
||||
'volume-space-attributes',
|
||||
'volume-state-attributes',
|
||||
'volume-qos-attributes']}
|
||||
|
||||
FAKE_CALL_ARGS_LIST = [mock.call(80), mock.call(8088), mock.call(443),
|
||||
mock.call(8488)]
|
||||
|
||||
FAKE_RESULT_API_ERR_REASON = api.NaElement('result')
|
||||
FAKE_RESULT_API_ERR_REASON.add_attr('errno', '000')
|
||||
FAKE_RESULT_API_ERR_REASON.add_attr('reason', 'fake_reason')
|
||||
|
||||
FAKE_RESULT_API_ERRNO_INVALID = api.NaElement('result')
|
||||
FAKE_RESULT_API_ERRNO_INVALID.add_attr('errno', '000')
|
||||
|
||||
FAKE_RESULT_API_ERRNO_VALID = api.NaElement('result')
|
||||
FAKE_RESULT_API_ERRNO_VALID.add_attr('errno', '14956')
|
||||
|
||||
FAKE_RESULT_SUCCESS = api.NaElement('result')
|
||||
FAKE_RESULT_SUCCESS.add_attr('status', 'passed')
|
||||
|
||||
FAKE_HTTP_OPENER = urllib.request.build_opener()
|
||||
|
||||
@@ -18,9 +18,14 @@
|
||||
"""
|
||||
Tests for NetApp API layer
|
||||
"""
|
||||
import ddt
|
||||
import mock
|
||||
from six.moves import urllib
|
||||
|
||||
from manila import exception
|
||||
from manila.share.drivers.netapp.dataontap.client import api
|
||||
from manila import test
|
||||
from manila.tests.share.drivers.netapp.dataontap.client import fakes as fake
|
||||
|
||||
|
||||
class NetAppApiElementTransTests(test.TestCase):
|
||||
@@ -154,3 +159,82 @@ class NetAppApiElementTransTests(test.TestCase):
|
||||
api.NaElement('root').__setitem__,
|
||||
None,
|
||||
'value')
|
||||
|
||||
|
||||
@ddt.ddt
|
||||
class NetAppApiServerTests(test.TestCase):
|
||||
"""Test case for NetApp API server methods"""
|
||||
def setUp(self):
|
||||
self.root = api.NaServer('127.0.0.1')
|
||||
super(NetAppApiServerTests, self).setUp()
|
||||
|
||||
@ddt.data(None, fake.FAKE_XML_STR)
|
||||
def test_invoke_elem_value_error(self, na_element):
|
||||
"""Tests whether invalid NaElement parameter causes error"""
|
||||
|
||||
self.assertRaises(ValueError, self.root.invoke_elem, na_element)
|
||||
|
||||
def test_invoke_elem_http_error(self):
|
||||
"""Tests handling of HTTPError"""
|
||||
na_element = fake.FAKE_NA_ELEMENT
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', fake.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(api, 'LOG')
|
||||
self.root._opener = fake.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root._opener, 'open', mock.Mock(
|
||||
side_effect=urllib.error.HTTPError(url='', hdrs='',
|
||||
fp=None, code='401',
|
||||
msg='httperror')))
|
||||
|
||||
self.assertRaises(api.NaApiError, self.root.invoke_elem,
|
||||
na_element)
|
||||
|
||||
def test_invoke_elem_urlerror(self):
|
||||
"""Tests handling of URLError"""
|
||||
na_element = fake.FAKE_NA_ELEMENT
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', fake.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(api, 'LOG')
|
||||
self.root._opener = fake.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root._opener, 'open', mock.Mock(
|
||||
side_effect=urllib.error.URLError(reason='urlerror')))
|
||||
|
||||
self.assertRaises(exception.StorageCommunicationException,
|
||||
self.root.invoke_elem,
|
||||
na_element)
|
||||
|
||||
def test_invoke_elem_unknown_exception(self):
|
||||
"""Tests handling of Unknown Exception"""
|
||||
na_element = fake.FAKE_NA_ELEMENT
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', fake.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(api, 'LOG')
|
||||
self.root._opener = fake.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root._opener, 'open', mock.Mock(
|
||||
side_effect=Exception))
|
||||
|
||||
exception = self.assertRaises(api.NaApiError, self.root.invoke_elem,
|
||||
na_element)
|
||||
self.assertEqual('unknown', exception.code)
|
||||
|
||||
def test_invoke_elem_valid(self):
|
||||
"""Tests the method invoke_elem with valid parameters"""
|
||||
na_element = fake.FAKE_NA_ELEMENT
|
||||
self.root._trace = True
|
||||
self.mock_object(self.root, '_create_request', mock.Mock(
|
||||
return_value=('abc', fake.FAKE_NA_ELEMENT)))
|
||||
self.mock_object(api, 'LOG')
|
||||
self.root._opener = fake.FAKE_HTTP_OPENER
|
||||
self.mock_object(self.root, '_build_opener')
|
||||
self.mock_object(self.root, '_get_result', mock.Mock(
|
||||
return_value=fake.FAKE_NA_ELEMENT))
|
||||
opener_mock = self.mock_object(
|
||||
self.root._opener, 'open', mock.Mock())
|
||||
opener_mock.read.side_effect = ['resp1', 'resp2']
|
||||
|
||||
self.root.invoke_elem(na_element)
|
||||
|
||||
self.assertEqual(2, api.LOG.debug.call_count)
|
||||
|
||||
@@ -2103,6 +2103,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
self.mock_object(self.library,
|
||||
'_deallocate_container',
|
||||
mock.Mock())
|
||||
self.mock_object(self.library,
|
||||
'_share_exists',
|
||||
mock.Mock(return_value=False))
|
||||
mock_dm_session = mock.Mock()
|
||||
self.mock_object(data_motion, "DataMotionSession",
|
||||
mock.Mock(return_value=mock_dm_session))
|
||||
@@ -2126,6 +2129,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
self.mock_object(self.library,
|
||||
'_deallocate_container',
|
||||
mock.Mock())
|
||||
self.mock_object(self.library,
|
||||
'_share_exists',
|
||||
mock.Mock(return_value=False))
|
||||
mock_dm_session = mock.Mock()
|
||||
self.mock_object(data_motion, "DataMotionSession",
|
||||
mock.Mock(return_value=mock_dm_session))
|
||||
@@ -2144,8 +2150,40 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
data_motion.get_client_for_backend.assert_called_once_with(
|
||||
fake.BACKEND_NAME, vserver_name=fake.VSERVER1)
|
||||
|
||||
def test_delete_replica_share_absent_on_backend(self):
|
||||
self.mock_object(self.library,
|
||||
'_deallocate_container',
|
||||
mock.Mock())
|
||||
self.mock_object(self.library,
|
||||
'_share_exists',
|
||||
mock.Mock(return_value=False))
|
||||
mock_dm_session = mock.Mock()
|
||||
self.mock_object(data_motion,
|
||||
"DataMotionSession",
|
||||
mock.Mock(return_value=mock_dm_session))
|
||||
self.mock_object(data_motion, 'get_client_for_backend')
|
||||
self.mock_object(mock_dm_session,
|
||||
'get_vserver_from_share',
|
||||
mock.Mock(return_value=fake.VSERVER1))
|
||||
|
||||
result = self.library.delete_replica(None,
|
||||
[fake.SHARE],
|
||||
fake.SHARE,
|
||||
share_server=None)
|
||||
|
||||
self.assertEqual(None, result)
|
||||
self.assertFalse(self.library._deallocate_container.called)
|
||||
mock_dm_session.delete_snapmirror.assert_called_with(fake.SHARE,
|
||||
fake.SHARE)
|
||||
self.assertEqual(2, mock_dm_session.delete_snapmirror.call_count)
|
||||
data_motion.get_client_for_backend.assert_called_with(
|
||||
fake.BACKEND_NAME, vserver_name=mock.ANY)
|
||||
self.assertEqual(1, data_motion.get_client_for_backend.call_count)
|
||||
|
||||
def test_update_replica_state_no_snapmirror_share_creating(self):
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2163,6 +2201,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
|
||||
def test_update_replica_state_no_snapmirror_create_failed(self):
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2183,6 +2223,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
@ddt.data(constants.STATUS_ERROR, constants.STATUS_AVAILABLE)
|
||||
def test_update_replica_state_no_snapmirror(self, status):
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2207,6 +2249,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'last-transfer-end-timestamp': '%s' % float(time.time() - 10000)
|
||||
}
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2233,6 +2277,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'last-transfer-end-timestamp': '%s' % float(time.time() - 10000)
|
||||
}
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2248,6 +2294,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
|
||||
def test_update_replica_state_fail_to_get_snapmirrors(self):
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2270,6 +2318,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'last-transfer-end-timestamp': '%s' % float(time.time() - 10000)
|
||||
}
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2295,6 +2345,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
timeutils.utcnow_ts() - 10000)
|
||||
}
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2315,6 +2367,8 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
'last-transfer-end-timestamp': '%s' % float(time.time())
|
||||
}
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=True))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
@@ -2328,6 +2382,20 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
|
||||
self.assertEqual(constants.REPLICA_STATE_IN_SYNC, result)
|
||||
|
||||
def test_update_replica_state_backend_volume_absent(self):
|
||||
vserver_client = mock.Mock()
|
||||
self.mock_object(vserver_client, 'volume_exists',
|
||||
mock.Mock(return_value=False))
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
vserver_client)))
|
||||
|
||||
self.assertRaises(exception.ShareResourceNotFound,
|
||||
self.library.update_replica_state,
|
||||
None, [fake.SHARE], fake.SHARE, None,
|
||||
share_server=None)
|
||||
|
||||
def test_promote_replica(self):
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
@@ -2362,6 +2430,31 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
self.assertEqual(constants.STATUS_ACTIVE,
|
||||
actual_replica_2['access_rules_status'])
|
||||
|
||||
def test_promote_replica_destination_unreachable(self):
|
||||
self.mock_object(self.library,
|
||||
'_get_vserver',
|
||||
mock.Mock(return_value=(fake.VSERVER1,
|
||||
mock.Mock())))
|
||||
self.mock_object(self.library,
|
||||
'_get_helper',
|
||||
mock.Mock(return_value=mock.Mock()))
|
||||
self.mock_object(self.library, '_create_export',
|
||||
mock.Mock(return_value='fake_export_location'))
|
||||
self.mock_object(
|
||||
self.library, '_convert_destination_replica_to_independent',
|
||||
mock.Mock(side_effect=exception.StorageCommunicationException))
|
||||
|
||||
replicas = self.library.promote_replica(
|
||||
None, [self.fake_replica, self.fake_replica_2],
|
||||
self.fake_replica_2, [], share_server=None)
|
||||
|
||||
self.assertEqual(1, len(replicas))
|
||||
actual_replica = replicas[0]
|
||||
self.assertEqual(constants.STATUS_ERROR,
|
||||
actual_replica['replica_state'])
|
||||
self.assertEqual(constants.STATUS_ERROR,
|
||||
actual_replica['status'])
|
||||
|
||||
def test_promote_replica_more_than_two_replicas(self):
|
||||
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
|
||||
fake_replica_3['id'] = fake.SHARE_ID3
|
||||
@@ -2466,8 +2559,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
mock.Mock(return_value=mock.Mock()))
|
||||
self.mock_object(self.library, '_create_export',
|
||||
mock.Mock(return_value='fake_export_location'))
|
||||
self.mock_object(self.mock_dm_session, 'update_snapmirror',
|
||||
mock.Mock(side_effect=netapp_api.NaApiError(code=0)))
|
||||
self.mock_object(
|
||||
self.mock_dm_session, 'update_snapmirror',
|
||||
mock.Mock(side_effect=exception.StorageCommunicationException))
|
||||
|
||||
replica = self.library._convert_destination_replica_to_independent(
|
||||
None, self.mock_dm_session, self.fake_replica,
|
||||
@@ -2614,8 +2708,29 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
||||
self.assertEqual(constants.REPLICA_STATE_OUT_OF_SYNC,
|
||||
replica['replica_state'])
|
||||
|
||||
def test_safe_change_replica_source_error(self):
|
||||
self.mock_dm_session.change_snapmirror_source.side_effect = Exception
|
||||
def test_safe_change_replica_source_destination_unreachable(self):
|
||||
self.mock_dm_session.change_snapmirror_source.side_effect = (
|
||||
exception.StorageCommunicationException
|
||||
)
|
||||
|
||||
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
|
||||
fake_replica_3['id'] = fake.SHARE_ID3
|
||||
fake_replica_3['replica_state'] = constants.REPLICA_STATE_OUT_OF_SYNC
|
||||
replica = self.library._safe_change_replica_source(
|
||||
self.mock_dm_session, self.fake_replica, self.fake_replica_2,
|
||||
fake_replica_3, [self.fake_replica, self.fake_replica_2,
|
||||
fake_replica_3]
|
||||
)
|
||||
self.assertEqual([], replica['export_locations'])
|
||||
self.assertEqual(constants.STATUS_ERROR,
|
||||
replica['replica_state'])
|
||||
self.assertEqual(constants.STATUS_ERROR,
|
||||
replica['status'])
|
||||
|
||||
def test_safe_change_replica_source_error(self):
|
||||
self.mock_dm_session.change_snapmirror_source.side_effect = (
|
||||
netapp_api.NaApiError(code=0)
|
||||
)
|
||||
|
||||
fake_replica_3 = copy.deepcopy(self.fake_replica_2)
|
||||
fake_replica_3['id'] = fake.SHARE_ID3
|
||||
|
||||
Reference in New Issue
Block a user