Creating a Mirrored Volume (Synchronous Mode) in a Remote Storage System

The following script creates a Volume on the remote storage system and creates a REC Session in synchronous mode. This script is used in production to create a mirrored volume in a remote storage system using REC (Synchronous Mode). For more information, see the description of Creating a Mirrored Volume in a Remote Storage System with REC (Synchronous Mode).

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 (Synchronous Mode) between the business volume and the volume you created in 1.

rec_sync_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(synchronous) session from the volume on storage1 to storage2.
# Volume ID on storage1 to backup.
storage1_volume_id = 100001
# TPP ID to create a backup volume on storage2.
storage2_backup_tpp_id = 1
# Name of the backup volume.
storage2_backup_name = "backup_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_sync_create(self, source_volume_id, destination_storage,
                        destination_volume_id):
        """Create REC(Synchronous) 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": "Synchronous",
            "split_mode": "manual",
            "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 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(Synchronous) 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_backup_tpp_id,
            storage2_backup_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(Synchronous) session.
    print("Creating REC(Synchronous) session (from {0!s} to {1!s})."
          .format(storage1_volume_id, storage2_volume_id))
    copysession_id = storage1.rec_sync_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(Synchronous) 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())

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

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


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