21.3 ボリューム割り当てのサンプルコード
ボリューム割り当てのサンプルコードの流れ
ステップ |
サンプルコードの流れ |
コードの構成要素 |
---|---|---|
1 |
必要なライブラリのインポートおよびパラメータの設定 |
― |
2 |
ヘッダの定義 |
リクエストヘッダの指定(既定のHTTPヘッダの場合) |
3 |
HTTPリクエストの発行と非同期処理の状態確認のための関数の定義 |
GET操作によるジョブの状態取得 ユーザ認証情報の設定(セッションベースの認証の場合) JSON形式によるリクエストボディの生成 ジョブの実行結果の取得 操作結果が反映されたリソースのURL取得 エラーコードの取得 |
4 |
REST APIのバージョンの確認 |
GET操作によるREST APIバージョン情報の取得 |
5 |
セッションの生成 |
リソースのURL取得(オブジェクトIDを指定しない場合) ユーザ認証情報の設定(ユーザIDとパスワードによる認証の場合) POST操作によるオブジェクトの新規作成 |
6 |
HDPボリュームの作成 |
リソースのURL取得(オブジェクトIDを指定しない場合) GET操作によるオブジェクトの取得(情報検索で取得する場合)※ ユーザ認証情報の設定(セッションベースの認証の場合) JSON形式によるリクエストボディの生成 POST操作によるオブジェクトの新規作成 |
7 |
ホストグループの作成 |
リソースのURL取得(オブジェクトIDを指定しない場合) JSON形式によるリクエストボディの生成 POST操作によるオブジェクトの新規作成 リソースのURL取得(操作結果から取得したオブジェクトIDを指定する場合) |
8 |
ホストモードの変更 |
JSON形式によるリクエストボディの生成 PUT操作によるオブジェクトの属性変更 |
9 |
ホストのWWNの登録 |
リソースのURL取得(オブジェクトIDを指定しない場合) JSON形式によるリクエストボディの生成 POST操作によるオブジェクトの新規作成 |
10 |
LUパスの設定 |
リソースのURL取得(オブジェクトIDを指定しない場合) JSON形式によるリクエストボディの生成 POST操作によるオブジェクトの新規作成 |
11 |
HDPボリュームの情報取得 |
GET操作によるオブジェクトの取得(特定のオブジェクトを取得する場合) ユーザ認証情報の設定(セッションベースの認証の場合) 取得した情報の出力 |
12 |
エラーメッセージの出力 |
エラーメッセージの出力 |
13 |
セッションの破棄 |
リソースのURL取得(操作結果から取得したオブジェクトIDを指定する場合) DELETE操作によるオブジェクトの削除 |
想定するシステム構成
このサンプルコードでは、次の概念図のようなシステム構成を想定しています。
サンプルコードのパラメータに設定している値を次に示します。必要に応じて、システムの環境や要件に合わせた設定に変更してください。
パラメータ |
設定値 |
説明 |
---|---|---|
USER_CREDENTIAL |
("user1", "pass1") |
ストレージシステムでの認証に使用する認証情報です。サンプルコードの例は、ユーザIDがuser1、パスワードがpass1の場合の設定例です。このユーザにはストレージ管理者(プロビジョニング)ロールが必要です。 |
POOL_ID |
8 |
HDPボリュームの作成元として使用する、作成済みのHDPプールのIDです。 |
BYTE_CAPACITY |
"1T" |
作成するHDPボリュームの容量です。 |
PORT_ID |
"CL1-A" |
ホストとのI/Oに使用するファイバーチャネルポート名です。 |
HOST_GRP_NAME |
"WindowsHost" |
ホストとポートを関連づけるために作成するホストグループ名です。 |
HOST_MODE |
"WIN" |
ホストグループに設定するホストモードです。 |
HOST_WWN |
"aaaabbbbcccc0123" |
ホストのWWNです。 |
FIRST_WAIT_TIME |
1 |
非同期処理の実行結果を取得する1回目の間隔(秒)です。通常は変更する必要はありません。 |
MAX_RETRY_COUNT |
6 |
非同期処理の実行結果を取得する最大リトライ回数です。通常は変更する必要はありません。 |
サンプルコードの内容
サンプルコードの内容について説明します。
- 必要なライブラリのインポートおよびパラメータの設定 ボリューム割り当ての処理を開始する前に、必要なライブラリやクラスをインポートします。サンプルコードでは、共通ライブラリのほか、URLを生成する関数を定義したBlockStorageAPIクラスをインポートしています。
# coding:utf-8 """ provisioning This program requires API version 1.3.x 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
サンプルコード内で使用するパラメータを設定します。# #################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") ###########################################################
- ヘッダの定義 HTTPリクエストヘッダを定義します。REST APIはJSON形式のデータだけをサポートしているため、データをJSON形式で扱うようヘッダ情報を定義しておきます。
# ###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 = 3 session_id = 0 ###########################################################
- HTTPリクエストの発行と非同期処理の状態確認のための関数の定義(invoke_async_command関数) HTTPリクエストの発行と非同期処理の状態を確認する関数を定義します。この関数は、メインのボリューム割り当て操作から呼び出して使用します。この関数の詳細については、サンプルコードで使用している関数の説明を参照してください。
- ヒント
-
サンプルコードでは、REST APIクライアントとストレージシステム間のSSL通信で使用するサーバ証明書が自己署名証明書の場合に発生するエラーを回避するため、リクエスト発行時にverify=Falseを指定することでサーバ証明書の検証処理をスキップしています。
""" 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 PUT) @param method_type HTTP request method (POST or PUT) @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 == "put": r = requests.put(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]
- REST APIのバージョンの確認 REST APIのバージョン情報を取得し、サポートしているバージョンであることを確認します。
- ヒント
-
次の場合は、ストレージシステムのマイクロコードのバージョンが適切であるかどうかも確認してください。
- REST APIサーバとストレージシステム間でSSL通信を利用する場合
- ストレージシステムがVSP 5000 シリーズ、VSP E シリーズ、VSP G150、G350、G370、G700、G900、VSP 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 # 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)
-
セッションの生成
REST APIサーバで、セッションを生成します。# 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"]
セッションを生成すると、セッションIDとトークンが返却されます。トークンは、これ以降の操作で必要な認証情報として、API実行時にAuthenticationヘッダに指定します。セッションIDは、一連の操作が終了したあと、セッションを破棄するときに使用します。
- HDPボリュームの作成
作成するHDPボリュームが使用するLDEV番号を取得します。HDPボリューム作成時に指定するLDEV番号は未使用である必要があるため、「未実装のLDEV」という条件で情報検索するURLを生成し、条件に合致するLDEV の情報を取得します。URLの生成にはblock_storage_api関数を使用しています。
# step3 Search for an unused LDEV ID # print("Search for an unused LDEV ID") url = block_storage_api.views_undefined_ldev() headers["Authorization"] = auth r = requests.get(url, headers=headers, verify=False) if r.status_code != http.client.OK: raise requests.HTTPError(r)
返されたLDEV情報から、未実装のLDEV番号を1件取得します。# Get an unused LDEV ID (smallest value) if len(r.json()["data"]) is 0: raise Exception("Free LDEV ID does not exist") undefined_ldev = r.json()["data"][0]["ldev"] ldev_id = undefined_ldev["ldevId"] print("Free LDEV ID:", ldev_id)
次に、取得した未実装のLDEV番号、プールID、ボリュームの容量を指定して、HDPボリュームを作成します。# step4 Add an LDEV # print("Add an LDEV") url = block_storage_api.ldevs() body = { "ldevId": ldev_id, "poolId": POOL_ID, "byteFormatCapacity": BYTE_CAPACITY } invoke_async_command("post", url, body)
invoke_async_command関数は、HDPボリュームを作成するリクエストを発行し、非同期に実行されるジョブの実行状態を確認して、作成したHDPボリュームのURLを実行結果として返します。 - ホストグループの作成 作成したHDPボリュームをホストに割り当てるため、ホストグループを作成します。サンプルコードでは、ホストグループが使用するポート番号とホストグループ名だけを指定し、ホストグループ番号の指定は省略しています。この場合、ホストグループ番号は自動で割り当てられます。
# step5 Add a host group # print("Add a host group") url = block_storage_api.host_groups() body = { "portId": PORT_ID, "hostGroupName": HOST_GRP_NAME } affected_resource_path = invoke_async_command("post", url, body)
作成したホストグループのURLと、割り当てられたホストグループ番号を取得します。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"]
- ホストモードの変更 作成したホストグループのホストモードを、ボリュームを割り当てるホストのプラットフォームに応じて変更します。 ホストグループの作成ステップで取得したホストグループのURLを使用し、ホストモードを変更するリクエストを発行します。
# step6 Modify the host group # print("Modify the host group") body = { "hostMode": HOST_MODE } invoke_async_command("put", url, body)
- ホストのWWNの登録 HDPボリュームを割り当てるホストを、作成したホストグループに登録します。登録するホストのHBAのWWNと、ホストグループのポート番号、割り当てられたホストグループ番号を指定します。 ホストグループ番号は、ホストグループの作成ステップで取得したものを使用します。
# step7 Add an HBA WWN # print("Add an HBA WWN") url = block_storage_api.host_wwns() body = { "hostWwn": HOST_WWN, "portId": PORT_ID, "hostGroupNumber": host_group_number } invoke_async_command("post", url, body)
- LUパスの設定 作成したボリュームとホストグループとを結び付けてLUパスを設定します。 サンプルコードでは、作成したHDPボリュームのLDEV番号と、ホストグループが使用するポート番号、ホストグループ番号を指定し、LUNの指定は省略しています。この場合、LUNは自動で割り当てられます。
# step8 Add a LUN path # print("Add a LUN path") url = block_storage_api.luns() body = { "ldevId": ldev_id, "portId": PORT_ID, "hostGroupNumber": host_group_number } invoke_async_command("post", url, body)
LUパスが設定され、ホストからHDPボリュームにアクセスできるようになります。 - HDPボリュームの情報取得 ここまでの操作が正しくリソースに反映されていることを確認するため、HDPボリュームの作成時に取得したLDEV番号を指定して、HDPボリュームの情報を取得します。サンプルコードでは、取得した情報から、LDEV番号、作成元のプールID、HDPボリュームの容量、割り当てられているポートを出力しています。
# step9 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()
- エラーメッセージの出力 サンプルコードでは、通信エラー、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")
-
セッションの破棄
一連の操作が完了したら、セッションを破棄します。セッションの作成時に取得したセッションIDを指定します。サンプルコードでは、APIの実行中にエラーが発生した場合にも必ずセッションが破棄されるよう、finally句で記述しています。セッションを破棄したら、処理を終了します。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()