Rotating Snapshot

Here is a script that deletes the oldest Snapshot and creates a new one (rotates Snapshots). Use this script for operations that use SnapOPC+ to back up generation-managed differential data. For more information, see the description of Generation Managed Differential Data Backup with SnapOPC+.

You can run this script at any interval, such as with a crontab, to obtain snapshots on a regular basis.

Note

During script execution, to ensure the integrity of Snapshot data, operations volume I/O and applications must be stopped momentarily to create a quiescent point. After the script is complete, the logical copy of the update data is complete and business can resume.

The sequence of script processing is as follows.

  1. Use POST /volume/{volume_id}/snapshot/rotate API to rotate Snapshots.

snapopcplus_rotate.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 source volume IDs of SnapOPC+ to rotate.
volume_id_list = [100001, 200001, 300001]

# If you want to schedule this script, you can use cron as follows.
# Examples: It is scheduled to rotate SnapOPC+s daily at 20:00.
# To ensure data consistency, host access must be stopped before rotation.
# crontab -e
# 0 20 * * * python3 /root/snapopcplus_rotate.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 snapopcplus_rotate(self, volume_id_list):
        """Rotate SnapOPC+s

        Args:
            volume_id_list (list[int]): List of source volume IDs of SnapOPC+s
              to rotate.

        Returns:
            list[int]: List of source volume IDs of successfully rotated
              SnapOPC+.
        """
        job_id_list = []
        for volume_id in volume_id_list:
            r = super().post("/api/v1/volume/{0!s}/snapshot/rotate"
                             .format(volume_id))
            if r.status_code != 202:
                print("Failed to request the snapshots rotation of volume "
                      "(ID: {0!s})".format(volume_id))
                print(r)
                print(r.json())
                continue
            job_info = (int(r.json()["job_id"]), volume_id)
            job_id_list.append(job_info)

        # Wait for the job completion
        success_volume_id_list = []
        for job_info in job_id_list:
            job_id = job_info[0]
            job_r = super().wait_job(job_id)
            if job_r.json()["status"] != "Success":
                print("Failed to rotate the snapshots of volume (ID: {0!s})"
                      .format(job_info[1]))
                print(job_r)
                print(job_r.json())
                continue
            success_volume_id_list.append(job_info[1])
        return success_volume_id_list


def main():
    storage = EtdxApi(storage_url)

    # Login
    if not storage.login(storage_user_name, storage_password):
        return False

    print("Rotating the SnapOPC+s specified by the list of source volume ID:",
          volume_id_list)

    # Initialize return value as True, since the procedure continues even if
    # error occurs.
    rtn = True

    # Rotate SnapOPC+s
    success_volume_list = storage.snapopcplus_rotate(volume_id_list)
    if success_volume_list == []:
        print("Failed to rotate all SnapOPC+s.")
        return False
    if set(success_volume_list) != set(volume_id_list):
        print("Failed to rotate some SnapOPC+s.")
        print("List of source volume IDs that failed rotation:",
              list(set(volume_id_list) - set(success_volume_list)))
        rtn = False
    else:
        print("All SnapOPC+s rotated successfully.")

    # Get information of all the specified SnapOPC+s.
    for volume_id in volume_id_list:
        r = storage.get("/api/v1/volume/{0!s}/copysession?backup_type=snapshot"
                        "&is_manual_snapshot=false".format(volume_id))
        print(r)
        print(r.json())

    # Logout
    storage.logout()
    return rtn


if __name__ == '__main__':
    if not main():
        sys.exit(1)