Rift.IO OSM R1 Initial Submission
[osm/UI.git] / skyquake / plugins / logging / api / logging.js
diff --git a/skyquake/plugins/logging/api/logging.js b/skyquake/plugins/logging/api/logging.js
new file mode 100644 (file)
index 0000000..086d864
--- /dev/null
@@ -0,0 +1,479 @@
+
+/*
+ * 
+ *   Copyright 2016 RIFT.IO Inc
+ *
+ *   Licensed under the Apache License, Version 2.0 (the "License");
+ *   you may not use this file except in compliance with the License.
+ *   You may obtain a copy of the License at
+ *
+ *       http://www.apache.org/licenses/LICENSE-2.0
+ *
+ *   Unless required by applicable law or agreed to in writing, software
+ *   distributed under the License is distributed on an "AS IS" BASIS,
+ *   WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ *   See the License for the specific language governing permissions and
+ *   limitations under the License.
+ *
+ */
+var Promise = require('bluebird');
+var utils = require('../../../framework/core/api_utils/utils.js');
+var request = utils.request;
+var constants = require('../../../framework/core/api_utils/constants.js');
+var _ = require('lodash');
+var APIVersion = '/v1';
+var transforms = require('./transforms.js');
+
+var foreverOn = true;
+
+var Config = {};
+var Console = {};
+var Filter = {};
+var Operational = {};
+var Sink = {};
+var SysLogViewer = {};
+var Aggregate = {};
+
+var Test = {};
+
+// Helper functions
+// TODO: Consolidate the build functions, provide method type as arg
+
+function buildGetRequestOptions(req, endpoint) {
+  var headers = _.extend({},
+    constants.HTTP_HEADERS.accept.data, {
+    'Authorization': req.get('Authorization')
+  });
+  var api_server = req.query["api_server"];
+  var requestOptions = {
+    url: utils.confdPort(api_server) + endpoint,
+    method: 'GET',
+    headers: headers,
+    forever: constants.FOREVER_ON,
+    rejectUnauthorized: false
+  };
+  return requestOptions;
+}
+
+function buildPutRequestOptions(req, endpoint, jsonData) {
+  var headers = _.extend({},
+    constants.HTTP_HEADERS.accept.data,
+    constants.HTTP_HEADERS.content_type.data, {
+    'Authorization': req.get('Authorization')
+  });
+  var api_server = req.query["api_server"];
+  var requestOptions = {
+    url: utils.confdPort(api_server) + endpoint,
+    method: 'PUT',
+    headers: headers,
+    forever: constants.FOREVER_ON,
+    rejectUnauthorized: false,
+    json: jsonData
+  };
+  return requestOptions;
+}
+
+
+function buildDeleteRequestOptions(req, endpoint) {
+  var headers = _.extend({},
+    constants.HTTP_HEADERS.accept.data,
+    constants.HTTP_HEADERS.content_type.data, {
+    'Authorization': req.get('Authorization')
+  });
+  var api_server = req.query["api_server"];
+  var requestOptions = {
+    url: utils.confdPort(api_server) + endpoint,
+    method: 'DELETE',
+    headers: headers,
+    forever: constants.FOREVER_ON,
+    rejectUnauthorized: false
+  };
+  return requestOptions;
+}
+
+/**
+* Used for simulating latency
+*/
+function resolve_with_delay(resolve, data, delay) {
+  return setTimeout(function() {
+    resolve(data)
+  }, delay);
+}
+
+/**
+ * This function provides the default callback for requests
+ */
+function requestCallback(resolve, reject, transformFunc) {
+  return function(error, response, body) {
+    if (utils.validateResponse('', error, response, body, resolve, reject)) {
+      if (transformFunc) {
+        var data = transformFunc(response.body);
+      } else {
+        var data = JSON.stringify(response.body);
+      }
+      return resolve_with_delay(resolve, {
+        statusCode: response.statusCode,
+        data: data
+      }, 0);
+    };
+  };
+}
+
+function handleGetRequest(req, endpoint, responseTransform) {
+  return new Promise(function(resolve, reject) {
+    request(
+      buildGetRequestOptions(req, endpoint),
+      requestCallback(resolve, reject, responseTransform)
+    );
+  });
+}
+
+/**
+ *
+ */
+// TODO: Add arg for transform function to transfrm req.body to json data
+// Right now we'll just pass the request through, until we need to implement
+// a transformer
+function handlePutRequest(req, endpoint, body) {
+  return new Promise(function(resolve, reject) {
+    request(
+      buildPutRequestOptions(req, endpoint, body||req.body),
+      requestCallback(resolve, reject)
+    );
+  });
+}
+
+function handleDeleteRequest(req, endpoint, body) {
+  return new Promise(function(resolve, reject) {
+    request(
+      buildDeleteRequestOptions(req, endpoint),
+      requestCallback(resolve, reject)
+    );
+  });
+}
+
+function handleMockResponse(req, success, statusCode, data, delay) {
+  delay = delay || 0;
+  return new Promise(function(resolve, reject) {
+    if (success) {
+      resolve_with_delay(resolve, { statusCode: statusCode, data: data }, delay)
+    } else { reject({ statusCode: statusCode, data: data }); }
+  });
+}
+
+
+function handleReject(req, statusCode, message) {
+  return new Promise(function(resolve, reject) {
+    reject({ statusCode: statusCode, data: message});
+  })
+}
+
+/**
+* Calllback function to parse the response body into an object and
+* remove the restconf top level key if it is present.
+*/
+function transformLoggingRootResponseCallback(responseBody) {
+  var data = JSON.parse(responseBody);
+  if (data['rwlog-mgmt:logging']) {
+    data = data['rwlog-mgmt:logging'];
+  }
+  return data;
+}
+
+
+/**
+ * Debug function
+ */
+function dumpLoggingConfig(data) {
+  console.log("dumpLoggingconfig");
+  var logConfig = data['lwlog-mgmt:logging'] || data;
+
+  console.log("keys=", Object.keys(logConfig));
+  console.log("stringify=", JSON.stringify(logConfig));
+  if (logConfig['default-severity']) {
+    logConfig['default-severity'].forEach(function(obj) {
+      console.log(obj);
+    })
+  }
+  if (logConfig['sink']) {
+    console.log('sink=', JSON.stringify(logConfig['sink']));
+  }
+  if (logConfig['console']) {
+    console.log('console=', JSON.stringify(logConfig['console']));
+  }
+  if (logConfig['deny']) {
+    console.log('deny=', JSON.stringify(logConfig['deny']));
+  }
+}
+
+// Aggregate calls
+
+/**
+* This method should fill out the full data set
+*/
+Aggregate.get = function(req) {
+  // get config data
+  // get operational data
+  //massage them
+  var configData = Config.get(req);
+  var operationalData = Operational.get(req);
+
+  return new Promise(function(resolve, reject) {
+    Promise.all([configData, operationalData]).then(function(resolves) {
+      //console.log("Resolved all request promises (config, operational logging data)");
+      // TODO: Make sure the statusCodes for each resulves is 200
+      var decoder = new transforms.LoggingConfigDecoder();
+      resolve({
+        statusCode: 200,
+        data: decoder.decode(resolves[0], resolves[1])
+      });
+
+    }).catch(function(error) {
+      console.log("Logging: Aggregate.get error: ", error);
+      reject({
+        statusCode: 404,
+        errorMessage: error
+      })
+    })
+  });
+}
+
+/**
+* This method expects the full data set (keys and values) for the logging
+* config to replace the existing logging config
+*/
+Aggregate.set = function(req) {
+  // NOTE: Left some debugging code remarked out
+
+  //console.log("Logging Aggregate.set called");
+  //console.log("data=", req.body);
+
+  // Do nothing to test delay in response
+  var encoder = new transforms.LoggingConfigEncoder();
+  var data = encoder.encode(req.body);
+  //console.log("Aggregate.set. encoded data=");
+  //console.log(data);
+  // dumpLoggingConfig(data);
+
+  return handlePutRequest(req, APIVersion + '/api/config/logging', data);
+  // if (this.mockResponse['set']) {
+  //   return handleMockResponse(req, true, 201, data, delay=100);
+  // }
+}
+
+
+
+
+
+// Config calls
+
+/**
+ * Get all currently set logging config data
+ */
+Config.get = function(req) {
+  return handleGetRequest(req, APIVersion + '/api/config/logging?deep',
+    transformLoggingRootResponseCallback
+  );
+}
+
+/**
+ * Top level put method. Restconf cannot currently handle a global put on
+ * logging, so this method is currently for testing
+ */
+Config.set = function(req) {
+  return handlePutRequest(req, APIVersion + '/api/config/logging');
+}
+
+
+Config.setConsole = function(req) {
+  return handlePutRequest(req, APIVersion + '/api/config/logging/console');
+}
+
+Config.setFilter = function(req) {
+  return handlePutRequest(req, APIVersion + '/api/config/logging/console/filter');
+}
+
+Config.setDefaultSeverity = function(req) {
+  // TODO: verify there is one key at root of data: 'default-severity'
+  // OR just filter on the request body
+  return handlePutRequest(req, APIVersion + '/api/config/logging/');
+}
+
+Config.deleteDefaultSeverity = function(req) {
+  // TODO: verify there is one key at root of data: 'default-severity'
+  // OR just filter on the request body
+  var Categories = req.body['default-severity'];
+  return new Promise(function(resolve, reject) {
+    var promises = Categories.map(function(c) {
+      return handleDeleteRequest(req, APIVersion + '/api/config/logging/default-severity/' + c.category);
+    });
+    return Promise.all(promises).then(
+      function(data) {
+        resolve(data[0]);
+      },
+      function(data) {
+        reject(data);
+      }
+    )
+  })
+
+}
+
+/*
+  get body of forms
+
+{
+  "allowDuplicateEvents" : true
+}
+*/
+
+/**
+ * TODO: Repeat delete calls (when 'allowDuplicateEvents' value is false) cause
+ * a 404 error
+ * TODO: the call to handleDeleteRequest returns stringified data, but the PUT
+ * does not (This is the behavior we want)
+ *
+ * Improvement? Allos string representation of true/false
+ */
+Config.setAllowDuplicateEvents = function(req) {
+  // TODO: verify there is one key at root of data: 'default-severity'
+  // OR just filter on the request body
+
+  if (req.body.hasOwnProperty('allowDuplicateEvents') &&
+    typeof req.body.allowDuplicateEvents == 'boolean') {
+    if (req.body.allowDuplicateEvents) {
+      return handlePutRequest(req, APIVersion + '/api/config/logging/allow', {
+        "duplicate": "events"
+      });
+    } else { // false, remove entry from logging config
+      return handleDeleteRequest(req, APIVersion + '/api/config/logging/allow/duplicate');
+    }
+  } else {
+    return handleReject(statusCode=400,
+      data={
+        "message": 'Expected key, "allowDuplicateEvents" not found',
+        "original-request" : req.body
+      });
+  }
+}
+
+/*
+  "denyEvents": {
+    "eventIDs": [
+      1
+    ]
+  },
+*/
+
+/*
+      "deny": {
+        "event": [
+          {
+            "event-Id": 1
+          }
+        ]
+      },
+*/
+
+Config.setDenyEvents = function(req) {
+  var reqBody = {
+    deny: {
+      events: req.body.denyEvents.eventIDs.map(function(eventID) {
+        return { "event-Id": eventID };
+      })
+    }
+  };
+  return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody);
+}
+
+Config.setSyslogViewer = function(req) {
+  // TODO: Verify structure of req.body
+  var reqBody = {
+    "syslog-viewer" : req.body['syslog-viewer']
+  }
+  return handlePutRequest(req, APIVersion + '/api/config/logging', reqBody);
+}
+
+
+// Operational calls
+
+Operational.get = function(req) {
+  return handleGetRequest(req, APIVersion + '/api/operational/logging?deep',
+    transformLoggingRootResponseCallback
+  );
+}
+
+
+/**
+ * Legacy call to get sys log viewer
+ */
+
+SysLogViewer.get = function(req) {
+  console.log("\n***\n SysLogViewer.get called");
+  var api_server = req.query['api_server'];
+  return new Promise(function(resolve, reject) {
+    request({
+      uri: utils.confdPort(api_server) + APIVersion + '/api/config/logging/syslog-viewer',
+      method: 'GET',
+      headers: _.extend({},
+        constants.HTTP_HEADERS.accept.data,
+        {
+          'Authorization': req.get('Authorization')
+        }),
+      forever: foreverOn,
+      rejectUnauthorized: false
+    },
+    function(error, response, body) {
+      if(error) {
+        console.log('Logging.get failed. Error:', error);
+        reject({
+          statusCode: response ? response.statusCode : 404,
+          errorMessage: 'Issue retrieving syslog-viewer url'
+        });
+      } else {
+        var data;
+        try {
+          data = JSON.parse(response.body);
+        } catch (e) {
+          console.log('Logging.get failed while parsing response.body. Error:', e);
+          reject({
+            statusCode: 500,
+            errorMessage: 'Error parsing response.body during Logging.get'
+          });
+        }
+        resolve(data);
+      }
+    });
+  });
+};
+
+/**
+ * Test methods
+ */
+Test.roundtrip = function(req) {
+  return new Promise(function(resolve, reject) {
+    Aggregate.get(req).then(function(result) {
+      var data = (new transforms.LoggingConfigEncoder()).encode(result.data);
+      resolve({
+        statusCode: 200,
+        data: {
+          'rwlog-mgmt:logging': data
+        }
+      });
+    }, function(err) {
+      console.log('Test.get error:', err);
+      reject({
+        statusCode: 500,
+        errorMessage: err
+      });
+    });
+  });
+}
+
+module.exports = {
+  aggregate: Aggregate,
+  config : Config,
+  operational: Operational,
+  sysLogViewer: SysLogViewer,
+  test: Test
+}