Creating Backup of Business Volumes to Remote Storage Systems

The following script creates the Copy destination Volume and Clone of a REC Session in asynchronous stack mode on a remote storage system. This script is used in the operation of periodically backing up the business volume on the remote storage system using REC in asynchronous stack mode and QuickOPC. For more information, see the description of Creating Backups to Remote Storage Systems Using REC and QuickOPC.

This script should only be run once when building the environment.

The sequence of script processing is as follows.

  1. Use POST /volume API to create a Volume in the remote storage system to use as a Copy destination for REC Sessions.

  2. Using POST /copysession/rec API, create a REC Session (Asynchronous Stack Mode) between the business volume and the volume you created in 1.

  3. Use POST /volume/{volume_id}/clone API to create a Clone of the Volume you created in 1.

rec_stack_create.py


import sys

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"
# Target storage (storage that has the destination volume)
storage2_url = "https://192.168.0.2:5665"
storage2_user_name = "username"
storage2_password = "password"

# Change the following values as required.
# Create a REC(Stack) session from the volume on storage1 to storage2.
# The destination volume of REC has been cloned using QuickOPC on storage2.
# Volume ID on storage1 to backup.
storage1_volume_id = 100001
# TPP ID to create a backup volume on storage2.
storage2_rec_backup_tpp_id = 1
# Name of the backup volume of REC.
storage2_rec_backup_volume_name = "backup_vol"
# TPP ID to create a QuickOPC clone for the backup of the destination
# volume of the REC(Stack) on storage2.
storage2_tpp_id = 0
# Name of the QuickOPC clone volume.
storage2_clone_volume_name = "clone_vol"


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_create_backup_volume(self, destination_tpp_id,
                                 destination_volume_name, source_storage,
                                 source_volume_id):
        """Create a backup volume for REC destination volume.

        Create a volume on the target storage with the same capacity as the
        source volume.

        Args:
            destination_tpp_id (int): TPP ID to create a backup volume.
            destination_volume_name (str): Name of the backup volume.
            source_storage (obj): Instance of EtdxApi class of the backup
              source storage.
            source_volume_id (int): Volume ID of the backup source volume.

        Returns:
            int: Volume ID of the destination volume that was created.
              If volume creation fails, returns a value of -1.
              This value cannot be used as a volume ID.
        """
        # Get the capacity of source volume.
        r = source_storage.get("/api/v1/volume/{0!s}?fields=capacity"
                               .format(source_volume_id))
        if r.status_code != 200:
            print("Failed to get information of the source volume "
                  "(ID: {0!s})."
                  .format(source_volume_id))
            print(r)
            print(r.json())
            return -1
        capacity = r.json()["capacity"]

        # Create backup volume
        body = {
            "name": destination_volume_name,
            "capacity": capacity,
            "tpp_id": destination_tpp_id
        }
        r = super().post("/api/v1/volume", body)
        if r.status_code != 202:
            print("Failed to request a volume creation.")
            print(r)
            print(r.json())
            return -1

        # Wait for the job completion
        job_r = super().wait_job(r.json()["job_id"])
        if job_r.json()["status"] != "Success":
            print("Failed to create a volume.")
            print(job_r)
            print(job_r.json())
            return -1
        backup_volume_href = job_r.json()["resource_href_list"][0]
        backup_volume_id = int(backup_volume_href.split("/")[4])

        return backup_volume_id

    def rec_delete_volume(self, volume_id):
        """Delete the volume.

        Args:
            volume_id (int): Volume ID to delete.

        Returns:
            bool: True if successful, False otherwise.
        """
        r = super().delete("/api/v1/volume/{0!s}".format(volume_id))
        if r.status_code != 202:
            print("Failed to request the volume deletion.")
            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 delete the volume.")
            print(job_r)
            print(job_r.json())
            return False

        return True

    def rec_stack_create(self, source_volume_id, destination_storage,
                         destination_volume_id):
        """Create REC(Stack) session.

        Args:
            source_volume_id (int): Volume ID of the backup source volume.
            destination_storage (obj): Instance of EtdxApi class of the backup
              destination storage.
            destination_volume_id (int): Volume ID of the backup destination
              volume.

        Returns:
            int: Copy session ID that is created. If REC creation fails,
              returns a value of -1. This value cannot be used as copy
              session ID.
        """
        body = {
            "source_volume_id": source_volume_id,
            "destination_volume_id": destination_volume_id,
            "source_boxid": self.boxid,
            "destination_boxid": destination_storage.boxid,
            "transfer_mode": "Stack",
            "recovery_mode": "automatic"
        }
        r = super().post("/api/v1/copysession/rec", body)
        if r.status_code != 202:
            print("Failed to request a REC session creation.")
            print(r)
            print(r.json())
            return -1

        # Wait for the job completion
        job_r = super().wait_job(r.json()["job_id"])
        if job_r.json()["status"] != "Success":
            print("Failed to create a REC session.")
            print(job_r)
            print(job_r.json())
            return -1
        copysession_href = job_r.json()["resource_href_list"][0]
        copysession_id = int(copysession_href.split("/")[4])

        return copysession_id

    def quickopc_create(self, volume_id, tpp_id, clone_volume_name):
        """Create clone using QuickOPC

        Create clone volume and QuickOPC session.

        Args:
            volume_id (int): Volume ID to create clone.
            tpp_id (int): TPP ID to create the clone volume.
            clone_volume_name (str): Name of the clone volume.

        Returns:
            int: Clone volume ID. If the clone fails, returns a value of -1.
              This value cannot be used as volume ID.
        """
        body = {
            "name": clone_volume_name,
            "tpp_id": tpp_id,
            "is_data_tracking_disabled": False
        }
        r = super().post("/api/v1/volume/{0!s}/clone".format(volume_id), body)
        if r.status_code != 202:
            print("Failed to request a clone creation.")
            print(r)
            print(r.json())
            return -1

        # Wait for the job completion
        job_r = super().wait_job(r.json()["job_id"])
        if job_r.json()["status"] != "Success":
            print("Failed to create a clone.")
            print(job_r)
            print(job_r.json())
            return -1
        clone_volume_href = job_r.json()["resource_href_list"][0]
        clone_volume_id = int(clone_volume_href.split("/")[4])

        return clone_volume_id


