使用 Grafana 做 Ping 和 測速

文章目錄

背景

開一個網站, 然後要記錄從各地方連過來的 Ping 狀況還有 Speed Test

測速工具

  1. 使用 e7d/speedtest, 他提供 IP, Ping, Jitter, Upload/Download 的相關資料, 並且會把資料存到 DB. (可惜這專案在 2023/7/4 archived 了)
  2. 使用 Grafana 讀取該 DB, 並做成 dashboard.

使用方式

  1. e7d/speedtest 有提供 docker image, 在 https://hub.docker.com/r/e7db/speedtest, 可直接使用, docker-compose.yml 如下 (他需要搭配一個 PostgreSQL):

     1speedtest:
     2    image: e7db/speedtest
     3    container_name: speedtest
     4    networks:
     5    - speedtest
     6    expose:
     7    - 80
     8    ports:
     9    - 80:80
    10    volumes:
    11    - /opt/speedtest/results:/app/results
    12    - /etc/hosts:/etc/hosts
    13postgres:
    14    image: postgres:14
    15    container_name: postgres
    16    networks:
    17    - speedtest
    18    expose:
    19    - 9105
    20    ports:
    21    - 9105:5432                 # Postgres port
    22        #- 443:5432                 # Postgres port
    23    volumes:
    24    - /opt/speedtest/postgresql/:/var/lib/postgresql
    25    - /opt/speedtest/postgresql/data/:/var/lib/postgresql/data
    26    - /etc/hosts:/etc/hosts
    27    environment:
    28    POSTGRES_USER: postgres     # define credentials
    29    POSTGRES_PASSWORD: ******** # define credentials
    30    POSTGRES_DB: postgres       # define database
    31    TZ: Asia/Singapore       # This is for PostgreSQL's time zone
    32    PGTZ: Asia/Singapore     # This is for PostgreSQL's time zone
    
  2. 測速, 直接打開上面的網站, 做幾次測速. 然後可以用 browser 開一些跨國 VPN 來模擬從其他國家來的測速.

  3. 在幾次測速之後, 資料應該已經存到 DB 了. 假設 table name 的名字是 speedtest2

  4. 回到 Grafana, 新增一個 Connection 連到該 DB.

  5. 新增一個 panel, data source 使用上面新增的 connection, SQL 指令輸入如下

    1SELECT time - interval '8 hour' as time, ping, ip, org 
    2FROM speedtest2
    3WHERE ip in ($ip)
    4AND org in ($org)
    5ORDER BY time
    
  6. 執行 Run query, 就可以看到類似以下 SPC 的圖了

  7. 依序設定其他 Panel, 這是 Jitter

    1SELECT time - interval '8 hour' as time, jitter, ip, org 
    2FROM speedtest2
    3WHERE ip in ($ip)
    4AND org in ($org)
    5ORDER BY time
    
  8. 這是 Upload speed

    1SELECT time - interval '8 hour' as time, upload, ip, org
    2FROM speedtest2
    3WHERE ip in ($ip)
    4AND org in ($org)
    5ORDER BY time
    
  9. 這是 Download speed

    1SELECT time - interval '8 hour' as time, download, ip, org
    2FROM speedtest2
    3WHERE ip in ($ip)
    4AND org in ($org)
    5ORDER BY time
    
  10. 這是 raw data, 注意, 前面都是 Time series, 這個是 Text

    1SELECT s.time - interval '8 hour' as time, s.ping, s.jitter, s.upload, s.download, s.ip, i.country_name, i.city_name, s.org
    2FROM speedtest2 s, ip2proxy i
    3WHERE s.ip >= i.ip_from AND s.ip <= i.ip_to
    4AND ip in ($ip)
    5AND org in ($org)
    6AND s.time >= to_timestamp($__from/1000)
    7AND s.time <= to_timestamp($__to/1000)
    8ORDER BY time DESC
    
  11. 最後這個比較特別, 他是 Geomap, 他是 Grafana 內建的 plugin. 給他經緯度, 他就可以幫你標記在地圖上, 很方便.

    1SELECT s.ping, time - interval '8 hour' as time, s.download, s.upload, i.country_name, i.city_name, i.latitude, i.longitude
    2FROM speedtest2 s, ip2proxy i
    3WHERE s.ip >= i.ip_from AND s.ip <= i.ip_to
    4AND ip in ($ip)
    5AND org in ($org)
    6AND s.time >= to_timestamp($__from/1000)
    7AND s.time <= to_timestamp($__to/1000)
    8ORDER BY time DESC
    

