From da4d9f2f5e65b5d135f226da447ea4bbb8f330ad Mon Sep 17 00:00:00 2001 From: Jamie Zhuang Date: Tue, 5 Jun 2018 00:48:46 -0700 Subject: [PATCH] Add update-lookups for redfish range discovery Here is the scenario we are trying to accomplish (The user already has the nodes powered on) 1. knowing the Idrac IP, we want to run the redfish discovery workflow. This step will create the obm and catalog redfish. We can add a step here to get the host mac addresses and populate the lookup table. 2. Since the node is discovered a pxe discovery shouldn't trigger automatically but we should have a pxe discovery workflow that the user can run against a discovered node to populate the catalog with pxe cataloging data. https://rackhd.atlassian.net/browse/RAC-6777 --- lib/jobs/general-redfish-catalog.js | 1 - lib/jobs/redfish-update-lookups.js | 87 +++++++++++ .../base-tasks/redfish-update-lookups.js | 12 ++ lib/task-data/tasks/redfish-update-lookups.js | 11 ++ spec/lib/jobs/redfish-update-lookups-spec.js | 142 ++++++++++++++++++ .../base-tasks/redfish-update-lookups-spec.js | 16 ++ .../tasks/redfish-update-lookups-spec.js | 16 ++ 7 files changed, 284 insertions(+), 1 deletion(-) create mode 100644 lib/jobs/redfish-update-lookups.js create mode 100644 lib/task-data/base-tasks/redfish-update-lookups.js create mode 100644 lib/task-data/tasks/redfish-update-lookups.js create mode 100644 spec/lib/jobs/redfish-update-lookups-spec.js create mode 100644 spec/lib/task-data/base-tasks/redfish-update-lookups-spec.js create mode 100644 spec/lib/task-data/tasks/redfish-update-lookups-spec.js diff --git a/lib/jobs/general-redfish-catalog.js b/lib/jobs/general-redfish-catalog.js index 6ad75f37..220a8723 100644 --- a/lib/jobs/general-redfish-catalog.js +++ b/lib/jobs/general-redfish-catalog.js @@ -235,7 +235,6 @@ function GeneralRedfishCatalogJobFactory( }) .catch(function (err) { logger.info(JSON.stringify(err, null,4)); - self._done(err); }); }; diff --git a/lib/jobs/redfish-update-lookups.js b/lib/jobs/redfish-update-lookups.js new file mode 100644 index 00000000..e22b6057 --- /dev/null +++ b/lib/jobs/redfish-update-lookups.js @@ -0,0 +1,87 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; + +module.exports = RedfishUpdateLookupsJobFactory; + +var di = require('di'); +di.annotate(RedfishUpdateLookupsJobFactory, new di.Provide('Job.Redfish.Update.Lookups')); +di.annotate(RedfishUpdateLookupsJobFactory, new di.Inject( + 'Job.Base', + 'Assert', + 'Logger', + 'Util', + 'Promise', + 'Services.Waterline', + '_' +)); + +function RedfishUpdateLookupsJobFactory( + BaseJob, + assert, + Logger, + util, + Promise, + waterline, + _ +) { + var logger = Logger.initialize(RedfishUpdateLookupsJobFactory); + + function RedfishUpdateLookupsJob(options, context, taskId) { + RedfishUpdateLookupsJob.super_.call(this, logger, options, context, taskId); + this.chassis = this.context.chassis || []; + this.systems = this.context.systems || []; + this.cooling = this.context.cooling || []; + this.power = this.context.power || []; + this.networks = this.context.networks || []; + this.allEndpoints = _.union( + this.systems, + this.power, + this.cooling, + this.networks, + this.chassis + ); + } + + util.inherits(RedfishUpdateLookupsJob, BaseJob); + + RedfishUpdateLookupsJob.prototype._run = function() { + var self = this; + return Promise.resolve(self.allEndpoints) + .map(self.updateLookups.bind(self), {concurrency: 128}) + .then(function() { + return self._done(); + }) + .catch(function(err) { + return self._done(err); + }); + }; + + RedfishUpdateLookupsJob.prototype.updateLookups = function(nodeId) { + var ethernetInterfacesSource = '/redfish/v1/Systems/' + nodeId + '/EthernetInterfaces'; + return waterline.catalogs.findLatestCatalogOfSource(nodeId, ethernetInterfacesSource) + .then(function(ethernetInterfacesCatalog){ + if(!_.has(ethernetInterfacesCatalog, 'data.Members')) { + throw new Error('Could not find Members in EthernetInterfaces catalog!'); + } + return _.map(ethernetInterfacesCatalog.data.Members, function(member){ + return _.last(_.get(member, "@odata_id").split('/')); + }); + }) + .map(function(nicId){ + var nicSource = ethernetInterfacesSource + '/' + nicId; + return waterline.catalogs.findLatestCatalogOfSource(nodeId, nicSource) + .then(function(nicCatalog) { + if(!nicCatalog || !nicCatalog.data || !nicCatalog.data.MacAddress) { + throw new Error('Could not find MacAddress in NIC catalog!'); + } + return waterline.lookups.upsertNodeToMacAddress( + nodeId, + nicCatalog.data.MacAddress + ); + }); + }); + }; + + return RedfishUpdateLookupsJob; +} diff --git a/lib/task-data/base-tasks/redfish-update-lookups.js b/lib/task-data/base-tasks/redfish-update-lookups.js new file mode 100644 index 00000000..45b5e47a --- /dev/null +++ b/lib/task-data/base-tasks/redfish-update-lookups.js @@ -0,0 +1,12 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; + +module.exports = { + friendlyName: 'Redfish Update Lookups', + injectableName: 'Task.Base.Redfish.Update.Lookups', + runJob: 'Job.Redfish.Update.Lookups', + requiredOptions: [], + requiredProperties: {}, + properties: {} +}; diff --git a/lib/task-data/tasks/redfish-update-lookups.js b/lib/task-data/tasks/redfish-update-lookups.js new file mode 100644 index 00000000..30f2bf77 --- /dev/null +++ b/lib/task-data/tasks/redfish-update-lookups.js @@ -0,0 +1,11 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; + +module.exports = { + friendlyName: 'Redfish Update Lookups', + injectableName: 'Task.Redfish.Update.Lookups', + implementsTask: 'Task.Base.Redfish.Update.Lookups', + options: {}, + properties: {} +}; diff --git a/spec/lib/jobs/redfish-update-lookups-spec.js b/spec/lib/jobs/redfish-update-lookups-spec.js new file mode 100644 index 00000000..7f8fcb76 --- /dev/null +++ b/spec/lib/jobs/redfish-update-lookups-spec.js @@ -0,0 +1,142 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; +var uuid = require('node-uuid'); + +describe('redfish-update-lookups-job', function() { + var waterline = { catalogs: {}, lookups: {} }, + RedfishUpdateLookupsJob, + job; + + var ethernetInterfacesCatalog = { + "id": "abf88cc8-ea5c-4db8-961c-5faf39bec1b9", + "node": "/api/2.0/nodes/5afbe4080ff1d4bb0e2c0232", + "createdAt": "2018-05-16T07:56:23.785Z", + "updatedAt": "2018-05-16T07:56:23.785Z", + "source": "/redfish/v1/Systems/5afbe4080ff1d4bb0e2c0232/EthernetInterfaces", + "data": { + "@odata_context": "/redfish/v1/$metadata#EthernetInterfaceCollection.EthernetInterfaceCollection", + "@odata_id": "/redfish/v1/Systems/18D2182-CN7475158B0188/EthernetInterfaces", + "@odata_type": "#EthernetInterfaceCollection.EthernetInterfaceCollection", + "Description": "Collection of Ethernet Interfaces for this System", + "Members": [ + { + "@odata_id": "/redfish/v1/Systems/18D2182-CN7475158B0188/EthernetInterfaces/NIC.Embedded.1-1-1" + } + ], + "Members@odata_count": 1, + "Name": "System Ethernet Interface Collection" + } + }; + var nicCatalog = { + "id": "7fab2fdb-0e7a-4348-b600-15ce532c7ced", + "node": "/api/2.0/nodes/5afbe4080ff1d4bb0e2c0232", + "createdAt": "2018-05-16T07:56:27.744Z", + "updatedAt": "2018-05-16T07:56:27.744Z", + "source": "/redfish/v1/Systems/5afbe4080ff1d4bb0e2c0232/EthernetInterfaces/NIC.Embedded.1-1-1", + "data": { + "@odata_context": "/redfish/v1/$metadata#EthernetInterface.EthernetInterface", + "@odata_id": "/redfish/v1/Systems/18D2182-CN7475158B0188/EthernetInterfaces/NIC.Embedded.1-1-1", + "@odata_type": "#EthernetInterface.v1_0_2.EthernetInterface", + "AutoNeg": false, + "Description": "Embedded NIC 1 Port 1 Partition 1", + "FQDN": null, + "FullDuplex": true, + "HostName": null, + "IPV6DefaultGateway": null, + "IPv4Addresses": [], + "IPv4Addresses@odata_count": 0, + "IPv6AddressPolicyTable": [], + "IPv6AddressPolicyTable@odata_count": 0, + "IPv6Addresses": [], + "IPv6Addresses@odata_count": 0, + "IPv6StaticAddresses": [], + "IPv6StaticAddresses@odata_count": 0, + "Id": "NIC.Embedded.1-1-1", + "InterfaceEnabled": null, + "MTUSize": null, + "MacAddress": "00:8C:FA:F3:78:18", + "MaxIPv6StaticAddresses": null, + "Name": "System Ethernet Interface", + "NameServers": [], + "NameServers@odata_count": 0, + "PermanentMACAddress": "00:8C:FA:F3:78:18", + "SpeedMbps": 10240, + "Status": { + "Health": "Ok", + "State": "Enabled" + }, + "UefiDevicePath": "PciRoot(0x1)/Pci(0x1,0x0)/Pci(0x0,0x0)", + "VLAN": null + } + }; + + before(function() { + helper.setupInjector([ + helper.require('/lib/jobs/base-job.js'), + helper.require('/lib/jobs/redfish-update-lookups.js'), + helper.di.simpleWrapper(waterline, 'Services.Waterline') + ]); + this.sandbox = sinon.sandbox.create(); + RedfishUpdateLookupsJob = helper.injector.get('Job.Redfish.Update.Lookups'); + }); + + beforeEach(function() { + waterline.catalogs.findLatestCatalogOfSource = this.sandbox.stub(); + waterline.lookups.upsertNodeToMacAddress = this.sandbox.stub(); + job = new RedfishUpdateLookupsJob({}, { systems: ['someNodeId']}, uuid.v4()); + }); + + afterEach(function() { + this.sandbox.restore(); + }); + + it('should update lookups from catalog', function() { + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces' + ).resolves(ethernetInterfacesCatalog); + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces/NIC.Embedded.1-1-1' + ).resolves(nicCatalog); + return job._run() + .then(function() { + expect(waterline.lookups.upsertNodeToMacAddress).to.be.calledOnce; + expect(waterline.lookups.upsertNodeToMacAddress).to.be + .calledWith('someNodeId', '00:8C:FA:F3:78:18'); + }); + }); + + it('should fail if lookups insertion fails', function() { + var error = new Error('some Waterline error'); + waterline.lookups.upsertNodeToMacAddress.rejects(error); + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces' + ).resolves(ethernetInterfacesCatalog); + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces/NIC.Embedded.1-1-1' + ).resolves(nicCatalog); + return expect(job._run()).to.be.rejectedWith('some Waterline error'); + }); + + it('should fail if EthernetInterfaces data is unavailable in catalog', function() { + waterline.catalogs.findLatestCatalogOfSource.resolves(undefined); + return expect(job._run()) + .to.be.rejectedWith('Could not find Members in EthernetInterfaces catalog!'); + }); + + it('should fail if NIC data is unavailable in catalog', function() { + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces' + ).resolves(ethernetInterfacesCatalog); + waterline.catalogs.findLatestCatalogOfSource.withArgs( + 'someNodeId', + '/redfish/v1/Systems/someNodeId/EthernetInterfaces/NIC.Embedded.1-1-1' + ).resolves(undefined); + return expect(job._run()).to.be.rejectedWith('Could not find MacAddress in NIC catalog!'); + }); +}); diff --git a/spec/lib/task-data/base-tasks/redfish-update-lookups-spec.js b/spec/lib/task-data/base-tasks/redfish-update-lookups-spec.js new file mode 100644 index 00000000..4b484ce7 --- /dev/null +++ b/spec/lib/task-data/base-tasks/redfish-update-lookups-spec.js @@ -0,0 +1,16 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; + +describe(require('path').basename(__filename), function () { + var base = require('./base-task-data-spec'); + + base.before(function (context) { + context.taskdefinition = helper.require('/lib/task-data/base-tasks/redfish-update-lookups.js'); + }); + + describe('task-data', function () { + base.examples(); + }); +}); + diff --git a/spec/lib/task-data/tasks/redfish-update-lookups-spec.js b/spec/lib/task-data/tasks/redfish-update-lookups-spec.js new file mode 100644 index 00000000..877fe105 --- /dev/null +++ b/spec/lib/task-data/tasks/redfish-update-lookups-spec.js @@ -0,0 +1,16 @@ +// Copyright © 2018 Dell Inc. or its subsidiaries. All Rights Reserved. + +'use strict'; + +describe(require('path').basename(__filename), function () { + var base = require('./base-tasks-spec'); + + base.before(function (context) { + context.taskdefinition = helper.require('/lib/task-data/tasks/redfish-update-lookups.js'); + }); + + describe('task-data', function () { + base.examples(); + }); +}); +