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.

Note

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.

  1. 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)