blob: f5156bbc6ba5a95d8548c9b9651c39e457a3d557 [file] [log] [blame]
/*
* Copyright 2017-present Open Networking Foundation
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
/*
ONOS GUI -- Pipeconf View Module
*/
(function () {
'use strict';
// injected refs
var $log, $scope, $loc, $interval, $timeout, fs, ns, wss, ls, ps, mast, is, dps;
// Constants
var pipeconfRequest = "pipeconfRequest",
pipeConfResponse = "pipeConfResponse",
noPipeconfResp = "noPipeconfResp",
invalidDevId = "invalidDevId",
pipeconf = "pipeconf",
pipelineModel = "pipelineModel",
devId = "devId",
topPdg = 28,
pName = 'pipeconf-detail-panel',
refreshRate = 5000;
// For request handling
var handlers,
refreshPromise;
// Details panel
var pWidth = 600,
pTopHeight = 111,
pStartY,
wSize,
pHeight,
detailsPanel;
// create key bindings to handle panel
var keyBindings = {
esc: [closePanel, 'Close the details panel'],
_helpFormat: ['esc'],
};
function fetchPipeconfData() {
if ($scope.autoRefresh && wss.isConnected() && !ls.waiting()) {
ls.start();
var requestData = {
devId: $scope.devId
};
wss.sendEvent(pipeconfRequest, requestData);
}
}
function pipeConfRespCb(data) {
ls.stop();
if (!data.hasOwnProperty(pipeconf)) {
$scope.pipeconf = null;
return;
}
$scope.pipeconf = data[pipeconf];
$scope.pipelineModel = data[pipelineModel];
$scope.$apply();
}
function noPipeconfRespCb(data) {
ls.stop();
$scope.pipeconf = null;
$scope.pipelineModel = null;
$scope.$apply();
}
function viewDestroy() {
wss.unbindHandlers(handlers);
$interval.cancel(refreshPromise);
refreshPromise = null;
ls.stop();
}
function headerSelectCb($event, header) {
if ($scope.selectedId !== null &&
$scope.selectedId.type === 'header' &&
$scope.selectedId.name === header.name) {
// Hide the panel when select same row
closePanel();
return;
}
$scope.selectedId = {
type: 'header',
name: header.name,
};
var subtitles = [
{
label: 'Header Type: ',
value: header.type.name,
},
{
label: 'Is metadata: ',
value: header.isMetadata,
},
{
label: 'Index: ',
value: header.index,
},
];
var tables = [
{
title: 'Fields',
headers: ['Name', 'Bit width'],
data: header.type.fields,
noDataText: 'No header fields'
},
];
populateDetailPanel(header.name, subtitles, tables);
}
function actionSelectCb($event, action) {
if ($scope.selectedId !== null &&
$scope.selectedId.type === 'action' &&
$scope.selectedId.name === action.name) {
// Hide the panel when select same row
closePanel();
return;
}
$scope.selectedId = {
type: 'action',
name: action.name,
};
var subtitles = [];
var tables = [
{
title: 'Parameters',
headers: ['Name', 'Bit width'],
data: action.params,
noDataText: 'No action parameters',
},
];
populateDetailPanel(action.name, subtitles, tables);
}
function tableSelectCb($event, table) {
if ($scope.selectedId !== null &&
$scope.selectedId.type === 'table' &&
$scope.selectedId.name === table.name) {
// Hide the panel when select same row
closePanel();
return;
}
$scope.selectedId = {
type: 'table',
name: table.name,
};
var subtitles = [
{
label: 'Max Size: ',
value: table.maxSize,
},
{
label: 'Has counters: ',
value: table.hasCounters,
},
{
label: 'Support Aging: ',
value: table.supportAging,
},
];
var matchFields = table.matchFields.map(function(mp) {
return {
name: mp.header.name + '.' + mp.field.name,
bitWidth: mp.field.bitWidth,
matchType: mp.matchType,
}
});
var tables = [
{
title: 'Match fields',
headers: ['Name', 'Bit width', 'Match type'],
data: matchFields,
noDataText: 'No match fields'
},
{
title: 'Actions',
headers: ['Name'],
data: table.actions,
noDataText: 'No actions'
},
];
populateDetailPanel(table.name, subtitles, tables);
}
function closePanel() {
if (detailsPanel.isVisible()) {
detailsPanel.hide();
// Avoid Angular inprog error
$timeout(function() {
$scope.selectedId = null;
}, 0);
return true;
}
return false;
}
function populateDetailTable(tableContainer, table) {
var tableTitle = table.title;
var tableData = table.data;
var tableHeaders = table.headers;
var noDataText = table.noDataText;
tableContainer.append('h2').classed('detail-panel-bottom-title', true).text(tableTitle);
var detailPanelTable = tableContainer.append('table').classed('detail-panel-table', true);
var headerTr = detailPanelTable.append('tr').classed('detail-panel-table-header', true);
tableHeaders.forEach(function(h) {
headerTr.append('th').text(h);
});
if (tableData.length === 0) {
var row = detailPanelTable.append('tr').classed('detail-panel-table-row', true);
row.append('td')
.classed('detail-panel-table-col no-data', true)
.attr('colspan', tableHeaders.length)
.text(noDataText);
}
tableData.forEach(function(data) {
var row = detailPanelTable.append('tr').classed('detail-panel-table-row', true);
if (fs.isS(data)) {
row.append('td').classed('detail-panel-table-col', true).text(data);
} else {
Object.keys(data).forEach(function(k) {
row.append('td').classed('detail-panel-table-col', true).text(data[k]);
});
}
});
tableContainer.append('hr');
}
function populateDetailTables(tableContainer, tables) {
tables.forEach(function(table) {
populateDetailTable(tableContainer, table);
})
}
function populateDetailPanel(topTitle, topSubtitles, tables) {
dps.empty();
dps.addContainers();
dps.addCloseButton(closePanel);
var top = dps.top();
top.append('h2').classed('detail-panel-header', true).text(topTitle);
topSubtitles.forEach(function(st) {
var typeText = top.append('div').classed('top-info', true);
typeText.append('p').classed('label', true).text(st.label);
typeText.append('p').classed('value', true).text(st.value);
});
var bottom = dps.bottom();
var bottomHeight = pHeight - pTopHeight - 60;
bottom.style('height', bottomHeight + 'px');
populateDetailTables(bottom, tables);
detailsPanel.width(pWidth);
detailsPanel.show();
resizeDetailPanel();
}
function heightCalc() {
pStartY = fs.noPxStyle(d3.select('.tabular-header'), 'height')
+ mast.mastHeight() + topPdg;
wSize = fs.windowSize(pStartY);
pHeight = wSize.height - 20;
}
function resizeDetailPanel() {
if (detailsPanel.isVisible()) {
heightCalc();
var bottomHeight = pHeight - pTopHeight - 60;
d3.select('.bottom').style('height', bottomHeight + 'px');
detailsPanel.height(pHeight);
}
}
angular.module('ovPipeconf', [])
.controller('OvPipeconfCtrl',
['$log', '$scope', '$location', '$interval', '$timeout', 'FnService', 'NavService', 'WebSocketService',
'LoadingService', 'PanelService', 'MastService', 'IconService', 'DetailsPanelService',
function (_$log_, _$scope_, _$loc_, _$interval_, _$timeout_, _fs_,
_ns_, _wss_, _ls_, _ps_, _mast_, _is_, _dps_) {
$log = _$log_;
$scope = _$scope_;
$loc = _$loc_;
$interval = _$interval_;
$timeout = _$timeout_;
fs = _fs_;
ns = _ns_;
wss = _wss_;
ls = _ls_;
ps = _ps_;
mast = _mast_;
is = _is_;
dps = _dps_;
$scope.deviceTip = 'Show device table';
$scope.flowTip = 'Show flow view for this device';
$scope.portTip = 'Show port view for this device';
$scope.groupTip = 'Show group view for this device';
$scope.meterTip = 'Show meter view for selected device';
$scope.pipeconfTip = 'Show pipeconf view for selected device';
var params = $loc.search();
if (params.hasOwnProperty(devId)) {
$scope.devId = params[devId];
}
$scope.nav = function (path) {
if ($scope.devId) {
ns.navTo(path, { devId: $scope.devId });
}
};
handlers = {
pipeConfResponse: pipeConfRespCb,
noPipeconfResp: noPipeconfRespCb,
invalidDevId: noPipeconfRespCb,
};
wss.bindHandlers(handlers);
$scope.$on('$destroy', viewDestroy);
$scope.autoRefresh = true;
fetchPipeconfData();
// On click callbacks, initialize select id
$scope.selectedId = null;
$scope.headerSelectCb = headerSelectCb;
$scope.actionSelectCb = actionSelectCb;
$scope.tableSelectCb = tableSelectCb;
// Make them collapsable
$scope.collapsePipeconf = false;
$scope.collapseHeaders = false;
$scope.collapseActions = false;
$scope.collapseTables = false;
$scope.mapToNames = function(data) {
return data.map(function(d) {
return d.name;
});
};
$scope.matMatchFields = function(matchFields) {
return matchFields.map(function(mf) {
return mf.header.name + '.' + mf.field.name;
});
};
refreshPromise = $interval(function() {
fetchPipeconfData();
}, refreshRate);
$log.log('OvPipeconfCtrl has been created');
}])
.directive('autoHeight', ['$window', 'FnService',
function($window, fs) {
return function(scope, element) {
var autoHeightElem = d3.select(element[0]);
scope.$watchCollection(function() {
return {
h: $window.innerHeight
};
}, function() {
var wsz = fs.windowSize(140, 0);
autoHeightElem.style('height', wsz.height + 'px');
});
};
}
])
.directive('pipeconfViewDetailPanel', ['$rootScope', '$window', '$timeout', 'KeyService',
function($rootScope, $window, $timeout, ks) {
function createDetailsPanel() {
detailsPanel = dps.create(pName, {
width: wSize.width,
margin: 0,
hideMargin: 0,
scope: $scope,
keyBindings: keyBindings,
nameChangeRequest: null,
});
$scope.hidePanel = function () { detailsPanel.hide(); };
detailsPanel.hide();
}
function initPanel() {
heightCalc();
createDetailsPanel();
}
return function(scope) {
var unbindWatch;
// Safari has a bug where it renders the fixed-layout table wrong
// if you ask for the window's size too early
if (scope.onos.browser === 'safari') {
$timeout(initPanel);
} else {
initPanel();
}
// if the panelData changes
scope.$watch('panelData', function () {
if (!fs.isEmptyObject(scope.panelData)) {
// populateDetails(scope.panelData);
detailsPanel.show();
}
});
// if the window size changes
unbindWatch = $rootScope.$watchCollection(
function () {
return {
h: $window.innerHeight,
w: $window.innerWidth,
};
}, function () {
resizeDetailPanel();
}
);
scope.$on('$destroy', function () {
unbindWatch();
ks.unbindKeys();
ps.destroyPanel(pName);
});
};
}
]);
}());