Hitachi

Hitachi Command Suite Configuration Manager REST API リファレンスガイド


21.6 TrueCopyペア操作のサンプルコード

TrueCopyペア操作のサンプルコードについて説明します。

TrueCopyペア操作のサンプルコードの流れ

TrueCopyペア操作のサンプルコードの流れと、対応するコードの構成要素を次に示します。
ステップ

サンプルコードの流れ

コードの構成要素

1

必要なライブラリのインポートおよびパラメータの設定

2

ヘッダの定義

リクエストヘッダの指定(既定のHTTPヘッダの場合)

3

非同期処理の状態変化取得のための関数の定義

GET操作によるジョブの状態取得

ユーザ認証情報の設定(セッションベースの認証の場合)

ジョブの実行結果の取得

エラーコードの取得

4

REST APIのバージョンの確認

GET操作によるREST APIバージョン情報の取得

5

セッションの生成

リソースのURL取得(オブジェクトIDを指定しない場合)

ユーザ認証情報の設定(ユーザIDとパスワードによる認証の場合)

POST操作によるオブジェクトの新規作成

6

リソースのロック

リソースのURL取得(オブジェクトIDを指定しない場合)

ユーザ認証情報の設定(セッションベースの認証の場合)

PUT操作によるサービスのアクションの実行

7

TrueCopyペアの作成

リソースのURL取得(オブジェクトIDを指定しない場合)

リソースのURL取得(操作結果から取得したオブジェクトIDを指定する場合)

ユーザ認証情報の設定(セッションベースの認証の場合)

JSON形式によるリクエストボディの生成

複数装置でのセッション生成を必要とする操作(リモートコピー操作)

POST操作によるオブジェクトの新規作成

8

リソースのアンロック

リソースのURL取得(オブジェクトIDを指定しない場合)

ユーザ認証情報の設定(セッションベースの認証の場合)

PUT操作によるサービスのアクションの実行

9

ペア作成完了の確認

10

TrueCopyペアの情報取得

GET操作による単一のオブジェクトの取得

ユーザ認証情報の設定(セッションベースの認証の場合)

操作結果が反映されたリソースのURL取得

取得した情報の出力

11

エラーメッセージの出力

エラーメッセージの出力

12

セッションの破棄

リソースのURL取得(操作結果から取得したオブジェクトIDを指定する場合)

ユーザ認証情報の設定(セッションベースの認証の場合)

DELETE操作によるオブジェクトの削除

想定するシステム構成

このサンプルコードでは、次の概念図のようなシステム構成を想定しています。

[図データ]

サンプルコードのパラメータに設定している値を次に示します。必要に応じて、システムの環境や要件に合わせた設定に変更してください。

パラメータ

設定値

説明

LOCAL_USER _CREDENTIAL

("local_copy_user", "local_copy_pass")

ローカルストレージシステムでの認証に使用する認証情報です。サンプルコードの例は、ユーザIDがlocal_copy_user、パスワードがlocal_copy_passの場合の設定例です。このユーザには、ストレージ管理者(プロビジョニング)およびストレージ管理者(リモートバックアップ管理)のロールが必要です。

REMOTE_USER _CREDENTIAL

("remote_copy_user", "remote_copy_pass")

リモートストレージシステムでの認証に使用する認証情報です。サンプルコードの例は、ユーザIDがremote_copy_user、パスワードがremote_copy_passの場合の設定例です。このユーザには、ストレージ管理者(プロビジョニング)およびストレージ管理者(リモートバックアップ管理)のロールが必要です。

COPY_GROUP_NAME

"TC_GROUP"

作成するTrueCopyペアのコピーグループ名です。

COPY_PAIR_NAME

"p_347-348"

作成するTrueCopyペアのコピーペア名です。

PVOL_LDEV_ID

347

プライマリボリュームに使用する、作成済みLDEVの番号です。

SVOL_LDEV_ID

348

セカンダリボリュームに使用する、作成済みLDEVの番号です。

FIRST_WAIT_TIME

1

非同期処理の実行結果を取得する1回目の間隔(秒)です。通常は変更する必要はありません。

