371 lines
11 KiB
JavaScript
371 lines
11 KiB
JavaScript
/*
|
|
* Copyright 2016 FUJITSU LIMITED
|
|
*
|
|
* 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.
|
|
*/
|
|
|
|
'use strict';
|
|
|
|
angular
|
|
.module('monitoring.directives', [
|
|
'horizon.framework.widgets',
|
|
'monitoring.filters',
|
|
'monitoring.services',
|
|
'gettext'
|
|
])
|
|
.directive('monAlarmExpression',
|
|
['monitoringApp.staticPath', monAlarmExpressionsDirective]
|
|
)
|
|
.directive('monAlarmSubExpression',
|
|
['monitoringApp.staticPath', monAlarmSubExpressionDirective]
|
|
);
|
|
|
|
function monAlarmExpressionsDirective(staticPath){
|
|
|
|
return {
|
|
restrict: 'E',
|
|
scope: {
|
|
metrics: '=metrics',
|
|
functions: '=functions',
|
|
operators: '=operators',
|
|
comparators: '=comparators',
|
|
connectable: '=connectable'
|
|
},
|
|
templateUrl: staticPath + 'expression/expression.tpl.html',
|
|
controller: ['$q', '$scope', 'monExpressionBuilder', AlarmExpressionController],
|
|
controllerAs: 'vm',
|
|
bindToController: true
|
|
};
|
|
|
|
function AlarmExpressionController($q, $scope, monExpressionBuilder) {
|
|
// private
|
|
var vm = this,
|
|
deterministic = false,
|
|
matchBy = undefined;
|
|
|
|
// scope
|
|
vm.expression = '';
|
|
vm.subExpressions = undefined;
|
|
vm.expressionValid = true;
|
|
|
|
// api
|
|
vm.touch = touch;
|
|
vm.addExpression = addExpression;
|
|
vm.removeExpression = removeExpression;
|
|
vm.reorderExpression = reorderExpression;
|
|
|
|
// listen
|
|
$scope.$on('$destroy', destroy);
|
|
|
|
// init
|
|
$scope.$applyAsync(init);
|
|
|
|
function addExpression($event, $index) {
|
|
if ($event) {
|
|
$event.preventDefault();
|
|
}
|
|
|
|
vm.subExpressions.splice($index, 0, {});
|
|
if ($index >= 1 && vm.subExpressions[$index - 1].$valid) {
|
|
// hide previous expression
|
|
// if it is valid
|
|
vm.subExpressions[$index - 1]['preview'] = true;
|
|
}
|
|
|
|
applyConnectable();
|
|
|
|
return true;
|
|
}
|
|
|
|
function removeExpression($event, index) {
|
|
if ($event) {
|
|
$event.preventDefault();
|
|
}
|
|
vm.subExpressions.splice(index, 1);
|
|
|
|
applyConnectable();
|
|
touch();
|
|
|
|
if (vm.subExpressions.length === 0) {
|
|
vm.expression = '';
|
|
vm.subExpressions.push({});
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
function reorderExpression($event, which, where) {
|
|
$event.preventDefault();
|
|
vm.subExpressions[where]['op'] = [
|
|
vm.subExpressions[which]['op'],
|
|
vm.subExpressions[which]['op'] = vm.subExpressions[where]['op']
|
|
][0];
|
|
vm.subExpressions[where] = vm.subExpressions.splice(which, 1, vm.subExpressions[where])[0];
|
|
|
|
applyConnectable();
|
|
|
|
return true;
|
|
}
|
|
|
|
function touch() {
|
|
var hasInvalid = false;
|
|
var expression = undefined;
|
|
|
|
matchBy = [];
|
|
deterministic = true;
|
|
|
|
angular.forEach(vm.subExpressions, subExpressionIt);
|
|
|
|
if (hasInvalid) {
|
|
expression = undefined;
|
|
} else {
|
|
$scope.$emit('mon_match_by_changed', matchBy.sort());
|
|
$scope.$emit('mon_deterministic_changed', deterministic);
|
|
|
|
expression = monExpressionBuilder.asString(vm.subExpressions, true);
|
|
|
|
// change preview only if valid
|
|
vm.expression = expression;
|
|
}
|
|
|
|
// update that always, regardless if it's valid or not
|
|
// for invalid case that will reset input's value to empty
|
|
// disallowing form to be accepted by django
|
|
$('#expression').val(expression);
|
|
vm.expressionValid = !hasInvalid;
|
|
|
|
return true;
|
|
|
|
function subExpressionIt(expr) {
|
|
if (!expr.$valid) {
|
|
return !(hasInvalid = true);
|
|
}
|
|
|
|
angular.forEach(expr.matchBy || [], function it(mb) {
|
|
if(matchBy.indexOf(mb) < 0) {
|
|
matchBy.push(mb);
|
|
}
|
|
});
|
|
deterministic = deterministic && (expr.deterministic || false);
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
function init() {
|
|
if(vm.metrics.length) {
|
|
vm.subExpressions = [];
|
|
vm.matchBy = [];
|
|
|
|
addExpression(undefined, 0);
|
|
}
|
|
}
|
|
|
|
function destroy() {
|
|
delete vm.metrics;
|
|
delete vm.expression;
|
|
delete vm.subExpressions;
|
|
delete vm.deterministic;
|
|
}
|
|
|
|
function applyConnectable() {
|
|
var count = vm.subExpressions.length;
|
|
|
|
switch(count) {
|
|
case 1:
|
|
vm.subExpressions[0]['connectable'] = false;
|
|
break;
|
|
default: {
|
|
angular.forEach(vm.subExpressions, function(expr, index) {
|
|
expr.connectable = index >= 1 && index < vm.subExpressions.length;
|
|
if (!expr.connectable) {
|
|
delete expr['op'];
|
|
}
|
|
});
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
}
|
|
|
|
function monAlarmSubExpressionDirective(staticPath) {
|
|
return {
|
|
restrict: 'E',
|
|
require: '^monAlarmExpression',
|
|
scope: {
|
|
metrics: '=metrics',
|
|
functions: '=functions',
|
|
comparators: '=comparators',
|
|
operators: '=operators',
|
|
model: '=subExpression',
|
|
connectable: '=connectable',
|
|
preview: '=preview'
|
|
},
|
|
templateUrl: staticPath + 'expression/sub-expression.tpl.html',
|
|
link: linkFn,
|
|
controller: ['$q', '$scope', 'monExpressionBuilder', AlarmSubExpressionController],
|
|
controllerAs: 'vm',
|
|
bindToController: true
|
|
};
|
|
|
|
function linkFn(scope, el, attrs, monAlarmExpressions) {
|
|
el.on('$destroy', (function(){
|
|
|
|
var watcher = scope.$watch('vm.model', function(expr, oldExpr) {
|
|
if (expr !== oldExpr) {
|
|
monAlarmExpressions.touch();
|
|
}
|
|
}, true);
|
|
|
|
return function destroyer() {
|
|
watcher();
|
|
};
|
|
|
|
}()));
|
|
}
|
|
|
|
function AlarmSubExpressionController($q, $scope, monExpressionBuilder) {
|
|
var vm = this;
|
|
|
|
vm.tags = [];
|
|
vm.matchingMetrics = [];
|
|
|
|
// api
|
|
vm.possibleDimensions = possibleDimensions;
|
|
vm.onMetricChanged = onMetricChanged;
|
|
vm.onDimensionsUpdated = onDimensionsUpdated;
|
|
|
|
vm.updateExpression = updateExpression;
|
|
vm.resetExpression = resetExpression;
|
|
|
|
// init
|
|
$scope.$on('$destroy', destroyerFactory());
|
|
|
|
function opRemoverListener(nval) {
|
|
if (vm.model && 'op' in vm.model && !nval) {
|
|
delete vm.model['op'];
|
|
}
|
|
}
|
|
|
|
function destroyerFactory() {
|
|
|
|
var watcher = $scope.$watch('vm.model.connectable', opRemoverListener, true);
|
|
|
|
return function destroyer() {
|
|
watcher();
|
|
|
|
delete vm.tags;
|
|
delete vm.matchingMetrics;
|
|
delete vm.model;
|
|
};
|
|
|
|
}
|
|
|
|
function updateExpression() {
|
|
var dim = [],
|
|
formController = $scope.$$childHead.subExpressionForm;
|
|
|
|
vm.model.$valid = !formController.$invalid;
|
|
|
|
if (vm.model.$valid) {
|
|
|
|
if (vm.tags.length > 0) {
|
|
angular.forEach(vm.tags, function(value, key) {
|
|
dim.push(value['text']);
|
|
});
|
|
vm.model.dimensions = dim;
|
|
} else {
|
|
vm.model.dimensions = [];
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
function resetExpression() {
|
|
vm.matchingMetrics = [];
|
|
vm.tags = [];
|
|
}
|
|
|
|
function possibleDimensions(query) {
|
|
return $q(function(resolve) {
|
|
var dim = {},
|
|
dimList = [];
|
|
|
|
angular.forEach(vm.matchingMetrics, function(value, name) {
|
|
for (var key in value.dimensions) {
|
|
if (value.dimensions.hasOwnProperty(key)) {
|
|
var dimStr = key + "=" + value.dimensions[key];
|
|
if (dimStr.indexOf(query) === 0) {
|
|
dim[dimStr] = dimStr;
|
|
}
|
|
}
|
|
}
|
|
});
|
|
|
|
angular.forEach(dim, function(value, name) {
|
|
dimList.push(value);
|
|
});
|
|
|
|
resolve(dimList);
|
|
});
|
|
}
|
|
|
|
function onDimensionsUpdated() {
|
|
onMetricChanged(vm.model.metric);
|
|
}
|
|
|
|
function onMetricChanged(metric) {
|
|
handleMetricChanged(metric);
|
|
updateExpression();
|
|
}
|
|
|
|
function handleMetricChanged(metric) {
|
|
var mm = [],
|
|
matchBy = [],
|
|
tags = vm.tags || [];
|
|
|
|
angular.forEach(vm.metrics, function(value, key) {
|
|
if (value.name === metric.name) {
|
|
var match = true;
|
|
for (var i = 0; i < tags.length; i++) {
|
|
var vals = tags[i]['text'].split('=');
|
|
if (value.dimensions[vals[0]] !== vals[1]) {
|
|
match = false;
|
|
break;
|
|
}
|
|
|
|
}
|
|
if (match) {
|
|
mm.push(value);
|
|
}
|
|
}
|
|
});
|
|
|
|
angular.forEach(mm, function(value, key){
|
|
angular.forEach(value.dimensions, function(value, key){
|
|
if(matchBy.indexOf(key) < 0){
|
|
matchBy.push(key);
|
|
}
|
|
});
|
|
});
|
|
|
|
vm.matchingMetrics = mm;
|
|
vm.model.matchBy = matchBy.sort();
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|