# VMware Skyline Health Diagnostics(SHD)使用 - Part4: API 使用 ###### tags: `skyline` `vmware` :::success 用人力做試誤測試,叫做**浪費時間**。但是透過電腦做同樣的事,卻是**機器學習**? ::: 先說 VMware 官方並未提供 SHD 的 API 相關資訊。發現 SHD 的 API 真的是**無心之過**! :::danger **以下 API 相關操作並非按照由官方文件,僅作為類研究性質**。或許下個版本官方會出說明文件。 ::: [toc] # 從日誌紀錄開始 在研究 SHD 安裝時發現,SHD 將相關程式及服務都放在目錄 **`/opt/vmware-shd/vmware-shd/`** 中,以下是目錄結構。 ![080.png](https://i.imgur.com/W0fEMjf.png) 另外,相關 SHD 的系統日誌及分析報告紀錄都放在 **`/opt/vmware-shd/vmware-shd/log`**。 ![081.png](https://i.imgur.com/Tn2tdV2.png) 至於目錄裡有哪些日誌紀錄,請自行研究。有關 **`vmware-shd-task-{TASK_ID}.log`** 的紀錄都是診斷報告產生的,若想了解執行診斷報告時的過程,或是發生什麼問題無法產出報告,都可根據 **`TASK ID`** 找到對應的日誌紀錄查詢。 其他的日誌內容從檔名也可大致**猜**出用途。這邊就不浪費時間研究了。 回到不小心發現 API 的日誌檔是 **access.log**。這應該是 Nginx 網頁伺服器的存取紀錄。 ![082.png](https://i.imgur.com/OzbG7wr.png) 從以上的截圖可以看到,從我的筆電(10.7.30.98)向 SHD 設備(Nginx)進行請求 **`GET`** 呼叫,回覆狀態碼為 `200`,後面接著就是 API 的 URI? 另外在目錄 **`/opt/vmware-shd/vmware-shd/app/apiserver`** 發現以下檔案? ![083.png](https://i.imgur.com/F3XIgBn.png) :::success 當然使用瀏覽器提供的開發者檢測(Inspect)工具,也是不錯的選擇。 ![109.png](https://i.imgur.com/yh7etTG.png) 不過,這次就從日誌紀錄文件簡單開始吧!? ::: # 觀察日誌紀錄 大概方向就按照上述模式,使用以下兩種界面同時進行觀察。 - 使用瀏覽器連線 SHD 管理界面,正常登入並進行界面操作。 - 使用 SSH 連線 SHD 終端界面,並即時監控 **`/opt/vmware-shd/vmware-shd/log/access.log`**。 ```bash tail -f access.log ``` ![084.png](https://i.imgur.com/pI35Bdr.png) ## 登入界面 從登入紀錄查看,當成功登入 SHD 管理界面時,會從以下動作開始: **POST `/api/v1/token/get`** 看來要先取得**授權金鑰**。接著管理界面會進入 **Analyze** 頁面,並進行不同的 API 呼叫取得相關資訊,並正確顯示於 UI 界面。 其中,UI 界面下方會顯示分析報告的任務狀態,看來是透過以下 API 請求達成: **GET `/api/v1/task/status/<TASK_ID>`** 就以上述已知的資訊,先透過 API 的好伴侶 **Postman** 進行初步的簡易呼叫測試吧。 ## 取得授權金鑰 撰寫 Postman 的部份就不贅述了,套用先前使用 Postman 的經驗,完成請求設定。 :::info **資訊** - API: **POST | `https://{{SHD_IP}}/api/v1/token/get`** - Request Body: ```json { "username": "{{shd_username}}", "password": "{{shd_password}}" } ``` ::: ![085.png](https://i.imgur.com/rWDMFb3.png) - 使用 Test 腳本,將授權金鑰儲存成環境變數。 ```javascript= let res = pm.response.json(); let message = res.message; pm.environment.set("refresh_token", res.refresh_token); pm.environment.set("access_token", res.access_token); console.log(message); ``` ![086.png](https://i.imgur.com/kyLzFVo.png) - 執行請求,確認測試狀態。 ![087.png](https://i.imgur.com/FUiNL64.png) - 完成後同時將授權金鑰寫入環境變數,以便後續請求使用。 ![088.png](https://i.imgur.com/O2baqq6.png) # 任務摘要資訊 在 **Analyze** 界面中下方提供任務(Tasks)執行摘要資訊。從日誌紀錄顯示與 **`GET /api/v1/task/list`** 有關。 :::info **資訊** - API: **GET | `https://{{SHD_IP}}/api/v1/task/list/{{limit}}`** - 其中 {{limit}} 應是顯示限制,預設值為 **`10`**。可以是任意正整數,但必須存在。 ::: - 撰寫簡單請求。 ![089.png](https://i.imgur.com/FY0RwOu.png) - 取得任務資訊。 ![090.png](https://i.imgur.com/7m2iBxo.png) - 從 Postman Console 就能快速得到訊息。 ![091.png](https://i.imgur.com/4t7dFG0.png) # 更新資訊 既然基本操作模式已經確認,就在 SHD 的管理 UI 界面任意逛逛吧。 ## 工具升級 選擇 **Settings** > **Upgrade & History** > **Tool Update**。目前 SHD 為最新版本,所以並無更新紀錄。若要更新 SHD ,可點擊 **CHECK TOOL UPDATES**。 ![103.png](https://i.imgur.com/a4mVPrV.png) :::info **資訊** - **`GET /api/v1/manage/update/upgrades/5`** - **`GET /api/v1/manage/update/downloads/5`** - **`5`** 是預設的顯示限制。 - **`POST /api/v1/manage/update/test`** - **`GET /api/v1/manage/update/check`** ::: 為了完成以上一系列的 API 動作,可以執行 Postman 的 **Run Collection**。先將上述的請求設定完成,然後透過 **Run Collection**,便能進行整個流程的呼叫操作。 ### 請求設定 以下是摘要截圖紀錄。 - Collection: Tool Update ![092.png](https://i.imgur.com/kC88gwD.png) - Request: Refresh Token ![093.png](https://i.imgur.com/5OL88NH.png) > 全部請求的認證方式都採用 **Bearer Token**。 ![094.png](https://i.imgur.com/ZGJAhQE.png) - Request: Get Upgrade History Summary ![095.png](https://i.imgur.com/PsWBVzd.png) - Reuqest: Get Download History Summary ![096.png](https://i.imgur.com/dKDKdDa.png) - Request: Test Tool Update Respository ![097.png](https://i.imgur.com/gEW0Dcw.png) - Request: Check Tool Updates ![098.png](https://i.imgur.com/3hAMUSi.png) ### 執行集合請求 選擇建立的集合(Collection),點擊 **Run collection**。 ![099.png](https://i.imgur.com/6cnPvie.png) 選擇集合中所有請求設定,若有需要可調整執行順序。預設重複執行次數為 **`1`** 次。確認後點擊藍色按鈕執行。 ![100.png](https://i.imgur.com/sJ8Aige.png) 執行完成後,點擊橘色 **View Summary** 可查看執行摘要。每個請求都似乎執行成功! ![101.png](https://i.imgur.com/aYaSMF9.png) 再從 Console 視窗檢視透過 Test 腳本所取得的資訊是否符合。 ![102.png](https://i.imgur.com/DynTs1v.png) :+1: 看起來還可以喔。 :::info 關於更新日誌可檢視 **`/opt/vmware-shd/vmware-shd/log/vmware-shd-update.log`** 內容。 ::: :::success 透過簡單的 API 請求呼叫,就可以不用透過 UI 界面完成所需作業。 ::: ## VCG 升級 選擇 **Settings** > **Upgrade & History** > **VCG Update**。若要更新資料 ,可點擊 **UPDATE VCG DATABASE**。 ![104.png](https://i.imgur.com/RdbKhWC.png) :::info **資訊** - **`GET /api/v1/extentions/vcg/status`** - **`POST /api/v1/extentions/vcg/check`** - **`POST /api/v1/extentions/vcg/update`** ::: 完成以上系列動作,便能進行 VCG 資料庫更新,並取得目前更新後狀態資訊。 ![104.png](https://i.imgur.com/ZBEpwnz.png) ### 執行! 按照先前的模式及架構完成請求設定,執行集合請求。流程正常並無錯誤訊息。 ![105.png](https://i.imgur.com/I7jEk9i.png) 從 Console 視窗顯示 Test 腳本執行結果。看來使用 API 請求可以正確執行 VCG 資料庫更新作業。 ![106.png](https://i.imgur.com/5a071Bv.png) 從 **`/opt/vmware-shd/vmware-shd/log/access.log`** 紀錄中比對執行的 API 行為。 ![107.png](https://i.imgur.com/drv3TDr.png) 關於 VCG 更新的日誌紀錄,可查詢 **`/opt/vmware-shd/vmware-shd/log/vmware-shd-vcgupdate.log`**。 ![108.png](https://i.imgur.com/SFHgHKx.png) # 報告排程 報告排程似乎也可以!選擇上方功能 **Scheduler**。 ## 查詢排程資訊 :::info - **`GET /api/v1/manage/scheduler/schedule`** ::: ![110.png](https://i.imgur.com/S99qdQ1.png) ![111.png](https://i.imgur.com/R8hiA4y.png) ## 查詢排程功能選項 :::info - **`GET /api/v1/manage/scheduler/options`** ::: ![112.png](https://i.imgur.com/6DnMB8c.png) ![113.png](https://i.imgur.com/ZPlR8sD.png) ## 查詢排程功能狀態 :::info - **`GET /api/v1/extentions/enc/state`** ::: ![115.png](https://i.imgur.com/J6lmGya.png) ![116.png](https://i.imgur.com/IU4Umj6.png) ## 關閉排程功能 :::info - **`DELETE /api/v1/extentions/enc/state`** ::: ![114.png](https://i.imgur.com/P9NQVgB.png) 使用集合請求,執行關閉排程後檢查排程狀態。 ![117.png](https://i.imgur.com/75lFXf7.png) ![118.png](https://i.imgur.com/545y9di.png) ## 開啟排程功能 :::info - **`POST /api/v1/extentions/enc/state`** ::: <font color=red>**目前這個功能實際用法無法得知,僅能知道使用何種請求方式**</font>。還記得要(重新)啟用排程功能時,需要設定一組密碼?所以需要引入相關參數,不過因為沒有 **VMware 官方說明**且能力限制,並無法確實知道其帶入參數及引用方式,如果之後原廠願意釋出 API 說明再說吧!殘念! :cry: :no_good: :::danger 能透過 API 使用軟體,對於不太習慣 UI 方式或需要客製化時,還真的方便不少。目前原廠對於 SHD 有關 API 的資訊並不太多! ::: 有點喪氣,再看看其他幾個地方吧。 # 設備資訊 選擇上方功能選單 **Settings** > **About**。 ![119.png](https://i.imgur.com/VQ7dPtX.png) 這裡的 **Tool Details** 和 **Resource Utilization** 可透過以下 API 請求取得。 :::info - **`GET /api/v1`** - **`GET /api/v1/manage/osstats`** ::: - 取得 SHD 工具資訊 ![120.png](https://i.imgur.com/IXIyAmw.png) - 顯示 SHD 使用資源 ![121.png](https://i.imgur.com/YX1NIHh.png) - Postman Console 結果 ![122.png](https://i.imgur.com/o6zcmDL.png) # CEIP 設定 選擇上方功能選單 **Settings** > **Customer Experience Improvement Program**。 ![123.png](https://i.imgur.com/2VG9dKV.png) 可透過以下方式進行。 :::info - **`GET /api/v1/manage/ceip`** - **`POST /api/v1/manage/ceip?ceip=1`** - **`POST /api/v1/manage/ceip?ceip=0`** - **`GET /api/v1/manage/ceipsample`** ::: ## 取得 CEIP 狀態 ![124.png](https://i.imgur.com/LJJ6Ez7.png) ## 加入 CEIP ![125.png](https://i.imgur.com/Qy8jiRI.png) ## 退出 CEIP ![126.png](https://i.imgur.com/PcfvUCl.png) ## 顯示 CEIP 傳送的資料範本 ![127.png](https://i.imgur.com/UU2Ktzf.png) # 驗證主機進行分析 應該這是惡搞 API 使用的最後部份了,基本上還是以查看報告資訊為主。 在進行查看報告部份之前,讓我們先回到檢視任務狀態。選擇 **Analyze** > **Connect and Analyze**,界面中間下方顯示任務執行列表。 其中 **任務編號(ID)** 也是在檢視報告時會利用的 **報告編號(ID)**。而 **Bundle Name** 就是報告名稱。 ![128.png](https://i.imgur.com/iJPvmyZ.png) ![131.png](https://i.imgur.com/KtBUu4S.png) ## 驗證 vCenter 從以下任務狀態截圖,可得知一個排程報告任務(Schedule Task)進行時發生問題,原因是 **Task Failed - This Target is not part of Trusted List**。 ![132.png](https://i.imgur.com/UiZEqQ4.png) 所以在執行診斷報告或日常排程報告之前,SHD 都必須先將要進行分析的主機或是 vCenter/SDDC Manager 完成連線驗證,並加入至**信任清單(Trusted List)**。這部份的動作需要透過以下的 **Check Connection** 來達成。而且**沒有完成連結,是沒有辦法執行診斷分析的**。 ![133.png](https://i.imgur.com/GwAC5sJ.png) ![134.png](https://i.imgur.com/ez6ZrXR.png) ## 查詢信任清單 可以透過以下來查詢主機是否存在於信任清單中。 :::info - **`GET /api/v1/extentions/trust/entry?target={HOSTNAMW | HOST_IP}`** ::: vCenter 主機驗證並非在信任清單中。 ![135.png](https://i.imgur.com/La4NE6D.png) ![136.png](https://i.imgur.com/xfmOXMt.png) ## 信任清單新增紀錄 若要增加紀錄於信任清單,可使用以下請求完成。 :::info - **`POST /api/v1/extentions/trust/entry`** - Request Body: ```json { "target": "{{target}}", "issuer": "{{traget_issuer}}", "thumbprint": "{{traget_thumbprint}}" } ``` > 以上 **Body** 是測試過可以成功的。 ::: ![137.png](https://i.imgur.com/qjjBdg3.png) ![138.png](https://i.imgur.com/dv5booW.png) ## 確認 API 連結 可以透過以下方式進行。目前這個請求應該只是用 vCenter/ESXi 主機。SDDC Manager 的部份沒有環境無法測試,應該也差不多吧。 :::info - **`POST /api/v1/extentions/vcenter/check`** - Request Body: ```json { "target": "{{target}}", "username": "{{target_user}}", "password": "{{target_pass}}" } ``` > Body 內容需要提供對應驗證主機的登錄資訊。 ::: 指揮挺組合! ![139.png](https://i.imgur.com/I9o3jqg.png) 若要一次完成上列動作,可以將相關請求放在同一個資料夾執行。 ![140.png](https://i.imgur.com/r8bim49.png) ![141.png](https://i.imgur.com/1dygCe5.png) ![142.png](https://i.imgur.com/v8UCWch.png) :::warning **注意** 在沒有將主機加入至信任清單前,是無法成功結合的!也表示無法透過該主機進行分析診斷。 ![143.png](https://i.imgur.com/vvmhwqt.png) ::: ## 信任清單刪除紀錄 若要在信任清單中刪除紀錄,可使用以下請求完成。 :::info - **`DELETE /api/v1/extentions/trust/entry`** - Request Body: ```json { "target": "{{target}}" } ``` ::: ![144.png](https://i.imgur.com/WSYWRpX.png) # 報告檢視 檢視報告前必須要建立診斷報告,可以使用手動或定期排程的方式產生。還記得手動產生報告需要選擇要執行的插件(Plugins)和詳細目錄(Inventory)。 ![145.png](https://i.imgur.com/y2ssTrs.png) 另外還可指定標籤(Tag)和日誌天數。 ![146.png](https://i.imgur.com/cfncpiH.png) ## 產生報告 先說結果:<font color=red>**目前還是無法透過 API 產生分析報告!**</font> 沒有原廠的 API 應用指南,能力有限的我沒法測試出對應的請求參數。 雖然如此,還是找到幾個可以利用的 API URI。 :::info - **`POST /api/v1/extentions/vcenter/inventory`** - **`GET /api/v1/analyze/limitdays`** - **`POST /api/v1/extentions/vcenter/rundiag`** ::: - 取得欲診斷的詳細目錄。 ![150.png](https://i.imgur.com/02gwaZo.png) - 取得診斷的日誌限制天數設定。<font color=red>目前找不到該如何組態日誌限制天數</font>! ![151.png](https://i.imgur.com/VmPpX9O.png) - 執行診斷分析。<font color=red>目前可以啟動診斷報告流程,但可能缺少關鍵的參數,所以請求的診斷都會失敗!</font> ![152.png](https://i.imgur.com/zVjZrAV.png) 從請求本體(Request Body)需要提供診斷需要執行的插件(plugincats)。 另外撰寫 Test 腳本可以在 Consloe 畫面顯示: - 目前報告執行狀態。 - 任務編號。 - 檢視任務的 URI 位置。 ![153.png](https://i.imgur.com/0FOH8St.png) :::warning - 目前執行診斷報告這部份算是沒有幫助。若有進一步官方提供的 API 資訊的話,應該就不是問題了!對於自動化需求應該還是需要使用 API 請求的方式。 - 或許解答在這!這邊沒能力浪費時間了。 ![154.png](https://i.imgur.com/LyuJrmd.png) - 還是覺得有點可惜! ::: ## 檢視報告製作流程 但如果這部份暫時先從 UI 執行診斷報告,還是可以透過請求的方式來檢視出一些有用的資訊。 執行診斷報告時,會產生一組任務編號(Task ID),使用先前介紹的請求檢視指定報告編號的執行狀態。 :::info - **`GET /api/v1/task/status/{TASK_ID}`** ::: 從截圖可得知任務編號(`45e9d1a77f9ff872`)相關資訊: - 執行狀態(Status) - 狀態訊息(Message) - 執行總步驟(Steps)及數量(Totalsteps) - 目前執行步驟(Currentstep) ![147.png](https://i.imgur.com/jkXxLpf.png) 在 Console 畫面也可透過 Test 腳本直接顯示訊息。 ![148.png](https://i.imgur.com/HRbgbAo.png) 與 UI 界面顯示內容比較。在下圖 UI 界面右上方的 **REFRESH TASKS** 按鈕,在報告執行診斷階段時點擊可刷新執行狀態。使用 API 請求就是重複執行請求即可。 ![149.png](https://i.imgur.com/gNMTQM6.png) ## 檢視報告 ### 顯示報告摘要 當診斷報告執行完成,會在任務狀態中顯示報告執行摘要。可以根據任務編號(Task ID)選擇,點擊 **`SHOW SUMMARY`** 檢視報告執行摘要。 ![155.png](https://i.imgur.com/Vibwyq1.png) 或是點擊 **`SHOW REPORT`** 檢視報告。便會轉至 **Show Reports** 功能頁面。UI 界面中因為有指定任務編號(即報告編號)所以只會顯示該報告,如果想要看目前所有的報告清單,點擊左側 **`All reports`**,便會顯示目前**成功完成**的診斷報告列表。 ![156.png](https://i.imgur.com/2hjjkHV.png) 檢視報告可使用以下請求達成。 :::info - **`GET /api/v1/task/list/10`** - **`GET /api/v1/extentions/vcenter/diagsummary/{TASK_ID}`** - **`GET /api/v1/summary/latest/50`** - 報告清單預設顯示最近的 **`50`** 筆報告紀錄。不過,在 API 請求中將顯示筆記調整 **> 50**,似乎也沒有問題。 ::: 先檢視目前成功完成的診斷報告清單。 ![157.png](https://i.imgur.com/YmSHacj.png) ![158.png](https://i.imgur.com/DYbC24Z.png) 指定任務編號 **`25c090df291aa236`** 進行檢視。首先使用任務狀態請求,查詢報告相關資訊。其中可以發現**報告編號(Bundle ID)其實與任務編號(Task ID)是相同一致的**。 ![159.png](https://i.imgur.com/Ha6ThKQ.png) 檢視診斷報告的摘要資訊。 ![160.png](https://i.imgur.com/8cCsMls.png) 與 UI 界面比較。 ![161.png](https://i.imgur.com/kCwzgl2.png) ### 查看指定報告 選擇顯示報告後,點擊報告名稱(Bundle Name)左側 **`>>`** 圖示,將會在右側區域展開報告中所有的日誌紀錄資訊(Log)。以下表示該報告包含 **5** 組日誌紀錄。其中每組日誌紀錄會顯示發現的問題數量(No of Issues)。 ![162.png](https://i.imgur.com/ZHcpZ0p.png) 點選各個日誌紀錄名稱(Log Name)左側 **`>`** 圖示,可顯示該日誌發現出問題的描述說明。 ![163.png](https://i.imgur.com/wKE4GIh.png) 可用以下請求達成。 :::info - **`GET /api/v1/summary/bundle/{BUNDLE_ID}`** ::: 回覆訊息大致可了解: - Pluginsrun: 診斷插件執行數量。 - Pluginsmatched: 診斷插件執行符合數量。符合表示有問題! - Pluginsfailed: 診斷插件執行失敗數量。 - records: 表示該報告中包含的日誌分析紀錄數量。 > 其他部份可憑參數名稱臆測。 ![164.png](https://i.imgur.com/WpkfEel.png) 透過 Test 腳本撰寫,可將請求回覆訊息快速整理。另外,每個紀錄都會有對應的編號(ID)。 ![165.png](https://i.imgur.com/85oGHdm.png) 若要進一步了解該日誌訊息,可指定該紀錄編號進行請求。 :::info - **`GET /api/v1/summary/log/{LOG_ID}`** ::: ![166.png](https://i.imgur.com/9POjwp6.png) 透過 Test 腳本撰寫可以整理請求回覆訊息。可與先前 UI 界面顯示比較。 ![167.png](https://i.imgur.com/O7GHdk2.png) 其實產生的診斷分析報告便是依照 **Bundle Name(ID)** > **Log Name(ID)** > **Plugin Name(ID)** 逐層建構的,所以只要從最上層的 **Task ID(等同 Bundle ID)** 查詢對應的 Log ID/Plugin ID,便能透過對應的 API 請求獲取對應的資訊。 ![168.png](https://i.imgur.com/FL3oGaV.png) 透過以上找出來的 API 應用一下,就可以找出對應關係。 ![173.png](https://i.imgur.com/l3AVoRu.png) 從 **bundle id** 找出對應的 **log id**。這邊可以知道診斷報告目前有 **`7`** 份(**Bundle Count = 7**)。每個報告裡包含的 **[Log 數量]** 就是對應的分析目標的日誌項目。 ![172.png](https://i.imgur.com/AhBLVVP.png) 然後每個 **Log Report** 裡包含執行符合的 **Plugin 數量**,就是診斷出有問題的數量。 ![171.png](https://i.imgur.com/3EDeteg.png) 透過迴圈執行就可以將所有的編號取出! ![169.png](https://i.imgur.com/dN82AMw.png) ![170.png](https://i.imgur.com/46PjGX0.png) ## 下載報告和線上檢視報告 成功完成的報告都可以下載和線上檢視。 ![174.png](https://i.imgur.com/WFL8JfR.png) ![175.png](https://i.imgur.com/JXrBWWa.png) 也可以透過以下方式進行操作。 :::info - **`GET /api/v1/report/bundle/{BUNDLE_ID}?plugincats=DIAGNOSTICS,VMSA_SCAN,VSAN_SCAN,VCG_CHECK,VC_HEALTH`** - **`GET /api/v1/report/log/{LOG_ID}`** - **`GET /api/v1/report/plugin/{PLUGIN_ID}`** ::: 不過透過這種方式下載或顯示的 HTML 檔案,<font color=red>頁面並沒有顯示很正確</font>。不知道還要套用什麼格式,這部份就真的不知道了! ![176.png](https://i.imgur.com/9Dc0ynG.png) ![177.png](https://i.imgur.com/5GPs6Ck.png) ## 刪除報告 終於,剩下最後的清除部份。只要知道 **bundle id** 就可以使用以下操作刪除整份報告。若要進行刪除多筆報告(Delete Multiple Bundles),就是執行多次動作即可。 :::info - **`POST /api/v1/summary/delete/{BUNDLE_ID}`** ::: ![178.png](https://i.imgur.com/5HgTNSC.png) --- :::success - 雖然浪費了一些時間,但每次透過這樣的 API 測試操作,往往都會對於該功能進一步的了解,多少還是有所幫助的。 - 這套產品對於離線使用環境的管理者,還是能增加對於管理環境的問題取得一定程度的掌握程度,還是可以利用的。 - 如果可以,原廠還是把 API 文件補齊吧! ::: --- - [Part1: 安裝及界面](https://hackmd.io/@farmer87/shd_01) - [Part2: 功能操作](https://hackmd.io/@farmer87/shd_02) - [Part3: 報告排程](https://hackmd.io/@farmer87/shd_03) - [Part4: API 使用](https://hackmd.io/@farmer87/shd_04)