Update timedelta and old schedules as per netapp_snapmirror_schedule
Asynchronous SnapMirror schedules are set using netapp config option 'netapp_snapmirror_schedule'. The delta for determining replica is in-sync or out-of-sync updated to twice the schedule time seconds. Also, not only new snapmirrors, but also old ones should have a schedule according to the current 'netapp_snapmirror_schedule' config. Closes-bug: #1996859 Depends-On: I0390f82dfdc130d49e3af6928996dd730e3cf69f Change-Id: Ifbe0575f6c359929344763666e4d93d8c6084e83
This commit is contained in:
parent
e31be16130
commit
da23b652fb
@ -2192,7 +2192,8 @@ class NetAppRestClient(object):
|
|||||||
|
|
||||||
fields = ['state', 'source.svm.name', 'source.path',
|
fields = ['state', 'source.svm.name', 'source.path',
|
||||||
'destination.svm.name', 'destination.path',
|
'destination.svm.name', 'destination.path',
|
||||||
'transfer.end_time', 'uuid', 'policy.type']
|
'transfer.end_time', 'uuid', 'policy.type',
|
||||||
|
'transfer_schedule.name']
|
||||||
|
|
||||||
query = {}
|
query = {}
|
||||||
query['fields'] = ','.join(fields)
|
query['fields'] = ','.join(fields)
|
||||||
@ -2223,6 +2224,7 @@ class NetAppRestClient(object):
|
|||||||
snapmirrors.append({
|
snapmirrors.append({
|
||||||
'relationship-status': record.get('state'),
|
'relationship-status': record.get('state'),
|
||||||
'mirror-state': record.get('state'),
|
'mirror-state': record.get('state'),
|
||||||
|
'schedule': record['transfer_schedule']['name'],
|
||||||
'source-vserver': record['source']['svm']['name'],
|
'source-vserver': record['source']['svm']['name'],
|
||||||
'source-volume': (record['source']['path'].split(':')[1] if
|
'source-volume': (record['source']['path'].split(':')[1] if
|
||||||
record.get('source') else None),
|
record.get('source') else None),
|
||||||
@ -2959,7 +2961,7 @@ class NetAppRestClient(object):
|
|||||||
def _set_snapmirror_state(self, state, source_path, destination_path,
|
def _set_snapmirror_state(self, state, source_path, destination_path,
|
||||||
source_vserver, source_volume,
|
source_vserver, source_volume,
|
||||||
destination_vserver, destination_volume,
|
destination_vserver, destination_volume,
|
||||||
wait_result=True):
|
wait_result=True, schedule=None):
|
||||||
"""Change the snapmirror state between two volumes."""
|
"""Change the snapmirror state between two volumes."""
|
||||||
|
|
||||||
snapmirror = self.get_snapmirrors(source_path, destination_path,
|
snapmirror = self.get_snapmirrors(source_path, destination_path,
|
||||||
@ -2978,7 +2980,12 @@ class NetAppRestClient(object):
|
|||||||
raise na_utils.NetAppDriverException(msg)
|
raise na_utils.NetAppDriverException(msg)
|
||||||
|
|
||||||
uuid = snapmirror[0]['uuid']
|
uuid = snapmirror[0]['uuid']
|
||||||
body = {'state': state}
|
body = {}
|
||||||
|
if state:
|
||||||
|
body.update({'state': state})
|
||||||
|
if schedule:
|
||||||
|
body.update({"transfer_schedule": {'name': schedule}})
|
||||||
|
|
||||||
result = self.send_request(f'/snapmirror/relationships/{uuid}',
|
result = self.send_request(f'/snapmirror/relationships/{uuid}',
|
||||||
'patch', body=body,
|
'patch', body=body,
|
||||||
wait_on_accepted=wait_result)
|
wait_on_accepted=wait_result)
|
||||||
@ -3023,6 +3030,29 @@ class NetAppRestClient(object):
|
|||||||
source_vserver, source_volume,
|
source_vserver, source_volume,
|
||||||
dest_vserver, dest_volume, wait_result=False)
|
dest_vserver, dest_volume, wait_result=False)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def modify_snapmirror_vol(self, source_vserver, source_volume,
|
||||||
|
dest_vserver, dest_volume,
|
||||||
|
schedule=None, policy=None, tries=None,
|
||||||
|
max_transfer_rate=None):
|
||||||
|
"""Modifies a SnapMirror relationship between volumes."""
|
||||||
|
return self._modify_snapmirror(
|
||||||
|
source_vserver=source_vserver, dest_vserver=dest_vserver,
|
||||||
|
source_volume=source_volume, dest_volume=dest_volume,
|
||||||
|
schedule=schedule)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def _modify_snapmirror(self, source_path=None, dest_path=None,
|
||||||
|
source_vserver=None, dest_vserver=None,
|
||||||
|
source_volume=None, dest_volume=None,
|
||||||
|
schedule=None):
|
||||||
|
"""Modifies a SnapMirror relationship."""
|
||||||
|
return self._set_snapmirror_state(
|
||||||
|
None, source_path, dest_path,
|
||||||
|
source_vserver, source_volume,
|
||||||
|
dest_vserver, dest_volume, wait_result=False,
|
||||||
|
schedule=schedule)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def create_volume_clone(self, volume_name, parent_volume_name,
|
def create_volume_clone(self, volume_name, parent_volume_name,
|
||||||
parent_snapshot_name=None, split=False,
|
parent_snapshot_name=None, split=False,
|
||||||
|
@ -175,6 +175,7 @@ class DataMotionSession(object):
|
|||||||
source_volume=src_volume_name, dest_volume=dest_volume_name,
|
source_volume=src_volume_name, dest_volume=dest_volume_name,
|
||||||
desired_attributes=['relationship-status',
|
desired_attributes=['relationship-status',
|
||||||
'mirror-state',
|
'mirror-state',
|
||||||
|
'schedule',
|
||||||
'source-vserver',
|
'source-vserver',
|
||||||
'source-volume',
|
'source-volume',
|
||||||
'last-transfer-end-timestamp',
|
'last-transfer-end-timestamp',
|
||||||
@ -422,6 +423,27 @@ class DataMotionSession(object):
|
|||||||
dest_vserver,
|
dest_vserver,
|
||||||
dest_volume_name)
|
dest_volume_name)
|
||||||
|
|
||||||
|
def modify_snapmirror(self, source_share_obj, dest_share_obj,
|
||||||
|
schedule=None):
|
||||||
|
"""Modify SnapMirror relationship: set schedule"""
|
||||||
|
dest_volume_name, dest_vserver, dest_backend = (
|
||||||
|
self.get_backend_info_for_share(dest_share_obj))
|
||||||
|
dest_client = get_client_for_backend(dest_backend,
|
||||||
|
vserver_name=dest_vserver)
|
||||||
|
|
||||||
|
src_volume_name, src_vserver, __ = self.get_backend_info_for_share(
|
||||||
|
source_share_obj)
|
||||||
|
|
||||||
|
if schedule is None:
|
||||||
|
config = get_backend_configuration(dest_backend)
|
||||||
|
schedule = config.netapp_snapmirror_schedule
|
||||||
|
|
||||||
|
dest_client.modify_snapmirror_vol(src_vserver,
|
||||||
|
src_volume_name,
|
||||||
|
dest_vserver,
|
||||||
|
dest_volume_name,
|
||||||
|
schedule=schedule)
|
||||||
|
|
||||||
def resume_snapmirror(self, source_share_obj, dest_share_obj):
|
def resume_snapmirror(self, source_share_obj, dest_share_obj):
|
||||||
"""Resume SnapMirror relationship from a quiesced state."""
|
"""Resume SnapMirror relationship from a quiesced state."""
|
||||||
dest_volume_name, dest_vserver, dest_backend = (
|
dest_volume_name, dest_vserver, dest_backend = (
|
||||||
|
@ -23,6 +23,7 @@ import copy
|
|||||||
import datetime
|
import datetime
|
||||||
import json
|
import json
|
||||||
import math
|
import math
|
||||||
|
import re
|
||||||
import socket
|
import socket
|
||||||
|
|
||||||
from oslo_config import cfg
|
from oslo_config import cfg
|
||||||
@ -172,6 +173,8 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
self._backend_name = self.configuration.safe_get(
|
self._backend_name = self.configuration.safe_get(
|
||||||
'share_backend_name') or driver_name
|
'share_backend_name') or driver_name
|
||||||
self.message_api = message_api.API()
|
self.message_api = message_api.API()
|
||||||
|
self._snapmirror_schedule = self._convert_schedule_to_seconds(
|
||||||
|
schedule=self.configuration.netapp_snapmirror_schedule)
|
||||||
|
|
||||||
@na_utils.trace
|
@na_utils.trace
|
||||||
def do_setup(self, context):
|
def do_setup(self, context):
|
||||||
@ -2552,6 +2555,35 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
self._delete_share(replica, vserver, vserver_client,
|
self._delete_share(replica, vserver, vserver_client,
|
||||||
remove_export=is_readable, remove_qos=is_readable)
|
remove_export=is_readable, remove_qos=is_readable)
|
||||||
|
|
||||||
|
@na_utils.trace
|
||||||
|
def _convert_schedule_to_seconds(self, schedule='hourly'):
|
||||||
|
"""Convert snapmirror schedule to seconds."""
|
||||||
|
results = re.findall(r'[\d]+|[^d]+', schedule)
|
||||||
|
if not results or len(results) > 2:
|
||||||
|
return 3600 # default (1 hour)
|
||||||
|
|
||||||
|
if len(results) == 2:
|
||||||
|
try:
|
||||||
|
num = int(results[0])
|
||||||
|
except ValueError:
|
||||||
|
return 3600
|
||||||
|
schedule = results[1]
|
||||||
|
else:
|
||||||
|
num = 1
|
||||||
|
schedule = results[0]
|
||||||
|
schedule = schedule.lower()
|
||||||
|
if schedule in ['min', 'minute']:
|
||||||
|
return (num * 60)
|
||||||
|
if schedule in ['hour', 'hourly']:
|
||||||
|
return (num * 3600)
|
||||||
|
if schedule in ['day', 'daily']:
|
||||||
|
return (num * 24 * 3600)
|
||||||
|
if schedule in ['week', 'weekly']:
|
||||||
|
return (num * 7 * 24 * 3600)
|
||||||
|
if schedule in ['month', 'monthly']:
|
||||||
|
return (num * 30 * 24 * 2600)
|
||||||
|
return 3600
|
||||||
|
|
||||||
def update_replica_state(self, context, replica_list, replica,
|
def update_replica_state(self, context, replica_list, replica,
|
||||||
access_rules, share_snapshots, share_server=None,
|
access_rules, share_snapshots, share_server=None,
|
||||||
replication=True):
|
replication=True):
|
||||||
@ -2624,14 +2656,26 @@ class NetAppCmodeFileStorageLibrary(object):
|
|||||||
LOG.exception("Could not resync snapmirror.")
|
LOG.exception("Could not resync snapmirror.")
|
||||||
return constants.STATUS_ERROR
|
return constants.STATUS_ERROR
|
||||||
|
|
||||||
|
current_schedule = snapmirror.get('schedule')
|
||||||
|
new_schedule = self.configuration.netapp_snapmirror_schedule
|
||||||
|
if current_schedule != new_schedule:
|
||||||
|
dm_session.modify_snapmirror(active_replica, replica,
|
||||||
|
schedule=new_schedule)
|
||||||
|
LOG.debug('Modify snapmirror schedule for replica:'
|
||||||
|
'%(replica)s from %(from)s to %(to)s',
|
||||||
|
{'replica': replica['id'],
|
||||||
|
'from': current_schedule,
|
||||||
|
'to': new_schedule})
|
||||||
|
|
||||||
last_update_timestamp = float(
|
last_update_timestamp = float(
|
||||||
snapmirror.get('last-transfer-end-timestamp', 0))
|
snapmirror.get('last-transfer-end-timestamp', 0))
|
||||||
# TODO(ameade): Have a configurable RPO for replicas, for now it is
|
# Recovery Point Objective (RPO) indicates the point in time to
|
||||||
# one hour.
|
# which data can be recovered. The RPO target is typically less
|
||||||
|
# than twice the replication schedule.
|
||||||
if (last_update_timestamp and
|
if (last_update_timestamp and
|
||||||
(timeutils.is_older_than(
|
(timeutils.is_older_than(
|
||||||
datetime.datetime.utcfromtimestamp(last_update_timestamp)
|
datetime.datetime.utcfromtimestamp(last_update_timestamp)
|
||||||
.isoformat(), 3600))):
|
.isoformat(), (2 * self._snapmirror_schedule)))):
|
||||||
return constants.REPLICA_STATE_OUT_OF_SYNC
|
return constants.REPLICA_STATE_OUT_OF_SYNC
|
||||||
|
|
||||||
replica_backend = share_utils.extract_host(replica['host'],
|
replica_backend = share_utils.extract_host(replica['host'],
|
||||||
|
@ -3936,7 +3936,10 @@ SNAPMIRROR_GET_ITER_RESPONSE_REST = {
|
|||||||
"type": "async"
|
"type": "async"
|
||||||
},
|
},
|
||||||
"state": "snapmirrored",
|
"state": "snapmirrored",
|
||||||
"healthy": True
|
"healthy": True,
|
||||||
|
"transfer_schedule": {
|
||||||
|
"name": "hourly",
|
||||||
|
},
|
||||||
}
|
}
|
||||||
],
|
],
|
||||||
"num_records": 1,
|
"num_records": 1,
|
||||||
@ -3951,7 +3954,8 @@ REST_GET_SNAPMIRRORS_RESPONSE = [{
|
|||||||
'source-volume': SM_SOURCE_VOLUME,
|
'source-volume': SM_SOURCE_VOLUME,
|
||||||
'source-vserver': SM_SOURCE_VSERVER,
|
'source-vserver': SM_SOURCE_VSERVER,
|
||||||
'uuid': FAKE_UUID,
|
'uuid': FAKE_UUID,
|
||||||
'policy-type': 'async'
|
'policy-type': 'async',
|
||||||
|
'schedule': 'hourly'
|
||||||
}]
|
}]
|
||||||
|
|
||||||
FAKE_CIFS_RECORDS = {
|
FAKE_CIFS_RECORDS = {
|
||||||
|
@ -2185,7 +2185,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
'destination.path': (fake.SM_DEST_VSERVER +
|
'destination.path': (fake.SM_DEST_VSERVER +
|
||||||
':' + fake.SM_DEST_VOLUME),
|
':' + fake.SM_DEST_VOLUME),
|
||||||
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
||||||
'destination.path,transfer.end_time,uuid,policy.type'
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
||||||
|
'transfer_schedule.name'
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
||||||
@ -2216,7 +2217,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
'destination.path': (fake.SM_DEST_VSERVER +
|
'destination.path': (fake.SM_DEST_VSERVER +
|
||||||
':' + fake.SM_DEST_VOLUME),
|
':' + fake.SM_DEST_VOLUME),
|
||||||
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
||||||
'destination.path,transfer.end_time,uuid,policy.type'
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
||||||
|
'transfer_schedule.name'
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
||||||
@ -2243,7 +2245,8 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
'destination.path': (fake.SM_DEST_VSERVER +
|
'destination.path': (fake.SM_DEST_VSERVER +
|
||||||
':' + fake.SM_DEST_VOLUME),
|
':' + fake.SM_DEST_VOLUME),
|
||||||
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
'fields': 'state,source.svm.name,source.path,destination.svm.name,'
|
||||||
'destination.path,transfer.end_time,uuid,policy.type'
|
'destination.path,transfer.end_time,uuid,policy.type,'
|
||||||
|
'transfer_schedule.name'
|
||||||
}
|
}
|
||||||
|
|
||||||
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
mock_send_request.assert_called_once_with('/snapmirror/relationships',
|
||||||
@ -2944,6 +2947,34 @@ class NetAppRestCmodeClientTestCase(test.TestCase):
|
|||||||
|
|
||||||
self.assertEqual(expected_job, result)
|
self.assertEqual(expected_job, result)
|
||||||
|
|
||||||
|
def test_modify_snapmirror_vol(self):
|
||||||
|
|
||||||
|
expected_job = {
|
||||||
|
'operation-id': None,
|
||||||
|
'status': None,
|
||||||
|
'jobid': fake.FAKE_UUID,
|
||||||
|
'error-code': None,
|
||||||
|
'error-message': None,
|
||||||
|
}
|
||||||
|
|
||||||
|
mock_set_snapmirror_state = self.mock_object(
|
||||||
|
self.client,
|
||||||
|
'_set_snapmirror_state',
|
||||||
|
mock.Mock(return_value=expected_job))
|
||||||
|
|
||||||
|
result = self.client.modify_snapmirror_vol(
|
||||||
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
||||||
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
||||||
|
None)
|
||||||
|
|
||||||
|
mock_set_snapmirror_state.assert_called_once_with(
|
||||||
|
None, None, None,
|
||||||
|
fake.SM_SOURCE_VSERVER, fake.SM_SOURCE_VOLUME,
|
||||||
|
fake.SM_DEST_VSERVER, fake.SM_DEST_VOLUME,
|
||||||
|
wait_result=False, schedule=None)
|
||||||
|
|
||||||
|
self.assertEqual(expected_job, result)
|
||||||
|
|
||||||
def test__abort_snapmirror(self):
|
def test__abort_snapmirror(self):
|
||||||
return_snp = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
return_snp = fake.REST_GET_SNAPMIRRORS_RESPONSE
|
||||||
mock_get_snap = self.mock_object(self.client, '_get_snapmirrors',
|
mock_get_snap = self.mock_object(self.client, '_get_snapmirrors',
|
||||||
|
@ -799,6 +799,7 @@ class NetAppCDOTDataMotionSessionTestCase(test.TestCase):
|
|||||||
dest_volume=self.fake_dest_vol_name,
|
dest_volume=self.fake_dest_vol_name,
|
||||||
desired_attributes=['relationship-status',
|
desired_attributes=['relationship-status',
|
||||||
'mirror-state',
|
'mirror-state',
|
||||||
|
'schedule',
|
||||||
'source-vserver',
|
'source-vserver',
|
||||||
'source-volume',
|
'source-volume',
|
||||||
'last-transfer-end-timestamp',
|
'last-transfer-end-timestamp',
|
||||||
|
@ -3991,6 +3991,19 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
data_motion.get_client_for_backend.assert_called_once_with(
|
data_motion.get_client_for_backend.assert_called_once_with(
|
||||||
fake.BACKEND_NAME, vserver_name=fake.VSERVER1)
|
fake.BACKEND_NAME, vserver_name=fake.VSERVER1)
|
||||||
|
|
||||||
|
@ddt.data({'seconds': 3600, 'schedule': 'hourly'},
|
||||||
|
{'seconds': (5 * 3600), 'schedule': '5hourly'},
|
||||||
|
{'seconds': (30 * 60), 'schedule': '30minute'},
|
||||||
|
{'seconds': (2 * 24 * 3600), 'schedule': '2DAY'},
|
||||||
|
{'seconds': 3600, 'schedule': 'fake_shedule'},
|
||||||
|
{'seconds': 3600, 'schedule': 'fake2'},
|
||||||
|
{'seconds': 3600, 'schedule': '10fake'})
|
||||||
|
@ddt.unpack
|
||||||
|
def test__convert_schedule_to_seconds(self, seconds, schedule):
|
||||||
|
expected_return = seconds
|
||||||
|
actual_return = self.library._convert_schedule_to_seconds(schedule)
|
||||||
|
self.assertEqual(expected_return, actual_return)
|
||||||
|
|
||||||
def test_update_replica_state_no_snapmirror_share_creating(self):
|
def test_update_replica_state_no_snapmirror_share_creating(self):
|
||||||
vserver_client = mock.Mock()
|
vserver_client = mock.Mock()
|
||||||
self.mock_object(vserver_client, 'volume_exists',
|
self.mock_object(vserver_client, 'volume_exists',
|
||||||
@ -4209,6 +4222,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
def test_update_replica_state_stale_snapmirror(self):
|
def test_update_replica_state_stale_snapmirror(self):
|
||||||
fake_snapmirror = {
|
fake_snapmirror = {
|
||||||
'mirror-state': 'snapmirrored',
|
'mirror-state': 'snapmirrored',
|
||||||
|
'schedule': self.library.configuration.netapp_snapmirror_schedule,
|
||||||
'last-transfer-end-timestamp': '%s' % float(
|
'last-transfer-end-timestamp': '%s' % float(
|
||||||
timeutils.utcnow_ts() - 10000)
|
timeutils.utcnow_ts() - 10000)
|
||||||
}
|
}
|
||||||
@ -4224,6 +4238,9 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
self.mock_object(self.library,
|
self.mock_object(self.library,
|
||||||
'_is_readable_replica',
|
'_is_readable_replica',
|
||||||
mock.Mock(return_value=False))
|
mock.Mock(return_value=False))
|
||||||
|
mock_backend_config = fake.get_config_cmode()
|
||||||
|
self.mock_object(data_motion, 'get_backend_configuration',
|
||||||
|
mock.Mock(return_value=mock_backend_config))
|
||||||
|
|
||||||
result = self.library.update_replica_state(None, [fake.SHARE],
|
result = self.library.update_replica_state(None, [fake.SHARE],
|
||||||
fake.SHARE, None, [],
|
fake.SHARE, None, [],
|
||||||
@ -4234,6 +4251,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
def test_update_replica_state_in_sync(self):
|
def test_update_replica_state_in_sync(self):
|
||||||
fake_snapmirror = {
|
fake_snapmirror = {
|
||||||
'mirror-state': 'snapmirrored',
|
'mirror-state': 'snapmirrored',
|
||||||
|
'schedule': self.library.configuration.netapp_snapmirror_schedule,
|
||||||
'relationship-status': 'idle',
|
'relationship-status': 'idle',
|
||||||
'last-transfer-end-timestamp': '%s' % float(time.time())
|
'last-transfer-end-timestamp': '%s' % float(time.time())
|
||||||
}
|
}
|
||||||
@ -4276,6 +4294,7 @@ class NetAppFileStorageLibraryTestCase(test.TestCase):
|
|||||||
def test_update_replica_state_in_sync_with_snapshots(self):
|
def test_update_replica_state_in_sync_with_snapshots(self):
|
||||||
fake_snapmirror = {
|
fake_snapmirror = {
|
||||||
'mirror-state': 'snapmirrored',
|
'mirror-state': 'snapmirrored',
|
||||||
|
'schedule': self.library.configuration.netapp_snapmirror_schedule,
|
||||||
'relationship-status': 'idle',
|
'relationship-status': 'idle',
|
||||||
'last-transfer-end-timestamp': '%s' % float(time.time())
|
'last-transfer-end-timestamp': '%s' % float(time.time())
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,10 @@
|
|||||||
|
---
|
||||||
|
fixes:
|
||||||
|
- |
|
||||||
|
NetApp ONTAP driver fixed to consider timestamp delta calculated from
|
||||||
|
`netapp_snapmirror_schedule` config option instead of fixed one hour
|
||||||
|
value. Delta is calculated as twice the time of the option. Also, ensure
|
||||||
|
periodically that existent snapmirrors have the schedule property
|
||||||
|
according to the `netapp_snapmirror_schedule` configuration value. For
|
||||||
|
more details, please refer
|
||||||
|
`Launchpad bug #1996859 <https://bugs.launchpad.net/manila/+bug/1996859>`_
|
Loading…
Reference in New Issue
Block a user