Resynchronizing Clone
Here is a script to resynchronize the Clone. This script is used in the operation to create a regular full backup using QuickOPC. For more information, see the description of Creating Regular Full Backups with QuickOPC.
You can update the Clone at any time by running this script at the time you want to resynchronize, for example with the crontab command.
While the script is running, to ensure the data integrity of the Clone, it is necessary to create a quiescent point by momentarily stopping operation volume I/O or application processing. After the script has been processed, the logical copy of the update data is complete, so you can restart your operation without waiting for the background copy to complete.
The sequence of script processing is as follows.
Start resynchronizing the Clone using POST /volume/{volume_id}/resync/{backup_volume_id} API.
quickopc_resync.py
import sys
from eternus_rest import EtdxBaseApi
# Change the following values for your environment.
storage_url = "https://192.168.0.1:5665"
storage_user_name = "username"
storage_password = "password"
# Change the following values as required.
# Specify a list of clone pairs. A clone pair is a tuple with a source volume
# ID and a destination volume ID of a QuickOPC.
# If you want to resync multiple QuickOPCs at the same time, list the clone
# pairs.
# Examples:
# clone_pair_list = [(100001,1100001),(200001,1200001)]
# This is specified to resync the following QuickOPCs at the same time.
# QuickOPC#0 (Source volume ID 100001, destination volume ID 1100001)
# QuickOPC#1 (Source volume ID 200001, destination volume ID 1200001)
clone_pair_list = [(100001, 1100001), (200001, 1200001)]
# If you want to schedule this script, you can use cron as follows.
# Examples: It is scheduled to resynchronize daily at 20:00. To ensure data
# consistency, host access must be stopped before resynchronization.
# crontab -e
# 0 20 * * * python3 /root/quickopc_resync.py >> /root/cron.log 2>&1
class EtdxApi(EtdxBaseApi):
def __init__(self, base_url, boxid=None, proxies=None):
super().__init__(base_url, boxid=boxid, proxies=proxies)
def quickopc_is_tracking(self, clone_volume_id_list):
"""Check if all the clones using QuickOPCs are in "Tracking" phase.
Args:
clone_volume_id_list (list[int]): List of the clone volume IDs
using QuickOPCs to check.
Returns:
bool: True if all the specified QuickOPCs are in "Tracking" phase.
False otherwise. Returns false if retrieving QuickOPC
information failed.
"""
r = super().get("/api/v1/volume/copysession?volume_id={0}&"
"fields=volume_name,status,phase"
.format(",".join(map(str, clone_volume_id_list))))
data = r.json()
if r.status_code != 200 or data["list_count"] == 0:
print("Failed to get clones.")
print(r)
print(data)
return False
try:
status_list = [i["status"] for i in data["volume_list"]]
phase_list = [i["phase"] for i in data["volume_list"]]
except Exception:
print("Some specified volumes are not clones.")
print(data)
return False
# Returns false if not all of the specified QuickOPCs are in
# "Active" status.
if status_list.count("Active") != len(clone_volume_id_list):
print("Failed to get information about some clones or some clones"
" have an error.")
print(r)
print(data)
return False
# Returns false if not all of the volumes are in "tracking" or
# "tracking_and_copying" phase.
if (len(phase_list) != phase_list.count("Tracking")
+ phase_list.count("Tracking_and_Copying")):
print("Some clones are not in \"Tracking\" phase.")
print(r)
print(data)
return False
return True
def quickopc_resync(self, clone_pair_list):
"""Resync QuickOPCs
Args:
clone_pair_list (list[tuple]): List of clone pairs of source and
destination volume of QuickOPC.
Returns:
list[int]: List of clone pairs of QuickOPCs which have been
started synchronization.
"""
job_id_list = []
for clone_pair in clone_pair_list:
volume_id, clone_volume_id = clone_pair
r = super().post("/api/v1/volume/{0!s}/resync/{1!s}"
.format(volume_id, clone_volume_id))
if r.status_code != 202:
print("Failed to request the clone pair {0!s} resync."
.format(clone_pair))
print(r)
print(r.json())
continue
clone_pair_job = (int(r.json()["job_id"]), clone_pair)
job_id_list.append(clone_pair_job)
# Wait for the job completion
success_pair_list = []
for clone_pair_job in job_id_list:
job_id = clone_pair_job[0]
job_r = super().wait_job(job_id)
if job_r.json()["status"] != "Success":
print("Failed to resync the clone pair {0!s}."
.format(clone_pair_job[1]))
print(job_r)
print(job_r.json())
continue
success_pair_list.append(clone_pair_job[1])
return success_pair_list
def main():
storage = EtdxApi(storage_url)
# Login
if not storage.login(storage_user_name, storage_password):
return False
print("Resyncing the QuickOPCs.")
print("Clone pair list to resync:", clone_pair_list)
# Verify if QuickOPC can be resynchronized.
clone_volume_id_list = [i[1] for i in clone_pair_list]
if not storage.quickopc_is_tracking(clone_volume_id_list):
print("Some QuickOPCs cannot be resynchronized.")
return False
# Initialize return value as True, since the procedure continues even if
# error occurs.
rtn = True
# Resync QuickOPCs
success_clone_pair_list = storage.quickopc_resync(clone_pair_list)
if set(success_clone_pair_list) != set(clone_pair_list):
print("Failed to resync some QuickOPCs.")
print("List of clone pairs failed synchronized:",
list(set(clone_pair_list) - set(success_clone_pair_list)))
rtn = False
if success_clone_pair_list == []:
print("Failed to resync all the specified QuickOPCs.")
return False
print("Clone pair list that was started to resync QuickOPCs:",
success_clone_pair_list)
# Get information of all the specified QuickOPCs.
for clone_volume_id in clone_volume_id_list:
r = storage.get("/api/v1/volume/{0!s}/copysession"
.format(clone_volume_id))
print(r)
print(r.json())
# Logout
storage.logout()
return rtn
if __name__ == '__main__':
if not main():
sys.exit(1)

