update from RIFT as of 696b75d2fe9fb046261b08c616f1bcf6c0b54a9b third try
[osm/UI.git] / skyquake / plugins / logging / api / logging.js
1
2 /*
3 *
4 * Copyright 2016 RIFT.IO Inc
5 *
6 * Licensed under the Apache License, Version 2.0 (the "License");
7 * you may not use this file except in compliance with the License.
8 * You may obtain a copy of the License at
9 *
10 * http://www.apache.org/licenses/LICENSE-2.0
11 *
12 * Unless required by applicable law or agreed to in writing, software
13 * distributed under the License is distributed on an "AS IS" BASIS,
14 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
15 * See the License for the specific language governing permissions and
16 * limitations under the License.
17 *
18 */
19 'use strict';
20 var Promise = require('bluebird');
21 var utils = require('../../../framework/core/api_utils/utils.js');
22 var request = utils.request;
23 var constants = require('../../../framework/core/api_utils/constants.js');
24 var _ = require('lodash');
25 var APIVersion = '/v2';
26 var transforms = require('./transforms.js');
27
28 var foreverOn = true;
29
30 var Config = {};
31 var Console = {};
32 var Filter = {};
33 var Operational = {};
34 var Sink = {};
35 var SysLogViewer = {};
36 var Aggregate = {};
37
38 var Test = {};
39
40 // Helper functions
41 // TODO: Consolidate the build functions, provide method type as arg
42
43 function buildGetRequestOptions(req, endpoint) {
44 var headers = _.extend({},
45 constants.HTTP_HEADERS.accept.data, {
46 'Authorization': req.session && req.session.authorization
47 });
48 var api_server = req.query["api_server"];
49 var requestOptions = {
50 url: utils.confdPort(api_server) + endpoint,
51 method: 'GET',
52 headers: headers,
53 forever: constants.FOREVER_ON,
54 rejectUnauthorized: false
55 };
56 return requestOptions;
57 }
58
59 function buildPutRequestOptions(req, endpoint, jsonData) {
60 var headers = _.extend({},
61 constants.HTTP_HEADERS.accept.data,
62 constants.HTTP_HEADERS.content_type.data, {
63 'Authorization': req.session && req.session.authorization
64 });
65 var api_server = req.query["api_server"];
66 var requestOptions = {
67 url: utils.confdPort(api_server) + endpoint,
68 method: 'PUT',
69 headers: headers,
70 forever: constants.FOREVER_ON,
71 rejectUnauthorized: false,
72 json: jsonData
73 };
74 return requestOptions;
75 }
76
77
78 function buildDeleteRequestOptions(req, endpoint) {
79 var headers = _.extend({},
80 constants.HTTP_HEADERS.accept.data,
81 constants.HTTP_HEADERS.content_type.data, {
82 'Authorization': req.session && req.session.authorization
83 });
84 var api_server = req.query["api_server"];
85 var requestOptions = {
86 url: utils.confdPort(api_server) + endpoint,
87 method: 'DELETE',
88 headers: headers,
89 forever: constants.FOREVER_ON,
90 rejectUnauthorized: false
91 };
92 return requestOptions;
93 }
94
95 /**
96 * Used for simulating latency
97 */
98 function resolve_with_delay(resolve, data, delay) {
99 return setTimeout(function() {
100 resolve(data)
101 }, delay);
102 }
103
104 /**
105 * This function provides the default callback for requests
106 */
107 function requestCallback(resolve, reject, transformFunc) {
108 return function(error, response, body) {
109 if (utils.validateResponse('', error, response, body, resolve, reject)) {
110 if (transformFunc) {
111 var data = transformFunc(response.body);
112 } else {
113 var data = JSON.stringify(response.body);
114 }
115 return resolve({
116 statusCode: response.statusCode,
117 data: data
118 });
119 };
120 };
121 }
122
123 function handleGetRequest(req, endpoint, responseTransform) {
124 return new Promise(function(resolve, reject) {
125 request(
126 buildGetRequestOptions(req, endpoint),
127 requestCallback(resolve, reject, responseTransform)
128 );
129 });
130 }
131
132 /**
133 *
134 */
135 // TODO: Add arg for transform function to transfrm req.body to json data
136 // Right now we'll just pass the request through, until we need to implement
137 // a transformer
138 function handlePutRequest(req, endpoint, body) {
139 return new Promise(function(resolve, reject) {
140 request(
141 buildPutRequestOptions(req, endpoint, body||req.body),
142 requestCallback(resolve, reject)
143 );
144 });
145 }
146
147 function handleDeleteRequest(req, endpoint, body) {
148 return new Promise(function(resolve, reject) {
149 request(
150 buildDeleteRequestOptions(req, endpoint),
151 requestCallback(resolve, reject)
152 );
153 });
154 }
155
156 function handleMockResponse(req, success, statusCode, data, delay) {
157 delay = delay || 0;
158 return new Promise(function(resolve, reject) {
159 if (success) {
160 resolve_with_delay(resolve, { statusCode: statusCode, data: data }, delay)
161 } else { reject({ statusCode: statusCode, data: data }); }
162 });
163 }
164
165
166 function handleReject(req, statusCode, message) {
167 return new Promise(function(resolve, reject) {
168 reject({ statusCode: statusCode, data: message});
169 })
170 }
171
172 /**
173 * Calllback function to parse the response body into an object and
174 * remove the restconf top level key if it is present.
175 */
176 function transformLoggingRootResponseCallback(responseBody) {
177 var data = JSON.parse(responseBody);
178 if (data['rwlog-mgmt:logging']) {
179 data = data['rwlog-mgmt:logging'];
180 }
181 return data;
182 }
183
184
185 /**
186 * Debug function
187 */
188 function dumpLoggingConfig(data) {
189 console.log("dumpLoggingconfig");
190 var logConfig = data['lwlog-mgmt:logging'] || data;
191
192 console.log("keys=", Object.keys(logConfig));
193 console.log("stringify=", JSON.stringify(logConfig));
194 if (logConfig['default-severity']) {
195 logConfig['default-severity'].forEach(function(obj) {
196 console.log(obj);
197 })
198 }
199 if (logConfig['sink']) {
200 console.log('sink=', JSON.stringify(logConfig['sink']));
201 }
202 if (logConfig['console']) {
203 console.log('console=', JSON.stringify(logConfig['console']));
204 }
205 if (logConfig['deny']) {
206 console.log('deny=', JSON.stringify(logConfig['deny']));
207 }
208 }
209
210 // Aggregate calls
211
212 /**
213 * This method should fill out the full data set
214 */
215 Aggregate.get = function(req) {
216 // get config data
217 // get operational data
218 //massage them
219 var configData = Config.get(req);
220 var operationalData = Operational.get(req);
221
222 return new Promise(function(resolve, reject) {
223 Promise.all([configData, operationalData]).then(function(resolves) {
224 //console.log("Resolved all request promises (config, operational logging data)");
225 // TODO: Make sure the statusCodes for each resulves is 200
226 var decoder = new transforms.LoggingConfigDecoder();
227 resolve({
228 statusCode: 200,
229 data: decoder.decode(resolves[0], resolves[1])
230 });
231
232 }).catch(function(error) {
233 console.log("Logging: Aggregate.get error: ", error);
234 reject({
235 statusCode: 404,
236 errorMessage: error
237 })
238 })
239 });
240 }
241
242 /**
243 * This method expects the full data set (keys and values) for the logging
244 * config to replace the existing logging config
245 */
246 Aggregate.set = function(req) {
247 // NOTE: Left some debugging code remarked out
248
249 //console.log("Logging Aggregate.set called");
250 //console.log("data=", req.body);
251
252 // Do nothing to test delay in response
253 var encoder = new transforms.LoggingConfigEncoder();
254 var data = encoder.encode(req.body);
255 // console.log("Aggregate.set. encoded data=");
256 // console.log(data);
257 // dumpLoggingConfig(data);
258 let setData = {
259 'rwlog-mgmt:logging' : data
260 }
261 return handlePutRequest(req, APIVersion + '/api/config/logging', setData);
262 // if (this.mockResponse['set']) {
263 // return handleMockResponse(req, true, 201, data, delay=100);
264 // }
265 }
266
267
268
269
270
271 // Config calls
272
273 /**
274 * Get all currently set logging config data
275 */
276 Config.get = function(req) {
277 return handleGetRequest(req, APIVersion + '/api/config/logging?deep',
278 transformLoggingRootResponseCallback
279 );
280 }
281
282 /**
283 * Top level put method. Restconf cannot currently handle a global put on
284 * logging, so this method is currently for testing
285 */
286 Config.set = function(req) {
287 return handlePutRequest(req, APIVersion + '/api/config/logging');
288 }
289
290
291 Config.setConsole = function(req) {
292 return handlePutRequest(req, APIVersion + '/api/config/logging/console');
293 }
294
295 Config.setFilter = function(req) {
296 return handlePutRequest(req, APIVersion + '/api/config/logging/console/filter');
297 }
298
299 Config.setDefaultSeverity = function(req) {
300 // TODO: verify there is one key at root of data: 'default-severity'
301 // OR just filter on the request body
302 return handlePutRequest(req, APIVersion + '/api/config/logging/');
303 }
304
305 Config.deleteDefaultSeverity = function(req) {
306 // TODO: verify there is one key at root of data: 'default-severity'
307 // OR just filter on the request body
308 var Categories = req.body['default-severity'];
309 return new Promise(function(resolve, reject) {
310 var promises = Categories.map(function(c) {
311 return handleDeleteRequest(req, APIVersion + '/api/config/logging/default-severity/' + c.category);
312 });
313 return Promise.all(promises).then(
314 function(data) {
315 resolve(data[0]);
316 },
317 function(data) {
318 reject(data);
319 }
320 )
321 })
322
323 }
324
325 // NOTE: In rel_4.3 we are going to affect syslog sink category by default
326
327 Config.setDefaultSyslogSeverity = function(req) {
328 // TODO: verify there is one key at root of data: 'default-severity'
329 // OR just filter on the request body
330 return handlePutRequest(req, APIVersion + '/api/config/logging/sink/syslog');
331 }
332
333 Config.deleteDefaultSyslogSeverity = function(req) {
334 // TODO: verify there is one key at root of data: 'default-severity'
335 // OR just filter on the request body
336 var Categories = req.params.nulledCategories.split(',');
337 return new Promise(function(resolve, reject) {
338 return Promise.all(Categories.map(function(categoryName) {
339 return handleDeleteRequest(req, APIVersion + '/api/config/logging/sink/syslog/filter/category/' + categoryName);
340 })).then(
341 function(data) {
342 reject({statusCode: data[0].statusCode, data: data[0].data});
343 },
344 function(data) {
345 reject({statusCode: data[0].statusCode, data: data[0].data});
346 }
347 )
348 });
349 }
350
351 /*
352 get body of forms
353
354 {
355 "allowDuplicateEvents" : true
356 }
357 */
358
359 /**
360 * TODO: Repeat delete calls (when 'allowDuplicateEvents' value is false) cause
361 * a 404 error
362 * TODO: the call to handleDeleteRequest returns stringified data, but the PUT
363 * does not (This is the behavior we want)
364 *
365 * Improvement? Allos string representation of true/false
366 */
367 Config.setAllowDuplicateEvents = function(req) {
368 // TODO: verify there is one key at root of data: 'default-severity'
369 // OR just filter on the request body
370 console.log(req.body)
371 if (req.body.hasOwnProperty('allowDuplicateEvents')) {
372 if (req.body.allowDuplicateEvents.toUpperCase() == "TRUE") {
373 return handlePutRequest(req, '/api/config/logging/allow', {
374 "duplicate": "events"
375 });
376 } else { // false, remove entry from logging config
377 return handleDeleteRequest(req, '/api/config/logging/allow/duplicate');
378 }
379 } else {
380 return handleReject(statusCode=400,
381 data={
382 "message": 'Expected key, "allowDuplicateEvents" not found',
383 "original-request" : req.body
384 });
385 }
386 }
387
388 /*
389 "denyEvents": {
390 "eventIDs": [
391 1
392 ]
393 },
394 */
395
396 /*
397 "deny": {
398 "event": [
399 {
400 "event-Id": 1
401 }
402 ]
403 },
404 */
405
406 Config.setDenyEvents = function(req) {
407 var reqBody = {
408 deny: {
409 events: req.body.denyEvents.eventIDs.map(function(eventID) {
410 return { "event-Id": eventID };
411 })
412 }
413 };
414 return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody);
415 }
416
417 Config.setSyslogViewer = function(req) {
418 // TODO: Verify structure of req.body
419 var reqBody = {
420 "syslog-viewer" : req.body['syslog-viewer']
421 }
422 return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody);
423 }
424
425
426 // Operational calls
427
428 Operational.get = function(req) {
429 var APIVersion = '/v1'
430 return handleGetRequest(req, APIVersion + '/api/operational/logging?deep',
431 transformLoggingRootResponseCallback
432 );
433 }
434
435
436 /**
437 * Legacy call to get sys log viewer
438 */
439
440 SysLogViewer.get = function(req) {
441 console.log("\n***\n SysLogViewer.get called");
442 var api_server = req.query['api_server'];
443 return new Promise(function(resolve, reject) {
444 request({
445 uri: utils.confdPort(api_server) + APIVersion + '/api/config/logging/syslog-viewer',
446 method: 'GET',
447 headers: _.extend({},
448 constants.HTTP_HEADERS.accept.data,
449 {
450 'Authorization': req.session && req.session.authorization
451 }),
452 forever: foreverOn,
453 rejectUnauthorized: false
454 },
455 function(error, response, body) {
456 if(error) {
457 console.log('Logging.get failed. Error:', error);
458 reject({
459 statusCode: response ? response.statusCode : 404,
460 errorMessage: 'Issue retrieving syslog-viewer url'
461 });
462 } else {
463 var data;
464 try {
465 data = JSON.parse(response.body);
466 } catch (e) {
467 console.log('Logging.get failed while parsing response.body. Error:', e);
468 reject({
469 statusCode: 500,
470 errorMessage: 'Error parsing response.body during Logging.get'
471 });
472 }
473 resolve(data);
474 }
475 });
476 });
477 };
478
479 /**
480 * Test methods
481 */
482 Test.roundtrip = function(req) {
483 return new Promise(function(resolve, reject) {
484 Aggregate.get(req).then(function(result) {
485 var data = (new transforms.LoggingConfigEncoder()).encode(result.data);
486 resolve({
487 statusCode: 200,
488 data: {
489 'rwlog-mgmt:logging': data
490 }
491 });
492 }, function(err) {
493 console.log('Test.get error:', err);
494 reject({
495 statusCode: 500,
496 errorMessage: err
497 });
498 });
499 });
500 }
501
502 module.exports = {
503 aggregate: Aggregate,
504 config : Config,
505 operational: Operational,
506 sysLogViewer: SysLogViewer,
507 test: Test
508 }