Functions（サーバーレス）で時刻をトリガーとしてVSIを自動起動／停止する（VPC Gen1 VSI編）

クラウドの課金を節約する手段として、業務時間外に仮想サーバーを停止しておきたいというニーズはよくあると思います。時刻をトリガーとしてサーバーレスのプログラムを起動し、そのプログラムから仮想サーバーを起動または停止するAPIを発行してやればよさそうです。
IBM CloudにはFunctionsというサーバーレス環境があるので、これを使ってVPC（Gen1）のVSI（IBM Cloudの仮想サーバー）を起動／停止してみます。
作業の流れは次のとおりです。言語はPythonを使用します。

1. VSIを起動／停止するプログラムの作成
2. サーバーレス環境にプログラム（アクション）をデプロイ
3. サービスIDの作成とAPIキーの取得
4. アクションの動作確認
5. 時刻トリガーの登録と、アクションとの関連付け
6. トリガーの動作の確認

---

## 1. VSIを起動／停止するプログラムの作成
Functionsのアクションはmain関数で実装することになっています。以下に今回作成したプログラムのmainの部分を掲載します。全体は[こちら](https://github.com/takeyan/functions_vsi_gen1)に置いておきます。
APIキーや起動・停止したいインスタンス名、起動・停止の指示等はパラメータからdictで受け取ることにしてあります。


```python
def main(dict):
    api = dict['api']
    reg = dict['region']
    ver = dict['version']
    vpc = dict['vpc']
    act = dict['action']
    vsi = dict['instance']

    con = http.client.HTTPSConnection(f"{reg}.iaas.cloud.ibm.com")
    hdr = {
        'Content-Type': 'application/json',
        'Cache-Control': 'no-cache',
        'Accept': 'application/json',
        'Authorization': get_token(api),
        'cache-control': 'no-cache'
    }

    res = json.loads(fetch_instances(con, ver, hdr, vpc))
    instances = res['instances']

    res = "NOT FOUND"
    for instance in instances:
        if instance['name'] == vsi:
            id = instance['id']
            res = create_action(con, ver, hdr, id, act)
    return res
```

REST APIのリファレンスは[こちら](https://cloud.ibm.com/apidocs/vpc-on-classic)にあります。
インスタンス一覧の取得は[List all instances](https://cloud.ibm.com/apidocs/vpc-on-classic#list-all-instances)、インスタンスの起動・停止は[Create an instance action](https://cloud.ibm.com/apidocs/vpc-on-classic#create-an-instance-action)が参考にます。
現在VPCにはGen1とGen2があり、微妙に異なるので要注意です。今回はGen1の方を対象にしています。


## 2. サーバーレス環境にプログラム（アクション）をデプロイ
ダッシュボードのGUIで登録する方法、ibmcloud CLIを使う方法等がありますが、ここではダッシュボードのGUIを使用することにします。
Functionsのダッシュボードから「作成の開始」→「アクションの作成」でアクション名を指定し、ランタイムとしてPython 3を選択します。
![Functionsアクションの作成.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/975af4da-ce90-6a38-7bd0-8fab610bcd83.png)

コードの画面にプログラムを貼り付けて保存します。
![Functionsコード.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/cd8c2a1f-2024-15bb-4428-9ca1daf6296a.png)

パラメーターの画面で、プログラムに渡すパラメータを定義します。
![Functionsパラメータ.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/edca3953-74d7-588e-1370-01cf9d0883ad.png)

ここでは"us-south"リージョンの"vpc-dal-gen1-takeyan"というVPCに作成した"dal01take01"という名前のVSIを"start"するように設定しています。versionはVPC用REST APIのバージョンで、[APIリファレンス](https://cloud.ibm.com/apidocs/vpc-on-classic)のコマンドサンプルを参照して指定しました。
apiではこのVSIを起動・停止する権限を持っているユーザーIDかサービスIDのAPIキーを指定します。次章でサービスIDを作成してAPIキーを取得する方法を整理しておきます。

## 3. サービスIDの作成とAPIキーの取得
サービスIDはIAMの機能のひとつで、クラウドサービスへのアクセス・ポリシーとAPIキーを紐付けます。このAPIキーでIAMの認証を受けてトークンを取得し、このトークンを伴ってサービスにアクセスすると、アクセス・ポリシーに従ってアクセスが許可または拒否されます。
サービスIDはダッシュボード右上のプルダウンメニューから「管理」→「アクセス（IAM）」を選択してIAMのダッシュボードを開き、画面左側のメニューから「サービスID」を選択して「サービスID」の画面に移動し、そこで「作成」を選択して作成します。
![IAM.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/6c876cb9-d8e2-59c3-dd3b-bf0bd42b0c8f.png)
![サービスID.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/d0cf0806-c01f-9b8d-68b1-79c1e2d654af.png)

今回は、アクセス・ポリシーとして「リソース・グループ」＝すべてのリソース・グループ、「サービス」＝VPC Infrastructure、「リソース・タイプ」＝Virtual Server for VPC、「プラットフォーム・アクセス」＝エディターという条件を選択してサービスIDを作成しました。
![アクセスポリシー１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/3817509a-8c68-1e7f-9059-9aabcc2d34a0.png)
![アクセスポリシー２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/b4cfbc3c-8f10-68dc-4604-bda3463d80fc.png)
![アクセスポリシー３.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/0cd6ebf8-2e6b-2de5-dfd1-27f3332e79fb.png)

サービスIDを作成したら、APIキーを作成します。このAPIキーを前章のapiの値として使用します。
![APIキー１.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/06012415-71ad-fef8-f87d-0dc2f56939a3.png)
![APIキー２.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/59e494d8-22fc-ec76-4266-d584a0f296d0.png)


## 4. アクションの動作確認
アクションの「コード」の画面で「起動」をクリックします。
![Functions起動.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/98080ae3-e332-e85e-fd4e-03c929e72004.png)

![Functions実行結果.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/bdbdb32b-585c-a215-5fbe-48fbcf25e79b.png)

アクションのコードに登録したPythonプログラムは、mainの中でVSIの起動または停止を要求するREST APIをコールして、その応答を返り値にセットしていました。その返り値が「結果：」のところに表示されています。
[Create an instance action](https://cloud.ibm.com/apidocs/vpc-on-classic#create-an-instance-action)にはREST APIコールの正常応答のサンプルが掲載されています。このサンプルどおりの返り値が返ってきており、アクションが正常に実行されたことがわかります。
![Functions応答.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/95d5c7aa-5962-d0fb-686f-7bfbdaf5e0c5.png)


## 5. 時刻トリガーの登録と、アクションとの関連付け
毎週月～金の9時にスタートするトリガーを作成します。
トリガーは、Functionsのダッシュボードから「トリガー」→「作成」→「トリガーの作成」→「Periodic」とたどって「トリガーの接続」の画面を開き、ここでトリガー名を指定し要件にあったタイミングを選択します。タイミングはUTC曜日およびUTC時間で指定しますが、トリガーが発生する次回の時刻は日本時間で確認できます。
![Functionsトリガー.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/aefb1186-ee58-8a4d-1aa7-456d8850951b.png)

作成したトリガーをアクションに関連づけます。
![Functionsトリガー関連付け.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/b5eef8f3-784e-57e9-731e-8896f78b360f.png)

作成済のアクションを「既存の選択」のプルダウンリストから選択して、トリガーとアクションが関連付けられます。
![Functionsアクションとトリガー関連付け.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/39348fc4-2f0d-52a3-c56d-aec720ee313f.png)

## 6. トリガーの動作の確認
トリガーの実行結果は「モニター」の画面で確認することができます。
アクティビティー・サマリーのトリガー名（ここでは0800weekdayと1800weekday）と、その右にある棒グラフで成功と失敗の回数がわかります。またアクティビティー・ログで実行履歴とログの詳細が確認できます。
![Functionsモニター.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/32083/e44fa7d5-6436-1571-674e-d1d414326773.png)


ここまでVPC Gen1のVSIを定時に自動で起動・停止するために必要なFunctions関連のプログラミングと設定をひととおり体験してみました。

以上です。