Sample coding for ShadowImage pair operations

REST API Reference Guide for Virtual Storage Platform 5000, Virtual Storage Platform E Series, and Virtual Storage Platform G/F Series

Version
93-07-0x
90-09-0x
88-08-10
Audience
anonymous
Part Number
MK-98RD9014-17
This section explains the sample coding for the ShadowImage pair operations.

Sample coding operation flow for ShadowImage pair operations

The following table shows the sample coding operation flow for ShadowImage pair operations and the corresponding code constructs.

Step

Sample coding operation flow

Code constructs

1

Import necessary libraries and set parameters.

-

2

Define headers.

Specifying request headers (for the default HTTP headers)

Specifying request headers (for the custom HTTP headers)

3

Define functions for issuing an HTTP request and for verifying the status of asynchronous processing.

Getting information about the job status by performing a GET operation

Setting user authentication information (for authentication by using session-based authentication)

Generating a request body in JSON format

Getting the job execution results

Getting the URLs of the resources to which the operation results have been applied

Getting error codes

4

Check the version of the REST API.

Getting information about the version of the REST API by performing a GET operation

5

Generate a session.

Getting the URLs of the resources (when object IDs are not specified)

Setting user authentication information (for authentication by using a user ID and a password)

Creating objects by performing a POST operation

6

Create a ShadowImage pair.

Getting the URLs of the resources (when object IDs are not specified)

Getting the URLs of the resources (when object IDs that are obtained from the operation results are specified)

Generating a request body in JSON format

Creating objects by performing a POST operation

7

Split a ShadowImage pair.

Getting the action template by performing a GET operation

Setting user authentication information (for authentication by using session-based authentication)

Generating a request body in JSON format

Running actions that use the action template by using a POST operation

8

Get information about a ShadowImage pair.

Getting an object by performing a GET operation

Setting user authentication information (for authentication by using session-based authentication)

Outputting the obtained information

9

Output error messages.

Outputting error messages

10

Discard the session.

Getting the URLs of the resources (when object IDs that are obtained from the operation results are specified)

Deleting objects by performing a DELETE operation

Expected system configuration

This sample coding assumes the system configuration is as shown in the following figure.

The following table shows the values specified for the parameters in the sample coding. If necessary, change the settings to match the system environment and requirements.

Parameter

Value

Description

USER_CREDENTIAL

("user1", "pass1")

This is the authentication information to be used for authentication in the storage system. The coding sample shows a setting example when the user ID is user1, and the password is pass1. The user needs the Storage Administrator (Provisioning) role and the Storage Administrator (Local Copy) role.

COPY_GROUP_NAME

SI_347

The copy group name to be used for creating a ShadowImage pair. In the sample coding, a new copy group is created when creating a pair.

COPY_PAIR_NAME

p_347-348

The copy pair name of the ShadowImage pair to be created

PVOL_LDEV_ID

347

The LDEV number of the already created volume to be used as the primary volume

SVOL_LDEV_ID

348

The LDEV number of the already created volume to be used as the secondary volume

FIRST_WAIT_TIME

1

The first interval for collecting the execution result of asynchronous processing. Normally, you do not need to change this value.

MAX_RETRY_COUNT

10

The maximum number of retries for collecting the execution result of asynchronous processing. Normally, you do not need to change this value.

Contents of the sample coding