MAX_RETRY_COUNT

10

非同期処理の実行結果を取得する最大リトライ回数です。通常は変更する必要はありません。

また、ローカルストレージシステムおよびリモートストレージシステムの情報を、サンプルコード間での共通変数として使うために、remote_copy_param.pyファイルに定義したパラメータと値を次に示します。こちらも、必要に応じて、システムの環境や要件に合わせた設定に変更してください。

パラメータ

設定値

説明

LOCAL_REST_SERVER_IP_ADDR

192.0.2.100

ローカルストレージシステム側のREST APIサーバの IPアドレスです。

LOCAL_PORT

23451

ローカルストレージシステム側のREST APIサーバのSSL通信用ポートです。

LOCAL_STORAGE_MODEL

VSP G800

ローカルストレージシステムのモデル名です。

LOCAL_SERIAL_NUMBER

410000

ローカルストレージシステムのシリアル番号です。

REMOTE_REST_SERVER_IP_ADDR

192.0.2.200

リモートストレージシステム側のREST APIサーバの IPアドレスです。

REMOTE_PORT

23451

リモートストレージシステム側のREST APIサーバのSSL通信用ポートです。

REMOTE_STORAGE_MODEL

VSP G200

リモートストレージシステムのモデル名です。

REMOTE_SERIAL_NUMBER

420000

リモートストレージシステムのシリアル番号です。

サンプルコードの内容

