/*
*
* 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 request = require('request');
var Promise = require('bluebird');
var rp = require('request-promise');
var utils = require('../../../framework/core/api_utils/utils.js');
var constants = require('../../../framework/core/api_utils/constants.js');
var _ = require('underscore');
var URL = require('url');
var uuid = require('uuid');
var APIVersion = '/v1';
var PackageFileHandler = require('./packageFileHandler.js');
var Composer = {};
var FileManager = {};
var DataCenters = {};
// Catalog module methods
Composer.get = function(req) {
var api_server = req.query['api_server'];
var results = {}
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: utils.confdPort(api_server) + APIVersion + '/api/config/nsd-catalog/nsd?deep',
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}),
rp({
uri: utils.confdPort(api_server) + APIVersion + '/api/config/vnfd-catalog/vnfd?deep',
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}),
rp({
uri: utils.confdPort(api_server) + APIVersion + '/api/operational/ns-instance-opdata?deep',
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
})
// Not enabled for now
// rp({
// uri: utils.confdPort(api_server) + '/api/config/pnfd-catalog/pnfd?deep',
// method: 'GET',
// headers: _.extend({},
// constants.HTTP_HEADERS.accept.collection,
// {
// 'Authorization': req.get('Authorization')
// }),
// forever: constants.FOREVER_ON,
// rejectUnauthorized: false,
// resolveWithFullResponse: true
// })
]).then(function(result) {
var response = [{
"id": "GUID-1",
"name": "RIFT.ware™ NS Descriptors Catalog",
"short-name": "rift.ware-nsd-cat",
"description": "RIFT.ware™, an open source NFV development and deployment software platform that makes it simple to create, deploy and manage hyper-scale Virtual network functions and applications.",
"vendor": "RIFT.io",
"version": "",
"created-on": "",
"type": "nsd",
"meta": {
"icon-svg": "data:image/svg+xml,%3C%3Fxml%20version%3D%221.0%22%20encoding%3D%22iso-8859-1%22%3F%3E%0A%3C!--%20Generator%3A%20Adobe%20Illustrator%2018.0.0%2C%20SVG%20Export%20Plug-In%20.%20SVG%20Version%3A%206.00%20Build%200)%20%20--%3E%0A%3C!DOCTYPE%20svg%20PUBLIC%20%22-%2F%2FW3C%2F%2FDTD%20SVG%201.1%2F%2FEN%22%20%22http%3A%2F%2Fwww.w3.org%2FGraphics%2FSVG%2F1.1%2FDTD%2Fsvg11.dtd%22%3E%0A%3Csvg%20version%3D%221.1%22%20id%3D%22connection-icon-1%22%20xmlns%3D%22http%3A%2F%2Fwww.w3.org%2F2000%2Fsvg%22%20xmlns%3Axlink%3D%22http%3A%2F%2Fwww.w3.org%2F1999%2Fxlink%22%20x%3D%220px%22%20y%3D%220px%22%0A%09%20viewBox%3D%220%200%2050%2050%22%20style%3D%22enable-background%3Anew%200%200%2050%2050%3B%22%20xml%3Aspace%3D%22preserve%22%3E%0A%09%3Cpath%20d%3D%22M15%2030c-2.8%200-5-2.2-5-5s2.2-5%205-5%205%202.2%205%205-2.2%205-5%205zm0-8c-1.7%200-3%201.3-3%203s1.3%203%203%203%203-1.3%203-3-1.3-3-3-3z%22%2F%3E%3Cpath%20d%3D%22M35%2020c-2.8%200-5-2.2-5-5s2.2-5%205-5%205%202.2%205%205-2.2%205-5%205zm0-8c-1.7%200-3%201.3-3%203s1.3%203%203%203%203-1.3%203-3-1.3-3-3-3z%22%2F%3E%3Cpath%20d%3D%22M35%2040c-2.8%200-5-2.2-5-5s2.2-5%205-5%205%202.2%205%205-2.2%205-5%205zm0-8c-1.7%200-3%201.3-3%203s1.3%203%203%203%203-1.3%203-3-1.3-3-3-3z%22%2F%3E%3Cpath%20d%3D%22M19.007%2025.885l12.88%206.44-.895%201.788-12.88-6.44z%22%2F%3E%3Cpath%20d%3D%22M30.993%2015.885l.894%201.79-12.88%206.438-.894-1.79z%22%2F%3E%3C%2Fsvg%3E"
},
"descriptors": []
}, {
"id": "GUID-2",
"name": "RIFT.ware™ VNF Descriptors Catalog",
"short-name": "rift.ware-vnfd-cat",
"description": "RIFT.ware™, an open source NFV development and deployment software platform that makes it simple to create, deploy and manage hyper-scale Virtual network functions and applications.",
"vendor": "RIFT.io",
"version": "",
"created-on": "",
"type": "vnfd",
"meta": {
"icon-svg": "data:image/svg+xml, "
},
"descriptors": []
}, {
"id": "GUID-3",
"name": "RIFT.ware™ PNF Descriptors Catalog",
"short-name": "rift.ware-pnfd-cat",
"description": "RIFT.ware™, an open source NFV development and deployment software platform that makes it simple to create, deploy and manage hyper-scale Virtual network functions and applications.",
"vendor": "RIFT.io",
"version": "",
"created-on": "",
"type": "pnfd",
"meta": {
"icon-svg": "data:image/svg+xml, "
},
"descriptors": []
}];
if (result[0].body) {
response[0].descriptors = JSON.parse(result[0].body).collection['nsd:nsd'];
if (result[2].body) {
var data = JSON.parse(result[2].body);
if (data && data["nsr:ns-instance-opdata"] && data["nsr:ns-instance-opdata"]["rw-nsr:nsd-ref-count"]) {
var nsdRefCountCollection = data["nsr:ns-instance-opdata"]["rw-nsr:nsd-ref-count"];
response[0].descriptors.map(function(nsd) {
if (!nsd["meta"]) {
nsd["meta"] = {};
}
if (typeof nsd['meta'] == 'string') {
nsd['meta'] = JSON.parse(nsd['meta']);
}
nsd["meta"]["instance-ref-count"] = _.findWhere(nsdRefCountCollection, {
"nsd-id-ref": nsd.id
})["instance-ref-count"];
});
}
}
};
if (result[1].body) {
response[1].descriptors = JSON.parse(result[1].body).collection['vnfd:vnfd'];
};
// if (result[2].body) {
// response[2].descriptors = JSON.parse(result[2].body).collection['pnfd:pnfd'];
// };
resolve({
statusCode: response.statusCode || 200,
data: JSON.stringify(response)
});
}).catch(function(error) {
// Todo: Need better logic than all or nothing.
// Right now even if one of the southbound APIs fails - all fail
var res = {};
console.log('Problem with Composer.get', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: 'Failed to get catalogs' + error
};
reject(res);
});
});
};
Composer.delete = function(req) {
var api_server = req.query['api_server'];
var catalogType = req.params.catalogType;
var id = req.params.id;
console.log('Deleting', catalogType, id, 'from', api_server);
return new Promise(function(resolve, reject) {
request({
uri: utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog/' + catalogType + '/' + id,
method: 'DELETE',
headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
}, function(error, response, body) {
if (utils.validateResponse('Composer.delete', error, response, body, resolve, reject)) {
resolve({
statusCode: response.statusCode
});
}
});
});
};
Composer.getVNFD = function(req) {
var api_server = req.query['api_server'];
var vnfdID = req.body.data;
var authorization = req.get('Authorization');
var VNFDs = [];
if (typeof(vnfdID) == "object" && vnfdID.constructor.name == "Array") {
vnfdID.map(function(id) {
VNFDs.push(requestVNFD(id));
});
} else {
VNFDs.push(requestVNFD(vnfdID));
}
return new Promise(function(resolve, reject) {
Promise.all(VNFDs).then(function(data) {
resolve(data)
}).catch(function(error) {
// Todo: Need better logic than all or nothing.
// Right now even if one of the southbound APIs fails - all fail
var res = {};
console.log('Problem with Composer.getVNFD', error);
res.statusCode = 404;
res.errorMessage = {
error: 'Failed to get VNFDs' + error
};
reject(res);
});
});
function requestVNFD(id) {
return new Promise(function(resolve, reject) {
var url = utils.confdPort(api_server) + APIVersion + '/api/config/vnfd-catalog/vnfd' + (id ? '/' + id : '') + '?deep';
request({
uri: url,
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
'Authorization': authorization
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
}, function(error, response, body) {
if (utils.validateResponse('Composer.create', error, response, body, resolve, reject)) {
var data;
//Is this still needed?
try {
data = JSON.parse(response.body)
} catch (e) {
reject({
statusCode: response ? response.statusCode : 400,
errorMessage: 'Issue parsing VNFD ' + id + 'from ' + utils.confdPort(api_server) + '/api/config/vnfd-catalog/vnfd/' + id + '?deep'
});
}
resolve(data);
}
});
});
}
};
Composer.create = function(req) {
var api_server = req.query['api_server'];
var catalogType = req.params.catalogType;
var data = req.body;
console.log('Creating', catalogType, 'on', api_server);
var jsonData = {};
jsonData[catalogType] = [];
jsonData[catalogType].push(data);
return new Promise(function(resolve, reject) {
var requestHeaders = {};
_.extend(requestHeaders, constants.HTTP_HEADERS.accept.data, constants.HTTP_HEADERS.content_type.data, {
'Authorization': req.get('Authorization')
});
request({
uri: utils.confdPort(api_server) + '/api/config/' + catalogType + '-catalog',
method: 'POST',
headers: requestHeaders,
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
json: jsonData
}, function(error, response, body) {
if (utils.validateResponse('Composer.create', error, response, body, resolve, reject)) {
resolve({
statusCode: response.statusCode
});
}
});
});
};
Composer.updateSave = function(req) {
var api_server = req.query['api_server'];
var catalogType = req.params.catalogType;
var id = req.params.id;
var data = req.body;
console.log('Updating', catalogType, 'id', id, 'on', api_server);
var jsonData = {};
jsonData[catalogType] = {};
jsonData[catalogType] = data;
return new Promise(function(resolve, reject) {
var requestHeaders = {};
_.extend(requestHeaders, constants.HTTP_HEADERS.accept.data, constants.HTTP_HEADERS.content_type.data, {
'Authorization': req.get('Authorization')
});
request({
uri: utils.confdPort(api_server) + APIVersion + '/api/config/' + catalogType + '-catalog' + '/' + catalogType + '/' + id,
method: 'PUT',
headers: requestHeaders,
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
json: jsonData
}, function(error, response, body) {
if (utils.validateResponse('Composer.update', error, response, body, resolve, reject)) {
resolve({
statusCode: response.statusCode
});
}
});
});
}
Composer.update = function(req) {
console.log(' Updating file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
// dev_download_server is for testing purposes.
// It is the direct IP address of the Node server where the
// package will be hosted.
var download_host = req.query['dev_download_server'];
if (!download_host) {
download_host = api_server + ':' + utils.getPortForProtocol(req.protocol);
}
var input = {
'external-url': download_host + '/composer/update/' + req.file.filename,
'package-type': 'VNFD',
'package-id': uuid()
}
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: utils.confdPort(api_server) + '/api/operations/package-update',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
body: {
input: input
}
})
]).then(function(result) {
var data = {};
data['transaction_id'] = result[0].body['output']['transaction-id'];
// Add a status checker on the transaction and then to delete the file later
PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id'], true);
// Return status to composer UI to update the status.
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
data: data
});
}).catch(function(error) {
var res = {};
console.log('Problem with Composer.upload', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
};
reject(res);
});
});
};
Composer.upload = function(req) {
console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
// dev_download_server is for testing purposes.
// It is the direct IP address of the Node server where the
// package will be hosted.
var download_host = req.query['dev_download_server'];
if (!download_host) {
download_host = api_server + ':' + utils.getPortForProtocol(req.protocol);
}
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: utils.confdPort(api_server) + '/api/operations/package-create',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
body: {
input: {
'external-url': download_host + '/composer/upload/' + req.file.filename,
'package-type': 'VNFD',
'package-id': uuid()
}
}
})
]).then(function(result) {
var data = {};
data['transaction_id'] = result[0].body['output']['transaction-id'];
// Add a status checker on the transaction and then to delete the file later
PackageFileHandler.checkCreatePackageStatusAndHandleFile(req, data['transaction_id']);
// Return status to composer UI to update the status.
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
data: data
});
}).catch(function(error) {
var res = {};
console.log('Problem with Composer.upload', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
};
reject(res);
});
});
};
Composer.addFile = function(req) {
console.log(' Uploading file', req.file.originalname, 'as', req.file.filename);
var api_server = req.query['api_server'];
var download_host = req.query['dev_download_server'];
var package_id = req.query['package_id'];
var package_type = req.query['package_type'].toUpperCase();
var package_path = req.query['package_path'];
if (!download_host) {
download_host = api_server + ':' + utils.getPortForProtocol(req.protocol);
}
var input = {
'external-url': download_host + '/composer/upload/' + req.query['package_id'] + '/' + req.file.filename,
'package-type': package_type,
'package-id': package_id,
'package-path': package_path + '/' + req.file.filename
}
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: utils.confdPort(api_server) + '/api/operations/package-file-add',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
body: {
input: input
}
})
]).then(function(result) {
var data = {};
data['transaction_id'] = result[0].body['output']['task-id'];
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
data: data
});
}).catch(function(error) {
var res = {};
console.log('Problem with Composer.upload', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: 'Failed to upload package ' + req.file.originalname + '. Error: ' + error
};
reject(res);
});
});
}
Composer.exportPackage = function(req) {
// /api/operations/package-export
var api_server = req.query['api_server'];
return new Promise(function(resolve, reject) {
Promise.all([
rp({
uri: utils.confdPort(api_server) + '/api/operations/package-export',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true,
json: true,
body: { "input": req.body}
})
]).then(function(result) {
var data = {};
resolve({
statusCode: constants.HTTP_RESPONSE_CODES.SUCCESS.OK,
data: result[0].body
});
}).catch(function(error) {
var res = {};
console.log('Problem with Composer.exportPackage', error);
res.statusCode = error.statusCode || 500;
res.errorMessage = {
error: error
};
reject(res);
});
});
}
FileManager.get = function(req) {
var api_server = req.query['api_server'];
var type = req.query['package_type'] && req.query['package_type'].toUpperCase();
var id = req.query['package_id'];
var downloadUrl = req.query['url'];
var path = req.query['package_path'];
var payload = {
"input": {
"package-type": type,
"package-id": id
}
}
if(req.method == 'GET') {
if(downloadUrl && path) {
payload.input['external-url'] = downloadUrl;
payload.input['package-path'] = path;
return download(payload);
} else {
return list(payload);
}
}
if(req.method == 'DELETE') {
payload.input['package-path'] = path;
return deleteFile(payload)
}
function deleteFile(payload) {
return new Promise(function(resolve, reject) {
rp({
uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-delete',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
json: payload,
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}).then(function(data) {
if (utils.validateResponse('FileManager.delete', data.error, data, data.body, resolve, reject)) {
resolve({
statusCode: data.statusCode,
data: data.body
});
}
})
})
}
function download(payload) {
return new Promise(function(resolve, reject) {
rp({
uri: utils.confdPort(api_server) + '/api/operations/rw-pkg-mgmt:package-file-add',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
json: payload,
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}).then(function(data) {
if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
resolve({
statusCode: data.statusCode,
data: data.body
});
}
})
})
}
function list(payload) {
return new Promise(function(resolve, reject) {
rp({
uri: utils.confdPort(api_server) + '/api/operations/get-package-endpoint',
method: 'POST',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
json: payload,
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}).then(function(data) {
if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
var endpoint = null;
var parsedEndpoint = null;
try {
endpoint = data.body.output.endpoint
} catch(e) {
console.log('Something went wrong with the FileManager.get data that was returned');
reject({});
}
parsedEndpoint = URL.parse(endpoint);
rp({
uri: api_server + ':' + parsedEndpoint.port + parsedEndpoint.path,
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.collection, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
resolveWithFullResponse: true
}).then(function(data) {
if (utils.validateResponse('FileManager.get', data.error, data, data.body, resolve, reject)) {
resolve({
statusCode: data.statusCode,
data: data.body
});
}
}).catch(function(err) {
console.log('Failed to retrieve FileManager.list')
resolve(err);
})
}
})
})
}
}
FileManager.job = function(req) {
var api_server = req.query["api_server"];
var uri = utils.confdPort(api_server);
var url = '/api/operational/download-jobs';
var id = req.params['id'];
return new Promise(function(resolve, reject) {
request({
url: uri + url + '?deep',
method: 'GET',
headers: _.extend({}, constants.HTTP_HEADERS.accept.data, {
'Authorization': req.get('Authorization')
}),
forever: constants.FOREVER_ON,
rejectUnauthorized: false,
}, function(error, response, body) {
if (utils.validateResponse('restconfAPI.streams', error, response, body, resolve, reject)) {
var data = JSON.parse(response.body)['rw-pkg-mgmt:download-jobs'];
var returnData = [];
data && data.job.map(function(d) {
if(d['package-id'] == id) {
returnData.push(d)
}
})
resolve({
statusCode: response.statusCode,
data: returnData
})
};
})
})
}
module.exports = {
Composer:Composer,
FileManager: FileManager
};