This subsection explains the sample coding.

  1. Import necessary libraries and set parameters.
    Before performing operations for the ShadowImage pair, the sample coding imports the required libraries or classes. In addition to the common libraries, the sample coding also imports the BlockStorageAPI class that defines the function that generates URLs.
    # coding:utf-8
    
    """
    local_copy
    
    This program requires API version 1.9.0 or newer.
    """
    
    import traceback
    import requests
    import json
    import sys
    import http.client
    import time
    import rest_server_param
    import storage_param
    
    from block_storage_api import BlockStorageAPI
    
    Set parameters to be used in the sample coding.
    # #################Initialize parameters################# #
    # Change the following parameters to fit your environment
    
    # A copy group name
    COPY_GROUP_NAME = "SI_347"
    
    # A copy pair name
    COPY_PAIR_NAME = "p_347-348"
    
    # A primary volume ID
    # Specify already created and allocated volume ID by decimal
    PVOL_LDEV_ID = 347
    
    # A secondary volume ID which has the exactly same size
    # as the primary volume
    # Specify already created and allocated volume ID by decimal
    SVOL_LDEV_ID = 348
    
    
    # This parameter defines the first interval to access
    # an asynchronous job. (Unit: Second)
    FIRST_WAIT_TIME = 1
    
    # This parameter defines the maximum retry time
    # to confirm job status.
    MAX_RETRY_COUNT = 10
    
    # An user id and password of the target storage
    USER_CREDENTIAL = ("user1", "pass1")
    
    ###########################################################
    
  2. Define headers.
    Define the HTTP request header. Because the REST API only supports JSON format data, the sample coding defines header information so that data is handled in JSON format. In addition, for asynchronous processing, the sample coding specifies the settings of the Response-Job-Status header so that responses are returned after waiting for the completion of the jobs.
    # ###You don't have to change the following parameters### #
    block_storage_api = BlockStorageAPI(
        rest_server_param.REST_SERVER_IP_ADDR,
        rest_server_param.REST_SERVER_PORT,
        storage_param.STORAGE_MODEL,
        storage_param.SERIAL_NUMBER)
    
    headers = {"content-type": "application/json",
               "accept": "application/json",
               "Response-Job-Status": "Completed"}
    
    REQUIRED_MAJOR_VERSION = 1
    REQUIRED_MINOR_VERSION = 9
    
    session_id = 0
    
    ###########################################################
    
  3. Define the function for issuing an HTTP request and for verifying the status of asynchronous processing (the invoke_async_command function).
    Define the function that issues an HTTP request and verifies the status of asynchronous processing. Call and use this function from the main ShadowImage pair operation. For details on this function, see the section explaining the of functions used in the sample coding.
    Tip: To prevent errors that occur when the server certificate used for SSL communication between the REST API client and the storage system is a self-signed certificate, the sample coding specifies verify=False in the request message to skip verification of the server certificate.
    """
    Check whether the asynchronous command was finished.
    @param job_id The job ID to identify
                  the asynchronous command
    @return r.json() The JSON data that contains response data
    
    """
    
    
    def check_update(job_id):
        url = block_storage_api.job(str(job_id))
        r = requests.get(url, headers=headers, verify=False)
        return r
    
    
    """
    Execute the HTTP request (POST or PATCH)
    @param method_type HTTP request method (POST or PATCH)
    @param url URL to execute HTTP method
    @param body The information of a resource
    @return job_result.json()["affectedResources"][0]
             URL of an affected resource
    """
    
    
    def invoke_async_command(method_type, url, body):
        if method_type == "patch":
            r = requests.patch(url, headers=headers,
                             data=json.dumps(body), verify=False)
        elif method_type == "post":
            r = requests.post(
                url,
                headers=headers,
                data=json.dumps(body),
                verify=False)
        if r.status_code != http.client.ACCEPTED:
            raise requests.HTTPError(r)
        print("Request was accepted. JOB URL : " +
              r.json()["self"])
        status = "Initializing"
        job_result = None
        retry_count = 1
        wait_time = FIRST_WAIT_TIME
        while status != "Completed":
            if retry_count > MAX_RETRY_COUNT:
                raise Exception("Timeout Error! "
                                "Operation was not completed.")
            time.sleep(wait_time)
            job_result = check_update(r.json()["jobId"])
            status = job_result.json()["status"]
            double_time = wait_time * 2
            if double_time < 120:
                wait_time = double_time
            else:
                wait_time = 120
            retry_count += 1
        if job_result.json()["state"] == "Failed":
            error_obj = job_result.json()["error"]
            if "errorCode" in error_obj:
                if "SSB1" in error_obj["errorCode"]:
                    print("Error! SSB code : ",
                          error_obj["errorCode"]["SSB1"],
                          ", ", error_obj["errorCode"]["SSB2"])
                elif "errorCode" in error_obj["errorCode"]:
                    print("Error! error code : ",
                          error_obj["errorCode"]["errorCode"])
            raise Exception("Job Error!", job_result.text)
        print("Async job was succeeded. affected resource : " +
              job_result.json()["affectedResources"][0])
        return job_result.json()["affectedResources"][0]
    
  4. Check the version of the REST API.
    Get information about the version of the REST API to make sure that the version is supported.
    """
    Check whether this API version allows the REST
     Server to execute this program
    
    @param api_version api version of this REST Server
    @param required_major_version the lowest number of
           the major version that this program requires
    @param required_minor_version the lowest number of
           the minor version that this program requires
    
    """
    
    
    def check_api_version(api_version, required_major_version,
                          required_minor_version):
        version = api_version.split(".")
        major_version = int(version[0])
        minor_version = int(version[1])
        if not ((major_version == required_major_version and
                 minor_version >= required_minor_version) or
                major_version >= required_major_version + 1):
            sys.exit(
                "This program requires API Version " +
                str(required_major_version) +
                "." +
                str(required_minor_version) +
                "." +
                "x or newer.\n")
    
    try:
        # step1 Check the API version #
        print("Check the API version")
        url = block_storage_api.api_version()
        r = requests.get(url, headers=headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        check_api_version(
            r.json()["apiVersion"],
            REQUIRED_MAJOR_VERSION,
            REQUIRED_MINOR_VERSION)
    
  5. Generate a session.

    Generate sessions by using the REST API server.

        # step2 Generate a session #
        print("Generate a session")
        url = block_storage_api.generate_session()
        r = requests.post(url, headers=headers,
                          auth=USER_CREDENTIAL, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        token = r.json()["token"]
        auth = "Session " + token
        session_id = r.json()["sessionId"]
    

    When a session is generated, a session ID and a token are returned. When running the API, specify the token for the Authentication header as the required authentication information for the subsequent operations. Use the session ID to discard the session after a set of operations is completed.

  6. Create a ShadowImage pair.

    Use the already created volumes to create a ShadowImage pair. Also create a new copy group. Specify the copy group name, copy pair name, and the LDEV number of the volume to be used that are defined in advance in the parameters. In addition, specify the copy pair type, MU number, and whether to create a copy group, and then issue a request to create a ShadowImage pair. The block_storage_api function is used to generate the URL.

        # step3 Create a local copy pair #
        print("Create a local copy pair")
        url = block_storage_api.local_copy_pairs()
        body = {
            "copyGroupName": COPY_GROUP_NAME,
            "copyPairName": COPY_PAIR_NAME,
            "replicationType": "SI",
            "pvolLdevId": PVOL_LDEV_ID,
            "pvolMuNumber": 0,
            "svolLdevId": SVOL_LDEV_ID,
            "isNewGroupCreation": True,
        }
        headers["Authorization"] = auth
        affected_resource = invoke_async_command("post",
                                                 url, body)
        pair_url = block_storage_api.affected_resource(
            affected_resource)
    

    The invoke_async_command function issues the request to create a ShadowImage pair under the specified conditions, checks the execution status of the jobs that were run asynchronously, and then returns the URL of the created pair as the execution result.

  7. Split the ShadowImage pair.

    In the sample coding, a ShadowImage pair is split by using the action template. First, get the action template for splitting a ShadowImage pair by using the URL of the pair that was obtained when the ShadowImage pair was created.

        # step4 Split the local copy pair #
        print("Split the local copy pair")
        url = block_storage_api.split_local_copy_pair_template(
            pair_url)
        r = requests.get(url, headers=headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        print("Action template(split):")
        print(r.text)
    

    Set values in the obtained template, and then issue a request to split the created ShadowImage pair.

        body = r.json()
        body["parameters"]["copyPace"] = 3
        split_url = block_storage_api.split_local_copy_pair(
            pair_url)
        invoke_async_command("post", split_url, body)
    
  8. Get information about the ShadowImage pair.

    Get information about the pair by using the URL of the pair that was obtained when the ShadowImage pair was created. In the sample coding, the following items are output: the copy group name, copy pair name, LDEV number and pair volume status for the P-VOL, and the LDEV number and pair volume status for the S-VOL.

        # step5 Print the pair status #
        print("Print the pair status")
        r = requests.get(pair_url,
                         headers=headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
    
        print("COPY GROUP NAME : " + r.json()["copyGroupName"])
        print("COPY PAIR NAME : " + r.json()["copyPairName"])
        print("P-VOL LDEV ID : " + str(r.json()["pvolLdevId"]))
        print("S-VOL LDEV ID : " + str(r.json()["svolLdevId"]))
        print("P-VOL STATUS : " + r.json()["pvolStatus"])
        print("S-VOL STATUS : " + r.json()["svolStatus"])
        print("LOCAL CLONE COPY PAIR ID : " +
              r.json()["localCloneCopypairId"])
        print()
    
  9. Output error messages.
    In the sample coding, processing for communication errors, HTTP request errors, and job execution errors is described. If a communication error occurs, an error message is output. If an HTTP request error occurs, the error code, the error message, and the response body are output. If a job execution error occurs, all of the contents included in the job execution result are output.
    except requests.ConnectionError:
        sys.stderr.write("Connection Error!\n")
        sys.stderr.write(traceback.format_exc())
    except requests.HTTPError as he:
        sys.stderr.write("HTTP Error! status code : ")
        sys.stderr.write(str(he.args[0].status_code) + "\n")
        sys.stderr.write(he.args[0].text + "\n")
    except Exception as e:
        sys.stderr.write(traceback.format_exc())
        for msg in e.args:
            sys.stderr.write(str(msg) + "\n")
    
  10. Discard the session.

    After a set of operations is completed, discard the session. Specify the session ID that was obtained when the session was generated. The "finally" statement in the sample coding makes sure that the session will be discarded even if an error occurs while the API is running. After the session is discarded, the processing ends.

    finally:
        # step6 Discard the session #
        print("Discard the session")
        url = block_storage_api.discard_session(session_id)
        r = requests.delete(url, headers=headers, verify=False)
        try:
            if r.status_code != http.client.OK:
                raise requests.HTTPError(r)
        except requests.HTTPError as he:
            sys.stderr.write("HTTP Error! status code : ")
            sys.stderr.write(str(he.args[0].status_code) + "\n")
            sys.stderr.write(he.args[0].text + "\n")
    
        print("Operation was completed.")
        sys.exit()