Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 1 | #!/usr/bin/env python |
Zack Williams | 41513bf | 2018-07-07 20:08:35 -0700 | [diff] [blame] | 2 | # Copyright 2017-present Open Networking Foundation |
| 3 | # |
| 4 | # Licensed under the Apache License, Version 2.0 (the "License"); |
| 5 | # you may not use this file except in compliance with the License. |
| 6 | # You may obtain a copy of the License at |
| 7 | # |
| 8 | # http://www.apache.org/licenses/LICENSE-2.0 |
| 9 | # |
| 10 | # Unless required by applicable law or agreed to in writing, software |
| 11 | # distributed under the License is distributed on an "AS IS" BASIS, |
| 12 | # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| 13 | # See the License for the specific language governing permissions and |
| 14 | # limitations under the License. |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 15 | |
| 16 | from structlog import get_logger |
| 17 | from twisted.internet.defer import DeferredList, inlineCallbacks |
| 18 | import requests |
| 19 | import sys |
| 20 | # |
| 21 | # This file contains the dashboard template information. It gets pulled into |
| 22 | # the dashd module and used to createt he dashboards. The other option would |
| 23 | # be to put each of these in an individual text file and read them in when the |
| 24 | # dashd process starts. There isn't much advantage to doing so at this time. |
| 25 | # |
| 26 | # TODO: The creation of a template from Grafana is currently incomplete. |
| 27 | |
| 28 | log = get_logger() |
| 29 | |
| 30 | class DashTemplate(object): |
| 31 | def __init__(self, grafana_url): |
| 32 | self.grafana_url = grafana_url |
| 33 | |
| 34 | self.rowSelector = '%port%' # Not currently used |
| 35 | self.rows = [ |
| 36 | dict( |
| 37 | title = "%port% packet statistics" |
| 38 | ) |
| 39 | ] |
| 40 | self.panels = [ |
| 41 | dict( |
| 42 | title = "%port% Packet Receive Statistics", |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 43 | rx_64_pkts = \ |
| 44 | ("alias(perSecond(voltha.%device%.%deviceId%.%port%.rx_64_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 45 | "'64b pkts/sec')" |
| 46 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 47 | rx_65_127_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 48 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 49 | "voltha.%device%.%deviceId%.%port%.rx_65_127_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 50 | " '65-127b pkts/sec')" |
| 51 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 52 | rx_128_255_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 53 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 54 | "voltha.%device%.%deviceId%.%port%.rx_128_255_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 55 | "'128-255b pkts/sec')" |
| 56 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 57 | rx_256_511_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 58 | ("alias(perSecond" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 59 | "(voltha.%device%.%deviceId%.%port%.rx_256_511_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 60 | "'256-511b pkts/sec')" |
| 61 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 62 | rx_512_1023_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 63 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 64 | "voltha.%device%.%deviceId%.%port%.rx_512_1023_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 65 | "'512-1023b pkts/sec')" |
| 66 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 67 | rx_1024_1518_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 68 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 69 | "voltha.%device%.%deviceId%.%port%.rx_1024_1518_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 70 | "'1024-1518b pkts/sec')" |
| 71 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 72 | rx_1519_9k_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 73 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 74 | "voltha.%device%.%deviceId%.%port%.rx_1519_9k_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 75 | "'1519b-9kb pkts/sec')" |
| 76 | ) |
| 77 | ), |
| 78 | dict( |
| 79 | title = "%port% Packet Send Statistics", |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 80 | tx_64_pkts = \ |
| 81 | ("alias(perSecond(voltha.%device%.%deviceId%.%port%.tx_64_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 82 | "'64b pkts/sec')" |
| 83 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 84 | tx_65_127_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 85 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 86 | "voltha.%device%.%deviceId%.%port%.tx_65_127_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 87 | "'65-127b pkts/sec')" |
| 88 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 89 | tx_128_255_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 90 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 91 | "voltha.%device%.%deviceId%.%port%.tx_128_255_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 92 | "'128-255b pkts/sec')" |
| 93 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 94 | tx_256_511_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 95 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 96 | "voltha.%device%.%deviceId%.%port%.tx_256_511_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 97 | "'256-511b pkts/sec')" |
| 98 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 99 | tx_512_1023_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 100 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 101 | "voltha.%device%.%deviceId%.%port%.tx_512_1023_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 102 | "'512-1023b pkts/sec')" |
| 103 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 104 | tx_1024_1518_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 105 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 106 | "voltha.%device%.%deviceId%.%port%.tx_1024_1518_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 107 | "'1024-1518b pkts/sec')" |
| 108 | ), |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 109 | tx_1519_9k_pkts = \ |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 110 | ("alias(perSecond(" |
Sergio Slobodrian | ec6e391 | 2017-04-02 11:46:55 -0400 | [diff] [blame] | 111 | "voltha.%device%.%deviceId%.%port%.tx_1519_9k_pkts), " |
Sergio Slobodrian | f39aaf8 | 2017-02-28 16:10:16 -0500 | [diff] [blame] | 112 | "'1519b-9kb pkts/sec')" |
| 113 | ) |
| 114 | ) |
| 115 | ] |
| 116 | |
| 117 | |
| 118 | self.dashRow = ''' |
| 119 | { |
| 120 | "collapse": false, |
| 121 | "editable": true, |
| 122 | "height": "250px", |
| 123 | "title": "Row", |
| 124 | "panels": [] |
| 125 | } |
| 126 | ''' |
| 127 | |
| 128 | self.dashTarget = ''' |
| 129 | { |
| 130 | "refId": "", |
| 131 | "target": "" |
| 132 | } |
| 133 | ''' |
| 134 | |
| 135 | self.dashPanel = ''' |
| 136 | { |
| 137 | "aliasColors": {}, |
| 138 | "bars": false, |
| 139 | "datasource": "Voltha Stats", |
| 140 | "editable": true, |
| 141 | "error": false, |
| 142 | "fill": 0, |
| 143 | "grid": { |
| 144 | "threshold1": null, |
| 145 | "threshold1Color": "rgba(216, 200, 27, 0.27)", |
| 146 | "threshold2": null, |
| 147 | "threshold2Color": "rgba(234, 112, 112, 0.22)" |
| 148 | }, |
| 149 | "id": 1, |
| 150 | "isNew": true, |
| 151 | "legend": { |
| 152 | "avg": false, |
| 153 | "current": false, |
| 154 | "max": false, |
| 155 | "min": false, |
| 156 | "show": true, |
| 157 | "total": false, |
| 158 | "values": false |
| 159 | }, |
| 160 | "lines": true, |
| 161 | "linewidth": 1, |
| 162 | "links": [], |
| 163 | "nullPointMode": "connected", |
| 164 | "percentage": false, |
| 165 | "pointradius": 5, |
| 166 | "points": false, |
| 167 | "renderer": "flot", |
| 168 | "seriesOverrides": [], |
| 169 | "span": 6, |
| 170 | "stack": false, |
| 171 | "steppedLine": false, |
| 172 | "targets": [ |
| 173 | ], |
| 174 | "timeFrom": null, |
| 175 | "timeShift": null, |
| 176 | "title": "", |
| 177 | "tooltip": { |
| 178 | "msResolution": true, |
| 179 | "shared": true, |
| 180 | "value_type": "cumulative" |
| 181 | }, |
| 182 | "type": "graph", |
| 183 | "xaxis": { |
| 184 | "show": true |
| 185 | }, |
| 186 | "yaxes": [ |
| 187 | { |
| 188 | "format": "short", |
| 189 | "label": null, |
| 190 | "logBase": 1, |
| 191 | "max": null, |
| 192 | "min": null, |
| 193 | "show": true |
| 194 | }, |
| 195 | { |
| 196 | "format": "short", |
| 197 | "label": null, |
| 198 | "logBase": 1, |
| 199 | "max": null, |
| 200 | "min": null, |
| 201 | "show": true |
| 202 | } |
| 203 | ] |
| 204 | } |
| 205 | ''' |
| 206 | self.dashBoard = ''' |
| 207 | { |
| 208 | "dashboard":{ |
| 209 | "annotations": { |
| 210 | "list": [] |
| 211 | }, |
| 212 | "refresh": "1m", |
| 213 | "editable": true, |
| 214 | "hideControls": false, |
| 215 | "id": null, |
| 216 | "overwrite": true, |
| 217 | "links": [], |
| 218 | "rows": [ |
| 219 | ], |
| 220 | "schemaVersion": 12, |
| 221 | "sharedCrosshair": false, |
| 222 | "style": "dark", |
| 223 | "tags": [], |
| 224 | "templating": { |
| 225 | "list": [] |
| 226 | }, |
| 227 | "time": { |
| 228 | "from": "now-30m", |
| 229 | "to": "now" |
| 230 | }, |
| 231 | "timepicker": { |
| 232 | "refresh_intervals": [ |
| 233 | "5s", |
| 234 | "10s", |
| 235 | "30s", |
| 236 | "1m", |
| 237 | "5m", |
| 238 | "15m", |
| 239 | "30m", |
| 240 | "1h", |
| 241 | "2h", |
| 242 | "1d" |
| 243 | ], |
| 244 | "time_options": [ |
| 245 | "5m", |
| 246 | "15m", |
| 247 | "1h", |
| 248 | "6h", |
| 249 | "12h", |
| 250 | "24h", |
| 251 | "2d", |
| 252 | "7d", |
| 253 | "30d" |
| 254 | ] |
| 255 | }, |
| 256 | "timezone": "browser", |
| 257 | "title": "", |
| 258 | "version": 0 |
| 259 | } |
| 260 | } |
| 261 | ''' |
| 262 | |
| 263 | #TODO This functionality is a work in progress and needs to be completed. |
| 264 | def apply_template(self, tplt_info): |
| 265 | # The tplt_info is the record returned by Grafana as a result of a |
| 266 | # search request. This includes the id, title, uri, and other fields |
| 267 | # of no interest to us. The URI provides the key to access the |
| 268 | # dashboard definition from which we'll create a template. |
| 269 | try: |
| 270 | r = requests.get(self.grafana_url + "/dashboards/" + \ |
| 271 | tplt_info['uri']) |
| 272 | db = r.json() |
| 273 | # We don't need all the meta-data so just keep the dashboard |
| 274 | # definition |
| 275 | db = db['dashboard'] |
| 276 | # We need to null out the id to create new dashboards with the |
| 277 | # template. |
| 278 | db['id'] = None |
| 279 | # Extract the rows and empty them from the template |
| 280 | rows = db['rows'] |
| 281 | db['rows']=[] |
| 282 | # Determine if the rows are wildcarded or fixed, if wildcarded they |
| 283 | # need to map to the port which will create one row per port if |
| 284 | # they're not wildcarded then the title will be used as the port id |
| 285 | # and the same fixed number of rows will be used for every |
| 286 | # dashboard. |
| 287 | # Wildcarding implies a single row so check that first. |
| 288 | if len(rows) == 1: |
| 289 | # We might have wildcarding, search for it in the row titile |
| 290 | match = re.search(r'%port%',rows[0]['title']) |
| 291 | if match: |
| 292 | # Yes there is a wildcard, flag it |
| 293 | log.info("Wildcard found in template row") #debug |
| 294 | else: |
| 295 | log.info("No wildcard found in template row") #debug |
| 296 | else: |
| 297 | # We don't have wildcarding |
| 298 | log.info("No wildcard possible in multi-row template") #debug |
| 299 | |
| 300 | except: |
| 301 | e = sys.exc_info() |
| 302 | print("ERROR: ", e) |