blob: c35d874259fa42ea8158294455023da44cd2d270 [file] [log] [blame]
Matteo Scandoloe3de73d2015-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 Scandoloc2d31102015-12-08 14:35:55 -08009 'ngAnimate',
Matteo Scandolo0f5e1632015-12-09 16:09:59 -080010 'chart.js',
11 'ui.bootstrap.accordion'
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080012])
Matteo Scandoloc2d31102015-12-08 14:35:55 -080013.config(($stateProvider, $urlRouterProvider) => {
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080014 $stateProvider
15 .state('ceilometerDashboard', {
16 url: '/',
17 template: '<ceilometer-dashboard></ceilometer-dashboard>'
Matteo Scandoloec8ad422015-12-04 15:55:20 -080018 })
19 .state('samples', {
20 url: '/:name/:tenant/samples',
21 template: '<ceilometer-samples></ceilometer-samples>'
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080022 });
Matteo Scandoloc2d31102015-12-08 14:35:55 -080023 $urlRouterProvider.otherwise('/');
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080024})
25.config(function($httpProvider){
26 $httpProvider.interceptors.push('NoHyperlinks');
27})
Matteo Scandoloc2d31102015-12-08 14:35:55 -080028.run(function($rootScope){
29 $rootScope.stateName = 'ceilometerDashboard';
30 $rootScope.$on('$stateChangeStart', (event, toState) => {
Matteo Scandoloc2d31102015-12-08 14:35:55 -080031 $rootScope.stateName = toState.name;
32 })
33})
Matteo Scandolo5dd94182015-12-09 17:09:55 -080034.service('Ceilometer', function($http, $q, lodash){
Matteo Scandolod3e696d2015-12-08 15:24:23 -080035
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080036 this.getMeters = () => {
37 let deferred = $q.defer();
38
Matteo Scandolo5dd94182015-12-09 17:09:55 -080039 $http.get('/xoslib/meters/', {cache: true})
40 // $http.get('../meters_mock.json', {cache: true})
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080041 .then((res) => {
Matteo Scandoloec8ad422015-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 Scandolo6c788432015-12-07 17:32:39 -080054 $http.get(`/xoslib/metersamples/`, {params: {meter: name, tenant: tenant}})
Matteo Scandoloec8ad422015-12-04 15:55:20 -080055 .then((res) => {
56 deferred.resolve(res.data)
Matteo Scandoloe3de73d2015-12-04 10:14:40 -080057 })
58 .catch((e) => {
59 deferred.reject(e);
60 });
61
62 return deferred.promise;
63 }
Matteo Scandolo5dd94182015-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 Scandolod9e1c412015-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 Scandoloe3de73d2015-12-04 10:14:40 -080084})
Matteo Scandoloec8ad422015-12-04 15:55:20 -080085.directive('ceilometerDashboard', function(lodash){
Matteo Scandoloe3de73d2015-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 Scandolod9e1c412015-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 Scandoloe3de73d2015-12-04 10:14:40 -0800118 this.loadMeters = () => {
119 this.loader = true;
120
Matteo Scandolo48d890f2015-12-14 17:36:09 -0800121 // TODO rename projects in meters
Matteo Scandoloe3de73d2015-12-04 10:14:40 -0800122 Ceilometer.getMeters()
123 .then(meters => {
Matteo Scandolo30c23c72015-12-09 15:23:51 -0800124 //group project by service
125 this.projects = lodash.groupBy(meters, 'service');
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800126 lodash.forEach(Object.keys(this.projects), (project) => {
Matteo Scandolo30c23c72015-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 Scandoloa918ea82015-12-14 14:26:20 -0800131 this.projects[project][slice] = lodash.groupBy(this.projects[project][slice], 'resource_name');
Matteo Scandolo30c23c72015-12-09 15:23:51 -0800132 });
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800133 });
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800134
135 // open selected panels
136 this.openPanels();
Matteo Scandoloe3de73d2015-12-04 10:14:40 -0800137 })
138 .catch(err => {
Matteo Scandoloa918ea82015-12-14 14:26:20 -0800139 this.error = err.data.detail;
Matteo Scandoloe3de73d2015-12-04 10:14:40 -0800140 })
141 .finally(() => {
142 this.loader = false;
143 });
144 }
145
146 this.loadMeters();
147
Matteo Scandoloc2d31102015-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 Scandolod9e1c412015-12-15 14:37:27 -0800155 this.selectResources = (resources, slice, service) => {
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800156 //cleaning
157 this.selectedResources = null;
158 this.selectedResource = null;
159 this.selectedMeters = null;
160
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800161 // hold the resource list for the current slice
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800162 this.selectedResources = resources;
163 this.selectedSlice = slice;
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800164 this.selectedService = service;
165
166 // store the status
167 Ceilometer.selectedSlice = slice;
168 Ceilometer.selectedService = service;
Matteo Scandoloc2d31102015-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 Scandolod9e1c412015-12-15 14:37:27 -0800180
181 Ceilometer.selectedResource = resource;
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800182 this.selectedResource = resource;
183 }
184
Matteo Scandoloe3de73d2015-12-04 10:14:40 -0800185 }
186 };
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800187})
188.directive('ceilometerSamples', function(lodash, $stateParams){
189 return {
190 restrict: 'E',
Matteo Scandolo10e61732016-02-02 16:04:31 -0800191 scope: {},
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800192 bindToController: true,
193 controllerAs: 'vm',
194 templateUrl: 'templates/ceilometer-samples.tpl.html',
195 controller: function(Ceilometer) {
196
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800197 // console.log(Ceilometer.selectResource);
198
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800199 this.chartColors = [
Matteo Scandolod3e696d2015-12-08 15:24:23 -0800200 '#286090',
201 '#F7464A',
202 '#46BFBD',
203 '#FDB45C',
204 '#97BBCD',
205 '#4D5360',
206 '#8c4f9f'
207 ];
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800208
209 this.chart = {
210 series: [],
211 labels: [],
212 data: []
213 }
214
215 Chart.defaults.global.colours = this.chartColors;
Matteo Scandolod3e696d2015-12-08 15:24:23 -0800216
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800217 this.chartType = 'line';
218
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800219 if($stateParams.name && $stateParams.tenant){
220 this.name = $stateParams.name;
221 this.tenant = $stateParams.tenant;
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800222 // TODO rename tenant in project_id
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800223 }
Matteo Scandolo48d890f2015-12-14 17:36:09 -0800224 else{
225 throw new Error('Missing Name and Tenant Params!');
226 }
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800227
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800228 /**
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800229 * Goes trough the array and format date to be used as labels
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800230 *
231 * @param Array data
232 * @returns Array a list of labels
233 */
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800234
Matteo Scandolo52193d32015-12-05 18:44:45 -0800235 this.getLabels = (data) => {
236 return data.reduce((list, item) => {
237 let date = new Date(item.timestamp);
238 list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
239 return list;
240 }, []);
241 };
242
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800243 /**
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800244 * Goes trough the array and return a flat array of values
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800245 *
246 * @param Array data
247 * @returns Array a list of values
248 */
249
Matteo Scandolo52193d32015-12-05 18:44:45 -0800250 this.getData = (data) => {
251 return data.reduce((list, item) => {
252 list.push(item.volume);
253 return list;
254 }, []);
255 }
256
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800257 /**
258 * Add a samples to the chart
259 *
260 * @param string resource_id
261 */
262 this.chartMeters = [];
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800263 this.addMeterToChart = (project_id) => {
264 this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[project_id], 'timestamp'));
265 this.chart['series'].push(project_id);
266 this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[project_id], 'timestamp')));
267 this.chartMeters.push(this.samplesList[project_id][0]); //use the 0 as are all samples for the same resource and I need the name
268 lodash.remove(this.sampleLabels, {id: project_id});
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800269 }
270
Matteo Scandoloedea1db2015-12-14 14:42:28 -0800271 this.removeFromChart = (meter) => {
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800272 this.chart.data.splice(this.chart.series.indexOf(meter.project_id), 1);
273 this.chart.series.splice(this.chart.series.indexOf(meter.project_id), 1);
274 this.chartMeters.splice(lodash.findIndex(this.chartMeters, {project_id: meter.project_id}), 1);
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800275 this.sampleLabels.push({
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800276 id: meter.project_id,
277 name: meter.resource_name || meter.project_id
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800278 })
279 };
280
281 /**
282 * Format samples to create a list of labels and ids
283 */
284
285 this.formatSamplesLabels = (samples) => {
286
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800287 return lodash.uniq(samples, 'project_id')
Matteo Scandolo9e1bc292015-12-15 08:16:56 -0800288 .reduce((labels, item) => {
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800289 labels.push({
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800290 id: item.project_id,
291 name: item.resource_name || item.project_id
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800292 });
293 return labels;
Matteo Scandolo9e1bc292015-12-15 08:16:56 -0800294 }, []);
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800295 }
296
297
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800298 /**
299 * Load the samples and format data
300 */
301
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800302 this.showSamples = () => {
303 this.loader = true;
Matteo Scandoloc2d31102015-12-08 14:35:55 -0800304 // Ceilometer.getSamples(this.name, this.tenant) //fetch one
305 Ceilometer.getSamples(this.name) //fetch all
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800306 .then(res => {
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800307
308 // setup data for visualization
Matteo Scandolod9e1c412015-12-15 14:37:27 -0800309 this.samplesList = lodash.groupBy(res, 'project_id');
Matteo Scandoloc6a78982015-12-08 16:39:57 -0800310 this.sampleLabels = this.formatSamplesLabels(res);
311
312 // add current meter to chart
313 this.addMeterToChart(this.tenant);
314
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800315 })
316 .catch(err => {
Matteo Scandolo30c23c72015-12-09 15:23:51 -0800317 this.error = err.data.detail;
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800318 })
319 .finally(() => {
320 this.loader = false;
321 });
322 };
323
324 this.showSamples();
Matteo Scandoloec8ad422015-12-04 15:55:20 -0800325 }
326 }
Matteo Scandolo5dd94182015-12-09 17:09:55 -0800327})
328.directive('ceilometerStats', function(){
329 return {
330 restrict: 'E',
331 scope: {
332 name: '=name',
333 },
334 bindToController: true,
335 controllerAs: 'vm',
336 templateUrl: 'templates/ceilometer-stats.tpl.html',
337 controller: function($scope, Ceilometer) {
338 this.getStats = () => {
339 this.loader = true;
340 Ceilometer.getStats(this.name)
341 .then(res => {
342 this.stats = res;
343 })
344 .catch(err => {
345 this.error = err.data;
346 })
347 .finally(() => {
348 this.loader = false;
349 });
350 };
351
352 this.getStats();
353
354 $scope.$watch(() => this.name, () => {this.getStats();});
355 }
356 }
Matteo Scandolo76b4e982015-12-10 10:15:20 -0800357})
358.filter('orderObjectByKey', function(lodash) {
Matteo Scandoloa918ea82015-12-14 14:26:20 -0800359 return function(items) {
Matteo Scandolo76b4e982015-12-10 10:15:20 -0800360
361 if(!items){
362 return;
363 }
364
365 return lodash.reduce(Object.keys(items).reverse(), (list, key) => {
366 list[key] = items[key];
367 return list;
368 }, {});
369
370 };
371});
372;