RackHD/on-tasks

View on GitHub
lib/jobs/general-redfish-catalog.js

Summary

Maintainability
F
5 days
Test Coverage
//Copyright 2017, Dell EMC, Inc.
'use strict';

var di = require('di');
var request = require('requestretry');

module.exports = GeneralRedfishCatalogJobFactory;
di.annotate(GeneralRedfishCatalogJobFactory, new di.Provide('Job.General.Redfish.Catalog'));
di.annotate(GeneralRedfishCatalogJobFactory, new di.Inject(
    'Protocol.Events',
    'Job.Base',
    'Logger',
    'Promise',
    'Assert',
    'Util',
    'Services.Waterline',
    'Services.Encryption',
    '_',
    'JobUtils.RedfishTool',
    'Errors'
));

function GeneralRedfishCatalogJobFactory(
    eventsProtocol,
    BaseJob,
    Logger,
    Promise,
    assert,
    util,
    waterline,
    encryption,
    _,
    RedfishTool,
    Errors
) {
    var logger = Logger.initialize(GeneralRedfishCatalogJobFactory);

    /**
     * @param {Object} options task options object
     * @param {Object} context graph context object
     * @param {String} taskId running task identifier
     * @constructor
     */
    function GeneralRedfishCatalogJob(options, context, taskId) {
        GeneralRedfishCatalogJob.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);
        this.processedOdataIds = {};

        // TODO Manual Redfish discovery will return an array of nodes.

    }

    util.inherits(GeneralRedfishCatalogJob, BaseJob);

    /**
     * @memberOf GeneralRedfishCatalogJob
     */
    GeneralRedfishCatalogJob.prototype._run = function() {
        var self = this;
        return self.getAllCatalogs(self.allEndpoints)
            .then(function() {
                return;
            })
            .then(function () {
                self._done();
            })
            .catch(function (err) {
                self._done(err);
            });
    };
    GeneralRedfishCatalogJob.prototype.restRequest = function(nodeId, path) {
        if (!nodeId) {
            return Promise.resolve();
        }

        return waterline.obms.findByNode(nodeId, 'redfish-obm-service', true)
        .then(function(obm) {
            if (!obm) { throw new Errors.NotFoundError('Failed to find Redfish settings'); }
            return obm.config;
        })
        .then(function(settings) {
            var url = settings.protocol + '://' + settings.host + ':' + settings.port + path;
            return request.get(url, {
                'auth': {
                    'user': settings.username || '',
                    'pass': settings.password || '',
                },
                strictSSL: false,
                json: true
            });
        })
        .then(function(response){
            if (response.statusCode > 206) {

                throw new Error("Failed to get " + path + " for node " + nodeId + " with code "+ response.statusCode);
            }
            return response;
        });
    };



    GeneralRedfishCatalogJob.prototype.replacer = function(nodeId, odataId, res) {
        var self = this;

        var catalogString = JSON.stringify(res.body);
        var odataIdString = odataId;
        var catalogNodeId = nodeId;

        var idTable = {
            'systems': {},
            'chassis': {},
            'manager': {},
            'default': {}
        };

        return waterline.nodes.getNodeById(nodeId)
        .then(function(node) {
            var managerNodeId = node.relations[_.findIndex(node.relations, {'relationType': 'managedBy'})].targets[0];
            return [node, waterline.nodes.getNodeById(managerNodeId)];
        })
        .spread(function(node, managerNode) {

            var index;
            var filter;
            var re;
            var replace_str;

            idTable.default.id  = node.id;

            var replaceFilter = [
                {id: "systems", pattern: "Systems/"  , prefix: "Systems/", postfix: ""},
                {id: "chassis", pattern: "Chassis/" , prefix: "Chassis/", postfix: ""},
                {id: "manager", pattern: "Managers/" , prefix: "Managers/", postfix: ""},
                {id: "manager", pattern: "" , prefix: "", postfix: ""}
            ];

            switch(node.type)  {
                case "redfish":
                    idTable.systems.id  = node.id;
                    idTable.systems.find  = node.identifiers[0];
                    idTable.systems.replaceWith  = node.identifiers[2];
                    idTable.chassis.id  = node.id;
                    idTable.chassis.find  = node.identifiers[0];
                    idTable.chassis.replaceWith  = node.identifiers[2];
                    idTable.manager.id  = managerNode.id;
                    idTable.manager.find  = managerNode.identifiers[0];
                    idTable.manager.replaceWith = managerNode.id;
                    break;

                default:
                    logger.info("Node type: " + node.type);
                    return;
            }

            if (idTable['system'] !== 'undefined' && idTable['chassis'] !== 'undefined') {
                for (index = 0; index < replaceFilter.length; index += 1) {
                    filter = replaceFilter[index];
                    re = new RegExp(filter.pattern + idTable[filter.id].find, 'gi');
                    replace_str = filter.prefix + idTable[filter.id].replaceWith + filter.postfix;
                    catalogString =  catalogString.replace(re, replace_str);
                }

                for (index = 0; index < replaceFilter.length; index += 1) {
                    filter = replaceFilter[index];
                    re = new RegExp(filter.pattern + idTable[filter.id].find, 'i');
                    replace_str = filter.prefix + idTable[filter.id].id + filter.postfix;

                    if (odataIdString.match(re) !== null) {
                        odataIdString = odataIdString.replace(re, replace_str);
                        catalogNodeId = idTable[filter.id].id;
                        break;
                    }
                }

                //make this queryable
                re = new RegExp("%23", 'gi');
                catalogString =  catalogString.replace(re, "#");
                odataIdString = odataIdString.replace(re, "#");

            }
            return;
        })
        .then(function() {
            var dataToSave = {
                node: catalogNodeId,
                source: odataIdString,
                data: JSON.parse(catalogString)
            };

            var query = {
                node: catalogNodeId,
                source: odataIdString
            };
            return waterline.catalogs.count(query)
            .then(function(count) {
                if (count) {
                    return waterline.catalogs.find(query)
                    .then(function(catalog) {
                        if (!_.isEqual(catalog[0].data, dataToSave.data)) {
                            return waterline.catalogs.update(query, dataToSave);
                        }
                    });
                } else {
                    return waterline.catalogs.create(dataToSave)
                    .then(function() {
                        return waterline.catalogs.update(query, dataToSave);
                    });
                }
            })
            .then(function(cat){
                if (typeof cat !== 'undefined' && cat) {
            if(cat instanceof Array) {
                        return waterline.nodes.getNodeById(cat[0].node);
                    }
                    return waterline.nodes.getNodeById(cat.node);
                }
            }).then(function(catNode) {
                if (typeof catNode !== 'undefined' && catNode) {
                    var nodeInfo = {};
                    nodeInfo.catalogType = "NativeRedfish";
                    nodeInfo.catalogUpdated = odataIdString;
                    return eventsProtocol.publishNodeEvent(catNode, 'updated', nodeInfo);
                }
            });
        })
        .catch(function (err) {
            logger.info(JSON.stringify(err, null,4));
        });
    };

    GeneralRedfishCatalogJob.prototype.createRedfishCatalogs = function (odataId, nodeId) {
        var self = this;

        if (self.processedOdataIds[nodeId] === undefined) {
            self.processedOdataIds[nodeId] = {};
        }
        if (odataId in self.processedOdataIds[nodeId]) {
            return;
        }

        return self.restRequest(nodeId, odataId)
        .then(function (res) {
            self.processedOdataIds[nodeId][odataId] = odataId;
            return self.replacer(nodeId, odataId, res)
            .then(function() {
                var catalogString = JSON.stringify(res.body);
                var odataIds = catalogString.match(/"@odata.id":".*?"/g);
                return Promise.each(odataIds, function(item) {
                    var odataIdArray = item.split(':');
                    var newOdataId = odataIdArray[1].replace(/"/g, '');
                    return Promise.resolve(self.createRedfishCatalogs(newOdataId, nodeId))
                    .then(function() {
                        return nodeId;
                    });
                });
            })
            .catch(function(err){
                logger.error(JSON.stringify(err, null, 4));
                logger.error('could not access: ' + odataId);
            });
        })
        .catch(function(err){
                logger.debug(odataId);
                logger.error(JSON.stringify(err, null, 4));
        });
    };




    GeneralRedfishCatalogJob.prototype.getSystemsCatalogs = function (system) {
        var self = this;
        return Promise.resolve(system)
            .each(self.catalogSystem.bind(self));
    };

    GeneralRedfishCatalogJob.prototype.getAllCatalogs = function (allEndpoints) {
        var self = this;

        return Promise.resolve(allEndpoints)
            .map(self.catalogAll.bind(self), {concurrency: 128});
    };

    GeneralRedfishCatalogJob.prototype.catalogAll = Promise.method(function (nodeId) {
        var self = this;
        if (nodeId === undefined)
        {
            return;
        }
    var redfish = new RedfishTool();

        return redfish.setup(nodeId)
        .then(function () {
            return Promise.resolve(self.createRedfishCatalogs(redfish.settings.root, nodeId))
                .then(function () {
                    return nodeId;
                });
        })
        .catch(function (err) {
            logger.error('catalogAll', {
                error: err
            });
            return nodeId;
        });
    });



    GeneralRedfishCatalogJob.prototype.catalogSystem = Promise.method(function (nodeId) {

        var self = this;
        if (nodeId === undefined)
        {
            return;
        }
    var redfish = new RedfishTool();
        return redfish.setup(nodeId)
            .then(function () {
                var beforeSettings = redfish.settings.root;
                var redfishSettings = beforeSettings.replace('Chassis', 'Systems');
                return self.createRedfishCatalogs(redfishSettings, nodeId);
            })
            .then( function() {
                return nodeId;
            })
            .catch(function (err) {
                logger.error('catalogSystem', {
                    error: err
                });
                if (err.message !== 'Missing System Resource' &&
                    err.message !== 'No System Members') {
                    throw err;
                }
                return nodeId;
            });
    });


    GeneralRedfishCatalogJob.prototype.catalogEndpoints = Promise.method(function(nodeId) {
        var self = this;
        if (nodeId === undefined)
        {
            return;
        }
    var redfish = new RedfishTool();
        return redfish.setup(nodeId)
            .then(function() {
                return self.restRequest(nodeId, redfish.settings.root);
            })
            .then(function (res) {
                return Promise.all([_.get(res.body, 'Links.CooledBy', []), _.get(res.body, 'Links.PoweredBy', [])])
                    .spread(function (fanLinks, powerLinks) {
                        var endPoints = _.union(fanLinks, powerLinks);
                        return Promise.each(endPoints, function (Element) {
                            var pre_path = _.get(Element, '@odata.id', {});
                            var path = pre_path.replace(/(\||,)/g, '%7C');
                            return self.restRequest(nodeId, path)
                                .then(function (data) {

                                    return self.replacer(nodeId, data.body['@odata.id'], data)
                                    .then(function () {
                                        return nodeId;
                                    });
                                })
                                .catch(function (err) {
                                    logger.error('Redfish Enpoint', {
                                        error: err
                                    });
                                    if (err.message !== 'Missing Power Supply or Fan Endpoint' &&
                                        err.message !== 'No PowerSupply or Fan Endpoints') {
                                        throw err;
                                    }
                                    return nodeId;
                                });
                        });
                    });
            })
            .catch(function(err){
                logger.error('catalogEndpoints', {
                    error: err
                });
                if(err.message !== 'Missing catalogEndpoints Resource' &&
                    err.message !== 'No CooledBy/PoweredBy Members') {
                    throw err;
                }
                return nodeId;
            });
    });

    GeneralRedfishCatalogJob.prototype.driveEndpoints = Promise.method(function(nodeId) {
        var self = this;
    var redfish = new RedfishTool();
        return redfish.setup(nodeId)
            .then(function() {
                var beforeSettings = redfish.settings.root;
                var redfishSettings = beforeSettings.replace('Chassis', 'Systems');
                return self.restRequest(nodeId, redfishSettings);
            })
            .then(function (res) {
                return self.restRequest(nodeId, _.get(res.body, 'SimpleStorage', {})['@odata.id'])
                    .then(function (drive) {
                        return Promise.each(drive.body.Members, function (driveElem) {
                            return self.restRequest(nodeId, driveElem['@odata.id'])
                                .then(function (data) {
                                    return self.replacer(nodeId, data.body['@odata.id'], data)
                                    .then(function () {
                                        return nodeId;
                                    });
                                })
                                .catch(function (err) {
                                    logger.error('Drives', {
                                        error: err
                                    });
                                    if (err.message !== 'Missing Drives Resource' &&
                                        err.message !== 'No Drives Endpoints') {
                                        throw err;
                                    }
                                    return nodeId;
                                });
                        });
                    });
            })
            .catch(function(err){
                logger.error('driveEndpoints', {
                    error: err
                });
                if(err.message !== 'Missing driveEndpoints Resource' &&
                    err.message !== 'No driveEndpoints Members') {
                    throw err;
                }
                return nodeId;
            });


    });

    return GeneralRedfishCatalogJob;
}