def main():
    storage1 = EtdxApi(storage1_url)
    storage2 = EtdxApi(storage2_url)

    # Login
    if not storage1.login(storage1_user_name, storage1_password):
        return False
    if not storage2.login(storage2_user_name, storage2_password):
        return False

    print("Create a REC(Stack) of the volume (ID: {0!s})."
          .format(storage1_volume_id))

    # Create a backup volume on storage2.
    print("Creating backup volume on the destination storage.")
    storage2_volume_id = storage2.rec_create_backup_volume(
            storage2_rec_backup_tpp_id,
            storage2_rec_backup_volume_name,
            storage1,
            storage1_volume_id)
    if storage2_volume_id == -1:
        print("Failed to create a backup volume.")
        return False
    print("Backup volume ID created on storage2:", storage2_volume_id)

    # Create REC(Stack) session.
    print("Creating REC(Stack) session (from {0!s} to {1!s})."
          .format(storage1_volume_id, storage2_volume_id))
    copysession_id = storage1.rec_stack_create(storage1_volume_id, storage2,
                                               storage2_volume_id)
    if copysession_id == -1:
        print("Failed to create a REC session.")
        print("Deleting the created backup volume (ID: {0!s})."
              .format(storage2_volume_id))
        storage2.rec_delete_volume(storage2_volume_id)
        return False
    print("Created REC(Stack) session ID:", copysession_id)

    # Get information of the copy session.
    r = storage1.get("/api/v1/copysession/{0!s}".format(copysession_id))
    print(r)
    print(r.json())

    # Create a QuickOPC clone of the destination volume of the REC on storage2.
    print("Creating a QuickOPC clone of the destination volume "
          "(ID: {0!s}) of the REC."
          .format(storage2_volume_id))
    clone_volume_id = storage2.quickopc_create(storage2_volume_id,
                                               storage2_tpp_id,
                                               storage2_clone_volume_name)
    if clone_volume_id == -1:
        return False
    print("Clone Volume ID created on storage2:", clone_volume_id)

    # Get information of the clone of the destination volume of REC.
    r = storage2.get("/api/v1/volume/{0!s}/copysession"
                     .format(clone_volume_id))
    print(r)
    print(r.json())

    # Variables declaration for other REC stack scripts
    print()
    print("--- Variables declaration for other REC scripts ---")
    print("storage1_copysession_id = {0!s}".format(copysession_id))
    print("storage2_volume_id = {0!s}".format(storage2_volume_id))
    print("storage2_clone_volume_id = {0!s}".format(clone_volume_id))

    # Logout
    storage1.logout()
    storage2.logout()
    return True


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