Sample coding for volume allocation

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 volume allocation.

Sample coding operation flow for volume allocation

The following table shows the sample coding operation flow for volume allocation 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)

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 an HDP volume.

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

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

Generating a request body in JSON format

Creating objects by performing a POST operation

7

Create a host group.

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

Generating a request body in JSON format

Creating objects by performing a POST operation

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

8

Change the host mode.

Generating a request body in JSON format

Changing the object attribute by performing a PATCH operation

9

Register the WWN of the host.

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

Generating a request body in JSON format

Creating objects by performing a POST operation

10

Set an LU path.

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

Generating a request body in JSON format

Creating objects by performing a POST operation

11

Get HDP volume information.

Getting an object by performing the GET operation

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

Outputting the obtained information

12

Output error messages.

Outputting error messages

13

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.

POOL_ID

8

ID of the created HDP pool which will be used to create the HDP volume

BYTE_CAPACITY

1T

Capacity of the HDP volume to be created

PORT_ID

["CL1-A"]

The array of names of the Fibre Channel port that is used for I/O with the host

HOST_GRP_NAME

WindowsHost

The host group name to be created in order to associate the host and the port

HOST_MODE

WIN

The host mode to be specified for the host group

HOST_WWN

aaaabbbbcccc0123

The WWN of the host

FIRST_WAIT_TIME

1

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

MAX_RETRY_COUNT

6

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 starting the volume allocation processing, 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
    
    """
    provisioning
    
    This program requires API version 1.9.0 or newer.
    """
    
    import requests
    import json
    import sys
    import http.client
    import time
    import traceback
    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 POOL ID for creating a volume
    POOL_ID = 8
    
    # The DP volume capacity to create
    BYTE_CAPACITY = "1T"
    
    # A port name to add a LUN path
    PORT_ID = ["CL1-A"]
    
    # A host group name to create
    # You can assign any host group name
    HOST_GRP_NAME = "WindowsHost"
    
    # A Host mode for the created host group
    # Please refer to the manual and set an appropriate mode
    HOST_MODE = "WIN"
    
    # A World Wide Name of the host (HBA) to allocate the volume
    HOST_WWN = "aaaabbbbcccc0123"
    
    
    # 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 = 6
    
    # 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.

    # ###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"}
    
    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 volume allocation operation. For details on this function, see the section explaining the 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 the 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 an HDP volume.
    Specify the pool ID, and the volume capacity, and then create the HDP volume.
        # step3 Add an LDEV #
        print("Add an LDEV")
        url = block_storage_api.ldevs()
        headers["Authorization"] = auth
        body = {
            "poolId": POOL_ID,
            "byteFormatCapacity": BYTE_CAPACITY,
            "isParallelExecutionEnabled": True
        }
        ldev_id = invoke_async_command("post", url, body).split("/")[-1]
    
    The invoke_async_command function issues the request to create the HDP volume, checks the execution status of the jobs that were run asynchronously, and then returns the URL of the created HDP volume as the execution result.
  7. Create a host group.
    To allocate the created HDP volume to the host, create a host group. The sample coding only specifies the port number to be used by the host group and the host group name. Specification of the host group number is omitted. In this case, a host group number is automatically assigned.
        # step4 Add a host group #
        print("Add a host group")
        url = block_storage_api.host_groups()
        body = {
            "portId": PORT_ID[0],
            "hostGroupName": HOST_GRP_NAME
        }
        affected_resource_path = invoke_async_command("post",
                                                      url, body)
    
    Get the URL of the created host group and the assigned host group number.
        url = block_storage_api.affected_resource(
            affected_resource_path)
    
        r = requests.get(url, headers=headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
    
        host_group_number = r.json()["hostGroupNumber"]
    
  8. Change the host mode.
    Change the host mode of the created host group according to the platform of the host to which the volume will be allocated. To issue a request to change the host mode, use the URL of the host group that was obtained in the step when the host group was created.
        # step5 Modify the host group #
        print("Modify the host group")
        body = {
            "hostMode": HOST_MODE
        }
        invoke_async_command("patch", url, body)
    
  9. Register the WWN of the host.
    Register the host to which the HDP volume will be allocated in the host group that you created. Specify the WWN of the HBA of the host to be registered, the port number of the host group, and the assigned host group number. Use the host group number that was obtained when the host group was created.
        # step6 Add an HBA WWN #
        print("Add an HBA WWN")
        url = block_storage_api.host_wwns()
        body = {
            "hostWwn": HOST_WWN,
            "portId": PORT_ID[0],
            "hostGroupNumber": host_group_number
        }
        invoke_async_command("post", url, body)
    
  10. Set an LU path.
    Set the LU path by associating the created volume with the host group. The sample coding specifies the LDEV number of the created HDP volume, the port number to be used by the host group, and the host group number. Specification of the LUN is omitted. In this case, a LUN is automatically assigned.
        # step7 Add a LUN path #
        print("Add a LUN path")
        url = block_storage_api.luns()
        body = {
            "ldevId": ldev_id,
            "portIds": PORT_ID,
            "hostGroupNumber": host_group_number
        }
        invoke_async_command("post", url, body)
    
    The LU path is set, and the HDP volume is now accessible from the host.
  11. Get HDP volume information.
    To check whether the operations up to this step have been correctly applied to the resource, specify the LDEV number that was obtained when the HDP volume was created, and then obtain HDP volume information. From the collected information, the sample coding outputs the LDEV number, the ID of the pool from which the volume was created, the capacity of the HDP volume, and the assigned port.
        # step8 Print the LDEV #
        print("Print the LDEV")
        url = block_storage_api.ldev(ldev_id)
        r = requests.get(url, headers=headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
    
        print("LDEV ID : " + str(r.json()["ldevId"]))
        print("POOL ID : " + str(r.json()["poolId"]))
        print("CAPACITY : " +
              str(r.json()["byteFormatCapacity"]))
        print("PORT : " + str(r.json()["ports"]))
        print()
    
  12. 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")
    
  13. 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:
        # ----step10 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()