結果

  1. 總和以上 6 個 panel, 呈現出來的 dashboard 如下:

Grafana dashboard 的 json

  1. 其實用上面的 SQL 指令就可以了建出各 panel 了, 我並沒有做甚麼客製化調整, 但還是附上原始 json.

       1{
       2  "__inputs": [
       3    {
       4      "name": "DS_SPEEDTEST_(POSTGRESQL)",
       5      "label": "Speedtest (PostgreSQL)",
       6      "description": "",
       7      "type": "datasource",
       8      "pluginId": "postgres",
       9      "pluginName": "PostgreSQL"
      10    },
      11    {
      12      "name": "DS_PROMETHEUS",
      13      "label": "Prometheus",
      14      "description": "",
      15      "type": "datasource",
      16      "pluginId": "prometheus",
      17      "pluginName": "Prometheus"
      18    }
      19  ],
      20  "__elements": {},
      21  "__requires": [
      22    {
      23      "type": "panel",
      24      "id": "geomap",
      25      "name": "Geomap",
      26      "version": ""
      27    },
      28    {
      29      "type": "grafana",
      30      "id": "grafana",
      31      "name": "Grafana",
      32      "version": "10.0.2"
      33    },
      34    {
      35      "type": "datasource",
      36      "id": "postgres",
      37      "name": "PostgreSQL",
      38      "version": "1.0.0"
      39    },
      40    {
      41      "type": "datasource",
      42      "id": "prometheus",
      43      "name": "Prometheus",
      44      "version": "1.0.0"
      45    },
      46    {
      47      "type": "panel",
      48      "id": "table",
      49      "name": "Table",
      50      "version": ""
      51    },
      52    {
      53      "type": "panel",
      54      "id": "text",
      55      "name": "Text",
      56      "version": ""
      57    },
      58    {
      59      "type": "panel",
      60      "id": "timeseries",
      61      "name": "Time series",
      62      "version": ""
      63    }
      64  ],
      65  "annotations": {
      66    "list": [
      67      {
      68        "builtIn": 1,
      69        "datasource": {
      70          "type": "grafana",
      71          "uid": "-- Grafana --"
      72        },
      73        "enable": true,
      74        "hide": true,
      75        "iconColor": "rgba(0, 211, 255, 1)",
      76        "name": "Annotations & Alerts",
      77        "target": {
      78          "limit": 100,
      79          "matchAny": false,
      80          "tags": [],
      81          "type": "dashboard"
      82        },
      83        "type": "dashboard"
      84      }
      85    ]
      86  },
      87  "description": "   ",
      88  "editable": true,
      89  "fiscalYearStartMonth": 0,
      90  "graphTooltip": 0,
      91  "id": null,
      92  "links": [],
      93  "liveNow": false,
      94  "panels": [
      95    {
      96      "datasource": {
      97        "type": "postgres",
      98        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
      99      },
     100      "fieldConfig": {
     101        "defaults": {
     102          "color": {
     103            "mode": "thresholds"
     104          },
     105          "custom": {
     106            "axisCenteredZero": false,
     107            "axisColorMode": "text",
     108            "axisLabel": "",
     109            "axisPlacement": "auto",
     110            "barAlignment": 0,
     111            "drawStyle": "points",
     112            "fillOpacity": 0,
     113            "gradientMode": "none",
     114            "hideFrom": {
     115              "legend": false,
     116              "tooltip": false,
     117              "viz": false
     118            },
     119            "lineInterpolation": "linear",
     120            "lineWidth": 1,
     121            "pointSize": 10,
     122            "scaleDistribution": {
     123              "type": "linear"
     124            },
     125            "showPoints": "auto",
     126            "spanNulls": false,
     127            "stacking": {
     128              "group": "A",
     129              "mode": "none"
     130            },
     131            "thresholdsStyle": {
     132              "mode": "off"
     133            }
     134          },
     135          "mappings": [],
     136          "thresholds": {
     137            "mode": "absolute",
     138            "steps": [
     139              {
     140                "color": "green",
     141                "value": null
     142              },
     143              {
     144                "color": "#EAB839",
     145                "value": 30
     146              },
     147              {
     148                "color": "red",
     149                "value": 80
     150              }
     151            ]
     152          },
     153          "unit": "ms"
     154        },
     155        "overrides": []
     156      },
     157      "gridPos": {
     158        "h": 8,
     159        "w": 11,
     160        "x": 0,
     161        "y": 0
     162      },
     163      "id": 11,
     164      "options": {
     165        "legend": {
     166          "calcs": [],
     167          "displayMode": "list",
     168          "placement": "bottom",
     169          "showLegend": false
     170        },
     171        "tooltip": {
     172          "mode": "single",
     173          "sort": "none"
     174        }
     175      },
     176      "targets": [
     177        {
     178          "datasource": {
     179            "type": "postgres",
     180            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     181          },
     182          "editorMode": "code",
     183          "format": "time_series",
     184          "group": [],
     185          "key": "Q-571eb638-8680-4aea-ba7f-b876cd4588a7-0",
     186          "metricColumn": "none",
     187          "rawQuery": true,
     188          "rawSql": "SELECT time - interval '8 hour' as time, ping, ip, org \n  FROM speedtest2\n WHERE ip in ($ip)\n   AND org in ($org)\n ORDER BY time",
     189          "refId": "A",
     190          "select": [
     191            [
     192              {
     193                "params": [
     194                  "value"
     195                ],
     196                "type": "column"
     197              }
     198            ]
     199          ],
     200          "sql": {
     201            "columns": [
     202              {
     203                "parameters": [],
     204                "type": "function"
     205              }
     206            ],
     207            "groupBy": [
     208              {
     209                "property": {
     210                  "type": "string"
     211                },
     212                "type": "groupBy"
     213              }
     214            ],
     215            "limit": 50
     216          },
     217          "table": "speedtest_users",
     218          "timeColumn": "time",
     219          "where": []
     220        }
     221      ],
     222      "title": "Ping",
     223      "type": "timeseries"
     224    },
     225    {
     226      "datasource": {
     227        "type": "prometheus",
     228        "uid": "${DS_PROMETHEUS}"
     229      },
     230      "description": "",
     231      "gridPos": {
     232        "h": 2,
     233        "w": 13,
     234        "x": 11,
     235        "y": 0
     236      },
     237      "id": 10,
     238      "options": {
     239        "code": {
     240          "language": "plaintext",
     241          "showLineNumbers": false,
     242          "showMiniMap": false
     243        },
     244        "content": "Speed Test Site: http://140.110.11.5",
     245        "mode": "markdown"
     246      },
     247      "pluginVersion": "10.0.2",
     248      "title": "  ",
     249      "type": "text"
     250    },
     251    {
     252      "datasource": {
     253        "type": "postgres",
     254        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     255      },
     256      "fieldConfig": {
     257        "defaults": {
     258          "custom": {
     259            "align": "auto",
     260            "cellOptions": {
     261              "type": "auto"
     262            },
     263            "inspect": false
     264          },
     265          "mappings": [],
     266          "thresholds": {
     267            "mode": "absolute",
     268            "steps": [
     269              {
     270                "color": "green",
     271                "value": null
     272              }
     273            ]
     274          }
     275        },
     276        "overrides": [
     277          {
     278            "matcher": {
     279              "id": "byName",
     280              "options": "time"
     281            },
     282            "properties": [
     283              {
     284                "id": "custom.width",
     285                "value": 181
     286              },
     287              {
     288                "id": "unit",
     289                "value": "dateTimeAsIso"
     290              },
     291              {
     292                "id": "displayName",
     293                "value": "Time"
     294              }
     295            ]
     296          },
     297          {
     298            "matcher": {
     299              "id": "byName",
     300              "options": "ping"
     301            },
     302            "properties": [
     303              {
     304                "id": "custom.width",
     305                "value": 75
     306              },
     307              {
     308                "id": "displayName",
     309                "value": "Ping"
     310              },
     311              {
     312                "id": "unit",
     313                "value": "ms"
     314              }
     315            ]
     316          },
     317          {
     318            "matcher": {
     319              "id": "byName",
     320              "options": "jitter"
     321            },
     322            "properties": [
     323              {
     324                "id": "custom.width",
     325                "value": 84
     326              },
     327              {
     328                "id": "displayName",
     329                "value": "Jitter"
     330              },
     331              {
     332                "id": "unit",
     333                "value": "ms"
     334              }
     335            ]
     336          },
     337          {
     338            "matcher": {
     339              "id": "byName",
     340              "options": "upload"
     341            },
     342            "properties": [
     343              {
     344                "id": "custom.width",
     345                "value": 100
     346              },
     347              {
     348                "id": "unit",
     349                "value": "binBps"
     350              },
     351              {
     352                "id": "displayName",
     353                "value": "Upload"
     354              }
     355            ]
     356          },
     357          {
     358            "matcher": {
     359              "id": "byName",
     360              "options": "download"
     361            },
     362            "properties": [
     363              {
     364                "id": "custom.width",
     365                "value": 107
     366              },
     367              {
     368                "id": "unit",
     369                "value": "binBps"
     370              },
     371              {
     372                "id": "displayName",
     373                "value": "Download"
     374              }
     375            ]
     376          },
     377          {
     378            "matcher": {
     379              "id": "byName",
     380              "options": "ip"
     381            },
     382            "properties": [
     383              {
     384                "id": "custom.width",
     385                "value": 128
     386              },
     387              {
     388                "id": "displayName",
     389                "value": "IP Address"
     390              }
     391            ]
     392          },
     393          {
     394            "matcher": {
     395              "id": "byName",
     396              "options": "org"
     397            },
     398            "properties": [
     399              {
     400                "id": "custom.width",
     401                "value": 59
     402              },
     403              {
     404                "id": "displayName",
     405                "value": "Organization"
     406              }
     407            ]
     408          },
     409          {
     410            "matcher": {
     411              "id": "byName",
     412              "options": "Organization"
     413            },
     414            "properties": [
     415              {
     416                "id": "custom.width",
     417                "value": 410
     418              }
     419            ]
     420          },
     421          {
     422            "matcher": {
     423              "id": "byName",
     424              "options": "Time"
     425            },
     426            "properties": [
     427              {
     428                "id": "custom.width",
     429                "value": 156
     430              }
     431            ]
     432          },
     433          {
     434            "matcher": {
     435              "id": "byName",
     436              "options": "Ping (ms)"
     437            },
     438            "properties": [
     439              {
     440                "id": "custom.width",
     441                "value": 80
     442              }
     443            ]
     444          },
     445          {
     446            "matcher": {
     447              "id": "byName",
     448              "options": "Jitter (ms)"
     449            },
     450            "properties": [
     451              {
     452                "id": "custom.width",
     453                "value": 85
     454              }
     455            ]
     456          },
     457          {
     458            "matcher": {
     459              "id": "byName",
     460              "options": "country_name"
     461            },
     462            "properties": [
     463              {
     464                "id": "displayName",
     465                "value": "Country"
     466              }
     467            ]
     468          },
     469          {
     470            "matcher": {
     471              "id": "byName",
     472              "options": "city_name"
     473            },
     474            "properties": [
     475              {
     476                "id": "displayName",
     477                "value": "City"
     478              }
     479            ]
     480          },
     481          {
     482            "matcher": {
     483              "id": "byName",
     484              "options": "Country"
     485            },
     486            "properties": [
     487              {
     488                "id": "custom.width",
     489                "value": 214
     490              }
     491            ]
     492          },
     493          {
     494            "matcher": {
     495              "id": "byName",
     496              "options": "City"
     497            },
     498            "properties": [
     499              {
     500                "id": "custom.width",
     501                "value": 81
     502              }
     503            ]
     504          },
     505          {
     506            "matcher": {
     507              "id": "byName",
     508              "options": "Download"
     509            },
     510            "properties": [
     511              {
     512                "id": "custom.width"
     513              }
     514            ]
     515          }
     516        ]
     517      },
     518      "gridPos": {
     519        "h": 8,
     520        "w": 13,
     521        "x": 11,
     522        "y": 2
     523      },
     524      "id": 8,
     525      "options": {
     526        "cellHeight": "sm",
     527        "footer": {
     528          "countRows": false,
     529          "fields": "",
     530          "reducer": [
     531            "sum"
     532          ],
     533          "show": false
     534        },
     535        "showHeader": true,
     536        "sortBy": [
     537          {
     538            "desc": true,
     539            "displayName": "Time"
     540          }
     541        ]
     542      },
     543      "pluginVersion": "10.0.2",
     544      "targets": [
     545        {
     546          "datasource": {
     547            "type": "postgres",
     548            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     549          },
     550          "format": "table",
     551          "group": [],
     552          "key": "Q-3bd0fb03-3fed-4bba-b2ce-da323646e2d1-0",
     553          "metricColumn": "none",
     554          "rawQuery": true,
     555          "rawSql": "SELECT s.time - interval '8 hour' as time, s.ping, s.jitter, s.upload, s.download, s.ip, i.country_name, i.city_name, s.org\n  FROM speedtest2 s, ip2proxy i\n WHERE s.ip >= i.ip_from AND s.ip <= i.ip_to\n   AND ip in ($ip)\n   AND org in ($org)\n   AND s.time >= to_timestamp($__from/1000)\n   AND s.time <= to_timestamp($__to/1000)\n ORDER BY time DESC",
     556          "refId": "A",
     557          "select": [
     558            [
     559              {
     560                "params": [
     561                  "value"
     562                ],
     563                "type": "column"
     564              }
     565            ]
     566          ],
     567          "timeColumn": "time",
     568          "where": [
     569            {
     570              "name": "$__timeFilter",
     571              "params": [],
     572              "type": "macro"
     573            }
     574          ]
     575        }
     576      ],
     577      "title": "Raw data",
     578      "type": "table"
     579    },
     580    {
     581      "datasource": {
     582        "type": "postgres",
     583        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     584      },
     585      "fieldConfig": {
     586        "defaults": {
     587          "color": {
     588            "fixedColor": "green",
     589            "mode": "thresholds"
     590          },
     591          "custom": {
     592            "axisCenteredZero": false,
     593            "axisColorMode": "text",
     594            "axisLabel": "",
     595            "axisPlacement": "auto",
     596            "barAlignment": 0,
     597            "drawStyle": "points",
     598            "fillOpacity": 0,
     599            "gradientMode": "none",
     600            "hideFrom": {
     601              "legend": false,
     602              "tooltip": false,
     603              "viz": false
     604            },
     605            "lineInterpolation": "linear",
     606            "lineWidth": 1,
     607            "pointSize": 10,
     608            "scaleDistribution": {
     609              "type": "linear"
     610            },
     611            "showPoints": "auto",
     612            "spanNulls": false,
     613            "stacking": {
     614              "group": "A",
     615              "mode": "none"
     616            },
     617            "thresholdsStyle": {
     618              "mode": "off"
     619            }
     620          },
     621          "mappings": [],
     622          "thresholds": {
     623            "mode": "absolute",
     624            "steps": [
     625              {
     626                "color": "green",
     627                "value": null
     628              },
     629              {
     630                "color": "#EAB839",
     631                "value": 5
     632              },
     633              {
     634                "color": "red",
     635                "value": 10
     636              }
     637            ]
     638          },
     639          "unit": "ms"
     640        },
     641        "overrides": []
     642      },
     643      "gridPos": {
     644        "h": 8,
     645        "w": 11,
     646        "x": 0,
     647        "y": 8
     648      },
     649      "id": 2,
     650      "options": {
     651        "legend": {
     652          "calcs": [],
     653          "displayMode": "list",
     654          "placement": "bottom",
     655          "showLegend": false
     656        },
     657        "tooltip": {
     658          "mode": "single",
     659          "sort": "none"
     660        }
     661      },
     662      "targets": [
     663        {
     664          "datasource": {
     665            "type": "postgres",
     666            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     667          },
     668          "format": "time_series",
     669          "group": [],
     670          "key": "Q-571eb638-8680-4aea-ba7f-b876cd4588a7-0",
     671          "metricColumn": "none",
     672          "rawQuery": true,
     673          "rawSql": "SELECT time - interval '8 hour' as time, jitter, ip, org \n  FROM speedtest2\n WHERE ip in ($ip)\n   AND org in ($org)\n ORDER BY time",
     674          "refId": "A",
     675          "select": [
     676            [
     677              {
     678                "params": [
     679                  "value"
     680                ],
     681                "type": "column"
     682              }
     683            ]
     684          ],
     685          "table": "speedtest_users",
     686          "timeColumn": "time",
     687          "where": []
     688        }
     689      ],
     690      "title": "Jitter",
     691      "type": "timeseries"
     692    },
     693    {
     694      "datasource": {
     695        "type": "postgres",
     696        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     697      },
     698      "fieldConfig": {
     699        "defaults": {
     700          "color": {
     701            "mode": "thresholds"
     702          },
     703          "custom": {
     704            "hideFrom": {
     705              "legend": false,
     706              "tooltip": false,
     707              "viz": false
     708            }
     709          },
     710          "mappings": [],
     711          "thresholds": {
     712            "mode": "absolute",
     713            "steps": [
     714              {
     715                "color": "green",
     716                "value": null
     717              },
     718              {
     719                "color": "#EAB839",
     720                "value": 30
     721              },
     722              {
     723                "color": "red",
     724                "value": 100
     725              }
     726            ]
     727          },
     728          "unit": "ms"
     729        },
     730        "overrides": [
     731          {
     732            "matcher": {
     733              "id": "byName",
     734              "options": "ping"
     735            },
     736            "properties": [
     737              {
     738                "id": "displayName",
     739                "value": "Ping"
     740              },
     741              {
     742                "id": "unit",
     743                "value": "ms"
     744              }
     745            ]
     746          },
     747          {
     748            "matcher": {
     749              "id": "byName",
     750              "options": "download"
     751            },
     752            "properties": [
     753              {
     754                "id": "unit",
     755                "value": "binBps"
     756              },
     757              {
     758                "id": "displayName",
     759                "value": "Download speed"
     760              }
     761            ]
     762          },
     763          {
     764            "matcher": {
     765              "id": "byName",
     766              "options": "upload"
     767            },
     768            "properties": [
     769              {
     770                "id": "displayName",
     771                "value": "Upload speed"
     772              },
     773              {
     774                "id": "unit",
     775                "value": "binBps"
     776              }
     777            ]
     778          },
     779          {
     780            "matcher": {
     781              "id": "byName",
     782              "options": "country_name"
     783            },
     784            "properties": [
     785              {
     786                "id": "displayName",
     787                "value": "Country"
     788              }
     789            ]
     790          },
     791          {
     792            "matcher": {
     793              "id": "byName",
     794              "options": "city_name"
     795            },
     796            "properties": [
     797              {
     798                "id": "displayName",
     799                "value": "City"
     800              }
     801            ]
     802          },
     803          {
     804            "matcher": {
     805              "id": "byName",
     806              "options": "latitude"
     807            },
     808            "properties": [
     809              {
     810                "id": "displayName",
     811                "value": "Latitude"
     812              }
     813            ]
     814          },
     815          {
     816            "matcher": {
     817              "id": "byName",
     818              "options": "longitude"
     819            },
     820            "properties": [
     821              {
     822                "id": "displayName",
     823                "value": "Longitude"
     824              },
     825              {
     826                "id": "unit"
     827              }
     828            ]
     829          },
     830          {
     831            "matcher": {
     832              "id": "byName",
     833              "options": "time"
     834            },
     835            "properties": [
     836              {
     837                "id": "displayName",
     838                "value": "Time"
     839              }
     840            ]
     841          }
     842        ]
     843      },
     844      "gridPos": {
     845        "h": 21,
     846        "w": 13,
     847        "x": 11,
     848        "y": 10
     849      },
     850      "id": 14,
     851      "options": {
     852        "basemap": {
     853          "config": {
     854            "showLabels": true,
     855            "theme": "auto"
     856          },
     857          "name": "Layer 0",
     858          "type": "carto"
     859        },
     860        "controls": {
     861          "mouseWheelZoom": true,
     862          "showAttribution": true,
     863          "showDebug": false,
     864          "showMeasure": false,
     865          "showScale": false,
     866          "showZoom": true
     867        },
     868        "layers": [
     869          {
     870            "config": {
     871              "showLegend": true,
     872              "style": {
     873                "color": {
     874                  "field": "ping",
     875                  "fixed": "dark-green"
     876                },
     877                "opacity": 0.4,
     878                "rotation": {
     879                  "fixed": 0,
     880                  "max": 360,
     881                  "min": -360,
     882                  "mode": "mod"
     883                },
     884                "size": {
     885                  "field": "ping",
     886                  "fixed": 5,
     887                  "max": 15,
     888                  "min": 2
     889                },
     890                "symbol": {
     891                  "fixed": "img/icons/marker/circle.svg",
     892                  "mode": "fixed"
     893                },
     894                "text": {
     895                  "field": "ping",
     896                  "fixed": "",
     897                  "mode": "fixed"
     898                },
     899                "textConfig": {
     900                  "fontSize": 12,
     901                  "offsetX": 0,
     902                  "offsetY": 0,
     903                  "textAlign": "center",
     904                  "textBaseline": "middle"
     905                }
     906              }
     907            },
     908            "location": {
     909              "mode": "auto"
     910            },
     911            "name": "Layer 1",
     912            "tooltip": true,
     913            "type": "markers"
     914          }
     915        ],
     916        "tooltip": {
     917          "mode": "details"
     918        },
     919        "view": {
     920          "allLayers": true,
     921          "id": "zero",
     922          "lat": 0,
     923          "lon": 0,
     924          "zoom": 1
     925        }
     926      },
     927      "pluginVersion": "10.0.2",
     928      "targets": [
     929        {
     930          "datasource": {
     931            "type": "postgres",
     932            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     933          },
     934          "format": "table",
     935          "group": [],
     936          "key": "Q-5bd26bdd-0e13-48ba-8ccd-9b82337c1e13-0",
     937          "metricColumn": "none",
     938          "rawQuery": true,
     939          "rawSql": "SELECT s.ping, time - interval '8 hour' as time, s.download, s.upload, i.country_name, i.city_name, i.latitude, i.longitude\n  FROM speedtest2 s, ip2proxy i\n WHERE s.ip >= i.ip_from AND s.ip <= i.ip_to\n   AND ip in ($ip)\n   AND org in ($org)\n   AND s.time >= to_timestamp($__from/1000)\n   AND s.time <= to_timestamp($__to/1000)\n ORDER BY time DESC",
     940          "refId": "A",
     941          "select": [
     942            [
     943              {
     944                "params": [
     945                  "value"
     946                ],
     947                "type": "column"
     948              }
     949            ]
     950          ],
     951          "timeColumn": "time",
     952          "where": [
     953            {
     954              "name": "$__timeFilter",
     955              "params": [],
     956              "type": "macro"
     957            }
     958          ]
     959        }
     960      ],
     961      "title": "Map",
     962      "type": "geomap"
     963    },
     964    {
     965      "datasource": {
     966        "type": "postgres",
     967        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
     968      },
     969      "fieldConfig": {
     970        "defaults": {
     971          "color": {
     972            "mode": "thresholds"
     973          },
     974          "custom": {
     975            "axisCenteredZero": false,
     976            "axisColorMode": "text",
     977            "axisLabel": "",
     978            "axisPlacement": "auto",
     979            "barAlignment": 0,
     980            "drawStyle": "points",
     981            "fillOpacity": 0,
     982            "gradientMode": "none",
     983            "hideFrom": {
     984              "legend": false,
     985              "tooltip": false,
     986              "viz": false
     987            },
     988            "lineInterpolation": "linear",
     989            "lineWidth": 1,
     990            "pointSize": 10,
     991            "scaleDistribution": {
     992              "type": "linear"
     993            },
     994            "showPoints": "auto",
     995            "spanNulls": false,
     996            "stacking": {
     997              "group": "A",
     998              "mode": "none"
     999            },
    1000            "thresholdsStyle": {
    1001              "mode": "off"
    1002            }
    1003          },
    1004          "mappings": [],
    1005          "thresholds": {
    1006            "mode": "absolute",
    1007            "steps": [
    1008              {
    1009                "color": "green",
    1010                "value": null
    1011              }
    1012            ]
    1013          },
    1014          "unit": "binbps"
    1015        },
    1016        "overrides": []
    1017      },
    1018      "gridPos": {
    1019        "h": 7,
    1020        "w": 11,
    1021        "x": 0,
    1022        "y": 16
    1023      },
    1024      "id": 5,
    1025      "options": {
    1026        "legend": {
    1027          "calcs": [],
    1028          "displayMode": "list",
    1029          "placement": "bottom",
    1030          "showLegend": false
    1031        },
    1032        "tooltip": {
    1033          "mode": "single",
    1034          "sort": "none"
    1035        }
    1036      },
    1037      "targets": [
    1038        {
    1039          "datasource": {
    1040            "type": "postgres",
    1041            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
    1042          },
    1043          "format": "time_series",
    1044          "group": [],
    1045          "key": "Q-e80af284-077f-420b-80c5-f9bd9d7b8aab-0",
    1046          "metricColumn": "none",
    1047          "rawQuery": true,
    1048          "rawSql": "SELECT time - interval '8 hour' as time, upload, ip, org\n  FROM speedtest2\n WHERE ip in ($ip)\n   AND org in ($org)\n ORDER BY time",
    1049          "refId": "A",
    1050          "select": [
    1051            [
    1052              {
    1053                "params": [
    1054                  "value"
    1055                ],
    1056                "type": "column"
    1057              }
    1058            ]
    1059          ],
    1060          "timeColumn": "time",
    1061          "where": [
    1062            {
    1063              "name": "$__timeFilter",
    1064              "params": [],
    1065              "type": "macro"
    1066            }
    1067          ]
    1068        }
    1069      ],
    1070      "title": "Upload Speed",
    1071      "type": "timeseries"
    1072    },
    1073    {
    1074      "datasource": {
    1075        "type": "postgres",
    1076        "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
    1077      },
    1078      "fieldConfig": {
    1079        "defaults": {
    1080          "color": {
    1081            "mode": "thresholds"
    1082          },
    1083          "custom": {
    1084            "axisCenteredZero": false,
    1085            "axisColorMode": "text",
    1086            "axisLabel": "",
    1087            "axisPlacement": "auto",
    1088            "barAlignment": 0,
    1089            "drawStyle": "points",
    1090            "fillOpacity": 0,
    1091            "gradientMode": "none",
    1092            "hideFrom": {
    1093              "legend": false,
    1094              "tooltip": false,
    1095              "viz": false
    1096            },
    1097            "lineInterpolation": "linear",
    1098            "lineWidth": 1,
    1099            "pointSize": 10,
    1100            "scaleDistribution": {
    1101              "type": "linear"
    1102            },
    1103            "showPoints": "auto",
    1104            "spanNulls": false,
    1105            "stacking": {
    1106              "group": "A",
    1107              "mode": "none"
    1108            },
    1109            "thresholdsStyle": {
    1110              "mode": "off"
    1111            }
    1112          },
    1113          "mappings": [],
    1114          "thresholds": {
    1115            "mode": "absolute",
    1116            "steps": [
    1117              {
    1118                "color": "green",
    1119                "value": null
    1120              }
    1121            ]
    1122          },
    1123          "unit": "binbps"
    1124        },
    1125        "overrides": []
    1126      },
    1127      "gridPos": {
    1128        "h": 8,
    1129        "w": 11,
    1130        "x": 0,
    1131        "y": 23
    1132      },
    1133      "id": 6,
    1134      "options": {
    1135        "legend": {
    1136          "calcs": [],
    1137          "displayMode": "list",
    1138          "placement": "bottom",
    1139          "showLegend": false
    1140        },
    1141        "tooltip": {
    1142          "mode": "single",
    1143          "sort": "none"
    1144        }
    1145      },
    1146      "targets": [
    1147        {
    1148          "datasource": {
    1149            "type": "postgres",
    1150            "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
    1151          },
    1152          "format": "time_series",
    1153          "group": [],
    1154          "key": "Q-e80af284-077f-420b-80c5-f9bd9d7b8aab-0",
    1155          "metricColumn": "none",
    1156          "rawQuery": true,
    1157          "rawSql": "SELECT time - interval '8 hour' as time, download, ip, org\n  FROM speedtest2\n WHERE ip in ($ip)\n   AND org in ($org)\n ORDER BY time",
    1158          "refId": "A",
    1159          "select": [
    1160            [
    1161              {
    1162                "params": [
    1163                  "value"
    1164                ],
    1165                "type": "column"
    1166              }
    1167            ]
    1168          ],
    1169          "timeColumn": "time",
    1170          "where": [
    1171            {
    1172              "name": "$__timeFilter",
    1173              "params": [],
    1174              "type": "macro"
    1175            }
    1176          ]
    1177        }
    1178      ],
    1179      "title": "Download Speed",
    1180      "type": "timeseries"
    1181    }
    1182  ],
    1183  "refresh": "",
    1184  "schemaVersion": 38,
    1185  "style": "dark",
    1186  "tags": [],
    1187  "templating": {
    1188    "list": [
    1189      {
    1190        "current": {},
    1191        "datasource": {
    1192          "type": "postgres",
    1193          "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
    1194        },
    1195        "definition": "select distinct ip from speedtest2",
    1196        "description": "label_values(ip)",
    1197        "hide": 0,
    1198        "includeAll": true,
    1199        "label": "IP Address",
    1200        "multi": true,
    1201        "name": "ip",
    1202        "options": [],
    1203        "query": "select distinct ip from speedtest2",
    1204        "refresh": 2,
    1205        "regex": "",
    1206        "skipUrlSync": false,
    1207        "sort": 1,
    1208        "type": "query"
    1209      },
    1210      {
    1211        "current": {},
    1212        "datasource": {
    1213          "type": "postgres",
    1214          "uid": "${DS_SPEEDTEST_(POSTGRESQL)}"
    1215        },
    1216        "definition": "select distinct org from speedtest2",
    1217        "hide": 0,
    1218        "includeAll": true,
    1219        "label": "Organization",
    1220        "multi": true,
    1221        "name": "org",
    1222        "options": [],
    1223        "query": "select distinct org from speedtest2",
    1224        "refresh": 2,
    1225        "regex": "",
    1226        "skipUrlSync": false,
    1227        "sort": 1,
    1228        "type": "query"
    1229      }
    1230    ]
    1231  },
    1232  "time": {
    1233    "from": "now-6h",
    1234    "to": "now"
    1235  },
    1236  "timepicker": {},
    1237  "timezone": "",
    1238  "title": "Speed Test",
    1239  "uid": "TTE5-PzVk",
    1240  "version": 41,
    1241  "weekStart": ""
    1242}