Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 1 | /* |
| 2 | * Copyright 2016-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. |
| 15 | */ |
| 16 | |
| 17 | /* |
| 18 | ONOS GUI -- Openstack Telemetry View Module |
| 19 | */ |
| 20 | (function () { |
| 21 | 'use strict'; |
| 22 | |
| 23 | // injected references |
boyoung2 | a8549d2 | 2018-11-23 20:42:37 +0900 | [diff] [blame^] | 24 | var $log, $scope, $location, ks, fs, cbs, ns, tov, ots, wss, tts, api, gs, ps, ds; |
Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 25 | |
boyoung2 | a8549d2 | 2018-11-23 20:42:37 +0900 | [diff] [blame^] | 26 | // constants |
| 27 | var pCls = 'topo-p', |
| 28 | idTelemetry = 'topo-p-telemetry', |
| 29 | idTelemetryDiag = 'telemetry-dialog', |
| 30 | telemetryPanelOpts = { |
| 31 | edge: 'left', |
| 32 | width: 330, // telemetry panel width |
| 33 | } |
| 34 | |
| 35 | // panels |
| 36 | var telemetry; |
| 37 | |
| 38 | // selected info (by mouse click event) |
| 39 | var selectedItem |
| 40 | |
| 41 | // constants |
| 42 | var ostIsActReq = 'openstackFlowStatsIsActivatedRequest', |
| 43 | ostIsActResp = 'openstackFlowStatsIsActivatedResponse', |
| 44 | ostCreateReq = 'openstackFlowStatsCreateRequest', |
| 45 | ostCreateResp = 'openstackFlowStatsCreateResponse'; |
| 46 | |
| 47 | var topoLion = function (x) { |
| 48 | return '#ttrafov#' + x + '#'; |
| 49 | }; |
| 50 | |
| 51 | var overlay = { |
| 52 | overlayId: 'ostelemetry-overlay', |
| 53 | glyphId: 'm_details', |
| 54 | tooltip: 'Openstack Telemetry Overlay', |
| 55 | |
| 56 | activate: function () { |
| 57 | $log.debug("Openstack telemetry topology overlay is ACTIVATED"); |
| 58 | }, |
| 59 | deactivate: function () { |
| 60 | destroyTelemetryPanel(); |
| 61 | $log.debug("Openstack telemetry topology overlay is DEACTIVATED"); |
| 62 | }, |
| 63 | |
| 64 | // detail panel button definitions |
| 65 | buttons: { |
| 66 | createHostTelemetryBtn: { |
| 67 | gid: 'm_details', |
| 68 | tt: 'Create Host-to-Host Telemetry', |
| 69 | cb: displayTelemetry |
| 70 | }, |
| 71 | showRelatedTraffic: { |
| 72 | gid: 'm_relatedIntents', |
| 73 | tt: function () { return topoLion('tr_btn_show_related_traffic'); }, |
| 74 | cb: function (data) { tts.showRelatedIntents(); }, |
| 75 | }, |
| 76 | }, |
| 77 | |
| 78 | hooks: { |
| 79 | multi: function (selectOrder) { |
| 80 | var selectedHost = new Object(); |
| 81 | $log.debug("Selected object: ", selectOrder); |
| 82 | for (var index in selectOrder) { |
| 83 | if (index == 0) { |
| 84 | selectedHost.src = selectOrder[index]; |
| 85 | $log.debug("Source host: ", selectedHost.src); |
| 86 | } else if (index == 1) { |
| 87 | selectedHost.dst = selectOrder[index]; |
| 88 | $log.debug("Destination host: ", selectedHost.dst); |
| 89 | } |
| 90 | } |
| 91 | |
| 92 | selectedItem = selectedHost; |
| 93 | |
| 94 | tov.addDetailButton('showRelatedTraffic'); |
| 95 | if(selectOrder.length == 2) { |
| 96 | tov.addDetailButton('createHostTelemetryBtn'); |
| 97 | } |
| 98 | } |
| 99 | } |
| 100 | }; |
| 101 | |
| 102 | // Panel API |
| 103 | function createTopoPanel(id, opts) { |
| 104 | var p = ps.createPanel(id, opts), |
| 105 | pid = id, |
| 106 | header, body, footer; |
| 107 | p.classed(pCls, true); |
| 108 | |
| 109 | function panel() { |
| 110 | return p; |
| 111 | } |
| 112 | |
| 113 | function hAppend(x) { |
| 114 | return header.append(x); |
| 115 | } |
| 116 | |
| 117 | function bAppend(x) { |
| 118 | return body.append(x); |
| 119 | } |
| 120 | |
| 121 | function fAppend(x) { |
| 122 | return footer.append(x); |
| 123 | } |
| 124 | |
| 125 | function setup() { |
| 126 | p.empty(); |
| 127 | |
| 128 | p.append('div').classed('header', true); |
| 129 | p.append('div').classed('body', true); |
| 130 | p.append('div').classed('footer', true); |
| 131 | |
| 132 | header = p.el().select('.header'); |
| 133 | body = p.el().select('.body'); |
| 134 | footer = p.el().select('.footer'); |
| 135 | } |
| 136 | |
| 137 | function destroy() { |
| 138 | ps.destroyPanel(pid); |
| 139 | } |
| 140 | |
| 141 | return { |
| 142 | panel: panel, |
| 143 | setup: setup, |
| 144 | destroy: destroy, |
| 145 | appendHeader: hAppend, |
| 146 | appendBody: bAppend, |
| 147 | appendFooter: fAppend, |
| 148 | }; |
| 149 | } |
| 150 | |
| 151 | function hideTelemetryPanel() { |
| 152 | telemetry.panel().hide(); |
| 153 | } |
| 154 | |
| 155 | function destroyTelemetryPanel() { |
| 156 | if(telemetry != null) { |
| 157 | telemetry.destroy(); |
| 158 | } |
| 159 | telemetry = null; |
| 160 | } |
| 161 | |
| 162 | function addInput(tbody, type, id, label, value) { |
| 163 | var tr = tbody.append('tr'), |
| 164 | lab; |
| 165 | if (typeof label === 'string') { |
| 166 | lab = label.replace(/_/g, ' '); |
| 167 | } else { |
| 168 | lab = label; |
| 169 | } |
| 170 | |
| 171 | tr.append('td').attr('class', 'label').text(lab + ' :'); |
| 172 | |
| 173 | if (type == 'radio') { |
| 174 | var td = tr.append('td'); |
| 175 | for(var index in value) { |
| 176 | if(index == 0) { |
| 177 | td.append('input').classed( type + '-input', true) |
| 178 | .attr('type', type) |
| 179 | .attr('value', value[index]) |
| 180 | .attr('name', label) |
| 181 | .attr('id', id) |
| 182 | .attr('checked', 'true'); |
| 183 | } else { |
| 184 | td.append('input').classed( type + '-input', true) |
| 185 | .attr('type', type) |
| 186 | .attr('name', label) |
| 187 | .attr('id', id) |
| 188 | .attr('value', value[index]); |
| 189 | } |
| 190 | td.append('span').text(value[index]); |
| 191 | } |
| 192 | } else { |
| 193 | tr.append('td').append('input').classed(type + '-input', true).attr('type', type) |
| 194 | .attr('id', id).attr('value', value); |
| 195 | } |
| 196 | } |
| 197 | |
| 198 | function addButton(tr, callback, value) { |
| 199 | tr.append('td').append('input').classed('button-input', true).attr('type', 'button') |
| 200 | .attr('value', value).on('click', callback); |
| 201 | } |
| 202 | |
| 203 | function makeButton(callback, text, keyName) { |
| 204 | var cb = fs.isF(callback), |
| 205 | key = fs.isS(keyName); |
| 206 | |
| 207 | function invoke() { |
| 208 | cb && cb(); |
| 209 | } |
| 210 | |
| 211 | return createDiv('telemetry-button') |
| 212 | .text(text) |
| 213 | .on('click', invoke); |
| 214 | } |
| 215 | |
| 216 | function createDiv(cls) { |
| 217 | var div = d3.select(document.createElement('div')); |
| 218 | if (cls) { |
| 219 | div.classed(cls, true); |
| 220 | } |
| 221 | return div; |
| 222 | } |
| 223 | |
| 224 | function displayTelemetry() { |
| 225 | $log.debug("sendEvent openstackFlowStatsIsActivatedRequest: ", selectedItem); |
| 226 | wss.sendEvent(ostIsActReq, selectedItem); |
| 227 | } |
| 228 | |
| 229 | function respIsActCb(selected) { |
| 230 | $log.debug("openstackFlowStatsIsActivatedResponse: ", selected); |
| 231 | if(telemetry == null) { |
| 232 | telemetry = createTopoPanel(idTelemetry, telemetryPanelOpts); |
| 233 | } |
| 234 | telemetry.setup(); |
| 235 | |
| 236 | var svg = telemetry.appendHeader('div') |
| 237 | .classed('icon', true) |
| 238 | .append('svg'), |
| 239 | title = telemetry.appendHeader('h2'), |
| 240 | table = telemetry.appendBody('table'), |
| 241 | tbody = table.append('tbody'), |
| 242 | glyphId = 'm_details'; |
| 243 | |
| 244 | gs.addGlyph(svg, glyphId, 30, 0, [1, 1]); |
| 245 | |
| 246 | title.text('Create Openstack Telemetry'); |
| 247 | |
| 248 | addInput(tbody, 'text', 'srcIp', 'Source IP', selected.srcName); |
| 249 | addInput(tbody, 'text', 'dstIp', 'Destination IP', selected.dstName); |
| 250 | addInput(tbody, 'radio', 'ipProto', 'Protocol', selected.ipProtoList); |
| 251 | addInput(tbody, 'number', 'srcPort', 'Source Port', ""); |
| 252 | addInput(tbody, 'number', 'dstPort', 'Destination Port', ""); |
| 253 | |
| 254 | telemetry.appendFooter('hr'); |
| 255 | var footTr = telemetry.appendFooter('table').append('tbody').append('tr'); |
| 256 | |
| 257 | addButton(footTr, createTelemetry, 'Create'); |
| 258 | addButton(footTr, hideTelemetryPanel, 'Cancel'); |
| 259 | |
| 260 | telemetry.panel().show(); |
| 261 | } |
| 262 | |
| 263 | function createTelemetry() { |
| 264 | var telemetryInfo = {}; |
| 265 | |
| 266 | telemetryInfo.srcIp = document.getElementById('srcIp').value; |
| 267 | telemetryInfo.dstIp = document.getElementById('dstIp').value; |
| 268 | telemetryInfo.ipProto = document.querySelector('input[name="Protocol"]:checked').value; |
| 269 | telemetryInfo.srcPort = document.getElementById('srcPort').value; |
| 270 | telemetryInfo.dstPort = document.getElementById('dstPort').value; |
| 271 | |
| 272 | if(telemetryInfo.srcPort == ""){ |
| 273 | telemetryInfo.srcPort = 0; |
| 274 | } |
| 275 | if(telemetryInfo.dstPort == ""){ |
| 276 | telemetryInfo.dstPort = 0; |
| 277 | } |
| 278 | |
| 279 | $log.debug("Creation request: ", telemetryInfo); |
| 280 | wss.sendEvent(ostCreateReq, telemetryInfo); |
| 281 | hideTelemetryPanel(); |
| 282 | } |
| 283 | |
| 284 | function respCreateCb(result) { |
| 285 | $log.debug("Creation response: ", result); |
| 286 | |
| 287 | function dOK() { |
| 288 | if(result.result == "Failed"){ |
| 289 | displayTelemetry(selectedItem); |
| 290 | } else { |
| 291 | destroyTelemetryPanel(); |
| 292 | } |
| 293 | } |
| 294 | |
| 295 | function createContent(value) { |
| 296 | var content = ds.createDiv(); |
| 297 | content.append('p').text(value); |
| 298 | return content; |
| 299 | } |
| 300 | |
| 301 | ds.openDialog(idTelemetryDiag, telemetryPanelOpts) |
| 302 | .setTitle("Create Telemetry " + result.result) |
| 303 | .addContent(createContent(result.value)) |
| 304 | .addOk(dOK); |
| 305 | } |
| 306 | |
| 307 | /* |
| 308 | Variable for Chart View |
| 309 | */ |
Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 310 | var hasFlow; |
| 311 | |
| 312 | var gFlowId; |
| 313 | var gPeriod; |
| 314 | |
| 315 | var labels = new Array(1); |
| 316 | var data = new Array(2); |
| 317 | for (var i = 0; i < 2; i++) { |
| 318 | data[i] = new Array(1); |
| 319 | } |
| 320 | |
| 321 | var max; |
| 322 | |
| 323 | function ceil(num) { |
| 324 | if (isNaN(num)) { |
| 325 | return 0; |
| 326 | } |
| 327 | var pre = num.toString().length - 1 |
| 328 | var pow = Math.pow(10, pre); |
| 329 | return (Math.ceil(num / pow)) * pow; |
| 330 | } |
| 331 | |
| 332 | function maxInArray(array) { |
| 333 | var merged = [].concat.apply([], array); |
| 334 | return Math.max.apply(null, merged); |
| 335 | } |
| 336 | |
boyoung2 | a8549d2 | 2018-11-23 20:42:37 +0900 | [diff] [blame^] | 337 | /* |
| 338 | Chart View : Controller |
| 339 | */ |
Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 340 | angular.module('ovOpenstacktelemetry', ["chart.js"]) |
| 341 | .controller('OvOpenstacktelemetryCtrl', |
| 342 | ['$log', '$scope', '$location', 'FnService', 'ChartBuilderService', 'NavService', |
| 343 | |
| 344 | function (_$log_, _$scope_, _$location_, _fs_, _cbs_, _ns_) { |
| 345 | var params; |
| 346 | $log = _$log_; |
| 347 | $scope = _$scope_; |
| 348 | $location = _$location_; |
| 349 | fs = _fs_; |
| 350 | cbs = _cbs_; |
| 351 | ns = _ns_; |
| 352 | |
| 353 | params = $location.search(); |
| 354 | |
| 355 | if (params.hasOwnProperty('flowOpt')) { |
| 356 | $scope.flowOpt = params['flowOpt']; |
| 357 | hasFlow = true; |
| 358 | } else if (params.hasOwnProperty('periodOpt')) { |
| 359 | $scope.periodOpt = params['periodOpt']; |
| 360 | hasFlow = true; |
| 361 | } else { |
| 362 | hasFlow = false; |
| 363 | } |
| 364 | |
| 365 | cbs.buildChart({ |
| 366 | scope: $scope, |
| 367 | tag: 'openstacktelemetry', |
| 368 | query: params |
| 369 | }); |
| 370 | |
| 371 | $scope.$watch('chartData', function () { |
| 372 | if (!fs.isEmptyObject($scope.chartData)) { |
| 373 | $scope.showLoader = false; |
| 374 | var length = $scope.chartData.length; |
| 375 | labels = new Array(length); |
| 376 | for (var i = 0; i < 2; i++) { |
| 377 | data[i] = new Array(length); |
| 378 | } |
| 379 | |
| 380 | $scope.chartData.forEach(function (cm, idx) { |
| 381 | data[0][idx] = (cm.curr_acc_packet - cm.prev_acc_packet); |
| 382 | data[1][idx] = (cm.curr_acc_byte - cm.prev_acc_byte); |
| 383 | |
| 384 | labels[idx] = cm.label; |
| 385 | }); |
| 386 | } |
| 387 | |
| 388 | max = maxInArray(data) |
| 389 | $scope.labels = labels; |
| 390 | $scope.data = data; |
| 391 | $scope.options = { |
| 392 | scaleOverride : true, |
| 393 | scaleSteps : 10, |
| 394 | scaleStepWidth : ceil(max) / 10, |
| 395 | scaleStartValue : 0, |
| 396 | scaleFontSize : 16 |
| 397 | }; |
| 398 | $scope.onClick = function (points, evt) { |
| 399 | var label = labels[points[0]._index]; |
| 400 | if (label) { |
| 401 | ns.navTo('openstacktelemetry', { flowOpt: label }); |
| 402 | $log.log(label); |
| 403 | } |
| 404 | }; |
| 405 | |
| 406 | if (!fs.isEmptyObject($scope.annots)) { |
| 407 | $scope.flowIds = JSON.parse($scope.annots.flowIds); |
| 408 | $scope.periodOptions = JSON.parse($scope.annots.periodOptions); |
| 409 | } |
| 410 | |
| 411 | $scope.onChange = function (flowId) { |
| 412 | gFlowId = flowId; |
| 413 | ns.navTo('openstacktelemetry', { periodOpt: gPeriod , flowOpt: flowId }); |
| 414 | }; |
| 415 | |
| 416 | $scope.onPeriodChange = function (period) { |
| 417 | gPeriod = period; |
| 418 | ns.navTo('openstacktelemetry', { periodOpt: period , flowOpt: gFlowId }); |
| 419 | }; |
| 420 | }); |
| 421 | |
| 422 | $scope.series = ['Current Packet', 'Current Byte']; |
| 423 | |
| 424 | $scope.labels = labels; |
| 425 | $scope.data = data; |
| 426 | |
| 427 | $scope.chartColors = [ |
| 428 | '#286090', |
| 429 | '#F7464A', |
| 430 | '#46BFBD', |
| 431 | '#FDB45C', |
| 432 | '#97BBCD', |
| 433 | '#4D5360', |
| 434 | '#8c4f9f' |
| 435 | ]; |
| 436 | Chart.defaults.global.colours = $scope.chartColors; |
| 437 | |
| 438 | $scope.showLoader = true; |
| 439 | |
| 440 | $log.log('OvOpenstacktelemetryCtrl has been created'); |
boyoung2 | a8549d2 | 2018-11-23 20:42:37 +0900 | [diff] [blame^] | 441 | }]) |
| 442 | // Network Topology View : Factory |
| 443 | .factory('OpenstacktelemetryService', |
| 444 | ['$log', 'FnService', 'WebSocketService', 'GlyphService', 'PanelService', 'DialogService', |
Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 445 | |
boyoung2 | a8549d2 | 2018-11-23 20:42:37 +0900 | [diff] [blame^] | 446 | function (_$log_, _fs_, _wss_, _gs_, _ps_, _ds_) { |
| 447 | $log = _$log_; |
| 448 | fs = _fs_; |
| 449 | wss = _wss_; |
| 450 | gs = _gs_; |
| 451 | ps = _ps_; |
| 452 | ds = _ds_; |
| 453 | |
| 454 | var handlers = {}, |
| 455 | telemetryOverlay = 'ostelemetry-overlay', |
| 456 | defaultOverlay = 'traffic'; |
| 457 | |
| 458 | handlers[ostIsActResp] = respIsActCb; |
| 459 | handlers[ostCreateResp] = respCreateCb; |
| 460 | |
| 461 | wss.bindHandlers(handlers); |
| 462 | |
| 463 | return { |
| 464 | displayTelemetry: displayTelemetry |
| 465 | }; |
| 466 | }]) |
| 467 | .run(['$log', 'TopoOverlayService', 'OpenstacktelemetryService', 'TopoTrafficService', |
| 468 | |
| 469 | function (_$log_, _tov_, _ots_, _tts_) { |
| 470 | $log = _$log_; |
| 471 | tov = _tov_; |
| 472 | ots = _ots_; |
| 473 | tts = _tts_; |
| 474 | tov.register(overlay); |
| 475 | }] |
| 476 | ); |
Boyoung Jeong | 1cca5e8 | 2018-08-01 21:00:08 +0900 | [diff] [blame] | 477 | }()); |