はじめに
Autonomous Databaseの検証作業後に、検証環境のComputeインスタンスを停止し忘れてしまうことがよくあります。
そこで、検証用コンパートメントにあるComputeインスタンスをAutonomous Databaseからまとめて停止できたら手間が省けると思い、それを実現するPL/SQLプロシージャを作成してみました。
※こちらのプロシージャはあくまでサンプルです。
実環境でお使いになる場合は、適切なエラーハンドリング等を実装した上で、充分な検証を実施してください。
1. PL/SQLプロシージャの作成
今回は、コンパートメント名、リージョン識別子、実行したいアクションをパラメータとして渡すと、指定したリージョンにある指定したコンパートメント内の全てのComputeインスタンスを起動/停止するプロシージャmanage_compute_instancesを作成します。
こちらのプロシージャでは、Autonomous Databaseのリソース・プリンシパルを使用していますので、こちらのマニュアルを参照して、事前にリソース・プリンシパルを有効化しておきます。
※DBMS_CLOUD.CREATE_CREDENTIALプロシージャで作成したユーザ認証ベースのクレデンシャルも、もちろん使用可能です。
manage_compute_instancesのソースがこちらです。
各処理の詳細は、ソース内のコメントをご確認ください。
CREATE OR REPLACE PROCEDURE manage_compute_instances( target_compartment_name IN VARCHAR2, target_region IN VARCHAR2, action IN VARCHAR2)
IS
target_lifecyclestate VARCHAR2(100);
region VARCHAR2(100);
root_ocid VARCHAR2(100);
compartment_ocid VARCHAR2(100);
request_uri VARCHAR2(200);
resp DBMS_CLOUD_TYPES.resp;
instance_list JSON_ARRAY_T := JSON_ARRAY_T();
instance_object JSON_OBJECT_T := JSON_OBJECT_T();
instance_ocid VARCHAR2(200);
BEGIN
IF (UPPER(action) = 'START')
THEN
target_lifecyclestate := 'Stopped';
ELSIF (UPPER(action) = 'STOP' OR UPPER(action) = 'SOFTSTOP')
THEN target_lifecyclestate := 'Running';
ELSE RETURN;
END IF;
-- ルートコンパートメントのOCIDとADBが稼働しているリージョンを取得
SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.TENANT_OCID')) INTO region, root_ocid FROM v$pdbs;
-- 対象コンパートメントの情報を取得するREST APIのURIを生成
request_uri := 'https://identity.'||region||'.oraclecloud.com/20160918/compartments/?compartmentId='||root_ocid||CHR(38)||'name='||target_compartment_name;
-- REST APIをコールして対象コンパートメントの情報を取得
resp := DBMS_CLOUD.send_request(
credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_GET
);
-- 取得したコンパートメントの情報からコンパートメントのOCIDを取得
compartment_ocid := JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.id');
-- デバッグ用出力
DBMS_OUTPUT.put_line ('Compartment Name : '||JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.name'));
DBMS_OUTPUT.put_line ('Compartment OCID : ' ||JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.id')||CHR(10));
-- 対象コンパートメント内にある対象となる状態にあるComputeインスタンスのリストを取得するREST APIのURIを生成
request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/?compartmentId='||compartment_ocid||CHR(38)||'lifecycleState='||target_lifecyclestate;
-- REST APIをコールして対象コンパートメント内に対象となる状態にあるComputeインスタンスのリストを取得
resp := DBMS_CLOUD.send_request(
credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_GET
);
-- 取得したComputeインスタンスのリストを配列に格納
instance_list := JSON_ARRAY_T(DBMS_CLOUD.get_response_text(resp));
-- デバッグ用出力
DBMS_OUTPUT.put_line ('Number of Instances : ' || instance_list.get_size||CHR(10));
-- 取得したComputeインスタンスのリスト内にある各Computeインスタンスに対する操作
FOR indx IN 0 .. instance_list.get_size - 1
LOOP
-- 取得したComputeインスタンスのリスト(配列)からComputeインスタンスの情報を取得
instance_object := TREAT (instance_list.get (indx) AS json_object_t);
-- 取得したComputeインスタンスの情報からComputeインスタンスのOCIDを取得
instance_ocid := instance_object.get_string('id');
-- デバッグ用出力
DBMS_OUTPUT.put_line ('Instance Display Name : '||instance_object.get_string('displayName'));
DBMS_OUTPUT.put_line ('Instance OCID : '||instance_ocid);
-- Computeインスタンスに対して指定したアクションを実行するREST APIのURIを生成
request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/'||instance_ocid||'?action='||UPPER(action);
-- REST APIをコールしてComputeインスタンスに対するアクションを実行
resp := DBMS_CLOUD.send_request(
credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_POST,
body => UTL_RAW.CAST_TO_RAW('{"actionType" : "softreset"}')
);
END LOOP;
END;
/
上記の内容をSQL*PlusやDatabase Actionから実行して、プロシージャを作成します。
SQL> CREATE OR REPLACE PROCEDURE manage_compute_instances( target_compartment_name IN VARCHAR2, target_region IN VARCHAR2, action IN VARCHAR2)
2 IS
3 target_lifecyclestate VARCHAR2(100);
4 region VARCHAR2(100);
5 root_ocid VARCHAR2(100);
6 compartment_ocid VARCHAR2(100);
7 request_uri VARCHAR2(200);
8 resp DBMS_CLOUD_TYPES.resp;
9 instance_list JSON_ARRAY_T := JSON_ARRAY_T();
10 instance_object JSON_OBJECT_T := JSON_OBJECT_T();
11 instance_ocid VARCHAR2(200);
12
13 BEGIN
14 IF (UPPER(action) = 'START')
15 THEN
16 target_lifecyclestate := 'Stopped';
17 ELSIF (UPPER(action) = 'STOP' OR UPPER(action) = 'SOFTSTOP')
18 THEN target_lifecyclestate := 'Running';
19 ELSE RETURN;
20 END IF;
21
22 SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.TENANT_OCID')) INTO region, root_ocid FROM v$pdbs;
23
24 request_uri := 'https://identity.'||region||'.oraclecloud.com/20160918/compartments/?compartmentId='||root_ocid||CHR(38)||'name='||target_compartment_name;
25 resp := DBMS_CLOUD.send_request(
26 credential_name => 'OCI$RESOURCE_PRiNCIPAL',
27 uri => request_uri,
28 method => DBMS_CLOUD.METHOD_GET
29 );
30
31 compartment_ocid := JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.id');
32 DBMS_OUTPUT.put_line ('Compartment Name : '||JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.name'));
33 DBMS_OUTPUT.put_line ('Compartment OCID : ' ||JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.id')||CHR(10));
34
35 request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/?compartmentId='||compartment_ocid||CHR(38)||'lifecycleState='||target_lifecyclestate;
36 resp := DBMS_CLOUD.send_request(
37 credential_name => 'OCI$RESOURCE_PRiNCIPAL',
38 uri => request_uri,
39 method => DBMS_CLOUD.METHOD_GET
40 );
41
42 instance_list := JSON_ARRAY_T(DBMS_CLOUD.get_response_text(resp));
43 DBMS_OUTPUT.put_line ('Number of Instances : ' || instance_list.get_size||CHR(10));
44
45 FOR indx IN 0 .. instance_list.get_size - 1
46 LOOP
47 instance_object := TREAT (instance_list.get (indx) AS json_object_t);
48 instance_ocid := instance_object.get_string('id');
49 DBMS_OUTPUT.put_line ('Instance Display Name : '||instance_object.get_string('displayName'));
50 DBMS_OUTPUT.put_line ('Instance OCID : '||instance_ocid);
51
52 request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/'||instance_ocid||'?action='||UPPER(action);
53 resp := DBMS_CLOUD.send_request(
54 credential_name => 'OCI$RESOURCE_PRiNCIPAL',
55 uri => request_uri,
56 method => DBMS_CLOUD.METHOD_POST,
57 body => UTL_RAW.CAST_TO_RAW('{"actionType" : "softreset"}')
58 );
59 END LOOP;
60 END;
61 /
プロシージャが作成されました。
SQL>
PL/SQLプロシージャmanage_compute_instancesが作成できました。
manage_compute_instancesプロシージャを実行するには、こちらのような形式で、コンパートメント名、リージョン識別子、実行したいアクションをパラメータとして渡す必要があります。
exec manage_compute_instances('コンパートメント名','リージョン識別子','アクション')
コンパートメント名:コンパートメントの名前(大文字小文字も一致する必要あり)
リージョン識別子:ap-tokyo-1、ap-osaka-1など
アクション:start、stop、softstopのいずれか(大文字、小文字どちらでもOK)
2. 作成したPL/SQLプロシージャの動作確認
ここでは東京リージョン(ap-tokyo-1)のTestコンパートメント内にあるComputeインスタンスをアクションの対象として動作を確認してみます。
確認のために、OCIコンソールで東京リージョンのTestコンパートメント内にあるComputeインスタンスのリストを表示しておきます。
Testコンパートメント内には、Compute1、Compute2、Compute3の3つのComputeインスタンスがあり、全て状態が「停止中」になっています。
では、コンパートメント名に「Test」、リージョンに「ap-tokyo-1」、アクションに「start」を指定してmanage_compute_instancesプロシージャを実行してみます。
exec manage_compute_instances('Test','ap-tokyo-1','start')
上記の内容をSQL*PlusやDatabase Actionから実行します。
SQL> set serveroutput on
SQL> exec manage_compute_instances('Test','ap-tokyo-1','start')
Compartment Name : Test
Compartment OCID :
ocid1.compartment.oc1..aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwlq
Number of Instances : 3
Instance Display Name : Compute1
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2bq
Instance Display Name : Compute2
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxjjq
Instance Display Name : Compute3
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdwq
PL/SQLプロシージャが正常に完了しました。
SQL>
プロシージャの実行が正常に完了したので、OCIコンソールを確認してみます。
Testコンパートメント内の全てComputeインスタンスの状態が「起動中」になりました。
少し待つと、全てのTestコンパートメント内の全てComputeインスタンスの状態が「実行中」になりました。
次に、コンパートメント名に「Test」、リージョンに「ap-tokyo-1」、アクションに「softstop」を指定してmanage_compute_instancesプロシージャを実行してみます。
SQL> exec manage_compute_instances('Test','ap-tokyo-1','softstop')
Compartment Name : Test
Compartment OCID :
ocid1.compartment.oc1..aaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxwlq
Number of Instances : 3
Instance Display Name : Compute1
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx2bq
Instance Display Name : Compute2
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxhxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxjjq
Instance Display Name : Compute3
Instance OCID :
ocid1.instance.oc1.ap-tokyo-1.anxhxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxdwq
PL/SQLプロシージャが正常に完了しました。
SQL>
プロシージャの実行が正常に完了したので、OCIコンソールを確認してみます。
Testコンパートメント内の全てComputeインスタンスの状態が「停止中」になりました。
少し待つと、全てのTestコンパートメント内の全てComputeインスタンスの状態が「実行中」になりました。
起動、停止ともに想定通りの動作となっていることが確認できました。
コメント、デバッグ用出力を削除したmanage_compute_instancesプロシージャの完成版は以下のようになります。
CREATE OR REPLACE PROCEDURE manage_compute_instances( target_compartment_name IN VARCHAR2, target_region IN VARCHAR2, action IN VARCHAR2)
IS
target_lifecyclestate VARCHAR2(100);
region VARCHAR2(100);
root_ocid VARCHAR2(100);
compartment_ocid VARCHAR2(100);
request_uri VARCHAR2(200);
resp DBMS_CLOUD_TYPES.resp;
instance_list JSON_ARRAY_T := JSON_ARRAY_T();
instance_object JSON_OBJECT_T := JSON_OBJECT_T();
instance_ocid VARCHAR2(200);
BEGIN
IF (UPPER(action) = 'START')
THEN
target_lifecyclestate := 'Stopped';
ELSIF (UPPER(action) = 'STOP' OR UPPER(action) = 'SOFTSTOP')
THEN target_lifecyclestate := 'Running';
ELSE RETURN;
END IF;
SELECT json_value(cloud_identity, '$.REGION'), LOWER(json_value(cloud_identity, '$.TENANT_OCID')) INTO region, root_ocid FROM v$pdbs;
request_uri := 'https://identity.'||region||'.oraclecloud.com/20160918/compartments/?compartmentId='||root_ocid||CHR(38)||'name='||target_compartment_name;
resp := DBMS_CLOUD.send_request( credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_GET
);
compartment_ocid := JSON_VALUE(DBMS_CLOUD.get_response_text(resp), '$.id');
request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/?compartmentId='||compartment_ocid||CHR(38)||'lifecycleState='||target_lifecyclestate;
resp := DBMS_CLOUD.send_request( credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_GET
);
instance_list := JSON_ARRAY_T(DBMS_CLOUD.get_response_text(resp)); -- デバッグ用出力
FOR indx IN 0 .. instance_list.get_size - 1
LOOP
instance_object := TREAT (instance_list.get (indx) AS json_object_t);
instance_ocid := instance_object.get_string('id');
request_uri := 'https://iaas.'||target_region||'.oraclecloud.com/20160918/instances/'||instance_ocid||'?action='||UPPER(action);
resp := DBMS_CLOUD.send_request( credential_name => 'OCI$RESOURCE_PRiNCIPAL',
uri => request_uri,
method => DBMS_CLOUD.METHOD_POST,
body => UTL_RAW.CAST_TO_RAW('{"actionType" : "softreset"}')
);
END LOOP;
END;
/
まとめ
PL/SQLプロシージャを使用して、指定したコンパートメント内の全てのComputeインスタンスをAutonomous Databaseから起動/停止することができました。
こちらのプロシージャはAlways FreeのAutonomous Databaseでも作成可能ですので、プロシージャをAlways FreeのAutonomous Database内に作成し、DBMS_SCHEDULERで一定間隔で自動実行することで、検証環境のComputeインスタンスを毎朝自動的に起動し、毎晩自動的に停止するといった運用も追加コストなしで可能になるかもしれませんね。
めでたし、めでたし。
参考資料
・DBMS_CLOUD.SEND_REQUEST
・Oracle Cloud Infrastructure Documentation / API Reference : ListCompartments
・Oracle Cloud Infrastructure Documentation / API Reference : ListInstances
・Oracle Cloud Infrastructure Documentation / API Reference : InstanceAction