Added Instance stats to d3 and drawing container
diff --git a/views/ngXosViews/diagnostic/mocks/data/instances.json b/views/ngXosViews/diagnostic/mocks/data/instances.json
index a60b696..ec0f598 100644
--- a/views/ngXosViews/diagnostic/mocks/data/instances.json
+++ b/views/ngXosViews/diagnostic/mocks/data/instances.json
@@ -261,7 +261,7 @@
"enacted": "2016-02-17T22:05:44.887Z",
"policed": "2016-02-17T22:05:45.538Z",
"backend_register": "{\"next_run\": 0, \"last_success\": 1455746744.887768, \"exponent\": 0}",
- "backend_status": "1 - OK",
+ "backend_status": "0 - Provisioning in progress",
"deleted": false,
"write_protect": false,
"lazy_blocked": false,
@@ -285,5 +285,38 @@
7,
2
]
+ },
+ {
+ "humanReadableName": "mysite_vsg-3",
+ "id": 12,
+ "created": "2016-02-17T22:00:58.015Z",
+ "updated": "2016-02-17T22:05:44.877Z",
+ "enacted": "2016-02-17T22:05:44.887Z",
+ "policed": "2016-02-17T22:05:45.538Z",
+ "backend_register": "{\"next_run\": 0, \"last_success\": 1455746744.887768, \"exponent\": 0}",
+ "backend_status": "2 - Error",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "instance_id": "instance-00000004",
+ "instance_uuid": "075a3ae4-9e76-4198-8e6b-67c67b996746",
+ "name": "mysite_vsg",
+ "instance_name": "mysite_vsg-3",
+ "ip": "130.127.133.90",
+ "image": 1,
+ "creator": 1,
+ "slice": 1,
+ "deployment": 1,
+ "node": 3,
+ "numberCores": 0,
+ "flavor": 1,
+ "userData": null,
+ "networks": [
+ 1,
+ 6,
+ 7,
+ 2
+ ]
}
]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/mocks/data/tenants.json b/views/ngXosViews/diagnostic/mocks/data/tenants.json
index 77bea93..5d6fb15 100644
--- a/views/ngXosViews/diagnostic/mocks/data/tenants.json
+++ b/views/ngXosViews/diagnostic/mocks/data/tenants.json
@@ -206,5 +206,28 @@
"service_specific_id": null,
"service_specific_attribute": "{\"instance_id\": 11, \"creator_id\": 1}",
"connect_method": "na"
+ },
+ {
+ "humanReadableName": "vCPE-tenant-5",
+ "id": 14,
+ "created": "2016-02-17T19:36:04.650Z",
+ "updated": "2016-02-17T20:55:18.115Z",
+ "enacted": null,
+ "policed": null,
+ "backend_register": "{\"next_run\": 1455771318.072057, \"last_failure\": 1455742518.072061, \"last_success\": 1455737797.006782, \"exponent\": 871, \"failures\": 871}",
+ "backend_status": "2 - Exception('defer object vCPE-tenant-4 due to waiting on instance.instance_name',)",
+ "deleted": false,
+ "write_protect": false,
+ "lazy_blocked": false,
+ "no_sync": false,
+ "kind": "vCPE",
+ "provider_service": 2,
+ "subscriber_service": null,
+ "subscriber_tenant": 3,
+ "subscriber_user": null,
+ "subscriber_root": null,
+ "service_specific_id": null,
+ "service_specific_attribute": "{\"instance_id\": 12, \"creator_id\": 1}",
+ "connect_method": "na"
}
]
\ No newline at end of file
diff --git a/views/ngXosViews/diagnostic/src/css/serviceTopology.css b/views/ngXosViews/diagnostic/src/css/serviceTopology.css
index 4b88ccb..9d9aa60 100644
--- a/views/ngXosViews/diagnostic/src/css/serviceTopology.css
+++ b/views/ngXosViews/diagnostic/src/css/serviceTopology.css
@@ -105,6 +105,40 @@
stroke-width: 1px;
}
+logic-topology .node .instance.active.good > rect{
+ fill: green;
+}
+
+logic-topology .node .instance.active.provisioning > rect{
+ fill: yellow;
+}
+
+logic-topology .node .instance.active.bad > rect{
+ fill: red;
+}
+
+/* INSTANCE STATS */
+
+logic-topology .node .instance .stats-container rect {
+ fill: white;
+}
+
+logic-topology .node .instance .stats-container text.name{
+ font-weight: bold;
+}
+
+logic-topology .node .instance .stats-container text.ip{
+ font-style: italic;
+ font-size: 10px;
+}
+
+/* CONTAINERS */
+logic-topology .node .container rect{
+ fill: #eee;
+ stroke: steelblue;
+ stroke-width: 1px;
+}
+
/* LEGEND */
.legend {
diff --git a/views/ngXosViews/diagnostic/src/js/chart_data_service.js b/views/ngXosViews/diagnostic/src/js/chart_data_service.js
index 4c53a03..1b0fae8 100644
--- a/views/ngXosViews/diagnostic/src/js/chart_data_service.js
+++ b/views/ngXosViews/diagnostic/src/js/chart_data_service.js
@@ -54,7 +54,7 @@
this.logicTopologyData.children[0].children[0].children[0].subscriberTag = {
cTag: tags.c_tag,
sTag: tags.s_tag
- }
+ };
};
/**
@@ -69,8 +69,15 @@
};
this.getSubscriberTag = () => {
-
- this.addSubscriberTag(JSON.parse(this.currentServiceChain.children[0].tenant.service_specific_attribute));
+ const tags = JSON.parse(this.currentServiceChain.children[0].tenant.service_specific_attribute);
+ delete tags.creator_id;
+
+ this.addSubscriberTag(tags);
+ // add tags info to current subscriber
+ this.currentSubscriber.tags = {
+ cTag: tags.c_tag,
+ sTag: tags.s_tag
+ };
};
@@ -107,7 +114,10 @@
computeNodes.map((node) => {
node.instances.map((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;
});
@@ -119,18 +129,45 @@
this.getInstanceStatus = (service) => {
const deferred = $q.defer();
- // NOTE consider if subscriber is selected or not,
- // if not select instances
- // else select containers (and follow subscriber chain to find the correct instance)
-
let p;
+ // subscriber specific
if(this.currentSubscriber){
- let instances = [JSON.parse(service.tenant.service_specific_attribute).instance_id];
- p = Ceilometer.getInstancesStats(instances);
+
+ let attr;
+ try {
+ attr = JSON.parse(service.tenant.service_specific_attribute);
+ }
+ catch(e){
+ attr = null;
+ }
+
+ // if no instances are associated to the container
+ if(!attr || !attr.instance_id){
+ let d = $q.defer();
+ d.resolve([]);
+ p = d.promise;
+ }
+ else{
+ let instances = [attr.instance_id];
+ p = Ceilometer.getInstancesStats(instances)
+ .then((instances) => {
+ instances.map(i => {
+ i.container = {
+ name: `vcpe-${this.currentSubscriber.tags.sTag}-${this.currentSubscriber.tags.cTag}`
+ };
+ return i;
+ });
+
+ // TODO fetch container stats
+
+ return instances;
+ });
+ }
}
+ // global scope
else {
- let param = {
+ const param = {
'service_vsg': {kind: 'vCPE'},
'service_vbng': {kind: 'vBNG'},
'service_volt': {kind: 'vOLT'}
diff --git a/views/ngXosViews/diagnostic/src/js/logicTopology.js b/views/ngXosViews/diagnostic/src/js/logicTopology.js
index 8e7e741..f6798c6 100644
--- a/views/ngXosViews/diagnostic/src/js/logicTopology.js
+++ b/views/ngXosViews/diagnostic/src/js/logicTopology.js
@@ -50,17 +50,17 @@
$rootScope.$on('instance.detail', (evt, service) => {
ChartData.getInstanceStatus(service)
.then((instances) => {
- this.hideInstanceStats = false;
- // HACK if array is empty wait for animation
- if(instances.length === 0){
- this.hideInstanceStats = true;
- $timeout(() => {
- this.selectedInstances = instances;
- }, 500);
- }
- else{
- this.selectedInstances = instances;
- }
+ // this.hideInstanceStats = false;
+ // // HACK if array is empty wait for animation
+ // if(instances.length === 0){
+ // this.hideInstanceStats = true;
+ // $timeout(() => {
+ // this.selectedInstances = instances;
+ // }, 500);
+ // }
+ // else{
+ // this.selectedInstances = instances;
+ // }
LogicTopologyHelper.updateTree(svg);
})
})
diff --git a/views/ngXosViews/diagnostic/src/js/nodeDrawer.js b/views/ngXosViews/diagnostic/src/js/nodeDrawer.js
index 3007fd2..0249b3d 100644
--- a/views/ngXosViews/diagnostic/src/js/nodeDrawer.js
+++ b/views/ngXosViews/diagnostic/src/js/nodeDrawer.js
@@ -9,7 +9,7 @@
var instanceId = 0;
angular.module('xos.serviceTopology')
- .service('NodeDrawer', function(d3, serviceTopologyConfig, RackHelper){
+ .service('NodeDrawer', function(d3, serviceTopologyConfig, RackHelper, lodash){
var _this = this;
@@ -64,6 +64,9 @@
nodes.each(d => {
let [w, h] = RackHelper.getRackSize(d.computeNodes);
+ // TODO update instead of delete and redraw
+ nodes.select('g').remove();
+
let rack = nodes
.append('g');
@@ -74,7 +77,7 @@
.transition()
.duration(serviceTopologyConfig.duration)
.attr({
- transform: d => `translate(${- (w / 2)}, ${- (h / 2)})`
+ transform: () => `translate(${- (w / 2)}, ${- (h / 2)})`
});
rack
@@ -177,11 +180,120 @@
return name
.replace('app_', '')
.replace('service_', '')
- .replace('ovs_', '')
+ // .replace('ovs_', '')
.replace('mysite_', '')
.replace('_instance', '');
};
+ const 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 '';
+ }
+ };
+
+ const showInstanceStats = (container, instance) => {
+
+ // NOTE this should be dinamically positioned
+ // base on the number of element present
+ const statsContainer = container.append('g')
+ .attr({
+ transform: `translate(200, -32)`,
+ class: 'stats-container'
+ });
+
+
+ statsContainer.append('line')
+ .attr({
+ x1: -120,
+ y1: 50,
+ x2: 0,
+ y2: 50,
+ stroke: 'black'
+ })
+
+ // NOTE rect should be dinamically sized base on the presence of a container
+ let statsHeight = 110;
+ let statsWidth = 200;
+
+ if (instance.container){
+ statsHeight += serviceTopologyConfig.container.height + (serviceTopologyConfig.container.margin * 2)
+ }
+
+ statsContainer.append('rect')
+ .attr({
+ width: statsWidth,
+ height: statsHeight
+ });
+
+ // add instance info
+ statsContainer.append('text')
+ .attr({
+ y: 15,
+ x: serviceTopologyConfig.instance.margin,
+ class: 'name'
+ })
+ .text(instance.humanReadableName)
+
+ statsContainer.append('text')
+ .attr({
+ y: 30,
+ x: serviceTopologyConfig.instance.margin,
+ class: 'ip'
+ })
+ .text(instance.ip)
+
+ // add stats
+ const interestingMeters = ['memory', 'memory.usage', 'cpu', 'vcpus'];
+
+ interestingMeters.forEach((m, i) => {
+ const meter = lodash.find(instance.stats, {meter: m});
+ statsContainer.append('text')
+ .attr({
+ y: 55 + (i * 15),
+ x: serviceTopologyConfig.instance.margin
+ })
+ .text(`${meter.description}: ${meter.value} ${meter.unit}`);
+ });
+
+ if(instance.container){
+ // draw container
+
+ const containerBox = statsContainer.append('g')
+ .attr({
+ class: 'container',
+ transform: `translate(${serviceTopologyConfig.instance.margin}, 115)`
+ });
+
+ containerBox.append('rect')
+ .attr({
+ width: statsWidth - (serviceTopologyConfig.container.margin * 2),
+ height: serviceTopologyConfig.container.height,
+ });
+
+ containerBox.append('text')
+ .attr({
+ y: 20,
+ x: (statsWidth - (serviceTopologyConfig.container.margin * 2)) / 2,
+ 'text-anchor': 'middle'
+ })
+ .text(instance.container.name)
+ }
+
+ };
+
this.drawInstances = (container, instances) => {
// TODO check for stats field in instance and draw popup
@@ -196,7 +308,7 @@
instanceContainer
.attr({
transform: `translate(${width / 2}, ${ height / 2})`,
- class: d => `instance ${d.selected ? 'active' : ''}`,
+ class: d => `instance ${d.selected ? 'active' : ''} ${getInstanceStatusColor(d)}`,
})
.transition()
.duration(serviceTopologyConfig.duration)
@@ -223,16 +335,27 @@
x: 40, //FIXME
opacity: 0
})
- .text(d => formatInstanceName(d.name))
+ .text(d => formatInstanceName(d.humanReadableName))
.transition()
.duration(serviceTopologyConfig.duration)
.attr({
opacity: 1
});
+ // if stats are attached and instance is active,
+ // draw stats
+ instanceContainer.each(function(instance){
+
+ const container = d3.select(this);
+
+ if(angular.isDefined(instance.stats) && instance.selected){
+ showInstanceStats(container, instance);
+ }
+ });
+
instanceContainer
- .on('click', d => {
- console.log(d);
+ .on('click', function(d){
+ console.log(`Draw vignette with stats for instance: ${d.name}`);
});
};
diff --git a/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
index 518c41a..d47bf24 100644
--- a/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
+++ b/views/ngXosViews/diagnostic/src/js/serviceTopologyHelper.js
@@ -4,6 +4,7 @@
angular.module('xos.serviceTopology')
.service('ServiceTopologyHelper', function($rootScope, $window, $log, lodash, ServiceRelation, serviceTopologyConfig, d3){
+ // NOTE not used anymore
const drawLegend = (svg) => {
const legendContainer = svg.append('g')
.attr({
@@ -122,7 +123,7 @@
class: d => {
return `node ${d.type}`
},
- transform: `translate(${source.y0}, ${source.x0})`
+ transform: d => (d.x && d.y) ? `translate(${d.y}, ${d.x})` : `translate(${source.y0}, ${source.x0})`
});
const subscriberNodes = nodeEnter.filter('.subscriber');
diff --git a/views/ngXosViews/diagnostic/src/templates/subscriber-modal.tpl.html b/views/ngXosViews/diagnostic/src/templates/subscriber-modal.tpl.html
index 9c005a3..7672a29 100644
--- a/views/ngXosViews/diagnostic/src/templates/subscriber-modal.tpl.html
+++ b/views/ngXosViews/diagnostic/src/templates/subscriber-modal.tpl.html
@@ -1,5 +1,5 @@
<div class="modal fade" ng-class="{in: vm.open}" tabindex="-1" role="dialog">
- <div class="modal-dialog">
+ <div class="modal-dialog modal-sm">
<div class="modal-content">
<div class="modal-header">
<button ng-click="vm.close()" type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">×</span></button>