blob: a20d563abcaf51dedc11dd6a70b78b4d072bb049 [file] [log] [blame]
Matteo Scandolo68c2e722015-12-04 10:14:40 -08001'use strict';
2
3angular.module('xos.ceilometerDashboard', [
4 'ngResource',
5 'ngCookies',
6 'ngLodash',
7 'ui.router',
8 'xos.helpers',
Matteo Scandolo68856082015-12-08 14:35:55 -08009 'ngAnimate',
Matteo Scandoloc0582112015-12-09 16:09:59 -080010 'chart.js',
11 'ui.bootstrap.accordion'
Matteo Scandolo68c2e722015-12-04 10:14:40 -080012])
Matteo Scandolo68856082015-12-08 14:35:55 -080013.config(($stateProvider, $urlRouterProvider) => {
Matteo Scandolo68c2e722015-12-04 10:14:40 -080014 $stateProvider
15 .state('ceilometerDashboard', {
16 url: '/',
17 template: '<ceilometer-dashboard></ceilometer-dashboard>'
Matteo Scandolo7b80d842015-12-04 15:55:20 -080018 })
19 .state('samples', {
20 url: '/:name/:tenant/samples',
21 template: '<ceilometer-samples></ceilometer-samples>'
Matteo Scandolo68c2e722015-12-04 10:14:40 -080022 });
Matteo Scandolo68856082015-12-08 14:35:55 -080023 $urlRouterProvider.otherwise('/');
Matteo Scandolo68c2e722015-12-04 10:14:40 -080024})
25.config(function($httpProvider){
26 $httpProvider.interceptors.push('NoHyperlinks');
27})
Matteo Scandolo68856082015-12-08 14:35:55 -080028.run(function($rootScope){
29 $rootScope.stateName = 'ceilometerDashboard';
30 $rootScope.$on('$stateChangeStart', (event, toState) => {
Matteo Scandolo68856082015-12-08 14:35:55 -080031 $rootScope.stateName = toState.name;
32 })
33})
Matteo Scandolo41f5c152015-12-09 17:09:55 -080034.service('Ceilometer', function($http, $q, lodash){
Matteo Scandolo2b6583e2015-12-08 15:24:23 -080035
Matteo Scandolo68c2e722015-12-04 10:14:40 -080036 this.getMeters = () => {
37 let deferred = $q.defer();
38
Matteo Scandolo41f5c152015-12-09 17:09:55 -080039 $http.get('/xoslib/meters/', {cache: true})
40 // $http.get('../meters_mock.json', {cache: true})
Matteo Scandolo68c2e722015-12-04 10:14:40 -080041 .then((res) => {
Matteo Scandolo7b80d842015-12-04 15:55:20 -080042 deferred.resolve(res.data)
43 })
44 .catch((e) => {
45 deferred.reject(e);
46 });
47
48 return deferred.promise;
49 }
50
51 this.getSamples = (name, tenant) => {
52 let deferred = $q.defer();
53
Matteo Scandoloac812bd2015-12-07 17:32:39 -080054 $http.get(`/xoslib/metersamples/`, {params: {meter: name, tenant: tenant}})
Matteo Scandolo7b80d842015-12-04 15:55:20 -080055 .then((res) => {
56 deferred.resolve(res.data)
Matteo Scandolo68c2e722015-12-04 10:14:40 -080057 })
58 .catch((e) => {
59 deferred.reject(e);
60 });
61
62 return deferred.promise;
63 }
Matteo Scandolo41f5c152015-12-09 17:09:55 -080064
65 this.getStats = (sliceName) => {
66 let deferred = $q.defer();
67
68 $http.get('/xoslib/meterstatistics/', {cache: true})
69 // $http.get('../stats_mock.son', {cache: true})
70 .then((res) => {
71 deferred.resolve(lodash.filter(res.data, {slice: sliceName}))
72 })
73 .catch((e) => {
74 deferred.reject(e);
75 });
76
77 return deferred.promise;
78 };
Matteo Scandolo6c6b9282015-12-15 14:37:27 -080079
80 // hold dashboard status (opened service, slice, resource)
81 this.selectedService = null;
82 this.selectedSlice = null;
83 this.selectedResource = null;
Matteo Scandolo68c2e722015-12-04 10:14:40 -080084})
Matteo Scandolo7b80d842015-12-04 15:55:20 -080085.directive('ceilometerDashboard', function(lodash){
Matteo Scandolo68c2e722015-12-04 10:14:40 -080086 return {
87 restrict: 'E',
88 scope: {},
89 bindToController: true,
90 controllerAs: 'vm',
91 templateUrl: 'templates/ceilometer-dashboard.tpl.html',
92 controller: function(Ceilometer){
93
Matteo Scandolo6c6b9282015-12-15 14:37:27 -080094 console.log(Ceilometer.selectedService, Ceilometer.selectedSlice, Ceilometer.selectedResource);
95
96 // this open the accordion
97 this.accordion = {
98 open: {}
99 }
100
101 /**
102 * Open the active panel base on the service stored values
103 */
104 this.openPanels = () => {
105 if(Ceilometer.selectedService){
106 this.accordion.open[Ceilometer.selectedService] = true;
107 if(Ceilometer.selectedSlice){
108 this.selectedSlice = Ceilometer.selectedSlice;
109 this.selectedResources = this.projects[Ceilometer.selectedService][Ceilometer.selectedSlice]
110 if(Ceilometer.selectedResource){
111 this.selectedResource = Ceilometer.selectedResource;
112 this.selectedMeters = this.selectedResources[Ceilometer.selectedResource];
113 }
114 }
115 }
116 }
117
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800118 this.loadMeters = () => {
119 this.loader = true;
120
Matteo Scandolo1b9ffac2015-12-14 17:36:09 -0800121 // TODO rename projects in meters
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800122 Ceilometer.getMeters()
123 .then(meters => {
Matteo Scandoloc8f18e62015-12-09 15:23:51 -0800124 //group project by service
125 this.projects = lodash.groupBy(meters, 'service');
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800126 lodash.forEach(Object.keys(this.projects), (project) => {
Matteo Scandoloc8f18e62015-12-09 15:23:51 -0800127 // inside each service group by slice
128 this.projects[project] = lodash.groupBy(this.projects[project], 'slice');
129 lodash.forEach(Object.keys(this.projects[project]), (slice) => {
130 // inside each service => slice group by resource
Matteo Scandolo83869332015-12-14 14:26:20 -0800131 this.projects[project][slice] = lodash.groupBy(this.projects[project][slice], 'resource_name');
Matteo Scandoloc8f18e62015-12-09 15:23:51 -0800132 });
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800133 });
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800134
135 // open selected panels
136 this.openPanels();
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800137 })
138 .catch(err => {
Matteo Scandolo83869332015-12-14 14:26:20 -0800139 this.error = err.data.detail;
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800140 })
141 .finally(() => {
142 this.loader = false;
143 });
144 }
145
146 this.loadMeters();
147
Matteo Scandolo68856082015-12-08 14:35:55 -0800148 /**
149 * Select Resources for a slice
150 *
151 * @param Array resources The list of selected resources
152 * @returns void
153 */
154 this.selectedResources = null;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800155 this.selectResources = (resources, slice, service) => {
Matteo Scandolo68856082015-12-08 14:35:55 -0800156 //cleaning
157 this.selectedResources = null;
158 this.selectedResource = null;
159 this.selectedMeters = null;
160
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800161 // hold the resource list for the current slice
Matteo Scandolo68856082015-12-08 14:35:55 -0800162 this.selectedResources = resources;
163 this.selectedSlice = slice;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800164 this.selectedService = service;
165
166 // store the status
167 Ceilometer.selectedSlice = slice;
168 Ceilometer.selectedService = service;
Matteo Scandolo68856082015-12-08 14:35:55 -0800169 }
170
171 /**
172 * Select Meters for a resource
173 *
174 * @param Array meters The list of selected resources
175 * @returns void
176 */
177 this.selectedMeters = null;
178 this.selectMeters = (meters, resource) => {
179 this.selectedMeters = meters;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800180
181 Ceilometer.selectedResource = resource;
Matteo Scandolo68856082015-12-08 14:35:55 -0800182 this.selectedResource = resource;
183 }
184
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800185 }
186 };
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800187})
188.directive('ceilometerSamples', function(lodash, $stateParams){
189 return {
190 restrict: 'E',
191 scope: {
192 name: '=name',
193 tenant: '=tenant'
194 },
195 bindToController: true,
196 controllerAs: 'vm',
197 templateUrl: 'templates/ceilometer-samples.tpl.html',
198 controller: function(Ceilometer) {
199
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800200 // console.log(Ceilometer.selectResource);
201
Matteo Scandolo324df092015-12-08 16:39:57 -0800202 this.chartColors = [
Matteo Scandolo2b6583e2015-12-08 15:24:23 -0800203 '#286090',
204 '#F7464A',
205 '#46BFBD',
206 '#FDB45C',
207 '#97BBCD',
208 '#4D5360',
209 '#8c4f9f'
210 ];
Matteo Scandolo324df092015-12-08 16:39:57 -0800211
212 this.chart = {
213 series: [],
214 labels: [],
215 data: []
216 }
217
218 Chart.defaults.global.colours = this.chartColors;
Matteo Scandolo2b6583e2015-12-08 15:24:23 -0800219
Matteo Scandolo68856082015-12-08 14:35:55 -0800220 this.chartType = 'line';
221
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800222 if($stateParams.name && $stateParams.tenant){
223 this.name = $stateParams.name;
224 this.tenant = $stateParams.tenant;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800225 // TODO rename tenant in project_id
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800226 }
Matteo Scandolo1b9ffac2015-12-14 17:36:09 -0800227 else{
228 throw new Error('Missing Name and Tenant Params!');
229 }
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800230
Matteo Scandolo68856082015-12-08 14:35:55 -0800231 /**
Matteo Scandolo324df092015-12-08 16:39:57 -0800232 * Goes trough the array and format date to be used as labels
Matteo Scandolo68856082015-12-08 14:35:55 -0800233 *
234 * @param Array data
235 * @returns Array a list of labels
236 */
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800237
Matteo Scandolo1d8627f2015-12-05 18:44:45 -0800238 this.getLabels = (data) => {
239 return data.reduce((list, item) => {
240 let date = new Date(item.timestamp);
241 list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
242 return list;
243 }, []);
244 };
245
Matteo Scandolo68856082015-12-08 14:35:55 -0800246 /**
Matteo Scandolo324df092015-12-08 16:39:57 -0800247 * Goes trough the array and return a flat array of values
Matteo Scandolo68856082015-12-08 14:35:55 -0800248 *
249 * @param Array data
250 * @returns Array a list of values
251 */
252
Matteo Scandolo1d8627f2015-12-05 18:44:45 -0800253 this.getData = (data) => {
254 return data.reduce((list, item) => {
255 list.push(item.volume);
256 return list;
257 }, []);
258 }
259
Matteo Scandolo324df092015-12-08 16:39:57 -0800260 /**
261 * Add a samples to the chart
262 *
263 * @param string resource_id
264 */
265 this.chartMeters = [];
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800266 this.addMeterToChart = (project_id) => {
267 this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[project_id], 'timestamp'));
268 this.chart['series'].push(project_id);
269 this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[project_id], 'timestamp')));
270 this.chartMeters.push(this.samplesList[project_id][0]); //use the 0 as are all samples for the same resource and I need the name
271 lodash.remove(this.sampleLabels, {id: project_id});
Matteo Scandolo68856082015-12-08 14:35:55 -0800272 }
273
Matteo Scandoloe5d5ebd2015-12-14 14:42:28 -0800274 this.removeFromChart = (meter) => {
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800275 this.chart.data.splice(this.chart.series.indexOf(meter.project_id), 1);
276 this.chart.series.splice(this.chart.series.indexOf(meter.project_id), 1);
277 this.chartMeters.splice(lodash.findIndex(this.chartMeters, {project_id: meter.project_id}), 1);
Matteo Scandolo324df092015-12-08 16:39:57 -0800278 this.sampleLabels.push({
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800279 id: meter.project_id,
280 name: meter.resource_name || meter.project_id
Matteo Scandolo324df092015-12-08 16:39:57 -0800281 })
282 };
283
284 /**
285 * Format samples to create a list of labels and ids
286 */
287
288 this.formatSamplesLabels = (samples) => {
289
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800290 return lodash.uniq(samples, 'project_id')
Matteo Scandolo5074ccd2015-12-15 08:16:56 -0800291 .reduce((labels, item) => {
Matteo Scandolo324df092015-12-08 16:39:57 -0800292 labels.push({
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800293 id: item.project_id,
294 name: item.resource_name || item.project_id
Matteo Scandolo324df092015-12-08 16:39:57 -0800295 });
296 return labels;
Matteo Scandolo5074ccd2015-12-15 08:16:56 -0800297 }, []);
Matteo Scandolo324df092015-12-08 16:39:57 -0800298 }
299
300
Matteo Scandolo68856082015-12-08 14:35:55 -0800301 /**
302 * Load the samples and format data
303 */
304
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800305 this.showSamples = () => {
306 this.loader = true;
Matteo Scandolo68856082015-12-08 14:35:55 -0800307 // Ceilometer.getSamples(this.name, this.tenant) //fetch one
308 Ceilometer.getSamples(this.name) //fetch all
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800309 .then(res => {
Matteo Scandolo324df092015-12-08 16:39:57 -0800310
311 // setup data for visualization
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800312 this.samplesList = lodash.groupBy(res, 'project_id');
Matteo Scandolo324df092015-12-08 16:39:57 -0800313 this.sampleLabels = this.formatSamplesLabels(res);
314
315 // add current meter to chart
316 this.addMeterToChart(this.tenant);
317
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800318 })
319 .catch(err => {
Matteo Scandoloc8f18e62015-12-09 15:23:51 -0800320 this.error = err.data.detail;
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800321 })
322 .finally(() => {
323 this.loader = false;
324 });
325 };
326
327 this.showSamples();
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800328 }
329 }
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800330})
331.directive('ceilometerStats', function(){
332 return {
333 restrict: 'E',
334 scope: {
335 name: '=name',
336 },
337 bindToController: true,
338 controllerAs: 'vm',
339 templateUrl: 'templates/ceilometer-stats.tpl.html',
340 controller: function($scope, Ceilometer) {
341 this.getStats = () => {
342 this.loader = true;
343 Ceilometer.getStats(this.name)
344 .then(res => {
345 this.stats = res;
346 })
347 .catch(err => {
348 this.error = err.data;
349 })
350 .finally(() => {
351 this.loader = false;
352 });
353 };
354
355 this.getStats();
356
357 $scope.$watch(() => this.name, () => {this.getStats();});
358 }
359 }
Matteo Scandolo869acc52015-12-10 10:15:20 -0800360})
361.filter('orderObjectByKey', function(lodash) {
Matteo Scandolo83869332015-12-14 14:26:20 -0800362 return function(items) {
Matteo Scandolo869acc52015-12-10 10:15:20 -0800363
364 if(!items){
365 return;
366 }
367
368 return lodash.reduce(Object.keys(items).reverse(), (list, key) => {
369 list[key] = items[key];
370 return list;
371 }, {});
372
373 };
374});
375;