Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
160 changes: 112 additions & 48 deletions lib/jobs/ucs-discovery.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ di.annotate(UcsDiscoveryJobFactory, new di.Inject(
'Services.Waterline',
'Services.Encryption',
'_',
'JobUtils.UcsTool'
'JobUtils.UcsTool',
'Constants'
));

function UcsDiscoveryJobFactory(
Expand All @@ -28,7 +29,8 @@ function UcsDiscoveryJobFactory(
waterline,
encryption,
_,
UcsTool
UcsTool,
Constants
) {
var logger = Logger.initialize(UcsDiscoveryJobFactory);

Expand Down Expand Up @@ -77,7 +79,7 @@ function UcsDiscoveryJobFactory(

return self.getRoot()
.then(function(root) {
return [ root, self.createRackmounts(root) ];
return [ root, self.createRackmounts(root), self.createServers(root) ];
})
.spread(function() {
self._done();
Expand Down Expand Up @@ -140,44 +142,7 @@ function UcsDiscoveryJobFactory(
return res.body;
})
.map(function(data) {
assert.array(data.macs);
assert.string(data.name);
assert.string(data.path);

var config = Object.assign({}, self.settings);
obm = {
config: config,
service: 'ucs-obm-service'
};
var node = {
type: 'compute',
name: data.path,
identifiers: data.macs,
obm:[obm],
relations : []
};

// Update existing node or create a new one
return waterline.nodes.needOne({identifiers:data.macs})
.then(function(curNode) {
return waterline.nodes.updateOne(
{ id: curNode.id },
node
)
.then(function(data){
return data;
});
})
.catch(function(error) {
if (error.name === 'NotFoundError') {
return waterline.nodes.create(node);
}
throw error;
})
.then(function(data){
return self.createObms(data.id,obm);
});

self.createNode(data, Constants.NodeTypes.Compute);
});
}else{
logger.warning('No rackmount servers found Found');
Expand All @@ -186,15 +151,114 @@ function UcsDiscoveryJobFactory(
});
};


/**
* @function getRoot
* @description create obm setting for the node
* @function createServers
* @description discovers all the chassis servers in the Cisco UCS
*/
UcsDiscoveryJob.prototype.createObms = function (nodeId, obm){
return waterline.obms.upsertByNode(nodeId, obm)
.then(function(n) {
return n;
UcsDiscoveryJob.prototype.createServers = function (root) {
var self = this;
var username = this.settings.username;
var password = this.settings.password;
var ucs = this.settings.ucs;
var baseurl =
this.settings.protocol + "://" + this.settings.host + ":" + this.settings.port;
var url =
baseurl + "/chassis?host=" + ucs + "&user=" + username + "&password=" + password;
return Promise.try(function () {
if (!_.has(root, 'Chassis')) {
logger.warning('No rackmount servers found Found');
return;
}
return self.ucs.clientRequest(url)
.then(function(res) {
assert.object(res);
return res.body;
})
.map(function(chassisData) {
return self.createNode(chassisData, Constants.NodeTypes.Enclosure)
.spread(function (chassisNode) {
var chassisId = chassisNode.id;
var nodeList = [];
return Promise.map(chassisData.members, function (data) {
return self.createNode(data, Constants.NodeTypes.Compute)
.spread(function(newNode) {
var relations = [{
relationType: 'enclosedBy',
targets: [chassisId]
}];
nodeList.push(newNode.id);
return self.updateRelations(newNode.id, relations);
});
})
.then(function() {
var chassisRelations = [{
relationType: 'encloses',
targets: nodeList
}];
return self.updateRelations(chassisId, chassisRelations);
});
});
});
});
};

UcsDiscoveryJob.prototype.updateRelations = function(nodeId, relations) {
// Update existing node with new relations or create one
return waterline.nodes.needOneById(nodeId)
.then(function(curNode) {
relations = _.uniq(relations.concat(curNode.relations), 'relationType');
return waterline.nodes.updateOne(
{ id: curNode.id },
{ relations: relations }
);
});
};


UcsDiscoveryJob.prototype.createNode = function(data, type) {

var self = this;
var config = Object.assign({}, self.settings);

assert.string(data.name);
assert.string(data.path);

var id;
if (type === Constants.NodeTypes.Compute) {
assert.array(data.macs);
id = data.macs;
} else {
id = [config.ucs, data.path];
}

var obm = {
config: config,
service: 'ucs-obm-service'
};

var node = {
type: type,
name: data.path,
identifiers: id,
relations : []
};

// Update existing node or create a new one
return waterline.nodes.needOne({identifiers:data.macs})
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please confirm whether these macs will always be stored in fixed order for the same ucs server. For example, if the server has mac1 and mac2, if first time it returns [mac1, mac2], but the second time it is [mac2, mac1]. These two array are not identical but this should be considered to be the same server.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I will make sure the ucs-service always returns the macs in the same order.

.then(function(curNode) {
return waterline.nodes.updateOne(
{ id: curNode.id },
node
);
})
.catch(function(error) {
if (error.name === 'NotFoundError') {
return waterline.nodes.create(node);
}
throw error;
})
.then(function(data){
return [data, waterline.obms.upsertByNode(data.id,obm)];
Copy link
Contributor

@yyscamper yyscamper Feb 16, 2017

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is not a bug since this function is private and only used by your job, but may not a good design. This function will return Promise, but how should the caller know it should use the spread rather than the then to get the returned node document? The return value of waterline.obms.upsertByNode actually not be used anywhere, so it should be better hidden in this function itself.

For this comment, either change this or not is OK for me. Just give me two cents here.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The node data is needed by the calling function, both promises need to be returned by the function.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@geoff-reid It doesn't look like you ever use the fulfillment value from the obms upsert, so maybe you can just do this instead:

.tap(function(data) {
    return waterline.obms.upsertByNode(data.id,obm);
});

That will return a promise that fulfills to data when the upsert promise fulfills..

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@brianparry got my meaning.

});
};

Expand Down
83 changes: 58 additions & 25 deletions spec/lib/jobs/ucs-discovery-spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -9,34 +9,38 @@ describe('Ucs Discovery Job', function () {
ucsTool,
rackmountData,
rootData,
serverData,
waterline = {},
Error;


var obm = {
config: {
uri: "http://10.240.19.226:6080/sys",
host: "10.240.19.226",
root: "/sys",
port: "6080",
protocol: "http",
username: "ucspe",
password: "ucspe",
ucs: "10.240.19.236",
verifySSL: false
},
service: "ucs-obm-service"
};

var node = {
id: 'abc',
type: 'enclosure',
name: 'Node',
identifiers: [],
relations: [
{ relationType: 'encloses',
targets: [ '/fake' ] },
{ relationType: 'enclosedBy',
targets: [ '/fake' ]}
]
};
var obm = {
"config": {
"uri": "http://10.240.19.226:6080/sys",
"host": "10.240.19.226",
"root": "/sys",
"port": "6080",
"protocol": "http",
"username": "ucspe",
"password": "ucspe",
"ucs": "10.240.19.236",
"verifySSL": false
},
"service": "ucs-obm-service"
{ relationType: 'encloses',
targets: [ '/fake' ] },
{ relationType: 'enclosedBy',
targets: [ '/fake' ]}
],
obm: [obm]
};

before(function() {
Expand All @@ -50,7 +54,8 @@ describe('Ucs Discovery Job', function () {
waterline.nodes = {
create: sandbox.stub().resolves(node),
needOne: sandbox.stub().resolves(node),
updateOne: sandbox.stub().resolves(node)
updateOne: sandbox.stub().resolves(node),
needOneById: sandbox.stub().resolves(node)
};
waterline.obms = {
upsertByNode: sandbox.stub().resolves(obm)
Expand Down Expand Up @@ -105,21 +110,49 @@ describe('Ucs Discovery Job', function () {

};

serverData = {
body: [
{
members: [
{
macs: [
"00:00:FF:FF:32:02"
],
name: "blade-1",
path: "sys/chassis-6/blade-1"
}
],
name: "chassis-6",
path: "sys/chassis-6"
}
]
};
waterline.nodes.updateOne.reset();
});

describe('usc discovery', function() {
it('should successfully run the job', function() {
ucsTool.clientRequest.onCall(0).resolves(rootData);
ucsTool.clientRequest.onCall(1).resolves(rackmountData);
ucsJob._run()
.then(function() {
expect(waterline.nodes.updateOne).to.be.called.once;
});
ucsTool.clientRequest.onCall(2).resolves(serverData);
return ucsJob._run()
.then(function() {
expect(waterline.nodes.updateOne).to.be.called.once;
});
});

it('should not update a node', function() {
ucsTool.clientRequest.onCall(0).resolves([]);
return ucsJob._run()
.then(function() {
expect(waterline.nodes.updateOne).to.not.be.called.once;
});
});


it('should successfully log in', function() {
ucsTool.clientRequest.onCall(0).resolves({"body":"cookie"});
ucsJob.logIn()
return ucsJob.logIn()
.then(function(data) {
expect(data).to.deep.equal("cookie");
});
Expand Down