| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 1 | |
| 2 | /* |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 3 | * |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 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 | */ |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 19 | 'use strict'; |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 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'); |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 25 | var APIVersion = '/v2'; |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 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.get('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.get('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.get('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 | } |
| Laurence Maultsby | 660c2a1 | 2016-10-20 12:30:41 -0400 | [diff] [blame] | 115 | return resolve({ |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 116 | statusCode: response.statusCode, |
| 117 | data: data |
| Laurence Maultsby | 660c2a1 | 2016-10-20 12:30:41 -0400 | [diff] [blame] | 118 | }); |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 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); |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 255 | // console.log("Aggregate.set. encoded data="); |
| 256 | // console.log(data); |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 257 | // dumpLoggingConfig(data); |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 258 | let setData = { |
| 259 | 'rwlog-mgmt:logging' : data |
| 260 | } |
| 261 | return handlePutRequest(req, APIVersion + '/api/config/logging', setData); |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 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 | |
| KIRAN KASHALKAR | 27eea26 | 2016-10-19 16:14:50 -0400 | [diff] [blame] | 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 |
| Laurence Maultsby | 0aadbca | 2016-10-20 11:23:09 -0400 | [diff] [blame] | 336 | var Categories = req.params.nulledCategories.split(','); |
| 337 | var promises = []; |
| KIRAN KASHALKAR | 27eea26 | 2016-10-19 16:14:50 -0400 | [diff] [blame] | 338 | return new Promise(function(resolve, reject) { |
| Laurence Maultsby | 0aadbca | 2016-10-20 11:23:09 -0400 | [diff] [blame] | 339 | promises.concat(Categories.map(function(categoryName) { |
| 340 | return handleDeleteRequest(req, APIVersion + '/api/config/logging/sink/syslog/filter/category/' + categoryName); |
| 341 | })); |
| 342 | return Promise.all(promises).then( |
| 343 | function(data) { |
| Laurence Maultsby | 660c2a1 | 2016-10-20 12:30:41 -0400 | [diff] [blame] | 344 | resolve({statusCode: data[0].statusCode, data: data[0].data}); |
| Laurence Maultsby | 0aadbca | 2016-10-20 11:23:09 -0400 | [diff] [blame] | 345 | }, |
| 346 | function(data) { |
| Laurence Maultsby | 660c2a1 | 2016-10-20 12:30:41 -0400 | [diff] [blame] | 347 | reject({statusCode: data[0].statusCode, data: data[0].data}); |
| Laurence Maultsby | 0aadbca | 2016-10-20 11:23:09 -0400 | [diff] [blame] | 348 | } |
| 349 | ) |
| KIRAN KASHALKAR | 27eea26 | 2016-10-19 16:14:50 -0400 | [diff] [blame] | 350 | }); |
| KIRAN KASHALKAR | 27eea26 | 2016-10-19 16:14:50 -0400 | [diff] [blame] | 351 | } |
| 352 | |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 353 | /* |
| 354 | get body of forms |
| 355 | |
| 356 | { |
| 357 | "allowDuplicateEvents" : true |
| 358 | } |
| 359 | */ |
| 360 | |
| 361 | /** |
| 362 | * TODO: Repeat delete calls (when 'allowDuplicateEvents' value is false) cause |
| 363 | * a 404 error |
| 364 | * TODO: the call to handleDeleteRequest returns stringified data, but the PUT |
| 365 | * does not (This is the behavior we want) |
| 366 | * |
| 367 | * Improvement? Allos string representation of true/false |
| 368 | */ |
| 369 | Config.setAllowDuplicateEvents = function(req) { |
| 370 | // TODO: verify there is one key at root of data: 'default-severity' |
| 371 | // OR just filter on the request body |
| Laurence Maultsby | cf5edaf | 2016-10-20 21:39:20 -0400 | [diff] [blame^] | 372 | console.log(req.body) |
| 373 | if (req.body.hasOwnProperty('allowDuplicateEvents')) { |
| 374 | if (req.body.allowDuplicateEvents.toUpperCase() == "TRUE") { |
| 375 | return handlePutRequest(req, '/api/config/logging/allow', { |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 376 | "duplicate": "events" |
| 377 | }); |
| 378 | } else { // false, remove entry from logging config |
| Laurence Maultsby | cf5edaf | 2016-10-20 21:39:20 -0400 | [diff] [blame^] | 379 | return handleDeleteRequest(req, '/api/config/logging/allow/duplicate'); |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 380 | } |
| 381 | } else { |
| 382 | return handleReject(statusCode=400, |
| 383 | data={ |
| 384 | "message": 'Expected key, "allowDuplicateEvents" not found', |
| 385 | "original-request" : req.body |
| 386 | }); |
| 387 | } |
| 388 | } |
| 389 | |
| 390 | /* |
| 391 | "denyEvents": { |
| 392 | "eventIDs": [ |
| 393 | 1 |
| 394 | ] |
| 395 | }, |
| 396 | */ |
| 397 | |
| 398 | /* |
| 399 | "deny": { |
| 400 | "event": [ |
| 401 | { |
| 402 | "event-Id": 1 |
| 403 | } |
| 404 | ] |
| 405 | }, |
| 406 | */ |
| 407 | |
| 408 | Config.setDenyEvents = function(req) { |
| 409 | var reqBody = { |
| 410 | deny: { |
| 411 | events: req.body.denyEvents.eventIDs.map(function(eventID) { |
| 412 | return { "event-Id": eventID }; |
| 413 | }) |
| 414 | } |
| 415 | }; |
| 416 | return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody); |
| 417 | } |
| 418 | |
| 419 | Config.setSyslogViewer = function(req) { |
| 420 | // TODO: Verify structure of req.body |
| 421 | var reqBody = { |
| 422 | "syslog-viewer" : req.body['syslog-viewer'] |
| 423 | } |
| 424 | return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody); |
| 425 | } |
| 426 | |
| 427 | |
| 428 | // Operational calls |
| 429 | |
| 430 | Operational.get = function(req) { |
| Laurence Maultsby | b771a7f | 2016-10-18 09:59:29 -0400 | [diff] [blame] | 431 | var APIVersion = '/v1' |
| Jeremy Mordkoff | e29efc3 | 2016-09-07 18:59:17 -0400 | [diff] [blame] | 432 | return handleGetRequest(req, APIVersion + '/api/operational/logging?deep', |
| 433 | transformLoggingRootResponseCallback |
| 434 | ); |
| 435 | } |
| 436 | |
| 437 | |
| 438 | /** |
| 439 | * Legacy call to get sys log viewer |
| 440 | */ |
| 441 | |
| 442 | SysLogViewer.get = function(req) { |
| 443 | console.log("\n***\n SysLogViewer.get called"); |
| 444 | var api_server = req.query['api_server']; |
| 445 | return new Promise(function(resolve, reject) { |
| 446 | request({ |
| 447 | uri: utils.confdPort(api_server) + APIVersion + '/api/config/logging/syslog-viewer', |
| 448 | method: 'GET', |
| 449 | headers: _.extend({}, |
| 450 | constants.HTTP_HEADERS.accept.data, |
| 451 | { |
| 452 | 'Authorization': req.get('Authorization') |
| 453 | }), |
| 454 | forever: foreverOn, |
| 455 | rejectUnauthorized: false |
| 456 | }, |
| 457 | function(error, response, body) { |
| 458 | if(error) { |
| 459 | console.log('Logging.get failed. Error:', error); |
| 460 | reject({ |
| 461 | statusCode: response ? response.statusCode : 404, |
| 462 | errorMessage: 'Issue retrieving syslog-viewer url' |
| 463 | }); |
| 464 | } else { |
| 465 | var data; |
| 466 | try { |
| 467 | data = JSON.parse(response.body); |
| 468 | } catch (e) { |
| 469 | console.log('Logging.get failed while parsing response.body. Error:', e); |
| 470 | reject({ |
| 471 | statusCode: 500, |
| 472 | errorMessage: 'Error parsing response.body during Logging.get' |
| 473 | }); |
| 474 | } |
| 475 | resolve(data); |
| 476 | } |
| 477 | }); |
| 478 | }); |
| 479 | }; |
| 480 | |
| 481 | /** |
| 482 | * Test methods |
| 483 | */ |
| 484 | Test.roundtrip = function(req) { |
| 485 | return new Promise(function(resolve, reject) { |
| 486 | Aggregate.get(req).then(function(result) { |
| 487 | var data = (new transforms.LoggingConfigEncoder()).encode(result.data); |
| 488 | resolve({ |
| 489 | statusCode: 200, |
| 490 | data: { |
| 491 | 'rwlog-mgmt:logging': data |
| 492 | } |
| 493 | }); |
| 494 | }, function(err) { |
| 495 | console.log('Test.get error:', err); |
| 496 | reject({ |
| 497 | statusCode: 500, |
| 498 | errorMessage: err |
| 499 | }); |
| 500 | }); |
| 501 | }); |
| 502 | } |
| 503 | |
| 504 | module.exports = { |
| 505 | aggregate: Aggregate, |
| 506 | config : Config, |
| 507 | operational: Operational, |
| 508 | sysLogViewer: SysLogViewer, |
| 509 | test: Test |
| 510 | } |