Drawing subscriber and devices
diff --git a/views/ngXosViews/diagnostic/src/css/serviceTopology.css b/views/ngXosViews/diagnostic/src/css/serviceTopology.css
index 54beba3..39a5497 100644
--- a/views/ngXosViews/diagnostic/src/css/serviceTopology.css
+++ b/views/ngXosViews/diagnostic/src/css/serviceTopology.css
@@ -4,10 +4,17 @@
height: 50%;
}
-service-topology {
+.half-height + .half-height {
+ border-top: 1px solid black;
+}
+
+service-topology,
+logic-topology {
height: 100%;
width: 100%;
display: block;
+ position: absolute;
+ top: 0;
}
/* LEGEND */
@@ -61,7 +68,7 @@
font: 12px sans-serif;
}
-.link {
+.link, .device-link {
fill: none;
stroke: #ccc;
stroke-width: 2px;
diff --git a/views/ngXosViews/diagnostic/src/index.html b/views/ngXosViews/diagnostic/src/index.html
index 2c0e84d..d9bfc1e 100644
--- a/views/ngXosViews/diagnostic/src/index.html
+++ b/views/ngXosViews/diagnostic/src/index.html
@@ -33,8 +33,10 @@
<script src="/api/ng-xos.js"></script>
<script src="/api/ng-hpcapi.js"></script>
<script src="/.tmp/main.js"></script>
-<script src="/.tmp/services.js"></script>
+<script src="/.tmp/serviceTopologyHelper.js"></script>
<script src="/.tmp/serviceTopology.js"></script>
+<script src="/.tmp/rest_services.js"></script>
+<script src="/.tmp/logicTopologyHelper.js"></script>
<script src="/.tmp/logicTopology.js"></script>
<script src="/.tmp/diagnostic.js"></script>
<script src="/.tmp/d3.js"></script>
diff --git a/views/ngXosViews/diagnostic/src/js/d3.js b/views/ngXosViews/diagnostic/src/js/d3.js
index 027f08d..d94a22e 100644
--- a/views/ngXosViews/diagnostic/src/js/d3.js
+++ b/views/ngXosViews/diagnostic/src/js/d3.js
@@ -2,260 +2,8 @@
'use strict';
angular.module('xos.serviceTopology')
- .factory('d3', function($window){
- return $window.d3;
- })
- .service('TreeLayout', function($window, $log, lodash, ServiceRelation, serviceTopologyConfig){
-
- const drawLegend = (svg) => {
- const legendContainer = svg.append('g')
- .attr({
- class: 'legend'
- });
-
- legendContainer.append('rect')
- .attr({
- transform: d => `translate(10, 80)`,
- width: 100,
- height: 100
- });
-
- // service
- const service = legendContainer.append('g')
- .attr({
- class: 'node service'
- });
-
- service.append('circle')
- .attr({
- r: serviceTopologyConfig.circle.radius,
- transform: d => `translate(30, 100)`
- });
-
- service.append('text')
- .attr({
- transform: d => `translate(45, 100)`,
- dy: '.35em'
- })
- .text('Service')
- .style('fill-opacity', 1);
-
- // slice
- const slice = legendContainer.append('g')
- .attr({
- class: 'node slice'
- });
-
- slice.append('rect')
- .attr({
- width: 20,
- height: 20,
- x: -10,
- y: -10,
- transform: d => `translate(30, 130)`
- });
-
- slice.append('text')
- .attr({
- transform: d => `translate(45, 130)`,
- dy: '.35em'
- })
- .text('Slices')
- .style('fill-opacity', 1);
-
- // instance
- const instance = legendContainer.append('g')
- .attr({
- class: 'node instance'
- });
-
- instance.append('rect')
- .attr({
- width: 20,
- height: 20,
- x: -10,
- y: -10,
- transform: d => `translate(30, 160)`
- });
-
- instance.append('text')
- .attr({
- transform: d => `translate(45, 160)`,
- dy: '.35em'
- })
- .text('Instances')
- .style('fill-opacity', 1);
- };
-
- var _svg, _layout, _source;
-
- var i = 0;
-
- // given a canvas, a layout and a data source, draw a tree layout
- const updateTree = (svg, layout, source) => {
-
- //cache data
- _svg = svg;
- _layout = layout;
- _source = source;
-
- const maxDepth = ServiceRelation.depthOf(source);
-
- const diagonal = d3.svg.diagonal()
- .projection(d => [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
- const step = (($window.innerWidth - (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: d => `node ${d.type}`,
- transform: `translate(${source.y0}, ${source.x0})`
- });
-
- const subscriberNodes = nodeEnter.filter('.subscriber');
- const internetNodes = nodeEnter.filter('.internet');
- const serviceNodes = nodeEnter.filter('.service');
- const instanceNodes = nodeEnter.filter('.instance');
- const sliceNodes = nodeEnter.filter('.slice');
-
- subscriberNodes.append('rect')
- .attr(serviceTopologyConfig.square);
-
- internetNodes.append('rect')
- .attr(serviceTopologyConfig.square);
-
- serviceNodes.append('circle')
- .attr('r', 1e-6)
- .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
- .on('click', serviceClick);
-
- sliceNodes.append('rect')
- .attr({
- width: 20,
- height: 20,
- x: -10,
- y: -10
- });
-
- instanceNodes.append('rect')
- .attr({
- width: 20,
- height: 20,
- x: -10,
- y: -10,
- class: d => d.active ?'' : 'active'
- });
-
- nodeEnter.append('text')
- .attr({
- x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
- dy: '.35em',
- transform: d => {
- if (d.children && d.parent){
- if(d.parent.x < d.x){
- return 'rotate(-30)';
- }
- return 'rotate(30)';
- }
- },
- 'text-anchor': d => d.children ? 'end' : 'start'
- })
- .text(d => d.name)
- .style('fill-opacity', 1e-6);
-
- // Transition nodes to their new position.
- var nodeUpdate = node.transition()
- .duration(serviceTopologyConfig.duration)
- .attr({
- 'transform': d => `translate(${d.y},${d.x})`
- });
-
- nodeUpdate.select('circle')
- .attr('r', d => d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius)
- .style('fill', d => 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', d => `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;
- });
- };
-
- const serviceClick = function(d) {
-
- $log.info('TODO emit an event to highlight VMs');
-
- if(!d.service){
- return;
- }
-
- // toggling selected status
- d.selected = !d.selected;
-
- var selectedNode = d3.select(this);
-
- selectedNode
- .transition()
- .duration(serviceTopologyConfig.duration)
- .attr('r', serviceTopologyConfig.circle.selectedRadius);
- };
-
- this.updateTree = updateTree;
- this.drawLegend = drawLegend;
- });
+ .factory('d3', function($window){
+ return $window.d3;
+ })
}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/diagnostic.js b/views/ngXosViews/diagnostic/src/js/diagnostic.js
index 0a02b04..938b2f6 100644
--- a/views/ngXosViews/diagnostic/src/js/diagnostic.js
+++ b/views/ngXosViews/diagnostic/src/js/diagnostic.js
@@ -10,7 +10,6 @@
controller: function(Subscribers, ServiceRelation){
Subscribers.queryWithDevices().$promise
.then((subscribers) => {
- console.log(subscribers);
this.subscribers = subscribers;
return ServiceRelation.get(subscribers[0]);
})
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopology.js b/views/ngXosViews/diagnostic/src/js/logicTopology.js
index c00b5b6..0a9215e 100644
--- a/views/ngXosViews/diagnostic/src/js/logicTopology.js
+++ b/views/ngXosViews/diagnostic/src/js/logicTopology.js
@@ -5,15 +5,38 @@
return {
restrict: 'E',
scope: {
- serviceChain: '='
+ subscribers: '=',
+ selected: '='
},
bindToController: true,
controllerAs: 'vm',
template: '',
- controller: function($element, $log){
+ controller: function($element, $log, $scope, d3, LogicTopologyHelper){
$log.info('Logic Plane');
-
+ var svg;
+
+ $scope.$watch(() => this.subscribers, (subscribers) => {
+ if(subscribers){
+ LogicTopologyHelper.handleSubscribers(svg, subscribers);
+ }
+ });
+
+ $scope.$watch(() => this.selected, (selected) => {
+ if(selected){
+ $log.info(`Update logic layer for subscriber ${selected.humanReadableName}`);
+ }
+ });
+
+ const handleSvg = (el) => {
+
+ svg = d3.select(el)
+ .append('svg')
+ .style('width', `${el.clientWidth}px`)
+ .style('height', `${el.clientHeight}px`);
+ }
+
+ handleSvg($element[0]);
}
};
});
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
new file mode 100644
index 0000000..8aedda2
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/logicTopologyHelper.js
@@ -0,0 +1,105 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.serviceTopology')
+ .service('LogicTopologyHelper', function($window, $log, lodash, serviceTopologyConfig){
+
+ var hStep, vStep;
+
+ const createDevice = (container, device, xPos, yPos, target) => {
+
+ const deviceGroup = container.append('g')
+ .attr({
+ class: 'device',
+ transform: `translate(${xPos}, ${yPos})`
+ });
+
+ const deviceEl = deviceGroup.append('circle')
+ .attr({
+ r: serviceTopologyConfig.circle.radius
+ });
+
+ deviceGroup.append('text')
+ .attr({
+ x: - serviceTopologyConfig.circle.radius - 3,
+ dy: '.35em',
+ 'text-anchor': 'end'
+ })
+ .text(device.name)
+
+ const [deviceX, deviceY] = d3.transform(deviceEl.attr('transform')).translate;
+ const [deviceGroupX, deviceGroupY] = d3.transform(deviceGroup.attr('transform')).translate;
+ let [targetX, targetY] = d3.transform(target.attr('transform')).translate;
+
+ targetX = targetX - deviceGroupX;
+ targetY = targetY - deviceGroupY;
+
+ console.log('Device: ' + deviceX, deviceY);
+ console.log('Subscriber: ' + targetX, targetY);
+
+ var diagonal = d3.svg.diagonal()
+ .source({x: deviceX, y: deviceY})
+ .target({x: targetX, y: targetY})
+ .projection(d => {
+ return [d.x, d.y];
+ });
+
+ deviceGroup
+ .append('path')
+ .attr('class', 'device-link')
+ .attr('d', diagonal);
+ }
+
+ const createSubscriber = (container, subscriber, xPos, yPos) => {
+
+ const subscriberGroup = container.append('g')
+ .attr({
+ class: 'subscriber',
+ transform: `translate(${xPos * 2}, ${yPos})`
+ });
+
+ subscriberGroup.append('circle')
+ .attr({
+ r: serviceTopologyConfig.circle.radius
+ });
+
+ subscriberGroup.append('text')
+ .attr({
+ x: serviceTopologyConfig.circle.radius + 3,
+ dy: '.35em',
+ 'text-anchor': 'start'
+ })
+ .text(subscriber.humanReadableName)
+
+ // TODO
+ // starting from the subscriber position, we should center
+ // the device goup based on his own height
+ // const deviceContainer = container.append('g')
+ // .attr({
+ // class: 'devices-container',
+ // transform: `translate(${xPos}, ${yPos -(vStep / 2)})`
+ // });
+
+ angular.forEach(subscriber.devices, (device, j) => {
+ createDevice(container, device, xPos, ((vStep / subscriber.devices.length) * j) + (yPos - vStep / 2), subscriberGroup);
+ });
+ }
+
+ this.handleSubscribers = (svg, subscribers) => {
+
+ // HACKY
+ hStep = angular.element(svg[0])[0].clientWidth / 7;
+ vStep = angular.element(svg[0])[0].clientHeight / (subscribers.length + 1);
+
+ const container = svg.append('g')
+ .attr({
+ class: 'subscribers-container'
+ });
+
+ lodash.forEach(subscribers, (subscriber, i) => {
+ createSubscriber(container, subscriber, hStep, vStep * (i + 1));
+ })
+ }
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/js/services.js b/views/ngXosViews/diagnostic/src/js/rest_services.js
similarity index 96%
rename from views/ngXosViews/diagnostic/src/js/services.js
rename to views/ngXosViews/diagnostic/src/js/rest_services.js
index 669d028..beeb4fe 100644
--- a/views/ngXosViews/diagnostic/src/js/services.js
+++ b/views/ngXosViews/diagnostic/src/js/rest_services.js
@@ -21,21 +21,28 @@
isArray: true,
interceptor: {
response: function(res){
+
+ /**
+ * For each subscriber retrieve devices and append them
+ */
+
const deferred = $q.defer();
let requests = [];
angular.forEach(res.data, (subscriber) => {
- requests.push(SubscriberDevice.query({id: subscriber.id}));
+ requests.push(SubscriberDevice.query({id: subscriber.id}).$promise);
})
$q.all(requests)
.then((list) => {
- console.log(list);
res.data.map((subscriber, i) => {
subscriber.devices = list[i];
return subscriber;
});
+
+ // faking to have 2 subscriber
+ res.data.push(res.data[0]);
deferred.resolve(res.data);
})
diff --git a/views/ngXosViews/diagnostic/src/js/serviceTopology.js b/views/ngXosViews/diagnostic/src/js/serviceTopology.js
index 163ee34..820b3a9 100644
--- a/views/ngXosViews/diagnostic/src/js/serviceTopology.js
+++ b/views/ngXosViews/diagnostic/src/js/serviceTopology.js
@@ -11,13 +11,12 @@
bindToController: true,
controllerAs: 'vm',
template: '',
- controller: function($element, $window, $scope, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, TreeLayout){
+ controller: function($element, $window, $scope, d3, serviceTopologyConfig, ServiceRelation, Slice, Instances, Subscribers, ServiceTopologyHelper){
const el = $element[0];
d3.select(window)
.on('resize', () => {
- console.log('resize');
draw(this.serviceChain);
});
@@ -25,6 +24,8 @@
const draw = (tree) => {
+ // TODO update instead clear and redraw
+
// clean
d3.select($element[0]).select('svg').remove();
@@ -46,8 +47,8 @@
root.x0 = height / 2;
root.y0 = width / 2;
- TreeLayout.drawLegend(svg);
- TreeLayout.updateTree(treeContainer, treeLayout, root);
+ ServiceTopologyHelper.drawLegend(svg);
+ ServiceTopologyHelper.updateTree(treeContainer, treeLayout, root);
};
this.getInstances = (slice) => {
diff --git a/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
new file mode 100644
index 0000000..f3cb2d2
--- /dev/null
+++ b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
@@ -0,0 +1,258 @@
+(function () {
+ 'use strict';
+
+ angular.module('xos.serviceTopology')
+ .service('ServiceTopologyHelper', function($window, $log, lodash, ServiceRelation, serviceTopologyConfig){
+
+ const drawLegend = (svg) => {
+ const legendContainer = svg.append('g')
+ .attr({
+ class: 'legend'
+ });
+
+ legendContainer.append('rect')
+ .attr({
+ transform: d => `translate(10, 80)`,
+ width: 100,
+ height: 100
+ });
+
+ // service
+ const service = legendContainer.append('g')
+ .attr({
+ class: 'node service'
+ });
+
+ service.append('circle')
+ .attr({
+ r: serviceTopologyConfig.circle.radius,
+ transform: d => `translate(30, 100)`
+ });
+
+ service.append('text')
+ .attr({
+ transform: d => `translate(45, 100)`,
+ dy: '.35em'
+ })
+ .text('Service')
+ .style('fill-opacity', 1);
+
+ // slice
+ const slice = legendContainer.append('g')
+ .attr({
+ class: 'node slice'
+ });
+
+ slice.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ transform: d => `translate(30, 130)`
+ });
+
+ slice.append('text')
+ .attr({
+ transform: d => `translate(45, 130)`,
+ dy: '.35em'
+ })
+ .text('Slices')
+ .style('fill-opacity', 1);
+
+ // instance
+ const instance = legendContainer.append('g')
+ .attr({
+ class: 'node instance'
+ });
+
+ instance.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ transform: d => `translate(30, 160)`
+ });
+
+ instance.append('text')
+ .attr({
+ transform: d => `translate(45, 160)`,
+ dy: '.35em'
+ })
+ .text('Instances')
+ .style('fill-opacity', 1);
+ };
+
+ var _svg, _layout, _source;
+
+ var i = 0;
+
+ // given a canvas, a layout and a data source, draw a tree layout
+ const updateTree = (svg, layout, source) => {
+
+ //cache data
+ _svg = svg;
+ _layout = layout;
+ _source = source;
+
+ const maxDepth = ServiceRelation.depthOf(source);
+
+ const diagonal = d3.svg.diagonal()
+ .projection(d => [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
+ const step = (($window.innerWidth - (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: d => `node ${d.type}`,
+ transform: `translate(${source.y0}, ${source.x0})`
+ });
+
+ const subscriberNodes = nodeEnter.filter('.subscriber');
+ const internetNodes = nodeEnter.filter('.internet');
+ const serviceNodes = nodeEnter.filter('.service');
+ const instanceNodes = nodeEnter.filter('.instance');
+ const sliceNodes = nodeEnter.filter('.slice');
+
+ subscriberNodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ internetNodes.append('rect')
+ .attr(serviceTopologyConfig.square);
+
+ serviceNodes.append('circle')
+ .attr('r', 1e-6)
+ .style('fill', d => d._children ? 'lightsteelblue' : '#fff')
+ .on('click', serviceClick);
+
+ sliceNodes.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10
+ });
+
+ instanceNodes.append('rect')
+ .attr({
+ width: 20,
+ height: 20,
+ x: -10,
+ y: -10,
+ class: d => d.active ?'' : 'active'
+ });
+
+ nodeEnter.append('text')
+ .attr({
+ x: d => d.children ? -serviceTopologyConfig.circle.selectedRadius -3 : serviceTopologyConfig.circle.selectedRadius + 3,
+ dy: '.35em',
+ transform: d => {
+ if (d.children && d.parent){
+ if(d.parent.x < d.x){
+ return 'rotate(-30)';
+ }
+ return 'rotate(30)';
+ }
+ },
+ 'text-anchor': d => d.children ? 'end' : 'start'
+ })
+ .text(d => d.name)
+ .style('fill-opacity', 1e-6);
+
+ // Transition nodes to their new position.
+ var nodeUpdate = node.transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr({
+ 'transform': d => `translate(${d.y},${d.x})`
+ });
+
+ nodeUpdate.select('circle')
+ .attr('r', d => d.selected ? serviceTopologyConfig.circle.selectedRadius : serviceTopologyConfig.circle.radius)
+ .style('fill', d => 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', d => `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;
+ });
+ };
+
+ const serviceClick = function(d) {
+
+ $log.info('TODO emit an event to highlight VMs');
+
+ if(!d.service){
+ return;
+ }
+
+ // toggling selected status
+ d.selected = !d.selected;
+
+ var selectedNode = d3.select(this);
+
+ selectedNode
+ .transition()
+ .duration(serviceTopologyConfig.duration)
+ .attr('r', serviceTopologyConfig.circle.selectedRadius);
+ };
+
+ this.updateTree = updateTree;
+ this.drawLegend = drawLegend;
+ });
+
+}());
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html b/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html
index 6a13d57..5b8ce78 100644
--- a/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html
+++ b/views/ngXosViews/diagnostic/src/templates/diagnostic.tpl.html
@@ -3,16 +3,16 @@
<service-topology service-chain="vm.serviceChain"></service-topology>
</div>
<div class="half-height">
- <div class="row col-sm-3">
- <div class="panel panel-default">
+ <div class="row col-sm-offset-9 col-sm-3">
+ <div class="panel panel-primary">
<div class="panel-heading">Select a subscriber:</div>
<div class="panel-body">
- <select class="form-control" ng-options="s as s.name for s in vm.subscribers" ng-model="vm.selectedSubscriber" ng-change="vm.getServiceChain(vm.selectedSubscriber)">
+ <select class="form-control" ng-options="s as s.name for s in vm.subscribers" ng-model="vm.selectedSubscriber">
<option value="">Select a subscriber...</option>
</select>
</div>
</div>
- <logic-topology></logic-topology>
</div>
+ <logic-topology ng-if="vm.subscribers" subscribers="vm.subscribers" selected="vm.selectedSubscriber"></logic-topology>
</div>
</div>
\ No newline at end of file