lib/models/node.js
// Copyright 2015, EMC, Inc.
'use strict';
module.exports = NodeModelFactory;
NodeModelFactory.$provide = 'Models.Node';
NodeModelFactory.$inject = [
'Model',
'Services.Waterline',
'_',
'Promise',
'Constants',
'Services.Configuration',
'Protocol.Events',
'Protocol.Waterline'
];
var bson = require('bson');
function NodeModelFactory (
Model,
waterline,
_,
Promise,
Constants,
configuration,
eventsProtocol,
waterlineProtocol
) {
var dbType = configuration.get('databaseType', 'mongo');
var attributes = {
identifiers: {
type: 'array',
required: false
},
name: {
type: 'string',
required: true
},
obms: {
collection: 'obms',
via: 'node'
},
type: {
type: 'string',
enum: _.values(Constants.NodeTypes),
defaultsTo: 'compute'
},
workflows: {
collection: 'graphobjects',
via: 'node'
},
catalogs: {
collection: 'catalogs',
via: 'node'
},
sku: {
model: 'skus'
},
docker: {
type: 'json',
json: true,
required: false
},
bootSettings: {
type: 'json',
json: true,
required: false
},
ibms: {
collection: 'ibms',
via: 'node'
},
autoDiscover: {
type: 'boolean',
defaultsTo: false
},
relations: {
type: 'array',
defaultsTo: []
},
tags: {
type: 'array',
defaultsTo: []
},
// We only count a node as having been discovered if
// a node document exists AND it has any catalogs
// associated with it
discovered: function() {
var self = this;
return waterline.catalogs.findOne({"node": self.id})
.then(function(catalog) {
return !_.isEmpty(catalog);
});
}
};
if( dbType !== 'mongo' ) {
// For mongo compatibility we have to add the
// BSON id column to non-mongo DBs
attributes.id = {
type: 'string',
primaryKey: true
};
}
return Model.extend({
connection: dbType,
identity: 'nodes',
attributes: attributes,
$indexes: [
{
keys: { identifiers: 1 }
},
{
keys: { type: 1 }
}
],
addTags: function(id, tags) {
var self = this;
var dbType = _.first(self.connection);
switch(dbType) {
case 'mongo':
var query = { _id: waterline.nodes.mongo.objectId(id) };
var update = {
$addToSet: { tags: { $each: tags } },
$set: { updatedAt: new Date() }
};
var options = { new: true };
return self.findAndModifyMongo(query, {}, update, options)
.then(function(record) {
record.id = record._id;
waterlineProtocol.publishRecord(self, 'updated', record);
waterlineProtocol.publishRecord(self, 'tag-added', record);
return record;
});
case 'postgresql':
// sails-postgres stores 'array' types as 'text' containing JSON. We must
// unfurl the JSON, merge with the request tag array, ensure uniqueness of entries,
// then JSONify the result set and update the tags entries with it.
return self.postgresqlRunLockedQuery('' +
'WITH t as (select array_agg(elem::text) from nodes, json_array_elements(nodes.tags::json) elem where id = $2), ' + //jshint ignore: line
's as (select distinct unnest(array_cat(t.array_agg, ARRAY[$1])) from t) ' +
'UPDATE nodes SET tags = concat( \'[\', (select string_agg(unnest,\',\') from s), \']\'),"updatedAt"=$3 WHERE id = $2',//jshint ignore: line
[ '"' + tags.join('","') + '"', id, new Date()])
.then(function() {
return self.find({id: id})
.then(function(record) {
record.id = record._id;
waterlineProtocol.publishRecord(self, 'updated', record);
waterlineProtocol.publishRecord(self, 'tag-added', record);
return record;
});
});
}
},
remTags: function(id, tag) {
var self = this;
var dbType = _.first(self.connection);
switch(dbType) {
case 'mongo':
var query = { _id: waterline.nodes.mongo.objectId(id) };
var update = {
$pull: { tags: tag },
$set: { updatedAt: new Date() }
};
var options = { new: true };
return self.findAndModifyMongo(query, {}, update, options)
.then(function(record) {
record.id = record._id;
waterlineProtocol.publishRecord(self, 'updated', record);
waterlineProtocol.publishRecord(self, 'tag-removed', record);
return record;
});
case 'postgresql':
// sails-postgres stores 'array' types as 'text' containing JSON. We must
// unfurl the JSON, remove the entry from the array, ensure uniqueness of entries,
// then JSONify the result set and update the tags entries with it.
return self.postgresqlRunLockedQuery('' +
'WITH t as (select array_agg(elem::text) from nodes, json_array_elements(nodes.tags::json) elem where id = $2), ' + //jshint ignore: line
's as (select distinct unnest(array_remove(t.array_agg, $1)) from t) ' +
'UPDATE nodes SET tags = concat( \'[\', (select string_agg(unnest,\',\') from s), \']\'),"updatedAt"=$3 WHERE id = $2', //jshint ignore: line
[ JSON.stringify(tag), id, new Date()])
.then(function() {
return self.findOne({id: id})
.then(function(record) {
record.id = record._id;
waterlineProtocol.publishRecord(self, 'updated', record);
waterlineProtocol.publishRecord(self, 'tag-removed', record);
return record;
});
});
}
},
findByTag: function(tag) {
return waterline.nodes.find({tags: tag});
},
beforeCreate: function dbCompatibility(obj, next) {
var dbType = _.first(this.connection);
if(dbType !== 'mongo') {
var objId = new bson.ObjectID();
obj.id = obj.id || objId.toString();
obj.obmSettings = obj.obmSettings || [];
obj.bootSettings = obj.bootSettings || {};
return next();
}
return next();
},
getNodeById: function(nodeId) {
var self = this;
return self.findOne({id: nodeId}).populate('obms').populate('ibms');
},
});
}