blob: d51510549a0463d960d94d71cf62ee859e9922f5 [file] [log] [blame]
//Autogenerated, do not edit!!!
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic', ['ngResource', 'ngCookies', 'ngLodash', 'ngAnimate', 'ui.router', 'xos.helpers']).config(["$stateProvider", function ($stateProvider) {
$stateProvider.state('home', {
url: '/',
template: '<diagnostic-container></diagnostic-container>'
});
}]).config(["$httpProvider", function ($httpProvider) {
$httpProvider.interceptors.push('NoHyperlinks');
}]).run(["$log", function ($log) {
$log.info('Diagnostic Started');
}]);
})();
angular.module("xos.diagnostic").run(["$templateCache", function($templateCache) {$templateCache.put("templates/diagnostic.tpl.html","<div class=\"container-fluid\">\n <div ng-hide=\"vm.error && vm.loader\" style=\"height: 900px\">\n <div class=\"onethird-height\">\n <service-topology service-chain=\"vm.serviceChain\"></service-topology>\n </div>\n <div class=\"twothird-height\">\n <logic-topology ng-if=\"vm.subscribers\" subscribers=\"vm.subscribers\" selected=\"vm.selectedSubscriber\"></logic-topology>\n </div>\n </div>\n <div class=\"row\" ng-if=\"vm.error\">\n <div class=\"col-xs-12\">\n <div class=\"alert alert-danger\">\n {{vm.error}}\n </div>\n </div>\n </div>\n <div class=\"row\" ng-if=\"vm.loader\">\n <div class=\"col-xs-12\">\n <div class=\"loader\">Loading</div>\n </div>\n </div>\n</div>");
$templateCache.put("templates/logicTopology.tpl.html","<select-subscriber-modal open=\"vm.openSelectSubscriberModal\" subscribers=\"vm.subscribers\"></select-subscriber-modal>\n<subscriber-status-modal open=\"vm.openSubscriberStatusModal\" subscriber=\"vm.currentSubscriber\"></subscriber-status-modal>\n<div class=\"instances-stats animate\" ng-hide=\"vm.hideInstanceStats\">\n <div class=\"row\">\n <div class=\"col-sm-3 col-sm-offset-8\">\n <div class=\"panel panel-primary\" ng-repeat=\"instance in vm.selectedInstances\">\n <div class=\"panel-heading\">\n {{instance.humanReadableName}}\n </div>\n <ul class=\"list-group\">\n <li class=\"list-group-item\">Backend Status: {{instance.backend_status}}</li>\n <li class=\"list-group-item\">IP Address: {{instance.ip}}</li>\n </ul>\n <ul class=\"list-group\">\n <li class=\"list-group-item\" ng-repeat=\"stat in instance.stats\">\n <span class=\"badge\">{{stat.value}}</span>\n {{stat.meter}}\n </li>\n </ul>\n </div>\n </div> \n </div>\n </div>\n</div>");
$templateCache.put("templates/select-subscriber-modal.tpl.html","<div class=\"modal fade\" ng-class=\"{in: vm.open}\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog modal-sm\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\n <h4 class=\"modal-title\">Select a subscriber:</h4>\n </div>\n <div class=\"modal-body\">\n <select class=\"form-control\" ng-options=\"s as s.humanReadableName for s in vm.subscribers\" ng-model=\"vm.selected\"></select>\n </div>\n <div class=\"modal-footer\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\n <button ng-click=\"vm.select(vm.selected)\" type=\"button\" class=\"btn btn-primary\">Select</button>\n </div>\n </div><!-- /.modal-content -->\n </div><!-- /.modal-dialog -->\n</div><!-- /.modal -->");
$templateCache.put("templates/subscriber-status-modal.tpl.html","<div class=\"modal fade\" ng-class=\"{in: vm.open}\" tabindex=\"-1\" role=\"dialog\">\n <div class=\"modal-dialog modal-sm\">\n <div class=\"modal-content\">\n <div class=\"modal-header\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"close\" data-dismiss=\"modal\" aria-label=\"Close\"><span aria-hidden=\"true\">&times;</span></button>\n <h4 class=\"modal-title\">Manage subscriber:</h4>\n </div>\n <form name=\"vm.subscriber-detail\">\n <div class=\"modal-body\">\n <div class=\"row\">\n <div class=\"col-xs-12\">\n <label>Status</label>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'enabled\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'enabled\' ,\'btn-default\': vm.subscriber.status !== \'enabled\'}\"\n >Enabled</a>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'suspended\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'suspended\' ,\'btn-default\': vm.subscriber.status !== \'suspended\'}\"\n >Suspended</a>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'delinquent\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'delinquent\' ,\'btn-default\': vm.subscriber.status !== \'delinquent\'}\"\n >Delinquent <br> payment</a>\n </div>\n <div class=\"col-xs-6\">\n <a ng-click=\"vm.subscriber.status = \'copyrightviolation\'\"\n class=\"btn btn-block\"\n ng-class=\"{\'btn-primary\': vm.subscriber.status === \'copyrightviolation\' ,\'btn-default\': vm.subscriber.status !== \'copyrightviolation\'}\"\n >Copyright <br> violation</a>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <label>Uplink Speed</label>\n <input type=\"number\" class=\"form-control\" ng-model=\"vm.subscriber.uplink_speed\"/>\n </div>\n <div class=\"col-xs-6\">\n <label>Downlink Speed</label>\n <input type=\"number\" class=\"form-control\" ng-model=\"vm.subscriber.downlink_speed\"/>\n </div>\n </div>\n <div class=\"row\">\n <div class=\"col-xs-6\">\n <label>Enable Uverse</label>\n </div>\n <div class=\"col-xs-6\">\n <a \n ng-click=\"vm.subscriber.enable_uverse = !vm.subscriber.enable_uverse\" \n ng-class=\"{\'btn-success\': vm.subscriber.enable_uverse, \'btn-danger\': !vm.subscriber.enable_uverse}\"\n class=\"btn btn-block\">\n <span ng-show=\"vm.subscriber.enable_uverse === true\">Enabled</span>\n <span ng-show=\"vm.subscriber.enable_uverse !== true\">Disabled</span>\n </a>\n </div>\n </div>\n </div>\n <div class=\"modal-footer\" ng-show=\"vm.success || vm.formError\">\n <div class=\"alert alert-success\" ng-show=\"vm.success\">\n {{vm.success}}\n </div>\n <div class=\"alert alert-danger\" ng-show=\"vm.formError\">\n {{vm.formError}}\n </div>\n </div>\n <div class=\"modal-footer\">\n <button ng-click=\"vm.close()\" type=\"button\" class=\"btn btn-default\" data-dismiss=\"modal\">Close</button>\n <button ng-click=\"vm.updateSubscriber(vm.subscriber)\" type=\"button\" class=\"btn btn-primary\">Save</button>\n </div>\n </form>\n </div><!-- /.modal-content -->\n </div><!-- /.modal-dialog -->\n</div><!-- /.modal -->");}]);
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').directive('selectSubscriberModal', function () {
return {
scope: {
subscribers: '=',
open: '='
},
bindToController: true,
restrict: 'E',
templateUrl: 'templates/select-subscriber-modal.tpl.html',
controllerAs: 'vm',
controller: ["$rootScope", function controller($rootScope) {
var _this = this;
this.close = function () {
_this.open = false;
};
this.select = function (subscriber) {
$rootScope.$emit('subscriber.selected', subscriber);
_this.close();
};
}]
};
}).directive('subscriberStatusModal', function () {
return {
scope: {
open: '=',
subscriber: '='
},
bindToController: true,
restrict: 'E',
templateUrl: 'templates/subscriber-status-modal.tpl.html',
controllerAs: 'vm',
controller: ["$log", "$timeout", "$scope", "Subscribers", function controller($log, $timeout, $scope, Subscribers) {
var _this2 = this;
$scope.$watch(function () {
return _this2.open;
}, function () {
_this2.success = null;
_this2.formError = null;
});
this.close = function () {
_this2.open = false;
};
this.updateSubscriber = function (subscriber) {
Subscribers.update(subscriber).$promise.then(function () {
_this2.success = 'Subscriber successfully updated!';
})['catch'](function (e) {
_this2.formError = e;
})['finally'](function () {
$timeout(function () {
_this2.close();
}, 1500);
});
};
}]
};
});
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').service('ServiceTopologyHelper', ["$rootScope", "$window", "$log", "lodash", "ServiceRelation", "serviceTopologyConfig", "d3", function ($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3) {
var _svg, _layout, _source, _el;
var i = 0;
// given a canvas, a layout and a data source, draw a tree layout
var updateTree = function updateTree(svg, layout, source) {
var el = arguments.length <= 3 || arguments[3] === undefined ? _el : arguments[3];
if (el) {
_el = el;
}
//cache data
_svg = svg;
_layout = layout;
_source = source;
var maxDepth = ServiceRelation.depthOf(source);
var diagonal = d3.svg.diagonal().projection(function (d) {
return [d.y, d.x];
});
// Compute the new tree layout.
var nodes = layout.nodes(source).reverse(),
links = layout.links(nodes);
// Normalize for fixed-depth.
nodes.forEach(function (d) {
// position the child node horizontally
var step = (_el.clientWidth - serviceTopologyConfig.widthMargin * 2) / maxDepth;
d.y = d.depth * step;
});
// Update the nodes…
var node = svg.selectAll('g.node').data(nodes, function (d) {
return d.id || (d.id = ++i);
});
// Enter any new nodes at the parent's previous position.
var nodeEnter = node.enter().append('g').attr({
'class': function _class(d) {
return 'node ' + d.type;
},
transform: function transform(d) {
return d.x && d.y ? 'translate(' + d.y + ', ' + d.x + ')' : 'translate(' + source.y0 + ', ' + source.x0 + ')';
}
});
var subscriberNodes = nodeEnter.filter('.subscriber');
var internetNodes = nodeEnter.filter('.router');
var serviceNodes = nodeEnter.filter('.service');
subscriberNodes.append('rect').attr(serviceTopologyConfig.square)
// add event listener to subscriber
.on('click', function () {
$rootScope.$emit('subscriber.modal.open');
});
internetNodes.append('rect').attr(serviceTopologyConfig.square);
serviceNodes.append('circle').attr('r', 1e-6).style('fill', function (d) {
return d._children ? 'lightsteelblue' : '#fff';
}).on('click', serviceClick);
nodeEnter.append('text').attr({
x: function x(d) {
return d.children ? -serviceTopologyConfig.circle.selectedRadius - 3 : serviceTopologyConfig.circle.selectedRadius + 3;
},
dy: '.35em',
transform: function transform(d) {
if (d.children && d.parent) {
if (d.parent.x < d.x) {
return 'rotate(-30)';
}
return 'rotate(30)';
}
},
'text-anchor': function textAnchor(d) {
return d.children ? 'end' : 'start';
}
}).text(function (d) {
return d.name;
}).style('fill-opacity', 1e-6);
// Transition nodes to their new position.
var nodeUpdate = node.transition().duration(serviceTopologyConfig.duration).attr({
'transform': function transform(d) {
return 'translate(' + d.y + ',' + d.x + ')';
}
});
nodeUpdate.select('circle').attr('r', function (d) {
return d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius;
}).style('fill', function (d) {
return d.selected ? 'lightsteelblue' : '#fff';
});
nodeUpdate.select('text').style('fill-opacity', 1);
// Transition exiting nodes to the parent's new position.
var nodeExit = node.exit().transition().duration(serviceTopologyConfig.duration).remove();
nodeExit.select('circle').attr('r', 1e-6);
nodeExit.select('text').style('fill-opacity', 1e-6);
// Update the links…
var link = svg.selectAll('path.link').data(links, function (d) {
return d.target.id;
});
// Enter any new links at the parent's previous position.
link.enter().insert('path', 'g').attr('class', function (d) {
return 'link ' + d.target.type + ' ' + (d.target.active ? '' : 'active');
}).attr('d', function (d) {
var o = { x: source.x0, y: source.y0 };
return diagonal({ source: o, target: o });
});
// Transition links to their new position.
link.transition().duration(serviceTopologyConfig.duration).attr('d', diagonal);
// Transition exiting nodes to the parent's new position.
link.exit().transition().duration(serviceTopologyConfig.duration).attr('d', function (d) {
var o = { x: source.x, y: source.y };
return diagonal({ source: o, target: o });
}).remove();
// Stash the old positions for transition.
nodes.forEach(function (d) {
d.x0 = d.x;
d.y0 = d.y;
});
};
var serviceClick = function serviceClick(d) {
// if was selected
if (d.selected) {
d.selected = !d.selected;
$rootScope.$emit('instance.detail.hide', {});
return updateTree(_svg, _layout, _source);
}
$rootScope.$emit('instance.detail', { name: d.name, service: d.service, tenant: d.tenant });
// unselect all
_svg.selectAll('circle').each(function (d) {
return d.selected = false;
});
// toggling selected status
d.selected = !d.selected;
updateTree(_svg, _layout, _source);
};
this.updateTree = updateTree;
}]);
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').directive('serviceTopology', function () {
return {
restrict: 'E',
scope: {
serviceChain: '='
},
bindToController: true,
controllerAs: 'vm',
template: '',
controller: ["$element", "$window", "$scope", "d3", "serviceTopologyConfig", "ServiceRelation", "Slice", "Instances", "Subscribers", "ServiceTopologyHelper", function controller($element, $window, $scope, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, ServiceTopologyHelper) {
var _this = this;
var el = $element[0];
d3.select(window).on('resize', function () {
draw(_this.serviceChain);
});
var root, svg;
var draw = function draw(tree) {
if (!tree) {
console.error('Tree is missing');
return;
}
// TODO update instead clear and redraw
// clean
d3.select($element[0]).select('svg').remove();
var width = el.clientWidth - serviceTopologyConfig.widthMargin * 2;
var height = el.clientHeight - serviceTopologyConfig.heightMargin * 2;
var treeLayout = d3.layout.tree().size([height, width]);
svg = d3.select($element[0]).append('svg').style('width', el.clientWidth + 'px').style('height', el.clientHeight + 'px');
var treeContainer = svg.append('g').attr('transform', 'translate(' + serviceTopologyConfig.widthMargin * 4 + ',' + serviceTopologyConfig.heightMargin + ')');
root = tree;
root.x0 = height / 2;
root.y0 = width / 2;
// ServiceTopologyHelper.drawLegend(svg);
ServiceTopologyHelper.updateTree(treeContainer, treeLayout, root, el);
};
$scope.$watch(function () {
return _this.serviceChain;
}, function (chain) {
if (angular.isDefined(chain)) {
draw(chain);
}
});
}]
};
});
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').service('Services', ["$resource", function ($resource) {
return $resource('/xos/services/:id', { id: '@id' });
}]).service('Tenant', ["$resource", function ($resource) {
return $resource('/xos/tenants', { id: '@id' }, {
queryVsgInstances: {
method: 'GET',
isArray: true,
interceptor: {
response: function response(res) {
// NOTE
// Note that VCPETenant is now VSGTenant.
var instances = [];
angular.forEach(res.data, function (tenant) {
var info = JSON.parse(tenant.service_specific_attribute);
if (info && info.instance_id) {
instances.push(info.instance_id);
}
});
return instances;
}
}
},
getSubscriberTag: {
method: 'GET',
isArray: true,
interceptor: {
response: function response(res) {
// NOTE we should receive only one vOLT tenant here
return JSON.parse(res.data[0].service_specific_attribute);
}
}
}
});
}]).service('Ceilometer', ["$http", "$q", "Instances", function ($http, $q, Instances) {
var _this = this;
/**
* Get stats for a single instance
*/
this.getInstanceStats = function (instanceUuid) {
var deferred = $q.defer();
$http.get('/xoslib/xos-instance-statistics', { params: { 'instance-uuid': instanceUuid } }).then(function (res) {
deferred.resolve(res.data);
})['catch'](function (e) {
deferred.reject(e);
});
return deferred.promise;
};
/**
* Collect stats for an array of instances
*/
this.getInstancesStats = function (instances) {
var deferred = $q.defer();
var instancePromises = [];
var instanceList = [];
// retrieve instance details
instances.forEach(function (instanceId) {
instancePromises.push(Instances.get({ id: instanceId }).$promise);
});
// get all instance data
$q.all(instancePromises).then(function (_instanceList) {
instanceList = _instanceList;
var promises = [];
// foreach instance query stats
instanceList.forEach(function (instance) {
promises.push(_this.getInstanceStats(instance.instance_uuid));
});
return $q.all(promises);
}).then(function (stats) {
// augment instance with stats information
instanceList.map(function (instance, i) {
instance.stats = stats[i];
});
deferred.resolve(instanceList);
})['catch'](deferred.reject);
return deferred.promise;
};
this.getContainerStats = function (containerName) {
var deferred = $q.defer();
var res = {};
$http.get('/xoslib/meterstatistics', { params: { 'resource': containerName } }).then(function (containerStats) {
res.stats = containerStats.data;
return $http.get('/xoslib/meterstatistics', { params: { 'resource': containerName + '-eth0' } });
}).then(function (portStats) {
res.port = {
eth0: portStats.data
};
return $http.get('/xoslib/meterstatistics', { params: { 'resource': containerName + '-eth1' } });
}).then(function (portStats) {
res.port.eth1 = portStats.data;
deferred.resolve(res);
})['catch'](function (e) {
deferred.reject(e);
});
return deferred.promise;
};
}]).service('Slice', ["$resource", function ($resource) {
return $resource('/xos/slices', { id: '@id' });
}]).service('Instances', ["$resource", function ($resource) {
return $resource('/xos/instances/:id', { id: '@id' });
}]).service('Node', ["$resource", "$q", "Instances", function ($resource, $q, Instances) {
return $resource('/xos/nodes', { id: '@id' }, {
queryWithInstances: {
method: 'GET',
isArray: true,
interceptor: {
response: function response(res) {
// TODO update the API to include instances in nodes
// http://stackoverflow.com/questions/14573102/how-do-i-include-related-model-fields-using-django-rest-framework
var deferred = $q.defer();
var requests = [];
angular.forEach(res.data, function (node) {
requests.push(Instances.query({ node: node.id }).$promise);
});
$q.all(requests).then(function (list) {
res.data.map(function (node, i) {
node.instances = list[i];
return node;
});
deferred.resolve(res.data);
});
return deferred.promise;
}
}
}
});
}]).service('Subscribers', ["$resource", "$q", "SubscriberDevice", function ($resource, $q, SubscriberDevice) {
return $resource('/xoslib/cordsubscriber/:id', { id: '@id' }, {
update: {
method: 'PUT',
isArray: false
},
queryWithDevices: {
method: 'GET',
isArray: true,
interceptor: {
response: function response(res) {
/**
* For each subscriber retrieve devices and append them
*/
var deferred = $q.defer();
var requests = [];
angular.forEach(res.data, function (subscriber) {
requests.push(SubscriberDevice.query({ id: subscriber.id }).$promise);
});
$q.all(requests).then(function (list) {
// adding devices
res.data.map(function (subscriber, i) {
subscriber.devices = list[i];
subscriber.type = 'subscriber';
subscriber.devices.map(function (d) {
return d.type = 'device';
});
return subscriber;
});
// faking to have 2 subscriber
// res.data.push(angular.copy(res.data[0]));
deferred.resolve(res.data);
});
return deferred.promise;
}
}
},
getWithDevices: {
method: 'GET',
isArray: false,
interceptor: {
response: function response(res) {
var d = $q.defer();
SubscriberDevice.query({ id: res.data.id }).$promise.then(function (devices) {
devices.map(function (d) {
return d.type = 'device';
});
res.data.devices = devices;
res.data.type = 'subscriber';
d.resolve(res.data);
})['catch'](function (err) {
d.reject(err);
});
return d.promise;
}
}
}
});
}]).service('SubscriberDevice', ["$resource", function ($resource) {
return $resource('/xoslib/rs/subscriber/:id/users/', { id: '@id' });
}]).service('ServiceRelation', ["$q", "lodash", "Services", "Tenant", "Slice", "Instances", function ($q, lodash, Services, Tenant, Slice, Instances) {
// count the mas depth of an object
var depthOf = function depthOf(obj) {
var depth = 0;
if (obj.children) {
obj.children.forEach(function (d) {
var tmpDepth = depthOf(d);
if (tmpDepth > depth) {
depth = tmpDepth;
}
});
}
return 1 + depth;
};
// find all the relation defined for a given root
var findLevelRelation = function findLevelRelation(tenants, rootId) {
return lodash.filter(tenants, function (service) {
return service.subscriber_service === rootId;
});
};
var findSpecificInformation = function findSpecificInformation(tenants, rootId) {
var tenants = lodash.filter(tenants, function (service) {
return service.provider_service === rootId && service.subscriber_tenant;
});
var info;
tenants.forEach(function (tenant) {
if (tenant.service_specific_attribute) {
info = JSON.parse(tenant.service_specific_attribute);
}
});
return info;
};
// find all the service defined by a given array of relations
var findLevelServices = function findLevelServices(relations, services) {
var levelServices = [];
lodash.forEach(relations, function (tenant) {
var service = lodash.find(services, { id: tenant.provider_service });
levelServices.push(service);
});
return levelServices;
};
var buildLevel = function buildLevel(tenants, services, rootService, rootTenant) {
var parentName = arguments.length <= 4 || arguments[4] === undefined ? null : arguments[4];
// build an array of unlinked services
// these are the services that should still placed in the tree
var unlinkedServices = lodash.difference(services, [rootService]);
// find all relations relative to this rootElement
var levelRelation = findLevelRelation(tenants, rootService.id);
// find all items related to rootElement
var levelServices = findLevelServices(levelRelation, services);
// remove this item from the list (performance
unlinkedServices = lodash.difference(unlinkedServices, levelServices);
rootService.service_specific_attribute = findSpecificInformation(tenants, rootService.id);
var tree = {
name: rootService.humanReadableName,
parent: parentName,
type: 'service',
service: rootService,
tenant: rootTenant,
children: []
};
lodash.forEach(levelServices, function (service) {
var tenant = lodash.find(tenants, { subscriber_tenant: rootTenant.id, provider_service: service.id });
tree.children.push(buildLevel(tenants, unlinkedServices, service, tenant, rootService.humanReadableName));
});
// if it is the last element append internet
if (tree.children.length === 0) {
tree.children.push({
name: 'Router',
type: 'router',
children: []
});
}
return tree;
};
var buildSubscriberServiceTree = function buildSubscriberServiceTree(services, tenants) {
var subscriber = arguments.length <= 2 || arguments[2] === undefined ? { id: 1, name: 'fakeSubs' } : arguments[2];
// find the root service
// it is the one attached to subsriber_root
// as now we have only one root so this can work
var rootTenant = lodash.find(tenants, { subscriber_root: subscriber.id });
var rootService = lodash.find(services, { id: rootTenant.provider_service });
var serviceTree = buildLevel(tenants, services, rootService, rootTenant);
return {
name: subscriber.name || subscriber.humanReadableName,
parent: null,
type: 'subscriber',
children: [serviceTree]
};
};
// applying domain knowledge to build the global service tree
var buildServiceTree = function buildServiceTree(services, tenants) {
// TODO refactor
var buildChild = function buildChild(services, tenants, currentService) {
var response = {
type: 'service',
name: currentService.humanReadableName,
service: currentService
};
var tenant = lodash.find(tenants, { subscriber_service: currentService.id });
if (tenant) {
var next = lodash.find(services, { id: tenant.provider_service });
response.children = [buildChild(services, tenants, next)];
} else {
response.children = [{
name: 'Router',
type: 'router',
children: []
}];
}
delete currentService.id; // conflict with d3
return response;
};
var baseService = lodash.find(services, { id: 3 });
if (!angular.isDefined(baseService)) {
console.error('Missing Base service!');
return;
}
var baseData = {
name: 'Subscriber',
type: 'subscriber',
parent: null,
children: [buildChild(services, tenants, baseService)]
};
return baseData;
};
var getBySubscriber = function getBySubscriber(subscriber) {
var deferred = $q.defer();
var services, tenants;
Services.query().$promise.then(function (res) {
services = res;
return Tenant.query().$promise;
}).then(function (res) {
tenants = res;
deferred.resolve(buildSubscriberServiceTree(services, tenants, subscriber));
})['catch'](function (e) {
throw new Error(e);
});
return deferred.promise;
};
var get = function get() {
var deferred = $q.defer();
var services, tenants;
Services.query().$promise.then(function (res) {
services = res;
return Tenant.query({ kind: 'coarse' }).$promise;
}).then(function (res) {
tenants = res;
deferred.resolve(buildServiceTree(services, tenants));
})['catch'](function (e) {
throw new Error(e);
});
return deferred.promise;
};
// export APIs
return {
get: get,
buildServiceTree: buildServiceTree,
getBySubscriber: getBySubscriber,
buildLevel: buildLevel,
buildSubscriberServiceTree: buildSubscriberServiceTree,
findLevelRelation: findLevelRelation,
findLevelServices: findLevelServices,
depthOf: depthOf,
findSpecificInformation: findSpecificInformation
};
}]);
})();
'use strict';
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
(function () {
angular.module('xos.diagnostic').service('RackHelper', ["serviceTopologyConfig", "lodash", function (serviceTopologyConfig, lodash) {
var _this = this;
this.getComputeNodeLabelSize = function () {
return serviceTopologyConfig.computeNode.labelHeight + serviceTopologyConfig.instance.margin * 2;
};
/**
* Given a list of instance should get the Compute Node size.
* They are placed in rows of 2 with 5px margin on each side.
*/
this.getComputeNodeSize = lodash.memoize(function (instances) {
var width = serviceTopologyConfig.instance.margin * 3 + serviceTopologyConfig.instance.width * 2;
var rows = Math.round(instances.length / 2);
var labelSpace = _this.getComputeNodeLabelSize();
var height = serviceTopologyConfig.instance.height * rows + serviceTopologyConfig.instance.margin * (rows + 1) + labelSpace;
return [width, height];
});
/**
* Give a list on Compute Node should calculate the Rack Size.
* Compute nodes are placed in a single column with 5px margin on each side.
*/
this.getRackSize = function (nodes) {
var width = 0;
var height = serviceTopologyConfig.computeNode.margin;
lodash.forEach(nodes, function (node) {
var _getComputeNodeSize = _this.getComputeNodeSize(node.instances);
var _getComputeNodeSize2 = _slicedToArray(_getComputeNodeSize, 2);
var nodeWidth = _getComputeNodeSize2[0];
var nodeHeight = _getComputeNodeSize2[1];
width = nodeWidth + serviceTopologyConfig.computeNode.margin * 2;
height += nodeHeight + serviceTopologyConfig.computeNode.margin;
});
return [width, height];
};
/**
* Given an instance index, return the coordinates
*/
this.getInstancePosition = function (position) {
var row = Math.floor(position / 2);
var column = position % 2 ? 1 : 0;
// add ComputeNode label size
var labelSpace = _this.getComputeNodeLabelSize();
// x = margin + (width * column) + ( maring * column)
var x = serviceTopologyConfig.instance.margin + serviceTopologyConfig.instance.width * column + serviceTopologyConfig.instance.margin * column;
// y = label + margin + (height * row) + ( maring * row)
var y = labelSpace + serviceTopologyConfig.instance.margin + serviceTopologyConfig.instance.height * row + serviceTopologyConfig.instance.margin * row;
return [x, y];
};
/**
* Given an Compute Node index, return the coordinates
*/
this.getComputeNodePosition = function (nodes, position) {
var x = serviceTopologyConfig.computeNode.margin;
var previousElEight = lodash.reduce(nodes.slice(0, position), function (val, node) {
return val + _this.getComputeNodeSize(node.instances)[1];
}, 0);
var y = serviceTopologyConfig.computeNode.margin + serviceTopologyConfig.computeNode.margin * position + previousElEight;
return [x, y];
};
}]);
})();
'use strict';
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
(function () {
'use strict';
var shapes = {
cloud: ' M 79.72 49.60 C 86.00 37.29 98.57 29.01 111.96 26.42 C 124.27 24.11 137.53 26.15 148.18 32.90 C 158.08 38.78 165.39 48.87 167.65 60.20 C 176.20 57.90 185.14 56.01 194.00 57.73 C 206.08 59.59 217.92 66.01 224.37 76.66 C 227.51 81.54 228.85 87.33 229.23 93.06 C 237.59 93.33 246.22 95.10 253.04 100.19 C 256.69 103.13 259.87 107.67 258.91 112.59 C 257.95 118.43 252.78 122.38 247.78 124.82 C 235.27 130.43 220.23 130.09 207.98 123.93 C 199.33 127.88 189.76 129.43 180.30 128.57 C 173.70 139.92 161.70 147.65 148.86 149.93 C 133.10 153.26 116.06 148.15 104.42 137.08 C 92.98 143.04 78.96 143.87 66.97 139.04 C 57.75 135.41 49.70 128.00 46.60 118.43 C 43.87 109.95 45.81 100.29 51.30 93.32 C 57.38 85.18 67.10 80.44 76.99 78.89 C 74.38 69.20 74.87 58.52 79.72 49.60 Z'
};
var computeNodeId = 0;
var instanceId = 0;
angular.module('xos.diagnostic').service('NodeDrawer', ["d3", "serviceTopologyConfig", "RackHelper", "lodash", function (d3, serviceTopologyConfig, RackHelper, lodash) {
var _this2 = this;
var _this = this;
this.addNetworks = function (nodes) {
nodes.append('path').attr({
d: shapes.cloud,
transform: 'translate(-63, -52), scale(0.5)',
'class': 'cloud'
});
nodes.append('text').attr({
'text-anchor': 'middle'
}).text(function (d) {
return d.name;
});
nodes.each(function (n) {
var currentNode = d3.select(this);
// cicle trouch node to add Tags and Public IP
if (n.name === 'LAN' && angular.isDefined(n.subscriberTag)) {
currentNode.append('text').attr({
'text-anchor': 'middle',
y: 40
}).text(function () {
return 'C-Tag: ' + n.subscriberTag.cTag;
});
currentNode.append('text').attr({
'text-anchor': 'middle',
y: 60
}).text(function () {
return 'S-Tag: ' + n.subscriberTag.sTag;
});
}
if (n.name === 'WAN' && angular.isDefined(n.subscriberIP)) {
currentNode.append('text').attr({
'text-anchor': 'middle',
y: 40
}).text(function () {
return 'Public IP: ' + n.subscriberIP;
});
}
});
};
this.addRack = function (nodes) {
// loop because of D3
// rack will be only one
nodes.each(function (d) {
var _RackHelper$getRackSize = RackHelper.getRackSize(d.computeNodes);
var _RackHelper$getRackSize2 = _slicedToArray(_RackHelper$getRackSize, 2);
var w = _RackHelper$getRackSize2[0];
var h = _RackHelper$getRackSize2[1];
// TODO update instead of delete and redraw
nodes.select('g').remove();
var rack = nodes.append('g');
rack.attr({
transform: 'translate(0,0)'
}).transition().duration(serviceTopologyConfig.duration).attr({
transform: function transform() {
return 'translate(' + -(w / 2) + ', ' + -(h / 2) + ')';
}
});
rack.append('rect').attr({
width: 0,
height: 0
}).transition().duration(serviceTopologyConfig.duration).attr({
width: w,
height: h
});
rack.append('text').attr({
'text-anchor': 'middle',
y: -10,
x: w / 2,
opacity: 0
}).text(function (d) {
return d.name;
}).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
_this2.drawComputeNodes(rack, d.computeNodes);
});
};
this.drawComputeNodes = function (container, nodes) {
var elements = container.selectAll('.compute-nodes').data(nodes, function (d) {
if (!angular.isString(d.d3Id)) {
d.d3Id = 'compute-node-' + ++computeNodeId;
}
return d.d3Id;
});
var _container$node$getBoundingClientRect = container.node().getBoundingClientRect();
var width = _container$node$getBoundingClientRect.width;
var height = _container$node$getBoundingClientRect.height;
var nodeContainer = elements.enter().append('g');
nodeContainer.attr({
transform: 'translate(' + width / 2 + ', ' + height / 2 + ')',
'class': 'compute-node'
}).transition().duration(serviceTopologyConfig.duration).attr({
transform: function transform(d) {
return 'translate(' + RackHelper.getComputeNodePosition(nodes, d.d3Id.replace('compute-node-', '') - 1) + ')';
}
});
nodeContainer.append('rect').attr({
width: 0,
height: 0
}).transition().duration(serviceTopologyConfig.duration).attr({
width: function width(d) {
return RackHelper.getComputeNodeSize(d.instances)[0];
},
height: function height(d) {
return RackHelper.getComputeNodeSize(d.instances)[1];
}
});
nodeContainer.append('text').attr({
'text-anchor': 'start',
y: 15, //FIXME
x: 10, //FIXME
opacity: 0
}).text(function (d) {
return d.humanReadableName.split('.')[0];
}).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
// if there are Compute Nodes
if (nodeContainer.length > 0) {
// draw instances for each compute node
nodeContainer.each(function (a) {
_this.drawInstances(d3.select(this), a.instances);
});
}
};
// NOTE Stripping unuseful names to shorten labels.
// This is not elegant
var formatInstanceName = function formatInstanceName(name) {
return name.replace('app_', '').replace('service_', '')
// .replace('ovs_', '')
.replace('mysite_', '').replace('_instance', '');
};
var getInstanceStatusColor = function getInstanceStatusColor(instance) {
function startWith(val, string) {
return string.substring(0, val.length) === val;
}
if (startWith('0 - ', instance.backend_status)) {
return 'provisioning';
}
if (startWith('1 - ', instance.backend_status)) {
return 'good';
}
if (startWith('2 - ', instance.backend_status)) {
return 'bad';
} else {
return '';
}
};
var drawContainer = function drawContainer(container, docker) {
var containerBox = container.append('g').attr({
'class': 'container',
transform: 'translate(' + serviceTopologyConfig.instance.margin + ', 115)'
});
containerBox.append('rect').attr({
width: 250 - serviceTopologyConfig.container.margin * 2,
height: serviceTopologyConfig.container.height
});
containerBox.append('text').attr({
y: 20,
x: serviceTopologyConfig.instance.margin,
'class': 'name'
}).text(docker.name);
// add stats
var interestingMeters = ['memory', 'memory.usage', 'cpu_util'];
interestingMeters.forEach(function (m, i) {
var meter = lodash.find(docker.stats, { meter: m });
// if there is no meter stats skip rendering
if (!angular.isDefined(meter)) {
return;
}
containerBox.append('text').attr({
y: 40 + i * 15,
x: serviceTopologyConfig.instance.margin,
opacity: 0
}).text(meter.description + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
});
// add port stats
var ports = ['eth0', 'eth1'];
var interestingPortMeters = [{
meter: 'network.incoming.bytes.rate',
label: 'Incoming'
}, {
meter: 'network.outgoing.bytes.rate',
label: 'Outgoing'
}];
ports.forEach(function (p, j) {
// if there are no port stats skip rendering
if (docker.port[p].length === 0) {
return;
}
containerBox.append('text').attr({
y: 90,
x: serviceTopologyConfig.instance.margin + 120 * j,
'class': 'name'
}).text(docker.name + '-' + p);
interestingPortMeters.forEach(function (m, i) {
var meter = lodash.find(docker.port[p], { meter: m.meter });
// if there is no meter stats skip rendering
if (!angular.isDefined(meter)) {
return;
}
containerBox.append('text').attr({
y: 105 + i * 15,
x: serviceTopologyConfig.instance.margin + 120 * j,
opacity: 0
}).text(m.label + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
});
});
};
var showInstanceStats = function showInstanceStats(container, instance) {
// NOTE this should be dinamically positioned
// base on the number of element present
var statsContainer = container.append('g').attr({
transform: 'translate(200, -120)',
'class': 'stats-container'
});
statsContainer.append('line').attr({
x1: -160,
y1: 120,
x2: 0,
y2: 50,
stroke: 'black',
opacity: 0
}).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
// NOTE rect should be dinamically sized base on the presence of a container
var statsHeight = 110;
var statsWidth = 250;
if (instance.container) {
statsHeight += serviceTopologyConfig.container.height + serviceTopologyConfig.container.margin * 2;
}
statsContainer.append('rect').attr({
width: statsWidth,
height: statsHeight,
opacity: 0
}).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
// add instance info
statsContainer.append('text').attr({
y: 15,
x: serviceTopologyConfig.instance.margin,
'class': 'name',
opacity: 0
}).text(instance.humanReadableName).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
statsContainer.append('text').attr({
y: 30,
x: serviceTopologyConfig.instance.margin,
'class': 'ip',
opacity: 0
}).text(instance.ip).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
// add stats
var interestingMeters = ['memory', 'memory.usage', 'cpu', 'vcpus'];
interestingMeters.forEach(function (m, i) {
var meter = lodash.find(instance.stats, { meter: m });
statsContainer.append('text').attr({
y: 55 + i * 15,
x: serviceTopologyConfig.instance.margin,
opacity: 0
}).text(meter.description + ': ' + Math.round(meter.value) + ' ' + meter.unit).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
});
if (instance.container) {
// draw container
drawContainer(statsContainer, instance.container);
}
};
this.drawInstances = function (container, instances) {
// TODO check for stats field in instance and draw popup
var _container$node$getBoundingClientRect2 = container.node().getBoundingClientRect();
var width = _container$node$getBoundingClientRect2.width;
var height = _container$node$getBoundingClientRect2.height;
var elements = container.selectAll('.instances').data(instances, function (d) {
return angular.isString(d.d3Id) ? d.d3Id : d.d3Id = 'instance-' + ++instanceId;
});
var instanceContainer = elements.enter().append('g');
instanceContainer.attr({
transform: 'translate(' + width / 2 + ', ' + height / 2 + ')',
'class': function _class(d) {
return 'instance ' + (d.selected ? 'active' : '') + ' ' + getInstanceStatusColor(d);
}
}).transition().duration(serviceTopologyConfig.duration).attr({
transform: function transform(d, i) {
return 'translate(' + RackHelper.getInstancePosition(i) + ')';
}
});
instanceContainer.append('rect').attr({
width: 0,
height: 0
}).transition().duration(serviceTopologyConfig.duration).attr({
width: serviceTopologyConfig.instance.width,
height: serviceTopologyConfig.instance.height
});
instanceContainer.append('text').attr({
'text-anchor': 'middle',
y: 23, //FIXME
x: 40, //FIXME
opacity: 0
}).text(function (d) {
return formatInstanceName(d.humanReadableName);
}).transition().duration(serviceTopologyConfig.duration).attr({
opacity: 1
});
// if stats are attached and instance is active,
// draw stats
instanceContainer.each(function (instance, i) {
var container = d3.select(this);
if (angular.isDefined(instance.stats) && instance.selected) {
showInstanceStats(container, instance, i);
}
});
instanceContainer.on('click', function (d) {
console.log('Draw vignette with stats for instance: ' + d.name);
});
};
this.addPhisical = function (nodes) {
nodes.append('rect').attr(serviceTopologyConfig.square);
nodes.append('text').attr({
'text-anchor': 'middle',
y: serviceTopologyConfig.square.y - 10
}).text(function (d) {
return d.name || d.humanReadableName;
});
};
this.addDevice = function (nodes) {
nodes.append('circle').attr(serviceTopologyConfig.circle);
nodes.append('text').attr({
'text-anchor': 'end',
x: -serviceTopologyConfig.circle.r - 10,
y: serviceTopologyConfig.circle.r / 2
}).text(function (d) {
return d.name || d.mac;
});
};
}]);
})();
'use strict';
var _slicedToArray = (function () { function sliceIterator(arr, i) { var _arr = []; var _n = true; var _d = false; var _e = undefined; try { for (var _i = arr[Symbol.iterator](), _s; !(_n = (_s = _i.next()).done); _n = true) { _arr.push(_s.value); if (i && _arr.length === i) break; } } catch (err) { _d = true; _e = err; } finally { try { if (!_n && _i['return']) _i['return'](); } finally { if (_d) throw _e; } } return _arr; } return function (arr, i) { if (Array.isArray(arr)) { return arr; } else if (Symbol.iterator in Object(arr)) { return sliceIterator(arr, i); } else { throw new TypeError('Invalid attempt to destructure non-iterable instance'); } }; })();
(function () {
'use strict';
angular.module('xos.diagnostic').service('LogicTopologyHelper', ["$window", "$log", "$rootScope", "lodash", "serviceTopologyConfig", "NodeDrawer", "ChartData", function ($window, $log, $rootScope, lodash, serviceTopologyConfig, NodeDrawer, ChartData) {
var _this = this;
var diagonal,
nodes,
links,
i = 0,
svgWidth,
svgHeight,
layout;
var baseData = ChartData.logicTopologyData;
/**
* Calculate the horizontal position for each element.
* subsrcribers, devices and routers have the same fixed width 20
* network have a fixed width 104
* rack have a fixed width 105
* build and array of 6 elements representing the position of each element in the svg
* to equally space them
*/
this.computeElementPosition = function (svgWidth) {
var xPos = [];
var totalElWidth = lodash.reduce(serviceTopologyConfig.elWidths, function (el, val) {
return val + el;
}, 0);
var remainingSpace = svgWidth - totalElWidth - serviceTopologyConfig.widthMargin * 2;
var step = remainingSpace / (serviceTopologyConfig.elWidths.length - 1);
lodash.forEach(serviceTopologyConfig.elWidths, function (el, i) {
// get half of the previous elements width
var previousElWidth = 0;
if (i !== 0) {
previousElWidth = lodash.reduce(serviceTopologyConfig.elWidths.slice(0, i), function (el, val) {
return val + el;
}, 0);
}
var elPos = serviceTopologyConfig.widthMargin // right margin
+ step * i // space between elements
+ el / 2 // this el width
+ previousElWidth; // previous elements width
xPos.push(svgWidth - elPos);
});
return xPos;
};
/**
* from a nested data structure,
* create nodes and links for a D3 Tree Layout
*/
var computeLayout = function computeLayout(data) {
var nodes = layout.nodes(data);
// Normalize for fixed-depth.
nodes.forEach(function (d) {
// position the child node horizontally
d.y = _this.computeElementPosition(svgWidth)[d.depth];
});
var links = layout.links(nodes);
return [nodes, links];
};
/**
* Draw the containing group for any node or update the existing one
*/
var drawNodes = function drawNodes(svg, nodes) {
// Update the nodes…
var node = svg.selectAll('g.node').data(nodes, function (d) {
if (!angular.isString(d.d3Id)) {
d.d3Id = 'tree-' + ++i;
}
return d.d3Id;
});
// Enter any new nodes
var nodeEnter = node.enter().append('g').attr({
'class': function _class(d) {
return 'node ' + d.type;
},
transform: 'translate(' + svgWidth / 2 + ', ' + svgHeight / 2 + ')'
});
// create Nodes
NodeDrawer.addNetworks(node.filter('.network'));
NodeDrawer.addRack(node.filter('.rack'));
NodeDrawer.addPhisical(node.filter('.router'));
NodeDrawer.addPhisical(node.filter('.subscriber'));
NodeDrawer.addDevice(node.filter('.device'));
// add event listener to subscriber
node.filter('.subscriber').on('click', function () {
$rootScope.$emit('subscriber.modal.open');
});
//update nodes
// TODO if data change, only update them
// NodeDrawer.updateRack(node.filter('.rack'));
// Transition nodes to their new position.
var nodeUpdate = node.transition().duration(serviceTopologyConfig.duration).attr({
'transform': function transform(d) {
return 'translate(' + d.y + ',' + d.x + ')';
}
});
// TODO handle node remove
var nodeExit = node.exit().remove();
};
/**
* Handle links in the tree layout
*/
var drawLinks = function drawLinks(svg, links) {
diagonal = d3.svg.diagonal().projection(function (d) {
return [d.y, d.x];
});
// Update the links…
var link = svg.selectAll('path.link').data(links, function (d) {
return d.target.d3Id;
});
// Enter any new links at the parent's previous position.
link.enter().insert('path', 'g').attr('class', function (d) {
return 'link ' + d.target.type;
}).attr('d', function (d) {
var o = { x: svgHeight / 2, y: svgWidth / 2 };
return diagonal({ source: o, target: o });
});
// Transition links to their new position.
link.transition().duration(serviceTopologyConfig.duration).attr('d', diagonal);
link.exit().remove();
};
/**
* Calculate the svg size and setup tree layout
*/
this.setupTree = function (svg) {
svgWidth = svg.node().getBoundingClientRect().width;
svgHeight = svg.node().getBoundingClientRect().height;
var width = svgWidth - serviceTopologyConfig.widthMargin * 2;
var height = svgHeight - serviceTopologyConfig.heightMargin * 2;
layout = d3.layout.tree().size([height, width]);
};
/**
* Update the tree layout
*/
this.updateTree = function (svg) {
// console.log(baseData);
var _computeLayout = computeLayout(baseData);
// Compute the new tree layout.
var _computeLayout2 = _slicedToArray(_computeLayout, 2);
nodes = _computeLayout2[0];
links = _computeLayout2[1];
drawNodes(svg, nodes);
drawLinks(svg, links);
};
}]);
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').directive('logicTopology', function () {
return {
restrict: 'E',
scope: {
subscribers: '=',
selected: '='
},
bindToController: true,
controllerAs: 'vm',
templateUrl: 'templates/logicTopology.tpl.html',
controller: ["$element", "$log", "$scope", "$rootScope", "$timeout", "d3", "LogicTopologyHelper", "Node", "Tenant", "Ceilometer", "serviceTopologyConfig", "ChartData", function controller($element, $log, $scope, $rootScope, $timeout, d3, LogicTopologyHelper, Node, Tenant, Ceilometer, serviceTopologyConfig, ChartData) {
var _this = this;
$log.info('Logic Plane');
var svg;
this.selectedInstances = [];
this.hideInstanceStats = true;
var handleSvg = function handleSvg(el) {
svg = d3.select(el).append('svg').style('width', el.clientWidth + 'px').style('height', el.clientHeight + 'px');
};
ChartData.getLogicTree().then(function (tree) {
LogicTopologyHelper.updateTree(svg);
});
$scope.$watch(function () {
return _this.selected;
}, function (selected) {
if (selected) {
ChartData.selectSubscriber(selected);
LogicTopologyHelper.updateTree(svg);
}
});
$rootScope.$on('instance.detail.hide', function () {
_this.hideInstanceStats = true;
$timeout(function () {
_this.selectedInstances = [];
ChartData.highlightInstances([]);
LogicTopologyHelper.updateTree(svg);
}, 500);
});
$rootScope.$on('instance.detail', function (evt, service) {
ChartData.getInstanceStatus(service).then(function (instances) {
LogicTopologyHelper.updateTree(svg);
});
});
handleSvg($element[0]);
LogicTopologyHelper.setupTree(svg);
this.selectSubscriberModal = function () {
_this.openSelectSubscriberModal = true;
$scope.$apply();
};
this.subscriberStatusModal = function () {
_this.openSubscriberStatusModal = true;
$scope.$apply();
};
// listen for subscriber modal event
$rootScope.$on('subscriber.modal.open', function () {
if (ChartData.currentSubscriber) {
_this.subscriberStatusModal();
} else {
_this.selectSubscriberModal();
}
});
// listen for subscriber modal event
$rootScope.$on('subscriber.modal.open', function () {
if (ChartData.currentSubscriber) {
_this.currentSubscriber = ChartData.currentSubscriber;
_this.subscriberStatusModal();
} else {
_this.selectSubscriberModal();
}
});
}]
};
});
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').directive('diagnosticContainer', function () {
return {
restrict: 'E',
templateUrl: 'templates/diagnostic.tpl.html',
controllerAs: 'vm',
controller: ["ChartData", "Subscribers", "ServiceRelation", "$rootScope", "$log", function controller(ChartData, Subscribers, ServiceRelation, $rootScope, $log) {
var _this = this;
this.loader = true;
this.error = false;
Subscribers.query().$promise.then(function (subscribers) {
_this.subscribers = subscribers;
return ServiceRelation.get();
}).then(function (serviceChain) {
_this.serviceChain = serviceChain;
})['catch'](function (e) {
throw new Error(e);
_this.error = e;
})['finally'](function () {
_this.loader = false;
});
$rootScope.$on('subscriber.selected', function (evt, subscriber) {
ServiceRelation.getBySubscriber(subscriber).then(function (serviceChain) {
_this.serviceChain = serviceChain;
ChartData.currentServiceChain = serviceChain;
return Subscribers.getWithDevices({ id: subscriber.id }).$promise;
}).then(function (subscriber) {
_this.selectedSubscriber = subscriber;
ChartData.currentSubscriber = subscriber;
});
});
}]
};
});
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').factory('d3', ["$window", function ($window) {
return $window.d3;
}]);
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').constant('serviceTopologyConfig', {
widthMargin: 20,
heightMargin: 30,
duration: 750,
elWidths: [20, 104, 105, 104, 20], //this is not true
circle: {
radius: 10,
r: 10,
selectedRadius: 15
},
square: {
width: 20,
height: 20,
x: -10,
y: -10
},
rack: {
width: 105,
height: 50,
x: -30,
y: -25
},
computeNode: {
width: 50,
height: 20,
margin: 5,
labelHeight: 10,
x: -25,
y: -10
},
instance: {
width: 80,
height: 36,
margin: 5,
x: -40,
y: -18
},
container: {
width: 60,
height: 130,
margin: 5,
x: -30,
y: -15
}
});
})();
'use strict';
(function () {
'use strict';
angular.module('xos.diagnostic').service('ChartData', ["$rootScope", "$q", "lodash", "Tenant", "Node", "serviceTopologyConfig", "Ceilometer", "Instances", function ($rootScope, $q, lodash, Tenant, Node, serviceTopologyConfig, Ceilometer, Instances) {
var _this = this;
this.currentSubscriber = null;
this.currentServiceChain = null;
this.logicTopologyData = {
name: 'Router',
type: 'router',
children: [{
name: 'WAN',
type: 'network',
children: [{
name: 'Rack',
type: 'rack',
computeNodes: [],
children: [{
name: 'LAN',
type: 'network',
children: [{
name: 'Subscriber',
type: 'subscriber'
}] //subscribers goes here
}]
}]
}]
};
this.getLogicTree = function () {
var deferred = $q.defer();
Node.queryWithInstances().$promise.then(function (computeNodes) {
_this.logicTopologyData.children[0].children[0].computeNodes = computeNodes;
// LogicTopologyHelper.updateTree(svg);
deferred.resolve(_this.logicTopologyData);
});
return deferred.promise;
};
/**
* Add Subscriber tag to LAN Network
*/
this.addSubscriberTag = function (tags) {
_this.logicTopologyData.children[0].children[0].children[0].subscriberTag = {
cTag: tags.cTag,
sTag: tags.sTag
};
};
/**
* Add Subscribers to the tree
*/
this.addSubscriber = function (subscriber) {
subscriber.children = subscriber.devices;
// add subscriber to data tree
_this.logicTopologyData.children[0].children[0].children[0].children = [subscriber];
return _this.logicTopologyData;
};
this.getSubscriberTag = function (subscriber) {
var tags = {
cTag: subscriber.c_tag,
sTag: subscriber.s_tag
};
_this.addSubscriberTag(tags);
// add tags info to current subscriber
_this.currentSubscriber.tags = tags;
};
this.getSubscriberIP = function (subscriber) {
// const ip = JSON.parse(this.currentServiceChain.children[0].children[0].tenant.service_specific_attribute).wan_container_ip;
// const ip = this.currentServiceChain.children[0].children[0].tenant.wan_container_ip;
_this.logicTopologyData.children[0].subscriberIP = subscriber.wan_container_ip;
};
this.selectSubscriber = function (subscriber) {
// append the device with to config settings
serviceTopologyConfig.elWidths.push(160);
_this.addSubscriber(angular.copy(subscriber));
//clean selected instances
_this.highlightInstances([]);
_this.getSubscriberTag(subscriber);
_this.getSubscriberIP(subscriber);
};
this.highlightInstances = function (instances) {
var computeNodes = _this.logicTopologyData.children[0].children[0].computeNodes;
// unselect all
computeNodes.map(function (node) {
node.instances.map(function (instance) {
instance.selected = false;
return instance;
});
});
lodash.forEach(instances, function (instance) {
computeNodes.map(function (node) {
node.instances.map(function (d3instance) {
if (d3instance.id === instance.id) {
// console.log(d3instance, instance);
d3instance.selected = true;
d3instance.stats = instance.stats; //add stats to d3 node
d3instance.container = instance.container; // container info to d3 node
}
return d3instance;
});
});
});
};
this.getInstanceStatus = function (service) {
var deferred = $q.defer();
var p = undefined;
// subscriber specific
if (_this.currentSubscriber) {
var attr = undefined;
try {
attr = JSON.parse(service.tenant.service_specific_attribute);
} catch (e) {
attr = null;
}
// if no instances are associated to the subscriber
if (!attr || !attr.instance_id) {
var d = $q.defer();
d.resolve([]);
p = d.promise;
}
// if ther is an instance
else {
(function () {
var instance = {};
p = Instances.get({ id: attr.instance_id }).$promise.then(function (_instance) {
instance = _instance;
return Ceilometer.getInstanceStats(instance.instance_uuid);
}).then(function (stats) {
instance.stats = stats;
var containerName = 'vcpe-' + _this.currentSubscriber.tags.sTag + '-' + _this.currentSubscriber.tags.cTag;
// append containers
instance.container = {
name: containerName
};
// TODO fetch container stats
return Ceilometer.getContainerStats(containerName);
}).then(function (containerStats) {
instance.container.stats = containerStats.stats;
instance.container.port = containerStats.port;
return [instance];
});
})();
}
}
// global scope
else {
var param = {
'service_vsg': { kind: 'vCPE' },
'service_vbng': { kind: 'vBNG' },
'service_volt': { kind: 'vOLT' }
};
p = Tenant.queryVsgInstances(param[service.name]).$promise.then(function (instances) {
return Ceilometer.getInstancesStats(instances);
});
}
p.then(function (instances) {
_this.highlightInstances(instances);
deferred.resolve(instances);
})['catch'](function (e) {
deferred.reject(e);
});
return deferred.promise;
};
}]);
})();
angular.module('xos.diagnostic').run(function($location){
$location.path('/')
});
angular.bootstrap(angular.element('#xosDiagnostic'), ['xos.diagnostic']);