oodle-ai/agent-skills

oodle-dashboards

Create, update, and manage Oodle dashboards and folders — safe deletion, panel preservation, and folder organization.

ソースを見る
リポジトリの原文

見出し、例、コード、表、リンク、参照画像を含む原文を表示しています。

Oodle Dashboards — CRUD and Organization

This skill teaches the agent to manage Oodle dashboards and folders without losing panel state and without orphaning dashboards in the root folder.

Prerequisites

bash
brew install oodle-ai/oodle/oodle
oodle configure
# or
export OODLE_API_KEY=<key>
export OODLE_INSTANCE=<instance>
export OODLE_DEPLOYMENT=<url>

Verify dashboards endpoint works:

bash
oodle dashboards list -o json | jq 'length'
oodle folders list -o json | jq 'length'

Command Execution Order

Before running any oodle command:

  1. Check whether the required resource ID or name is already in context.
  2. If not, run the discovery command (e.g., oodle dashboards list -o json).
  3. If the result is ambiguous, ask the user to confirm before proceeding.
  4. Run the target command with the resolved ID.
  5. Do not run speculative commands (e.g., do not delete without first get-ing the resource).

Quick Reference

TaskCommand
List dashboardsoodle dashboards list -o json
Get dashboardoodle dashboards get <id> -o json
Create dashboardoodle dashboards create -f dashboard.json
Update dashboardoodle dashboards update <id> -f dashboard.json
Delete dashboardoodle dashboards delete <id> --force
List foldersoodle folders list -o json
Get folderoodle folders get <id> -o json
Create folderoodle folders create -f folder.json
Delete folderoodle folders delete <id> --force

Common Operations

Listing dashboards

bash
# ✅ CORRECT
oodle dashboards list -o json

# ✅ CORRECT — filter by folder
oodle dashboards list -o json | jq '.[] | select(.folderId=="fld_platform")'

# ❌ WRONG — parsing table output to find an ID
oodle dashboards list | grep "API Overview" | awk '{print $1}'

Reading a dashboard before changing it

bash
# ✅ CORRECT — fetch the full definition first; preserves all panels and queries
oodle dashboards get dash_123 -o json > dashboard.json
$EDITOR dashboard.json
oodle dashboards update dash_123 -f dashboard.json

# ❌ WRONG — sending an incomplete payload removes panels
oodle dashboards update dash_123 -f <(echo '{"title":"new title"}')

Creating a dashboard

A complete dashboard JSON places the dashboard in a known folder:

json
{
  "title": "API Overview",
  "folderId": "fld_platform",
  "description": "Latency, error rate, and throughput for the API service.",
  "tags": ["service:api", "team:platform"],
  "panels": [
    {
      "title": "Request rate",
      "type": "timeseries",
      "query": "sum(rate(http_requests_total{service=\"api\"}[$__rate_interval]))"
    },
    {
      "title": "Error rate",
      "type": "timeseries",
      "query": "sum(rate(http_requests_total{service=\"api\",status=~\"5..\"}[$__rate_interval]))"
    },
    {
      "title": "P99 latency",
      "type": "timeseries",
      "query": "histogram_quantile(0.99, sum by (le) (rate(http_request_duration_seconds_bucket{service=\"api\"}[$__rate_interval])))"
    }
  ]
}
bash
# ✅ CORRECT
oodle dashboards create -f dashboard.json

# ❌ WRONG — no folderId, dashboard ends up in root and is hard to find
oodle dashboards create -f <(echo '{"title":"API Overview","panels":[...]}')

Folder management

bash
# ✅ CORRECT — create folder first, capture id, then create dashboards in it
FOLDER_ID=$(oodle folders create -f <(echo '{"title":"Platform"}') -o json | jq -r '.id')
jq --arg fid "$FOLDER_ID" '.folderId=$fid' dashboard.json > dashboard.with-folder.json
oodle dashboards create -f dashboard.with-folder.json

# ❌ WRONG — creating dashboards before folders, then trying to move them later
oodle dashboards create -f dashboard.json
oodle folders create -f folder.json

Safe deletion — two-phase

Dashboards are linked from runbooks, slack messages, and bookmarks. Delete in two phases.

bash
# ✅ CORRECT — phase 1: rename so users see it's about to be removed
oodle dashboards get dash_123 -o json > dash.json
jq '.title = "[MARKED FOR DELETION] " + .title' dash.json > dash.deletion.json
oodle dashboards update dash_123 -f dash.deletion.json
# wait at least 7 days, then:
oodle dashboards delete dash_123 --force

# ❌ WRONG — immediate hard delete, breaks every existing link
oodle dashboards delete dash_123 --force

Best Practices

Never use $__range as a panel lookback window

