Reversing the REC Session
The following shows a script that reverses a synchronous and asynchronous consistency REC Session, copies data from the mirrored volume to the operation volume, and then reverses the REC Session back to its original state. This script is used in operations that create a mirrored volume in a remote storage system using REC in synchronous or asynchronous consistency mode. For more information, see the description of Creating a Mirrored Volume in a Remote Storage System with REC (Synchronous Mode) or Creating a Mirrored Volume in a Remote Storage with REC (Asynchronous Consistency Mode).
For notes on reversing the Copy Session, see the description of Reversing the REC Session.
The sequence of script processing is as follows.
Use GET /copysession/{copysession_id} API to get the Copy Session information to reverse.
Determine whether the Copy Session can be reversed or not.
If the Status of the Copy Session is not Suspended, Suspend the Copy Session using POST /copysession/{copysession_id}/suspend API.
Use POST /copysession/{copysession_id}/reverse API to reverse the Copy Session.
Resume the Copy Session using POST /copysession/{copysession_id}/resume API to start the transfer of data from the mirror volume to the operation volume.
Use GET /copysession/{copysession_id} API to periodically retrieve Copy Session information and wait for Phase to Equivalent.
Use POST /copysession/{copysession_id}/suspend API to suspend the Copy Session.
Use POST /copysession/{copysession_id}/reverse API to reverse the Copy Session (return to the original orientation).
If the Copy Session is suspended in 3, use the POST /copysession/{copysession_id}/resume API to return the Copy Session Status to the state it was in prior to the script execution (Active).
rec_sync_consis_recover.py
import sys
import time
from eternus_rest import EtdxBaseApi
# Change the following values for your environment.
# Source storage (storage that has the source volume)
storage1_url = "https://192.168.0.1:5665"
storage1_user_name = "username"
storage1_password = "password"
# Copy session ID to recover the source volume data.
storage1_copysession_id = 1
class EtdxApi(EtdxBaseApi):
def __init__(self, base_url, boxid=None, proxies=None):
"""Constructor of EtdxApi
If storage is used as target storage of REC without calling login
method in this script, need to specify the boxid of this storage as
follows.
storage2 = EtdxApi(storage2_url,
boxid="00ETERNUSDXHS3ET00000A####EI000001######")
Args:
base_url ([type]): [description]
boxid ([type], optional): [description]. Defaults to None.
proxies ([type], optional): [description]. Defaults to None.
"""
super().__init__(base_url, boxid=boxid, proxies=proxies)
def login(self, user_name="", password=""):
"""Create a session of RESTful API and get boxid.
Args:
user_name (str, optional): User name to login. Defaults to "".
password (str, optional): Password for the user. Defaults to "".
Returns:
bool: True if successful, False otherwise.
"""
rtn = super().login(user_name, password)
if rtn and self.boxid is None:
r = super().get("/api/v1/storagesystem")
if r.status_code != 200:
print(r)
print(r.json())
print("Failed to get boxid.")
return False
self.boxid = r.json()["boxid"]
return rtn
def rec_can_recover(self, copysession_id):
"""Check if the REC session can start the recovery.
Args:
copysession_id (int): Copy Session ID to check.
Returns:
bool: True if the specified REC session can start the recovery.
False otherwise. Returns false if retrieving copy session
information failed.
"""
r = super().get("/api/v1/copysession/{0!s}?fields=status,"
"is_local_source_volume".format(copysession_id))
data = r.json()
if r.status_code != 200:
print("Failed to get information about the copy session.")
print(r)
print(data)
return False
if data["status"] not in ["Active", "Suspended"]:
print("The REC session is not in normal status.")
print(data)
return False
if not data["is_local_source_volume"]:
print("The REC session might be in recovering.")
print(data)
return False
return True
def rec_wait_for_equivalent(self, copysession_id):
"""Wait for the REC session phase to be equivalent.
Args:
copysession_id (int): Copy session ID to wait.
Returns:
bool: True if the copy session phase is equivalent.
False if error occurs.
"""
while True:
r = super().get("/api/v1/copysession/{0!s}".format(copysession_id))
data = r.json()
if r.status_code != 200 or data["status"] == "Error":
print("Failed to get information of the copy session or"
" some error occurred on the copy session.")
print(r)
print(data)
return False
if data["phase"] == "Equivalent":
break
print("The REC session is not yet equivalent. Check again in"
" 60 seconds.")
print("Total Data Size: ", data["total_data_size"])
print("Copied Data Size:", data["copied_data_size"])
print("Progress: ", data["progress"])
print("Elapsed Time: ", data["elapsed_time"])
time.sleep(60)
return True
def rec_suspend(self, copysession_id, force=False):
"""Suspend the REC session.
Args:
copysession_id (int): Copy session ID to suspend.
force (bool, optional): True to force suspend. Defaults to False.
Returns:
bool: True if successful, False otherwise.
"""
if force:
r = super().post("/api/v1/copysession/{0!s}/suspend?force=true"
.format(copysession_id))
else:
r = super().post("/api/v1/copysession/{0!s}/suspend"
.format(copysession_id))
if r.status_code != 202:
print("Failed to request the REC session suspend.")
print(r)
print(r.json())
return False
# Wait for the job completion
job_r = super().wait_job(r.json()["job_id"])
if job_r.json()["status"] != "Success":
print("Failed to suspend the REC session.")
print(job_r)
print(job_r.json())
return False
return True
def rec_resume(self, copysession_id):
"""Resume the REC session
Args:
copysession_id (int): Copy session ID to resume.
Returns:
bool: True if successful, False otherwise.
"""
r = super().post("/api/v1/copysession/{0!s}/resume"
.format(copysession_id))
if r.status_code != 202:
print("Failed to request the REC session resume.")
print(r)
print(r.json())
return False
# Wait for the job completion
job_r = super().wait_job(r.json()["job_id"])
if job_r.json()["status"] != "Success":
print("Failed to resume the REC session.")
print(job_r)
print(job_r.json())
return False
return True
def rec_consistency_wait_for_suspend(self, copysession_id):
"""Wait for the REC concurrent copy session to suspend.
Args:
copysession_id (int): Copy session ID to suspend.
Returns:
bool: True if suspend the copy session is completed.
False if error occurs.
"""
while True:
r = super().get("/api/v1/copysession/{0!s}".format(copysession_id))
data = r.json()
if r.status_code != 200:
print("Failed to get information of the copy session.")
print(r)
print(data)
return False
if data["status"] == "Error":
print("The copy session is in error status.")
print(data)
return False
if data["status"] != "Suspended":
print("Failed to suspend the copy session. Stop access to the"
" source volume of the copy session and try again.")
print(data)
return False
if data["concurrent_suspend_result"] == "Error":
print("Failed to suspend the copy session.")
print(data)
return False
if (data["concurrent_suspend_result"] == "Success"
and data["status"] == "Suspended"):
break
# Get it again after remain_time_to_finish_suspend.
if data["remain_time_to_finish_suspend"] > 10:
time.sleep(data["remain_time_to_finish_suspend"])
else:
time.sleep(10)
return True
def rec_reverse(self, copysession_id):
"""Reverse the REC session
Args:
copysession_id (int): Copy session ID to reverse.
Returns:
bool: True if successful, False otherwise.
"""
r = super().post("/api/v1/copysession/{0!s}/reverse"
.format(copysession_id))
if r.status_code != 202:
print("Failed to request the REC session reverse.")
print(r)
print(r.json())
return False
# Wait for the job completion
job_r = super().wait_job(r.json()["job_id"])
if job_r.json()["status"] != "Success":
print("Failed to reverse the REC session.")
print(job_r)
print(job_r.json())
return False
return True
def rec_recover(self, copysession_id):
"""Restore the source volume data from the REC destination volume.
Args:
copysession_id (int): Copy session ID of the REC.
Returns:
bool: True if successful, False otherwise.
"""
is_suspended = False
is_consistency = False
# Get the information of the copy session.
r = super().get("/api/v1/copysession/{0!s}".format(copysession_id))
data = r.json()
if r.status_code != 200:
print("Failed to get information of the copy session.")
print(r)
print(data)
return False
if data["status"] == "Suspended":
is_suspended = True
if data["transfer_mode"] == "Consistency":
is_consistency = True
# Execute suspend if the REC session is not suspended.
if not is_suspended:
print("Suspending the REC session.")
if not self.rec_suspend(copysession_id):
return False
# Need to wait for the completion of suspend if the REC is consistency.
if is_consistency:
print("Waiting for the REC session to complete suspend.")
if not self.rec_consistency_wait_for_suspend(copysession_id):
print("Some error occurred before suspend completion of"
" the REC session.")
return False
# Reverse the REC session
print("Reversing the REC session.")
if not self.rec_reverse(copysession_id):
return False
# Restore the data from the destination volume to the source
# volume of the REC.
print("Resuming the REC session.")
if not self.rec_resume(copysession_id):
return False
print("Waiting for the REC session to be equivalent.")
if not self.rec_wait_for_equivalent(copysession_id):
return False
print("The data restore Completed.")
# Restore the REC session to its former state.
print("Suspending the REC session.")
if not self.rec_suspend(copysession_id):
return False
# Need to wait for the completion of suspend if the REC is consistency.
if is_consistency:
print("Waiting for the REC session to complete suspend.")
if not self.rec_consistency_wait_for_suspend(copysession_id):
print("Some error occurred before suspend completion of"
" the REC session.")
return False
print("Reversing the REC session.")
if not self.rec_reverse(copysession_id):
return False
# Execute resume if needed.
if not is_suspended:
print("Resuming the REC session.")
if not self.rec_resume(copysession_id):
return False
return True
def main():
storage1 = EtdxApi(storage1_url)
# Login
if not storage1.login(storage1_user_name, storage1_password):
return False
print("Start recovery of the source volume data about the REC session"
" (ID: {0!s}).".format(storage1_copysession_id))
# Change recovery process according to the operation condition.
try:
if not storage1.rec_can_recover(storage1_copysession_id):
print("Could not start recovery.")
return False
if not storage1.rec_recover(storage1_copysession_id):
print("Failed to recover.")
return False
except Exception as e:
print(e)
return False
print("Recovery completed.")
# Logout
storage1.logout()
return True
if __name__ == '__main__':
if not main():
sys.exit(1)

