blob: 0bf12db5b42c49b722524998e1bd2f39d877953b [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 Scandoloba4c9aa2016-02-11 09:35:29 -080034.service('Ceilometer', function($http, $q){
Matteo Scandolo2b6583e2015-12-08 15:24:23 -080035
Matteo Scandolo9db064f2016-02-08 14:17:42 -080036 this.getMappings = () => {
Matteo Scandolo68c2e722015-12-04 10:14:40 -080037 let deferred = $q.defer();
38
Matteo Scandolo9db064f2016-02-08 14:17:42 -080039 $http.get('/xoslib/xos-slice-service-mapping/')
40 .then((res) => {
41 deferred.resolve(res.data)
42 })
43 .catch((e) => {
44 deferred.reject(e);
45 });
46
47 return deferred.promise;
48 }
49
50 this.getMeters = (params) => {
51 let deferred = $q.defer();
52
53 $http.get('/xoslib/meters/', {cache: true, params: params})
Matteo Scandolo41f5c152015-12-09 17:09:55 -080054 // $http.get('../meters_mock.json', {cache: true})
Matteo Scandolo68c2e722015-12-04 10:14:40 -080055 .then((res) => {
Matteo Scandolo7b80d842015-12-04 15:55:20 -080056 deferred.resolve(res.data)
57 })
58 .catch((e) => {
59 deferred.reject(e);
60 });
61
62 return deferred.promise;
63 }
64
65 this.getSamples = (name, tenant) => {
66 let deferred = $q.defer();
67
Matteo Scandoloac812bd2015-12-07 17:32:39 -080068 $http.get(`/xoslib/metersamples/`, {params: {meter: name, tenant: tenant}})
Matteo Scandolo7b80d842015-12-04 15:55:20 -080069 .then((res) => {
70 deferred.resolve(res.data)
Matteo Scandolo68c2e722015-12-04 10:14:40 -080071 })
72 .catch((e) => {
73 deferred.reject(e);
74 });
75
76 return deferred.promise;
77 }
Matteo Scandolo41f5c152015-12-09 17:09:55 -080078
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -080079 this.getStats = (options) => {
Matteo Scandolo41f5c152015-12-09 17:09:55 -080080 let deferred = $q.defer();
81
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -080082 $http.get('/xoslib/meterstatistics/', {cache: true, params: options})
Matteo Scandolo41f5c152015-12-09 17:09:55 -080083 // $http.get('../stats_mock.son', {cache: true})
84 .then((res) => {
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -080085 deferred.resolve(res.data);
Matteo Scandolo41f5c152015-12-09 17:09:55 -080086 })
87 .catch((e) => {
88 deferred.reject(e);
89 });
90
91 return deferred.promise;
92 };
Matteo Scandolo6c6b9282015-12-15 14:37:27 -080093
94 // hold dashboard status (opened service, slice, resource)
95 this.selectedService = null;
96 this.selectedSlice = null;
97 this.selectedResource = null;
Matteo Scandolo68c2e722015-12-04 10:14:40 -080098})
Matteo Scandolo7b80d842015-12-04 15:55:20 -080099.directive('ceilometerDashboard', function(lodash){
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800100 return {
101 restrict: 'E',
102 scope: {},
103 bindToController: true,
104 controllerAs: 'vm',
105 templateUrl: 'templates/ceilometer-dashboard.tpl.html',
106 controller: function(Ceilometer){
107
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800108 this.showStats = false;
109
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800110 // this open the accordion
111 this.accordion = {
112 open: {}
113 }
114
115 /**
116 * Open the active panel base on the service stored values
117 */
118 this.openPanels = () => {
119 if(Ceilometer.selectedService){
120 this.accordion.open[Ceilometer.selectedService] = true;
121 if(Ceilometer.selectedSlice){
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800122 this.loadSliceMeter(Ceilometer.selectedSlice, Ceilometer.selectedService);
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800123 this.selectedSlice = Ceilometer.selectedSlice;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800124 if(Ceilometer.selectedResource){
125 this.selectedResource = Ceilometer.selectedResource;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800126 }
127 }
128 }
129 }
130
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800131 /**
132 * Load the list of service and slices
133 */
134
135 this.loadMappings = () => {
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800136 this.loader = true;
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800137 Ceilometer.getMappings()
138 .then((services) => {
139 this.services = services;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800140 this.openPanels();
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800141 })
142 .catch(err => {
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800143 this.error = (err.data && err.data.detail) ? err.data.detail : 'An Error occurred. Please try again later.';
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800144 })
145 .finally(() => {
146 this.loader = false;
147 });
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800148 };
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800149
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800150 this.loadMappings();
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800151
Matteo Scandolo68856082015-12-08 14:35:55 -0800152 /**
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800153 * Load the list of a single slice
Matteo Scandolo68856082015-12-08 14:35:55 -0800154 */
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800155
156 this.loadSliceMeter = (slice, service_name) => {
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800157
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800158 Ceilometer.selectedSlice = null;
159 Ceilometer.selectedService = null;
160 Ceilometer.selectedResources = null;
Matteo Scandolo68856082015-12-08 14:35:55 -0800161
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800162 // visualization info
163 this.loader = true;
Matteo Scandoloba4c9aa2016-02-11 09:35:29 -0800164 this.error = null;
165 this.ceilometerError = null;
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800166
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800167 Ceilometer.getMeters({tenant: slice.project_id})
168 .then((sliceMeters) => {
Matteo Scandoloba4c9aa2016-02-11 09:35:29 -0800169 this.selectedSlice = slice.slice;
170 this.selectedTenant = slice.project_id;
171
172 // store the status
173 Ceilometer.selectedSlice = slice;
174 Ceilometer.selectedService = service_name;
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800175 this.selectedResources = lodash.groupBy(sliceMeters, 'resource_name');
176
177 // hacky
178 if(Ceilometer.selectedResource){
179 this.selectedMeters = this.selectedResources[Ceilometer.selectedResource];
180 }
181 })
182 .catch(err => {
Matteo Scandoloba4c9aa2016-02-11 09:35:29 -0800183
184 // this means that ceilometer is not yet ready
185 if(err.status === 503){
186 return this.ceilometerError = err.data.detail.specific_error;
187 }
188
Matteo Scandolo81cfbf72016-02-11 09:49:34 -0800189 this.error = (err.data && err.data.detail.specific_error) ? err.data.detail.specific_error : 'An Error occurred. Please try again later.';
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800190 })
191 .finally(() => {
192 this.loader = false;
193 });
194 };
Matteo Scandolo68856082015-12-08 14:35:55 -0800195
196 /**
197 * Select Meters for a resource
198 *
199 * @param Array meters The list of selected resources
200 * @returns void
201 */
202 this.selectedMeters = null;
203 this.selectMeters = (meters, resource) => {
204 this.selectedMeters = meters;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800205
206 Ceilometer.selectedResource = resource;
Matteo Scandolo68856082015-12-08 14:35:55 -0800207 this.selectedResource = resource;
208 }
209
Matteo Scandolo68c2e722015-12-04 10:14:40 -0800210 }
211 };
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800212})
213.directive('ceilometerSamples', function(lodash, $stateParams){
214 return {
215 restrict: 'E',
Matteo Scandolo895b30a2016-02-02 16:04:31 -0800216 scope: {},
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800217 bindToController: true,
218 controllerAs: 'vm',
219 templateUrl: 'templates/ceilometer-samples.tpl.html',
220 controller: function(Ceilometer) {
221
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800222 // console.log(Ceilometer.selectResource);
223
Matteo Scandolo324df092015-12-08 16:39:57 -0800224 this.chartColors = [
Matteo Scandolo2b6583e2015-12-08 15:24:23 -0800225 '#286090',
226 '#F7464A',
227 '#46BFBD',
228 '#FDB45C',
229 '#97BBCD',
230 '#4D5360',
231 '#8c4f9f'
232 ];
Matteo Scandolo324df092015-12-08 16:39:57 -0800233
234 this.chart = {
235 series: [],
236 labels: [],
237 data: []
238 }
239
240 Chart.defaults.global.colours = this.chartColors;
Matteo Scandolo2b6583e2015-12-08 15:24:23 -0800241
Matteo Scandolo68856082015-12-08 14:35:55 -0800242 this.chartType = 'line';
243
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800244 if($stateParams.name && $stateParams.tenant){
245 this.name = $stateParams.name;
246 this.tenant = $stateParams.tenant;
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800247 // TODO rename tenant in project_id
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800248 }
Matteo Scandolo1b9ffac2015-12-14 17:36:09 -0800249 else{
250 throw new Error('Missing Name and Tenant Params!');
251 }
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800252
Matteo Scandolo68856082015-12-08 14:35:55 -0800253 /**
Matteo Scandolo324df092015-12-08 16:39:57 -0800254 * Goes trough the array and format date to be used as labels
Matteo Scandolo68856082015-12-08 14:35:55 -0800255 *
256 * @param Array data
257 * @returns Array a list of labels
258 */
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800259
Matteo Scandolo1d8627f2015-12-05 18:44:45 -0800260 this.getLabels = (data) => {
261 return data.reduce((list, item) => {
262 let date = new Date(item.timestamp);
263 list.push(`${date.getHours()}:${(date.getMinutes()<10?'0':'') + date.getMinutes()}:${date.getSeconds()}`);
264 return list;
265 }, []);
266 };
267
Matteo Scandolo68856082015-12-08 14:35:55 -0800268 /**
Matteo Scandolo324df092015-12-08 16:39:57 -0800269 * Goes trough the array and return a flat array of values
Matteo Scandolo68856082015-12-08 14:35:55 -0800270 *
271 * @param Array data
272 * @returns Array a list of values
273 */
274
Matteo Scandolo1d8627f2015-12-05 18:44:45 -0800275 this.getData = (data) => {
276 return data.reduce((list, item) => {
277 list.push(item.volume);
278 return list;
279 }, []);
280 }
281
Matteo Scandolo324df092015-12-08 16:39:57 -0800282 /**
283 * Add a samples to the chart
284 *
285 * @param string resource_id
286 */
287 this.chartMeters = [];
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800288 this.addMeterToChart = (project_id) => {
289 this.chart['labels'] = this.getLabels(lodash.sortBy(this.samplesList[project_id], 'timestamp'));
290 this.chart['series'].push(project_id);
291 this.chart['data'].push(this.getData(lodash.sortBy(this.samplesList[project_id], 'timestamp')));
292 this.chartMeters.push(this.samplesList[project_id][0]); //use the 0 as are all samples for the same resource and I need the name
293 lodash.remove(this.sampleLabels, {id: project_id});
Matteo Scandolo68856082015-12-08 14:35:55 -0800294 }
295
Matteo Scandoloe5d5ebd2015-12-14 14:42:28 -0800296 this.removeFromChart = (meter) => {
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800297 this.chart.data.splice(this.chart.series.indexOf(meter.project_id), 1);
298 this.chart.series.splice(this.chart.series.indexOf(meter.project_id), 1);
299 this.chartMeters.splice(lodash.findIndex(this.chartMeters, {project_id: meter.project_id}), 1);
Matteo Scandolo324df092015-12-08 16:39:57 -0800300 this.sampleLabels.push({
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800301 id: meter.project_id,
302 name: meter.resource_name || meter.project_id
Matteo Scandolo324df092015-12-08 16:39:57 -0800303 })
304 };
305
306 /**
307 * Format samples to create a list of labels and ids
308 */
309
310 this.formatSamplesLabels = (samples) => {
311
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800312 return lodash.uniq(samples, 'project_id')
Matteo Scandolo5074ccd2015-12-15 08:16:56 -0800313 .reduce((labels, item) => {
Matteo Scandolo324df092015-12-08 16:39:57 -0800314 labels.push({
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800315 id: item.project_id,
316 name: item.resource_name || item.project_id
Matteo Scandolo324df092015-12-08 16:39:57 -0800317 });
318 return labels;
Matteo Scandolo5074ccd2015-12-15 08:16:56 -0800319 }, []);
Matteo Scandolo324df092015-12-08 16:39:57 -0800320 }
321
322
Matteo Scandolo68856082015-12-08 14:35:55 -0800323 /**
324 * Load the samples and format data
325 */
326
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800327 this.showSamples = () => {
328 this.loader = true;
Matteo Scandolo68856082015-12-08 14:35:55 -0800329 // Ceilometer.getSamples(this.name, this.tenant) //fetch one
330 Ceilometer.getSamples(this.name) //fetch all
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800331 .then(res => {
Matteo Scandolo324df092015-12-08 16:39:57 -0800332
333 // setup data for visualization
Matteo Scandolo6c6b9282015-12-15 14:37:27 -0800334 this.samplesList = lodash.groupBy(res, 'project_id');
Matteo Scandolo324df092015-12-08 16:39:57 -0800335 this.sampleLabels = this.formatSamplesLabels(res);
336
337 // add current meter to chart
338 this.addMeterToChart(this.tenant);
339
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800340 })
341 .catch(err => {
Matteo Scandoloc8f18e62015-12-09 15:23:51 -0800342 this.error = err.data.detail;
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800343 })
344 .finally(() => {
345 this.loader = false;
346 });
347 };
348
349 this.showSamples();
Matteo Scandolo7b80d842015-12-04 15:55:20 -0800350 }
351 }
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800352})
353.directive('ceilometerStats', function(){
354 return {
355 restrict: 'E',
356 scope: {
357 name: '=name',
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800358 tenant: '=tenant'
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800359 },
360 bindToController: true,
361 controllerAs: 'vm',
362 templateUrl: 'templates/ceilometer-stats.tpl.html',
363 controller: function($scope, Ceilometer) {
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800364
365 this.getStats = (tenant) => {
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800366 this.loader = true;
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800367 Ceilometer.getStats({tenant: tenant})
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800368 .then(res => {
369 this.stats = res;
370 })
371 .catch(err => {
372 this.error = err.data;
373 })
374 .finally(() => {
375 this.loader = false;
376 });
377 };
378
Matteo Scandolo19c2a4e2016-02-02 16:29:40 -0800379 $scope.$watch(() => this.name, (val) => {
380 if(val){
381 this.getStats(this.tenant);
382 }
383 });
Matteo Scandolo41f5c152015-12-09 17:09:55 -0800384 }
385 }
Matteo Scandolo9db064f2016-02-08 14:17:42 -0800386});