$__range expands to the whole dashboard time range, so the window stays that wide at every step and each step re-reads almost the same data as the step before it. A 6h dashboard with a [$__range] window reads about 180 times more samples than the same panel with [$__dd_interval], and the multiple grows as the user zooms out — which is when the dashboard times out. Oodle marks such a panel with a warning triangle.

Use a window that scales with the time range instead:

VariableWhat it isUse it for
$__rate_intervalGrafana's built-in rate window, equal to max(step + scrape interval, 4 x scrape interval), so a rate always has enough samples to be defined.rate() and increase() on a graph
$__dd_intervalThe rollup window Oodle picks for the time range, matching the Datadog default. Capped at 4h.The default rollup, and stat panel totals
$__large_intervalA coarser rollup than $__dd_interval for the same range. Capped at 12h.Bar charts and long lookbacks

The two Oodle rollups resolve like this:

Dashboard range$__dd_interval$__large_interval
1 hour20s1m
6 hours2m5m
24 hours5m20m
7 days1h4h
30 days4h12h

All three grow and shrink with the dashboard time range, so the samples read per step stay about the same however far the user zooms out.

bash
# ✅ CORRECT — the window shrinks and grows with the dashboard range
"query": "sum(rate(http_requests_total[$__rate_interval]))"

# ✅ CORRECT — coarser rollup for a bar chart
"query": "topk(10, sum by (route) (rate(http_request_duration_seconds_sum[$__large_interval])))"

# ❌ WRONG — one flat line at the range average, read 180 times over
"query": "sum(rate(http_requests_total[$__range]))"

# ❌ WRONG — `$__range_s` is there to undo an `increase`; use `rate` instead
"query": "sum(increase(http_requests_total[$__range])) / $__range_s"

Total a stat panel over windows, not over $__range

A stat panel that shows one number for the whole range does need every sample, but it does not need to read them in one step. Split the range into windows and add the windows back up.

bash
# ✅ CORRECT — window == min step, range query, reduced back to one number
"query": "sum(increase(http_requests_total[$__dd_interval]))"
"interval": "$__dd_interval"
"instant": false
"transformations": [{"id": "reduce", "options": {"reducers": ["sum"]}}]

# ❌ WRONG — instant query that reads the whole range on every refresh
"query": "sum(increase(http_requests_total[$__range]))"
"instant": true

Keep the min step equal to the window, so the windows tile the range: a smaller step double counts samples, a larger one skips them. Match the reducer to the function — sum for increase, sum_over_time and count_over_time, max for max_over_time, min for min_over_time, mean for avg_over_time. Do not split quantile_over_time, changes or resets this way; the reduced value is not the value over the range.

Always get before update to preserve panel configuration

Update is a full-document replace. Missing panels in the payload will be removed.

bash
# ✅ CORRECT
oodle dashboards get dash_123 -o json > dash.json
jq '.panels[0].title = "Request rate (per second)"' dash.json > dash.new.json
oodle dashboards update dash_123 -f dash.new.json

# ❌ WRONG — sends a single-field payload; all panels disappear
oodle dashboards update dash_123 -f <(echo '{"description":"updated"}')

Always set folderId when creating a dashboard

Dashboards in the root folder are hard for teams to discover.

bash
# ✅ CORRECT
"folderId": "fld_platform"

# ❌ WRONG — omitting folderId means root
"folderId": null

Use the two-phase rename → wait → delete pattern for shared dashboards

Hard-deleting a dashboard breaks every external link (runbooks, slack reactions, bookmarks).

bash
# ✅ CORRECT — phase 1: rename
oodle dashboards update dash_123 -f dash.deletion.json
# wait, confirm no traffic, then phase 2: delete
oodle dashboards delete dash_123 --force

# ❌ WRONG — same-day delete on a shared dashboard
oodle dashboards delete dash_123 --force

Tag dashboards with service and team labels

Tags make dashboards searchable and let other tools (e.g. service catalogs) link to them.

bash
# ✅ CORRECT
"tags": ["service:api", "team:platform", "env:prod"]

# ❌ WRONG
"tags": []

Failure Handling

ErrorCauseFix
401 UnauthorizedInvalid or missing API keyRun oodle configure or set OODLE_API_KEY
404 Not FoundDashboard or folder ID does not existVerify with oodle dashboards list -o json
connection refusedWrong OODLE_DEPLOYMENT URLCheck OODLE_DEPLOYMENT env var
folder not foundfolderId references a deleted folderList folders with oodle folders list -o json; choose an existing id or create one
Panels disappeared after updateupdate was called with a partial payloadRe-create from the last get snapshot; in the future always get → edit → update
Cannot delete folderFolder still contains dashboardsMove or delete the dashboards first; `oodle dashboards list -o jsonjq '.[]select(.folderId=="fld_x")'`
429 Too Many RequestsBulk dashboard syncAdd --retries 3, throttle to <10 creates per second

References

同じリポジトリから

関連する Skills

すべての Skills