Initial release
Change-Id: I04faafd3e3a508d9359138f07e3a10fa2d84689f
This commit is contained in:
parent
29ecb6b8b6
commit
efc6c9f6da
4
.env
Normal file
4
.env
Normal file
@ -0,0 +1,4 @@
|
||||
ACTIVEMQ_PORT=
|
||||
ACTIVEMQ_HOST=
|
||||
ACTIVEMQ_USERNAME=
|
||||
ACTIVEMQ_PASSWORD=
|
111
lib/exn.js
111
lib/exn.js
@ -1,5 +1,5 @@
|
||||
|
||||
|
||||
require('dotenv').config();
|
||||
const container= require('rhea');
|
||||
|
||||
const connection_options={
|
||||
'port': process.env.ACTIVEMQ_PORT,
|
||||
@ -10,14 +10,15 @@ const connection_options={
|
||||
}
|
||||
|
||||
|
||||
if(!connection_options.port || !connection_options.host) {
|
||||
console.error("No connection option provided for EXN skipping asynchronous messaging")
|
||||
return
|
||||
if (!connection_options.port || !connection_options.host) {
|
||||
console.error("No connection option provided for EXN skipping asynchronous messaging");
|
||||
|
||||
} else {
|
||||
const connection = container.connect(connection_options);
|
||||
}
|
||||
|
||||
|
||||
const container= require('rhea');
|
||||
let connection;
|
||||
|
||||
let sender_sal_nodecandidate_get;
|
||||
let sender_sal_cloud_get;
|
||||
let sender_sal_cloud_post;
|
||||
@ -37,8 +38,6 @@ let sender_ui_policies_model_upsert;
|
||||
const correlations = {}
|
||||
|
||||
container.on('message', (context)=>{
|
||||
|
||||
// console.log("Received ",context.message)
|
||||
if(context.message.correlation_id in correlations){
|
||||
if(context.message.body.metaData['status'] >= 400){
|
||||
correlations[context.message.correlation_id]['reject'](context.message.body['message'])
|
||||
@ -53,16 +52,16 @@ container.on('connection_open', function (context) {
|
||||
|
||||
console.log("Connected ",context.container.id);
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.get.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.post.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.create.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.cloud.delete.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.nodecandidate.get.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.node.post.reply')
|
||||
context.connection.open_receiver('topic://eu.nebulouscloud.exn.sal.node.create.reply')
|
||||
|
||||
sender_sal_nodecandidate_get = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.nodecandidate.get');
|
||||
sender_sal_cloud_get = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.get');
|
||||
sender_sal_cloud_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.post');
|
||||
sender_sal_cloud_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.create');
|
||||
sender_sal_cloud_delete = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.cloud.delete');
|
||||
sender_sal_node_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.node.post');
|
||||
sender_sal_node_post = context.connection.open_sender('topic://eu.nebulouscloud.exn.sal.node.create');
|
||||
|
||||
sender_ui_application_new = context.connection.open_sender('topic://eu.nebulouscloud.ui.application.new');
|
||||
sender_ui_application_updated = context.connection.open_sender('topic://eu.nebulouscloud.ui.application.updated');
|
||||
@ -77,7 +76,6 @@ container.on('connection_open', function (context) {
|
||||
|
||||
|
||||
|
||||
connection = container.connect();
|
||||
|
||||
const {v4: uuidv4} = require("uuid");
|
||||
|
||||
@ -91,14 +89,15 @@ module.exports = {
|
||||
'reject':reject,
|
||||
};
|
||||
const message = {
|
||||
to: sender_ui_deploy_application_new.options.target.address,
|
||||
to: sender_ui_application_new.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
message_annotations: {application: uuid},
|
||||
body:{
|
||||
uuid: uuid
|
||||
}
|
||||
}
|
||||
console.log("Send ", message)
|
||||
sender_ui_deploy_application_new.send(message)
|
||||
sender_ui_application_new.send(message)
|
||||
|
||||
})
|
||||
},
|
||||
@ -113,21 +112,28 @@ module.exports = {
|
||||
const message = {
|
||||
to: sender_ui_application_dsl_json.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
message_annotations: {application: uuid},
|
||||
application_properties: {application: uuid},
|
||||
body:json
|
||||
}
|
||||
sender_ui_application_dsl_json.send(message)
|
||||
|
||||
console.log("Sending ", sender_ui_application_dsl_metric.options.target.address, uuid,json)
|
||||
const metrci_message = {
|
||||
const metric_model_payload ={
|
||||
'application': uuid,
|
||||
'yaml': yaml
|
||||
}
|
||||
|
||||
console.log("Sending ", sender_ui_application_dsl_metric.options.target.address, uuid, metric_model_payload)
|
||||
const metric_message = {
|
||||
to: sender_ui_application_dsl_metric.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
body:{
|
||||
'yaml': yaml
|
||||
}
|
||||
message_annotations: {application: uuid},
|
||||
application_properties: {application: uuid},
|
||||
body:metric_model_payload
|
||||
}
|
||||
sender_ui_application_dsl_metric.send(message)
|
||||
|
||||
sender_ui_application_dsl_metric.send(metric_message)
|
||||
|
||||
return resolve()
|
||||
|
||||
})
|
||||
},
|
||||
@ -139,18 +145,20 @@ module.exports = {
|
||||
'reject':reject,
|
||||
};
|
||||
const message = {
|
||||
to: sender_ui_deploy_application_new.options.target.address,
|
||||
to: sender_ui_application_updated.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
message_annotations: {application: uuid},
|
||||
application_properties: {application: uuid},
|
||||
body:{
|
||||
uuid: uuid
|
||||
}
|
||||
}
|
||||
console.log("Send ", message)
|
||||
sender_ui_deploy_application_new.send(message)
|
||||
sender_ui_application_updated.send(message)
|
||||
|
||||
})
|
||||
},
|
||||
register_cloud:( uuid, user ,secret ) =>{
|
||||
register_cloud:(doc) =>{
|
||||
return new Promise((resolve,reject)=>{
|
||||
|
||||
const correlation_id = uuidv4()
|
||||
@ -159,6 +167,23 @@ module.exports = {
|
||||
'reject':reject,
|
||||
};
|
||||
|
||||
/**
|
||||
*
|
||||
* // Amazon Web Service Elastic Compute Cloud
|
||||
* AWSEC2("aws-ec2"),
|
||||
* // Azure VM
|
||||
* AZUREVM("azure"),
|
||||
* // Google CLoud Engine
|
||||
* GCE("gce"),
|
||||
* // OpenStack NOVA
|
||||
* OPENSTACKNOVA("openstack"),
|
||||
* // BYON, to be used for on-premise baremetal
|
||||
* BYON("byon"),
|
||||
* // EDGE nodes
|
||||
* EDGE("edge");
|
||||
*
|
||||
*/
|
||||
|
||||
const message = {
|
||||
to: sender_sal_cloud_post.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
@ -167,27 +192,27 @@ module.exports = {
|
||||
userId: "admin"
|
||||
},
|
||||
body: JSON.stringify([{
|
||||
"cloudId": uuid,
|
||||
"cloudProviderName": "aws-ec2",
|
||||
"cloudId": doc.uuid,
|
||||
"cloudProviderName": doc._platform[0].provider_name,
|
||||
"cloudType": "PUBLIC",
|
||||
"securityGroup": null,
|
||||
"subnet": null,
|
||||
"securityGroup": doc.securityGroup || '',
|
||||
"subnet": doc.subnet || null,
|
||||
"sshCredentials": {
|
||||
"username": null,
|
||||
"keyPairName": "mkl",
|
||||
"privateKey": null
|
||||
"username": doc.sshCredentials.username,
|
||||
"keyPairName": doc.sshCredentials.keyPairName,
|
||||
"privateKey": doc.sshCredentials.privateKey
|
||||
},
|
||||
"endpoint": null,
|
||||
"endpoint": doc.endpoint,
|
||||
"scope": {
|
||||
"prefix": null,
|
||||
"value": null
|
||||
},
|
||||
"identityVersion": null,
|
||||
"defaultNetwork": null,
|
||||
"identityVersion": doc.identityVersion,
|
||||
"defaultNetwork": doc.defaultNetwork,
|
||||
"credentials": {
|
||||
"user": user,
|
||||
"secret": secret,
|
||||
"domain": null
|
||||
"user": doc.credentials.user,
|
||||
"secret": doc.credentials.secret,
|
||||
"domain": doc.credentials.domain || null
|
||||
},
|
||||
"blacklist": null
|
||||
}])
|
||||
@ -196,10 +221,6 @@ module.exports = {
|
||||
console.log("Send ", message)
|
||||
sender_sal_cloud_post.send(message)
|
||||
})
|
||||
},
|
||||
deploy_application: (uuid) => {
|
||||
|
||||
|
||||
},
|
||||
get_cloud_candidates: () => {
|
||||
return new Promise((resolve,reject)=> {
|
||||
@ -213,7 +234,9 @@ module.exports = {
|
||||
const message = {
|
||||
to: sender_sal_nodecandidate_get.options.target.address,
|
||||
correlation_id: correlation_id,
|
||||
body: {}
|
||||
body: {
|
||||
body:[]
|
||||
}
|
||||
}
|
||||
sender_sal_nodecandidate_get.send(message)
|
||||
})
|
||||
|
@ -4,8 +4,7 @@ const math = require('mathjs');
|
||||
module.exports = {
|
||||
extractFromEquation: (equation)=>{
|
||||
equation = equation || '';
|
||||
const lowerCaseEquation = equation.toLowerCase();
|
||||
return math.parse(lowerCaseEquation);
|
||||
return math.parse(equation);
|
||||
},
|
||||
extractVariableNames: (mathNode) => {
|
||||
let variableNames = new Set();
|
||||
|
@ -4,92 +4,107 @@ const yaml = require('yaml');
|
||||
|
||||
module.exports = {
|
||||
yaml: (doc) => {
|
||||
let object = _.clone(doc)
|
||||
let object = _.cloneDeep(doc);
|
||||
let componentsForAppSpecComp = [];
|
||||
let componentsForAppWideScope = [];
|
||||
|
||||
const protectedVariables = ["_id", "type", "metaType", "organization", "_edit", "_publish", "variables", "utilityFunctions", "resources", "parameters",];
|
||||
const protectedVariables = ["_id", "type", "metaType", "organization", "_edit", "_publish", "variables", "utilityFunctions", "resources"];
|
||||
protectedVariables.forEach(p => {
|
||||
delete object[p];
|
||||
});
|
||||
|
||||
|
||||
//Templates
|
||||
if (object.templates) {
|
||||
object.templates = object.templates.map(v => {
|
||||
|
||||
return {
|
||||
|
||||
id: v.id,
|
||||
type: v.type,
|
||||
range: [v.minValue, v.maxValue],
|
||||
unit: v.unit
|
||||
}
|
||||
});
|
||||
object.templates = object.templates.map(v => ({
|
||||
id: v.id,
|
||||
type: v.type,
|
||||
range: [v.minValue, v.maxValue],
|
||||
unit: v.unit
|
||||
}));
|
||||
}
|
||||
|
||||
object.metrics_comp = [];
|
||||
object.metrics_global = [];
|
||||
|
||||
//Metrics
|
||||
if (object.metrics) {
|
||||
object.metrics.forEach(v => {
|
||||
let metricsDetail = {};
|
||||
|
||||
if (v.type === 'composite') {
|
||||
const componentNames = v.components.map(component => component.componentName).join(', ');
|
||||
|
||||
let windowDetail = {};
|
||||
|
||||
if (v.isWindowInput && v.input.type && v.input.interval && v.input.unit) {
|
||||
windowDetail.type = v.input.type;
|
||||
windowDetail.size = `${v.input.interval} ${v.input.unit}`;
|
||||
windowDetail = {
|
||||
type: v.input.type,
|
||||
size: `${v.input.interval} ${v.input.unit}`,
|
||||
};
|
||||
}
|
||||
if (v.isWindowOutput && v.output.type && v.output.interval && v.output.unit) {
|
||||
windowDetail.output = `${v.output.type} ${v.output.interval} ${v.output.unit}`;
|
||||
|
||||
// Only add windowDetail to metricsDetail if it's not empty
|
||||
if (Object.keys(windowDetail).length > 0) {
|
||||
metricsDetail.window = windowDetail;
|
||||
}
|
||||
|
||||
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
|
||||
|
||||
if (componentNames.length === 1) {
|
||||
componentsForAppSpecComp.push(componentNames[0]);
|
||||
} else if (componentNames.length > 1) {
|
||||
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
|
||||
}
|
||||
|
||||
metricsDetail = {
|
||||
name: v.name,
|
||||
type: v.type,
|
||||
template: componentNames,
|
||||
window: windowDetail
|
||||
template: v.template,
|
||||
formula: v.formula,
|
||||
...metricsDetail,
|
||||
};
|
||||
|
||||
if (v.isWindowOutput && v.output.type && v.output.interval && v.output.unit) {
|
||||
metricsDetail.output = `${v.output.type} ${v.output.interval} ${v.output.unit}`;
|
||||
}
|
||||
} else if (v.type === 'raw') {
|
||||
let configs = v.config.map(configItem => ({
|
||||
[configItem.name]: configItem.value
|
||||
}));
|
||||
|
||||
let windowDetailRaw = {};
|
||||
let configObject = configs.reduce((acc, current) => ({ ...acc, ...current }), {});
|
||||
|
||||
if (v.isWindowInputRaw && v.inputRaw.type && v.inputRaw.interval && v.inputRaw.unit) {
|
||||
windowDetailRaw.type = v.inputRaw.type;
|
||||
windowDetailRaw.size = `${v.inputRaw.interval} ${v.inputRaw.unit}`;
|
||||
}
|
||||
if (v.isWindowOutputRaw && v.outputRaw.type && v.outputRaw.interval && v.outputRaw.unit) {
|
||||
windowDetailRaw.output = `${v.outputRaw.type} ${v.outputRaw.interval} ${v.outputRaw.unit}`;
|
||||
}
|
||||
metricsDetail = {
|
||||
name: v.name,
|
||||
type: v.type,
|
||||
sensor: {
|
||||
type: v.sensor
|
||||
},
|
||||
window: windowDetailRaw
|
||||
type: v.sensor,
|
||||
config: configObject,
|
||||
}
|
||||
};
|
||||
|
||||
if (v.outputRaw && v.outputRaw.type && v.outputRaw.interval && v.outputRaw.unit) {
|
||||
metricsDetail.output = `${v.outputRaw.type} ${v.outputRaw.interval} ${v.outputRaw.unit}`;
|
||||
}
|
||||
if (v.template) {
|
||||
metricsDetail.template = v.template;
|
||||
}
|
||||
|
||||
let componentNames = v.components ? v.components.map(component => component.componentName) : [];
|
||||
|
||||
if (componentNames.length === 1) {
|
||||
componentsForAppSpecComp.push(componentNames[0]);
|
||||
} else if (componentNames.length > 1) {
|
||||
componentsForAppWideScope = componentsForAppWideScope.concat(componentNames);
|
||||
}
|
||||
}
|
||||
|
||||
const metric = {
|
||||
metrics: metricsDetail
|
||||
};
|
||||
|
||||
if (v.type === 'composite' && v.components.length < 2) {
|
||||
object.metrics_global.push(metric);
|
||||
} else if (v.type === 'composite' && v.components.length >= 2) {
|
||||
object.metrics_comp.push(metric);
|
||||
} else if (v.type === 'raw') {
|
||||
object.metrics_global.push(metric);
|
||||
if (v.components && v.components.length === 1) {
|
||||
object.metrics_comp.push({...metricsDetail});
|
||||
} else {
|
||||
object.metrics_global.push({...metricsDetail});
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
//SLO Violations
|
||||
if (object.sloViolations) {
|
||||
const processSloViolations = (violations) => {
|
||||
const buildConstraint = (v, parentCondition = '') => {
|
||||
@ -99,29 +114,58 @@ module.exports = {
|
||||
} else {
|
||||
const childConstraints = v.children.map(child => buildConstraint(child, v.condition)).join(` ${v.condition} `);
|
||||
|
||||
if (v.not) {
|
||||
constraint = `NOT (${childConstraints})`;
|
||||
} else {
|
||||
constraint = `(${childConstraints})`;
|
||||
}
|
||||
constraint = v.not ? `NOT (${childConstraints})` : `(${childConstraints})`;
|
||||
}
|
||||
return constraint;
|
||||
};
|
||||
|
||||
const combinedConstraint = buildConstraint(violations);
|
||||
const combinedConstraint = buildConstraint(JSON.parse(doc['sloViolations']));
|
||||
|
||||
const requirement = {
|
||||
return [{
|
||||
name: 'Combined SLO',
|
||||
type: 'slo',
|
||||
constraint: combinedConstraint
|
||||
};
|
||||
|
||||
return [requirement];
|
||||
}];
|
||||
};
|
||||
|
||||
object.sloViolations = processSloViolations(JSON.parse(doc['sloViolations']));
|
||||
object.sloViolations = processSloViolations(object.sloViolations);
|
||||
}
|
||||
|
||||
let specContent = {
|
||||
components: [
|
||||
...componentsForAppSpecComp,
|
||||
{
|
||||
name: "spec-comp",
|
||||
metrics: object.metrics_comp
|
||||
}
|
||||
], scopes: [{
|
||||
name: "app-wide-scope",
|
||||
components: componentsForAppWideScope,
|
||||
metrics: object.metrics_global,
|
||||
requirements: object.sloViolations ? object.sloViolations : [],
|
||||
}]
|
||||
};
|
||||
|
||||
//Parameters + push to app wide scope
|
||||
if (object.parameters) {
|
||||
const parametersMetrics = object.parameters.map(v => ({
|
||||
name: v.name,
|
||||
type: "constant",
|
||||
initialValue: v.initialValue,
|
||||
template: v.template
|
||||
}));
|
||||
|
||||
if (parametersMetrics.length > 0) {
|
||||
specContent.scopes.push({
|
||||
name: "parameters-scope",
|
||||
components: [],
|
||||
metrics: parametersMetrics,
|
||||
});
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
//Construct and return the final version
|
||||
const yamlDoc = {
|
||||
apiVersion: "nebulous/v1",
|
||||
kind: "MetricModel",
|
||||
@ -131,19 +175,10 @@ module.exports = {
|
||||
app: object.title,
|
||||
}
|
||||
},
|
||||
common: object.templates,
|
||||
spec: {
|
||||
components: object.metrics_comp
|
||||
},
|
||||
scopes: [
|
||||
{
|
||||
name: "app-wide-scope",
|
||||
requirements: object.sloViolations,
|
||||
components: object.metrics_global,
|
||||
}
|
||||
]
|
||||
templates: object.templates,
|
||||
spec: specContent
|
||||
};
|
||||
|
||||
return yamlDoc;
|
||||
}
|
||||
};
|
||||
|
||||
|
@ -134,7 +134,11 @@ module.exports = {
|
||||
template: {
|
||||
type: 'string',
|
||||
label: 'Template'
|
||||
}
|
||||
},
|
||||
initialValue: {
|
||||
type: 'float',
|
||||
label: 'Initial Value'
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -188,8 +192,8 @@ module.exports = {
|
||||
type: 'select',
|
||||
label: 'Level',
|
||||
choices: [
|
||||
{ label: 'Global', value: 'Global' },
|
||||
{ label: 'Components', value: 'Components' }
|
||||
{ label: 'Global', value: 'global' },
|
||||
{ label: 'Components', value: 'components' }
|
||||
],
|
||||
def: 'global'
|
||||
},
|
||||
@ -197,7 +201,7 @@ module.exports = {
|
||||
type: 'array',
|
||||
label: 'Components',
|
||||
if: {
|
||||
level: 'Components'
|
||||
level: 'components'
|
||||
},
|
||||
fields: {
|
||||
add: {
|
||||
@ -210,10 +214,12 @@ module.exports = {
|
||||
},
|
||||
name: {
|
||||
type: 'string',
|
||||
label: 'Name',
|
||||
if: {
|
||||
type: ['composite', 'raw']
|
||||
}
|
||||
label: 'Name'
|
||||
},
|
||||
template: {
|
||||
type: 'string',
|
||||
label: 'Template',
|
||||
textarea: true,
|
||||
},
|
||||
formula: {
|
||||
type: 'string',
|
||||
@ -239,7 +245,7 @@ module.exports = {
|
||||
type: 'select',
|
||||
label: 'Type Input',
|
||||
choices: [
|
||||
{label: 'All', value: 'all'},
|
||||
{label: 'Batch', value: 'batch'},
|
||||
{label: 'Sliding', value: 'sliding'}
|
||||
],
|
||||
},
|
||||
@ -255,7 +261,8 @@ module.exports = {
|
||||
{label: 'Sec', value: 'sec'},
|
||||
{label: 'Min', value: 'min'},
|
||||
{label: 'Hour', value: 'hour'},
|
||||
{label: 'Day', value: 'day'}
|
||||
{label: 'Day', value: 'day'},
|
||||
{label: 'Events', value: 'events'}
|
||||
],
|
||||
},
|
||||
},
|
||||
@ -280,7 +287,8 @@ module.exports = {
|
||||
type: 'select',
|
||||
choices: [
|
||||
{label: 'All', value: 'all'},
|
||||
{label: 'Sliding', value: 'sliding'}
|
||||
{label: 'First', value: 'first'},
|
||||
{label: 'Last', value: 'last'}
|
||||
],
|
||||
},
|
||||
interval: {
|
||||
@ -330,47 +338,6 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
isWindowInputRaw: {
|
||||
type: 'boolean',
|
||||
label: 'Window Input',
|
||||
if: {
|
||||
type: 'raw'
|
||||
}
|
||||
},
|
||||
inputRaw: {
|
||||
type: 'object',
|
||||
label: 'Input',
|
||||
fields: {
|
||||
add: {
|
||||
type: {
|
||||
type: 'select',
|
||||
label: 'Type Input',
|
||||
choices: [
|
||||
{label: 'All', value: 'all'},
|
||||
{label: 'Sliding', value: 'sliding'}
|
||||
],
|
||||
},
|
||||
interval: {
|
||||
type: 'integer',
|
||||
label: 'Interval',
|
||||
},
|
||||
unit: {
|
||||
type: 'select',
|
||||
label: 'Unit',
|
||||
choices: [
|
||||
{label: 'Ms', value: 'ms'},
|
||||
{label: 'Sec', value: 'sec'},
|
||||
{label: 'Min', value: 'min'},
|
||||
{label: 'Hour', value: 'hour'},
|
||||
{label: 'Day', value: 'day'}
|
||||
],
|
||||
},
|
||||
},
|
||||
},
|
||||
if: {
|
||||
isWindowInputRaw: true
|
||||
}
|
||||
},
|
||||
isWindowOutputRaw: {
|
||||
type: 'boolean',
|
||||
label: 'Window Output',
|
||||
@ -387,7 +354,8 @@ module.exports = {
|
||||
type: 'select',
|
||||
choices: [
|
||||
{label: 'All', value: 'all'},
|
||||
{label: 'Sliding', value: 'sliding'}
|
||||
{label: 'First', value: 'first'},
|
||||
{label: 'Last', value: 'last'}
|
||||
],
|
||||
},
|
||||
interval: {
|
||||
@ -433,6 +401,7 @@ module.exports = {
|
||||
label: 'Function Type',
|
||||
choices: [
|
||||
{label: 'Maximize', value: 'maximize'},
|
||||
{label: 'Minimize', value: 'minimize'},
|
||||
{label: 'Constant', value: 'constant'}
|
||||
]
|
||||
},
|
||||
@ -558,11 +527,11 @@ module.exports = {
|
||||
application_update_sender.send({
|
||||
"body":{"uuid":doc.uuid},
|
||||
"message_annotations":{
|
||||
"subject":doc.uuid
|
||||
"application":doc.uuid
|
||||
}
|
||||
});
|
||||
application_dsl_generic.send({body:{}, "message_annotations":{
|
||||
"subject":doc.uuid
|
||||
"application":doc.uuid
|
||||
}});
|
||||
}
|
||||
}
|
||||
@ -585,11 +554,11 @@ module.exports = {
|
||||
application_update_sender.send({
|
||||
"body":{"uuid":doc.uuid},
|
||||
"message_annotations":{
|
||||
"subject":doc.uuid
|
||||
"application":doc.uuid
|
||||
}
|
||||
});
|
||||
application_dsl_generic.send({body:{}, "message_annotations":{
|
||||
"subject":doc.uuid
|
||||
"application":doc.uuid
|
||||
}});
|
||||
}
|
||||
}
|
||||
@ -786,10 +755,10 @@ module.exports = {
|
||||
'string.base': 'Function Name must be a string.',
|
||||
'any.required': 'Function Name is required.'
|
||||
}),
|
||||
functionType: Joi.string().valid('maximize', 'constant').insensitive().required().messages({
|
||||
functionType: Joi.string().valid('maximize', 'constant','minimize').insensitive().required().messages({
|
||||
'string.base': 'Function Type must be a string.',
|
||||
'any.required': 'Function Type is required.',
|
||||
'any.only': 'Function Type must be either "Maximize" or "Constant".'
|
||||
'any.only': 'Function Type must be either "Maximize" , "Constant" or "Minimize.'
|
||||
}),
|
||||
functionExpression: Joi.string().trim().required().messages({
|
||||
'string.base': 'Function Expression must be a string.',
|
||||
@ -982,10 +951,12 @@ module.exports = {
|
||||
try {
|
||||
|
||||
const updatedApp = await self.find(req,{ uuid: uuid , organization:adminOrganization }).project(projection).toArray();
|
||||
const result = await exn.application_dsl(uuid,
|
||||
kubevela.json(updatedApp.pop()),
|
||||
""
|
||||
)
|
||||
const updatedAppItem = updatedApp.pop();
|
||||
|
||||
await exn.application_dsl(uuid,
|
||||
kubevela.json(updatedAppItem), metric_model.yaml(updatedAppItem)
|
||||
);
|
||||
|
||||
//TODO refactor to use apostrophe CMS ORM
|
||||
await self.apos.doc.db.updateOne(
|
||||
{ uuid: uuid },
|
||||
|
@ -8,9 +8,8 @@ module.exports = {
|
||||
try {
|
||||
let parsedEquation = mathutils.extractFromEquation(req.body.equation)
|
||||
const variableNames = mathutils.extractVariableNames(parsedEquation);
|
||||
const uppercaseVariableNames = variableNames.map(name => name.toUpperCase());
|
||||
return {
|
||||
variables: uppercaseVariableNames,
|
||||
variables: variableNames,
|
||||
};
|
||||
} catch (error) {
|
||||
throw error;
|
||||
|
@ -1,8 +1,13 @@
|
||||
const {v4: uuidv4} = require("uuid");
|
||||
const _ = require('lodash');
|
||||
|
||||
module.exports = {
|
||||
extend: '@apostrophecms/piece-type',
|
||||
options: {
|
||||
label: 'Platform',
|
||||
sort:{
|
||||
sortOrder:1,
|
||||
}
|
||||
},
|
||||
|
||||
fields: {
|
||||
@ -11,12 +16,29 @@ module.exports = {
|
||||
type: 'string',
|
||||
label: 'UUID',
|
||||
required: false
|
||||
},
|
||||
provider_name: {
|
||||
type: 'string',
|
||||
label: 'Provider Name',
|
||||
required: false
|
||||
},
|
||||
public: {
|
||||
type: 'boolean',
|
||||
label: 'Public',
|
||||
required: true,
|
||||
def: true
|
||||
},
|
||||
sortOrder: {
|
||||
type: 'integer',
|
||||
label: 'Sort Order',
|
||||
required: true,
|
||||
def: 0,
|
||||
}
|
||||
},
|
||||
group: {
|
||||
basics: {
|
||||
label: 'Basics',
|
||||
fields: ['uuid']
|
||||
fields: ['uuid','public','sortOrder']
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -38,10 +60,12 @@ module.exports = {
|
||||
const projection = {
|
||||
title: 1,
|
||||
uuid: 1,
|
||||
sortOrder: 1,
|
||||
};
|
||||
try {
|
||||
const platforms = await self.find(req).project(projection).toArray();
|
||||
return platforms;
|
||||
|
||||
return _.map(platforms, (p)=> { return {'title': p.title, 'uuid': p.uuid} })
|
||||
} catch (error) {
|
||||
throw self.apos.error('notfound', 'Platforms not found');
|
||||
}
|
||||
|
@ -8,54 +8,130 @@ const projection = {
|
||||
uuid: 1,
|
||||
organization: 1,
|
||||
platform: 1,
|
||||
appId: 1,
|
||||
appSecret: 1
|
||||
subnet: 1,
|
||||
endpoint: 1,
|
||||
identityVersion: 1,
|
||||
credentials: 1,
|
||||
defaultNetwork: 1,
|
||||
sshCredentials: 1,
|
||||
_platform: 1
|
||||
};
|
||||
const resourcesSchema = Joi.object({
|
||||
appId: Joi.string().required().messages({
|
||||
'string.empty': 'App ID is required.',
|
||||
'any.required': 'App ID is a required field.'
|
||||
}),
|
||||
appSecret: Joi.string().required().messages({
|
||||
'string.empty': 'App Secret is required.',
|
||||
'any.required': 'App Secret is a required field.'
|
||||
}),
|
||||
platform: Joi.string().required().messages({
|
||||
'string.empty': 'Platform is required.',
|
||||
_platform: Joi.required().messages({
|
||||
'any.required': 'Platform is a required field.'
|
||||
})
|
||||
}).unknown().options({ abortEarly: false });
|
||||
|
||||
const credentialsSchema = Joi.object({
|
||||
user: Joi.string().required().messages({
|
||||
'string.empty': 'Credentials: Users is required.',
|
||||
'any.required': 'Credentials: Users is a required field.'
|
||||
}),
|
||||
secret: Joi.string().required().messages({
|
||||
'string.empty': 'Credentials: Secret is required.',
|
||||
'any.required': 'Credentials: Secret is a required field.'
|
||||
}),
|
||||
}).unknown().options({ abortEarly: false });
|
||||
|
||||
module.exports = {
|
||||
extend: '@apostrophecms/piece-type',
|
||||
options: {
|
||||
label: 'Resource',
|
||||
label: 'Resource'
|
||||
},
|
||||
fields: {
|
||||
add: {
|
||||
uuid: {
|
||||
type: 'string',
|
||||
label: 'UUID',
|
||||
readOnly: true
|
||||
label: 'UUID'
|
||||
},
|
||||
platform: {
|
||||
type: 'string',
|
||||
label: 'Platform'
|
||||
_platform: {
|
||||
label: 'Platform',
|
||||
type: 'relationship',
|
||||
withType: "platforms",
|
||||
max:1
|
||||
},
|
||||
appId: {
|
||||
securityGroup:{
|
||||
type: 'string',
|
||||
label: 'App ID',
|
||||
label: 'Security Group'
|
||||
},
|
||||
appSecret: {
|
||||
type: 'string',
|
||||
label: 'App Secret',
|
||||
sshCredentials: {
|
||||
type: 'object',
|
||||
label: 'SSH Credentials',
|
||||
fields: {
|
||||
add: {
|
||||
username: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
},
|
||||
privateKey: {
|
||||
type: 'string',
|
||||
label: 'Private Key',
|
||||
textarea: true,
|
||||
},
|
||||
keyPairName: {
|
||||
type: 'string',
|
||||
label: 'Key Pair Name',
|
||||
},
|
||||
}
|
||||
}
|
||||
},
|
||||
subnet:{
|
||||
type: 'string',
|
||||
label: 'Subnet'
|
||||
},
|
||||
endpoint:{
|
||||
type: 'string',
|
||||
label: 'Endpoint'
|
||||
},
|
||||
identityVersion:{
|
||||
type: 'select',
|
||||
label: 'Identity Version',
|
||||
choices: [
|
||||
{'label':'v3', value:'3'}
|
||||
],
|
||||
def: '3'
|
||||
},
|
||||
defaultNetwork:{
|
||||
type: 'string',
|
||||
label: 'Default Network'
|
||||
},
|
||||
credentials:{
|
||||
type: 'object',
|
||||
label: 'Credentials',
|
||||
fields: {
|
||||
add: {
|
||||
user: {
|
||||
type: 'string',
|
||||
label: 'Username',
|
||||
},
|
||||
secret: {
|
||||
type: 'string',
|
||||
label: 'Secret',
|
||||
textarea: true,
|
||||
},
|
||||
domain: {
|
||||
type: 'string',
|
||||
label: 'Domain',
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
group: {
|
||||
basics: {
|
||||
label: 'Details',
|
||||
fields: ['title', 'uuid', 'platform', 'appId', 'appSecret']
|
||||
fields: ['title', 'uuid', '_platform', 'identityVersion']
|
||||
},
|
||||
network: {
|
||||
label: 'Network',
|
||||
fields: ['defaultNetwork','endpoint', 'subnet']
|
||||
},
|
||||
security: {
|
||||
label: 'Security',
|
||||
fields: ['securityGroup','credentials','sshCredentials']
|
||||
}
|
||||
|
||||
}
|
||||
},
|
||||
handlers(self) {
|
||||
@ -79,16 +155,7 @@ module.exports = {
|
||||
await generateUuid(doc);
|
||||
try{
|
||||
|
||||
if(doc.aposMode === 'published'){
|
||||
const message = await exn.register_cloud(
|
||||
doc.uuid,
|
||||
doc.appId,
|
||||
doc.appSecret,
|
||||
)
|
||||
console.log("Registered ",message);
|
||||
|
||||
}
|
||||
await self.updateWithPlatformInfo(doc);
|
||||
await self.updateWithPlatformInfo(req,doc);
|
||||
await assignOrganization(req, doc);
|
||||
|
||||
}catch(e){
|
||||
@ -96,11 +163,10 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
},
|
||||
|
||||
|
||||
beforeSave: {
|
||||
async handler(req, doc, options) {
|
||||
try {
|
||||
await self.updateWithPlatformInfo(req,doc)
|
||||
self.validateDocument(doc);
|
||||
} catch (error) {
|
||||
if (error.name === 'required' && error.error && error.error.length > 0) {
|
||||
@ -113,28 +179,45 @@ module.exports = {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
}
|
||||
},
|
||||
|
||||
methods(self) {
|
||||
return {
|
||||
async updateWithPlatformInfo(doc) {
|
||||
if (doc.platform && !doc.platformUpdated) {
|
||||
const platformPiece = await self.apos.doc.db.findOne({
|
||||
type: 'platforms',
|
||||
uuid: doc.platform
|
||||
});
|
||||
async updateWithPlatformInfo(req, doc) {
|
||||
|
||||
if (platformPiece) {
|
||||
doc.platform = platformPiece.title;
|
||||
doc.platformUpdated = true;
|
||||
} else {
|
||||
throw self.apos.error('notfound', 'Platform not found');
|
||||
if(req.body.platform && req.body.platform.uuid){
|
||||
|
||||
const platform = await self.apos.modules['platforms'].find(req,{'uuid':req.body.platform.uuid}).toObject();
|
||||
if(!platform){
|
||||
throw self.apos.error('notfound', 'Platform not found or empty');
|
||||
}
|
||||
doc.platformIds = [platform._id]
|
||||
doc._platform = [platform]
|
||||
delete req.body.platform
|
||||
}
|
||||
},
|
||||
return doc
|
||||
|
||||
},
|
||||
cleanUp:function(self,req, resources){
|
||||
|
||||
_.each(resources,(r)=>{
|
||||
r= self.removeForbiddenFields(req,r)
|
||||
r['platform'] = null
|
||||
|
||||
if( r._platform.length > 0 ){
|
||||
r['platform'] = {
|
||||
'uuid': r._platform[0].uuid,
|
||||
'title': r._platform[0].title,
|
||||
}
|
||||
}
|
||||
|
||||
delete r._platform
|
||||
|
||||
})
|
||||
|
||||
return resources
|
||||
},
|
||||
validateDocument(doc) {
|
||||
const validateField = (data, schema) => {
|
||||
const {error} = schema.validate(data);
|
||||
@ -147,6 +230,7 @@ module.exports = {
|
||||
}
|
||||
};
|
||||
validateField(doc, resourcesSchema);
|
||||
validateField(doc.credentials, credentialsSchema);
|
||||
}
|
||||
}
|
||||
},
|
||||
@ -159,10 +243,24 @@ module.exports = {
|
||||
const adminOrganization = currentUser.organization;
|
||||
try {
|
||||
const filters = {
|
||||
organization: adminOrganization
|
||||
organization: adminOrganization,
|
||||
};
|
||||
const page = req.query.page || 1
|
||||
const pageSize = req.query.pageSize || 10
|
||||
const count = await self.find(req, filters).toCount()
|
||||
|
||||
const resources = await self.find(req, filters).project(projection)
|
||||
.withPublished(true)
|
||||
.perPage(pageSize)
|
||||
.page(page)
|
||||
.toArray()
|
||||
|
||||
|
||||
return {
|
||||
"total":count,
|
||||
"page": page,
|
||||
"results":self.cleanUp(self,req,resources)
|
||||
};
|
||||
const resources = await self.find(req, filters).project(projection).toArray();
|
||||
return resources;
|
||||
} catch (error) {
|
||||
throw self.apos.error('notfound', 'Resource not found');
|
||||
}
|
||||
@ -182,19 +280,31 @@ module.exports = {
|
||||
throw self.apos.error('notfound', 'Resource not found');
|
||||
}
|
||||
|
||||
return doc;
|
||||
return self.cleanUp(self,req, [doc])[0]
|
||||
} catch (error) {
|
||||
throw self.apos.error(error.name, error.message);
|
||||
}
|
||||
},
|
||||
async 'candidates'(req) {
|
||||
async ':uuid/candidates'(req) {
|
||||
const uuid = req.params.uuid;
|
||||
|
||||
if (!( req.user.organization)) {
|
||||
throw self.apos.error('forbidden', 'You do not have permission to perform this action');
|
||||
}
|
||||
|
||||
const currentUser = req.user;
|
||||
const adminOrganization = currentUser.organization;
|
||||
|
||||
try {
|
||||
|
||||
const doc = await self.find(req, { uuid: uuid , organization:adminOrganization}).project(projection).toObject();
|
||||
if (!doc) {
|
||||
throw self.apos.error('notfound', 'Resource not found');
|
||||
}
|
||||
|
||||
|
||||
await exn.register_cloud(doc)
|
||||
await new Promise(resolve => setTimeout(resolve, 10000));
|
||||
const message = await exn.get_cloud_candidates()
|
||||
return _.map(JSON.parse(message.body), (r)=>{
|
||||
return {
|
||||
@ -262,7 +372,6 @@ module.exports = {
|
||||
if (!(req.user.role === "admin" && req.user.organization)) {
|
||||
throw self.apos.error('forbidden', 'You do not have permission to perform this action');
|
||||
}
|
||||
self.validateDocument(updateData);
|
||||
|
||||
const adminOrganization = req.user.organization;
|
||||
|
||||
@ -271,33 +380,18 @@ module.exports = {
|
||||
uuid: uuid,
|
||||
organization: adminOrganization
|
||||
};
|
||||
const resourcesToUpdate = await self.find(req, filters).project(projection).toArray();
|
||||
|
||||
if (!resourcesToUpdate || resourcesToUpdate.length === 0) {
|
||||
const doc = await self.find(req, filters).toObject();
|
||||
if (!doc) {
|
||||
throw self.apos.error('notfound', 'Resource not found');
|
||||
}
|
||||
await self.updateWithPlatformInfo(req,doc)
|
||||
self.validateDocument(doc)
|
||||
|
||||
const doc = resourcesToUpdate[0];
|
||||
let update = { ...doc, ...updateData };
|
||||
await self.update(req,update)
|
||||
|
||||
return self.cleanUp(self,req,[update])[0]
|
||||
|
||||
if ('platform' in updateData) {
|
||||
let docToUpdate = { ...doc, ...updateData };
|
||||
await self.updateWithPlatformInfo(docToUpdate);
|
||||
|
||||
await self.apos.doc.db.updateOne(
|
||||
{ uuid: uuid },
|
||||
{ $set: docToUpdate }
|
||||
);
|
||||
} else {
|
||||
await self.apos.doc.db.updateOne(
|
||||
{ uuid: uuid },
|
||||
{ $set: updateData }
|
||||
);
|
||||
}
|
||||
|
||||
const resourceUpdated = await self.find(req, filters).project(projection).toArray();
|
||||
|
||||
return resourceUpdated;
|
||||
} catch (error) {
|
||||
throw self.apos.error(error.name, error.message);
|
||||
}
|
||||
|
@ -42,6 +42,7 @@
|
||||
"dependencies": {
|
||||
"@johmun/vue-tags-input": "^2.1.0",
|
||||
"apostrophe": "^3.61.1",
|
||||
"dotenv": "^16.4.5",
|
||||
"flat": "^5.0.2",
|
||||
"joi": "^17.11.0",
|
||||
"mathjs": "^12.2.1",
|
||||
|
Loading…
x
Reference in New Issue
Block a user