サンプルコードの内容について説明します。

  1. 必要なライブラリのインポートおよびパラメータの設定

    TrueCopyペア操作の処理を開始する前に、必要なライブラリやクラスをインポートします。サンプルコードでは、共通ライブラリのほか、URLを生成する関数を定義したBlockStorageAPIクラスをインポートしています。

    # coding:utf-8
    
    """
    synchronous_remote_copy
    
    This program requires API version 1.3.x or newer.
    """
    
    import traceback
    import requests
    import json
    import sys
    import http.client
    import time
    import remote_copy_param
    
    from block_storage_api import BlockStorageAPI
    

    サンプルコード内で使用するパラメータを設定します。

    # #################Initialize parameters################# #
    # Change the following parameters to fit your environment
    
    # A copy group name
    COPY_GROUP_NAME = "TC_GROUP"
    
    # 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 local storage
    LOCAL_USER_CREDENTIAL = ("local_copy_user",
                             "local_copy_pass")
    
    # An user id and password of the remote storage
    REMOTE_USER_CREDENTIAL = ("remote_copy_user",
                              "remote_copy_pass")
    
    ###########################################################
    
  2. ヘッダの定義

    HTTPリクエストヘッダを定義します。REST APIはJSON形式のデータだけをサポートしているため、データをJSON形式で扱うようヘッダ情報を定義しておきます。

    # ###You don't have to change the following parameters### #
    local_storage_api = BlockStorageAPI(
        remote_copy_param.LOCAL_REST_SERVER_IP_ADDR,
        remote_copy_param.LOCAL_PORT,
        remote_copy_param.LOCAL_STORAGE_MODEL,
        remote_copy_param.LOCAL_SERIAL_NUMBER)
    
    remote_storage_api = BlockStorageAPI(
        remote_copy_param.REMOTE_REST_SERVER_IP_ADDR,
        remote_copy_param.REMOTE_PORT,
        remote_copy_param.REMOTE_STORAGE_MODEL,
        remote_copy_param.REMOTE_SERIAL_NUMBER)
    
    local_headers = {"content-type": "application/json",
                     "accept": "application/json",
                     "Response-Job-Status": "Completed"}
    
    remote_headers = {"content-type": "application/json",
                      "accept": "application/json",
                      "Response-Job-Status": "Completed"}
    
    REQUIRED_MAJOR_VERSION = 1
    REQUIRED_MINOR_VERSION = 3
    
    local_session_id = 0
    remote_session_id = 0
    
    ###########################################################
    
  3. 非同期処理の状態変化取得のための関数の定義(wait_until_jobstatus_is_changed関数)

    非同期処理の状態変化を取得するための関数を定義します。この関数は、メインのTrueCopyペア操作から呼び出して使用します。この関数の詳細については、サンプルコードで使用している関数の説明を参照してください。

    ヒント

    サンプルコードでは、REST APIクライアントとストレージシステム間のSSL通信で使用するサーバ証明書が自己署名証明書の場合に発生するエラーを回避するため、リクエスト発行時にverify=Falseを指定することでサーバ証明書の検証処理をスキップしています。

    """
    Check whether the asynchronous command was finished.
    
    @param storage_api storage_api
    @param job_id the job ID to identify
          the asynchronous command
    @param headers the array of the http headers
    @return r the response data
    """
    
    
    def check_update(storage_api, job_id, headers):
        url = storage_api.job(str(job_id))
        r = requests.get(url, headers=headers, verify=False)
        return r
    
    """
    Wait until the job status is changed
    
    @param storage_api storage_api
    @param headers the array of the http headers
    @param job_id the job ID to identify
           the asynchronous command
    @param changed_status job status after waiting
    @param is_retry_count_enabled if true, wait
           until MAX_RETRY_COUNT. if false, wait forever
           until job status is changed.
    @return job_result.json()["affectedResources"][0]
             URL of an affected resource
    """
    
    
    def wait_until_jobstatus_is_changed(
            storage_api,
            headers,
            job_id,
            changed_status,
            is_retry_count_enabled):
        status = "Initializing"
        retry_count = 1
        wait_time = FIRST_WAIT_TIME
        while status != changed_status:
            if status == "Completed":
                print("Status was already changed" +
                      "to Completed.")
                break
            if is_retry_count_enabled and \
                    retry_count > MAX_RETRY_COUNT:
                raise Exception("Timeout Error! "
                                "Operation was not completed.")
            time.sleep(wait_time)
            job_result = check_update(storage_api,
                                      job_id, headers)
            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. REST APIのバージョンの確認

    ローカルストレージシステム側とリモートストレージシステム側のそれぞれのREST APIサーバで、REST APIのバージョン情報を取得し、サポートしているバージョンであることを確認します。

    ヒント

    次の場合は、ストレージシステムのマイクロコードのバージョンが適切であるかどうかも確認してください。

    • REST APIサーバとストレージシステム間でSSL通信を利用する場合
    • ストレージシステムがVSP 5000 シリーズVSP E シリーズVSP G150、G350、G370、G700、G900VSP F350、F370、F700、F900の場合

    REST APIのバージョンとストレージシステムのマイクロコードのバージョンの対応については、Configuration Managerバージョン対応表を参照してください。

    """
    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 of the local REST API #
        print("Check the API version of the local REST API")
        url = local_storage_api.api_version()
        r = requests.get(url, headers=local_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)
    
        # step1 Check the API version of the remote REST API #
        print("Check the API version of the remote REST API")
        url = remote_storage_api.api_version()
        r = requests.get(url, headers=remote_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. セッションの作成

    ローカルストレージシステム側とリモートストレージシステム側のそれぞれのREST APIサーバで、セッションを生成します。

        # step2 Generate a local session #
        print("Generate a local session")
        url = local_storage_api.generate_session()
        r = requests.post(
            url,
            headers=local_headers,
            auth=LOCAL_USER_CREDENTIAL,
            verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        local_token = r.json()["token"]
        local_auth = "Session " + local_token
        local_session_id = r.json()["sessionId"]
    
        # step2 Generate a remote session #
        print("Generate a remote session")
        url = remote_storage_api.generate_session()
        r = requests.post(
            url,
            headers=remote_headers,
            auth=REMOTE_USER_CREDENTIAL,
            verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        remote_token = r.json()["token"]
        remote_auth = "Session " + remote_token
        remote_session_id = r.json()["sessionId"]
    

    セッションを生成すると、セッションIDとトークンが返却されます。トークンは、これ以降の操作で必要な認証情報として、API実行時にAuthorizationヘッダに指定します。セッションIDは、一連の操作が終了したあと、セッションを破棄するときに使用します。

  6. リソースのロック

    操作対象のボリュームがほかのユーザから操作されないように、ロックを取得します。ローカルストレージシステム側では、プライマリボリューム用のLDEVが属するリソースグループを、リモートストレージシステムでは、セカンダリボリューム用のLDEVが属するリソースグループをロックします。

      try:
            # step3 Lock the local resource group #
            print("Lock the local resource group")
            url = local_storage_api.lock()
            local_headers["Authorization"] = local_auth
            r = requests.put(url, headers=local_headers,
                             verify=False)
            if r.status_code != http.client.ACCEPTED:
                raise requests.HTTPError(r)
            print("Request was accepted. JOB URL : " +
                  r.json()["self"])
            wait_until_jobstatus_is_changed(
                local_storage_api,
                local_headers,
                r.json()["jobId"],
                "Completed",
                True)
    
            # step3 Lock the remote resource group #
            print("Lock the remote resource group")
            remote_headers["Authorization"] = remote_auth
            url = remote_storage_api.lock()
            r = requests.put(url, headers=remote_headers,
                             verify=False)
            if r.status_code != http.client.ACCEPTED:
                raise requests.HTTPError(r)
            print("Request was accepted. JOB URL : " +
                  r.json()["self"])
            wait_until_jobstatus_is_changed(
                remote_storage_api,
                remote_headers,
                r.json()["jobId"],
                "Completed",
                True)
    

    wait_until_jobstatus_is_changed 関数は、非同期に実行されるジョブの実行状態を確認し、指定したジョブの状態になるまで待ちます。サンプルコードでは、ジョブの実行状態が"Completed"になり、ロックが掛けられたことを確認しています。

  7. TrueCopyペアの作成

    作成済みのLDEVを使用して、TrueCopyペアの作成を作成します。コピーグループも新規に作成します。コピーグループ名やコピーペア名、使用するボリュームのLDEV番号は、あらかじめパラメータで定義したものを指定します。そのほか、コピーペア種別、コピーグループの作成の有無、フェンスレベルなどを指定して、TrueCopyペアを作成するリクエストを発行します。URLの生成にはblock_storage_api関数を使用しています。

          # step4 Create a remote copy pair #
            print("Create a remote copy pair")
            url = local_storage_api.remote_copy_pairs()
            body = {
                "copyGroupName": COPY_GROUP_NAME,
                "copyPairName": COPY_PAIR_NAME,
                "replicationType": "TC",
                "remoteStorageDeviceId": remote_storage_api.
                get_storage_id(),
                "pvolLdevId": PVOL_LDEV_ID,
                "svolLdevId": SVOL_LDEV_ID,
                "isNewGroupCreation": "true",
                "fenceLevel": "data",
            }
            local_headers["Remote-Authorization"] = remote_auth
            r = requests.post(
                url,
                headers=local_headers,
                data=json.dumps(body),
                verify=False)
            if r.status_code != http.client.ACCEPTED:
                raise requests.HTTPError(r)
            print("Create remote copy pair request " +
                  "was accepted. JOB URL : " + r.json()["self"])
            wait_until_jobstatus_is_changed(
                local_storage_api,
                local_headers,
                r.json()["jobId"],
                "StorageAccepted",
                False)
            jobid = r.json()["jobId"]
    
            print("Status changed to StorageAccepted")
    

    wait_until_jobstatus_is_changed 関数は、非同期に実行されるジョブの実行状態を確認し、指定したジョブの状態になるまで待ちます。サンプルコードでは、ジョブの実行状態が"StorageAccepted"になり、ストレージシステムでTrueCopyペア作成のリクエストが受け付けられたことを確認しています。

  8. リソースのアンロック

    ストレージシステムでペア作成の処理が受け付けられたことが確認できたら、取得したロックを解除します。サンプルコードでは、APIの実行中にエラーが発生した場合にも必ずロックが解除されるよう、finally句で記述しています。

       finally:
            # step5 Unlock the local resource group #
            print("Unlock the local resource group")
            url = local_storage_api.unlock()
            r = requests.put(url, headers=local_headers,
                             verify=False)
            if r.status_code != http.client.ACCEPTED:
                raise requests.HTTPError(r)
            print("Request was accepted. JOB URL : " +
                  r.json()["self"])
            wait_until_jobstatus_is_changed(
                local_storage_api, local_headers,
                r.json()["jobId"], "Completed", True)
    
            # step5 Unlock the remote resource group #
            print("Unlock the remote resource group")
            url = remote_storage_api.unlock()
            r = requests.put(url, headers=remote_headers,
                             verify=False)
            if r.status_code != http.client.ACCEPTED:
                raise requests.HTTPError(r)
            print("Request was accepted. JOB URL : " +
                  r.json()["self"])
            wait_until_jobstatus_is_changed(
                remote_storage_api,
                remote_headers,
                r.json()["jobId"],
                "Completed",
                True)
    
  9. ペア作成完了の確認

    ストレージシステムでのペア作成の処理が完了したことを確認します。wait_until_jobstatus_is_changed関数で、ジョブの実行状態がCompletedになったことを確認します。

        # step6 Wait until the operation is complete #
        affected_resource_path = wait_until_jobstatus_is_changed(
            local_storage_api, local_headers,
            jobid, "Completed", False)
    
  10. TrueCopyペアの情報取得

    ペアが正しく作成されていることを確認するために、TrueCopyペアの作成ステップで取得したペアのURLを使用してペアの情報を取得します。サンプルコードでは、コピーグループ名、コピーペア名、ペア種別、P-VOLとS-VOLそれぞれのLDEV番号、ペアボリューム状態、およびストレージデバイスIDを出力しています。

        # step7 Print the remote copy pair #
        print("Print the remote copy pair")
        url = local_storage_api.affected_resource(
            affected_resource_path)
        r = requests.get(url, headers=local_headers,
                         verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
        print("COPY GROUP NAME : " +
              str(r.json()["copyGroupName"]))
        print("COPY PAIR NAME : " +
              str(r.json()["copyPairName"]))
        print("REPLICATION TYPE : " +
              str(r.json()["replicationType"]))
        print("PVOL LDEV ID : " + str(r.json()["pvolLdevId"]))
        print("SVOL LDEV ID : " + str(r.json()["svolLdevId"]))
        print("PVOL STATUS : " + str(r.json()["pvolStatus"]))
        print("SVOL STATUS : " + str(r.json()["svolStatus"]))
        print("PVOL STORAGE DEVICE ID : "
              + str(r.json()["pvolStorageDeviceId"]))
        print("SVOL STORAGE DEVICE ID : "
              + str(r.json()["svolStorageDeviceId"]))
        print("REMOTE MIRROR COPY PAIR ID : "
              + str(r.json()["remoteMirrorCopyPairId"]))
        print()
    
  11. エラーメッセージの出力

    サンプルコードでは、通信エラー、HTTP リクエストエラー、ジョブ実行時エラーの処理を記載しています。通信エラーの場合は、エラーメッセージを出力します。HTTP リクエストエラーの場合は、エラーコードとメッセージ、レスポンスボディを出力します。ジョブ実行時エラーの場合は、ジョブの実行結果に含まれる内容をすべて出力します。

    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")
    
  12. セッションの破棄

    一連の操作が完了したら、ローカルストレージシステム側とリモートストレージシステム側のREST APIサーバで、セッションを破棄します。セッションの作成時に取得したセッションIDを指定します。サンプルコードでは、APIの実行中にエラーが発生した場合にも必ずセッションが破棄されるよう、finally句で記述しています。セッションを破棄したら、処理を終了します。

    finally:
        # step8 Discard the local session #
        print("Discard the local session")
        url = local_storage_api. \
            discard_session(local_session_id)
        r = requests.delete(url, headers=local_headers,
                            verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
    
        # step8 Discard the remote session #
        print("Discard the remote session")
        url = remote_storage_api.discard_session(
            remote_session_id)
        r = requests.delete(url,
                            headers=remote_headers, verify=False)
        if r.status_code != http.client.OK:
            raise requests.HTTPError(r)
    
        print("Operation was completed.")
        sys.exit()