Python SDKを使ってOCIのダッシュボードで遊ぼう！

一発ネタ。だけと使い方によっては有用かも！？

# 事の始まり

[CloudWatchのダッシュボード&Lambda関数で遊ぶQiita記事](https://qiita.com/infra365/items/666193bbd90e60754235)を見て、OCIでもできないかなーと、OCIコンソールのダッシュボード機能を調べていて、残念ながらLambda相当であるOCI Functionsは呼べなかったけど、なんか遊べそうな感じだったので、試してみました。


# どこが遊べそうなの？

これっすよ、これ。
![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781934/33b02383-8bc6-4266-8c68-9645fda69796.png)

今日の運勢とか表示させるようにしたら、楽しいと思いません？


# 事前準備

OCI Functionsで実装も可能ですが、実装に時間がかかるので、今回はコンピュート上でスクリプト動かして更新します。


## まずは認可設定

インスタンス・プリンシパル、好きなんです。まずは動的グループ設定で対象のインスタンスのOCIDを指定。
```text:OCI-Dashboard-Dynamic-Group
All {instance.id = 'ocid1.instance.oc1.ap-toky-1.(省略)'}
```

次にIAMポリシー。ステートメント数を減らしたい人は、`use dashboards-family`で良いと思います。
```text:OCI-Dashboard-SDK-Policy
Allow dynamic-group OCI-Dashboard-Dynamic-Group to read dashboards-family in tenancy
Allow dynamic-group OCI-Dashboard-Dynamic-Group to use dashboards in tenancy
```


## ダッシュボードを書き換えるための仕様確認

見るのはこの辺ですね。
* APIリファレンスの[GetDashboard](https://docs.oracle.com/en-us/iaas/api/#/en/dashboard/20210731/Dashboard/GetDashboard)、および[UpdateDashboard](https://docs.oracle.com/en-us/iaas/api/#/en/dashboard/20210731/Dashboard/UpdateDashboard)
* OCI CLIコマンドリファレンスのdashboardの[get](https://docs.oracle.com/en-us/iaas/tools/oci-cli/latest/oci_cli_docs/cmdref/dashboard-service/dashboard/get.html)、および[update-dashboard-v1](https://docs.oracle.com/en-us/iaas/tools/oci-cli/latest/oci_cli_docs/cmdref/dashboard-service/dashboard/update-dashboard-v1.html)

OCI CLIを使ってデータの詳細を確認します。`export OCI_CLI_AUTH=instance_principal`しておけば、コマンド実行時に毎回指定が不要になります。

OCIコンソールからはどうも対象のダッシュボードのOCIDが確認できないようなのですが、URLを見ればわかります。だいたいこんな感じになってます。後ろのOCIDがダッシュボードのOCIDになります。

```
https://cloud.oracle.com/dashboard/group/ocid1.consoledashboardgroup.oc1..XXX/dashboards/ocid1.consoledashboard.oc1..XXX?region=ap-tokyo-1
```

では、ダッシュボードの詳細を確認しましょう。

```shell
[opc@test-compute1 ~]$ oci dashboard-service dashboard get --dashboard-id ocid1.consoledashboard.oc1..XXX
{
  "data": {
(省略)
    "widgets": [
      {
        "data": {
          "dataSource": "Markdown",
          "staticWidgetData": {
            "text": "* これはテストです\n* これは２行目です"
          }
        },
        "id": "Markdown_XXX",
(省略)
```

ほほう。widgetsリストの中からidで対象のwidgetを探して、text書き換えれば行けそうですな。


# それではスクリプト作成！

一応、元Ruby使いなのですが、サンプルはPythonの方が多いので、Pythonで。`XXX`の部分は上で確認したIDに変更してください。

せっかくなので、`if-match`を使って更新の重複を防ごうと思ったら、意外に参考になるドキュメントがなくてびっくり。

```python:change_markdown_widget.py
import oci

signer = oci.auth.signers.InstancePrincipalsSecurityTokenSigner()
dashboard_id="ocid1.consoledashboard.oc1..XXX"
markdown_id="Markdown_XXX"

# ダッシュボードの情報を取得
dashboard_service_client = oci.dashboard_service.DashboardClient(config={'region': signer.region}, signer=signer)

get_dashboard_response = dashboard_service_client.get_dashboard(
        dashboard_id= dashboard_id
        )

# 該当のwidgetsを検索
data = get_dashboard_response.data
widgets_num = 0

for num in range(len(data.widgets)):
    if data.widgets[num]['id'] == markdown_id :
       widgets_num = num

# textを変更
data.widgets[widgets_num]['data']['staticWidgetData']['text'] = "更新したいテキストをここに"

# ダッシュボードを更新
update_dashboard_response = dashboard_service_client.update_dashboard(
        dashboard_id = dashboard_id,
        if_match = get_dashboard_response.headers['ETag'],
        update_dashboard_details = oci.dashboard_service.models.UpdateV1DashboardDetails(
            widgets = data.widgets
            )
        )
```

# 注意点

スクリプトを使って更新しても、表示は変わりません。リフレッシュボタンを押してもダメ、編集ボタンを押しても前のテキストが残ったままだったりします。ページをリロードするか、ダッシュボードを切り替えましょう。


# おまけ

せっかくなので、こんなスクリプトも作って、文字列を生成AIに作ってもらいました。

```python:generate_cheerful_message.py
import locale
import datetime
from google import genai

locale.setlocale(locale.LC_TIME,'ja_JP.UTF-8')
today = datetime.datetime.now(datetime.timezone(datetime.timedelta(hours=9),'JST'))

client = genai.Client(api_key="XXX")

response = client.models.generate_content(
        model = "gemini-2.5-flash",
        contents = "今は{}の{}時です。あなたはチアリーダーです。働いている人に対して、やさしい応援の言葉を一文で作成してください。".format(today.strftime("%A"),today.hour)
)

print(response.text)
```

別スクリプトにしたのは、`import oci`と`from google import genai`がクラス重複？して動かなかったからですね。まあ、この方がいろいろカスタマイズしやすそう。

![image.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/3781934/f1494850-833e-4ce0-b74b-464cc65309c9.png)

うん。ばっちり！元のスクリプトをcronとかで定期的に動かしておけば、都度メッセージが切り替わります。

これでOCIコンソールにログインするのが楽しくなるかも☆