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