<commit_before>const users = require('../db/model/users');
const check = require('../db/model/check');
const schema = require('../schema');
const parse = require('co-body');
const status = require('http-status');
const _ = require('lodash');
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const mailTransport = mailTransportConfig();
const ra = require('./resource-access');
const exec = require('child_process').exec;
const Promise = require('bluebird');
const os = require('os');

function* updateProjectFavorites(next) {
    let attrs = yield parse(this);
    this.body = yield users.updateProjectFavorites(this.reqctx.user.id, this.params.project_id, attrs);
    this.status = status.OK;
    yield next;
}

function* updateUserSettings(next) {
    let args = yield parse(this);
    let errors = yield validateUserSettingsArgs(args, this.reqctx.user.id);
    if (errors != null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let settingArgs = {};
        if (args.default_project) {
            settingArgs.default_project = args.default_project;
        }

        if (args.default_experiment) {
            settingArgs.default_experiment = args.default_experiment;
        }

        if (args.fullname) {
            settingArgs.fullname = args.fullname;
        }

        let rv = yield users.updateUserSettings(this.reqctx.user.id, settingArgs);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            this.body = rv.val;
        }
    }
    yield next;
}

function* validateUserSettingsArgs(args, userId) {
    if (args.default_project) {
        if (!_.isString(args.default_project)) {
            return {error: `Bad argument for default_project`};
        }

        let isValidProject = yield check.userHasProjectAccess(userId, args.default_project);
        if (!isValidProject) {
            return {error: `No such project: ${args.default_project}`};
        }
    }

    if (args.default_experiment) {
        if (!_.isString(args.default_experiment)) {
            return {error: `Bad argument for default_experiment`};
        }

        if (!args.default_project) {
            return {error: `You must specify a default project when setting a default experiment`};
        }

        let experimentInProject = yield check.experimentExistsInProject(args.default_project, args.default_experiment);
        if (!experimentInProject) {
            return {error: `No such experiment ${args.default_experiment} in project ${args.default_project}`};
        }
    }

    if (args.fullname) {
        if (!_.isString(args.fullname)) {
            return {error: `Bad argument for fullname`};
        }
    }

    return null;
}

function* createAccount(next) {
    let accountArgs = yield parse(this);
    schema.prepare(schema.userAccountSchema, accountArgs);
    let errors = yield validateCreateAccount(accountArgs);
    if (errors !== null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let rv = yield users.createUnverifiedAccount(accountArgs);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            let evl = emailValidationLinkToUser(rv.val, accountArgs.site);
            if (evl.error) {
                this.status = status.BAD_REQUEST;
                this.body = evl;
            } else {
                this.body = evl.val;
            }
        }
    }
    yield next;
}

function* resetPasswordGenerateLink(next) {
    let resetArgs = yield parse(this);
    let user = yield users.get(resetArgs.email);
    if (!user) {
        this.status = status.BAD_REQUEST;
        let errorMessage = "No registered user for given email address: " + resetArgs.email + ". Please register anew.";
        this.body = {error: errorMessage};
    } else {
        let userId = user.id;
        let site = resetArgs.site;
        let rv = yield users.createPasswordResetRequest(user);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            schema.prepare(schema.userAccountSchema, user);
            let finalUser = yield users.get(userId);
            let evl = emailResetLinkToUser(finalUser, site);
            if (evl.error) {
                this.status = status.BAD_REQUEST;
                this.body = evl;
            } else {
                this.body = finalUser;
            }
        }
    }
    yield next;
}

function* getUserRegistrationFromUuid(next) {
    let result = yield users.getUserRegistrationFromUuid(this.params.validation_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* getUserForPasswordResetFromUuid(next) {
    let result = yield users.getUserForPasswordResetFromUuid(this.params.validation_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* setUserResetPasswordFlag(next) {
    let result = yield user.clearResetPassword(this.params.user_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* clearUserResetPasswordFlag(next) {
    let result = yield user.clearResetPassword(this.params.user_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* validateCreateAccount(accountArgs) {
    return yield schema.validate(schema.userAccountSchema, accountArgs);
}

function* createDemoProject(next) {
    let userId = this.params.user_id;
    let checkId = this.reqctx.user.id;
    if ((!this.reqctx.user.isAdmin) && (userId != checkId)) {
        this.status = status.BAD_REQUEST;
        this.body = "Must be admin to create demo project for non-self user: " + userId;
    } else {
        let user = yield users.getUser(userId);
        if (! user) {
            this.status = status.BAD_REQUEST;
            this.body = "Unable to create demo project; no user = " + userId;
        } else {
            let apikey = user.apikey;
            console.log("createDemoProject for " + user.id + ", " + apikey);
            let result = yield createDemoProjectRequest(apikey);
            if (result.error) {
                this.status = status.BAD_REQUEST;
                this.body = result;
            } else {
                this.status = status.OK;
                this.body = result.val;
            }
        }
    }
    yield next;
}

function* createDemoProjectRequest(apikey){
    let port = process.env.MCDB_PORT,
        hostname = os.hostname(),
        apihost = '',
        prefix = './',
        sourceDir = 'scripts/demo-project/';

    switch (hostname) {
        case 'materialscommons':
            apihost = port === '30815' ? 'test.materialscommons.org' : 'materialscommons.org';
            break;
        case 'lift.miserver.it.umich.edu':
            apihost = 'lift.materialscommons.org';
            break;
        default:
            apihost = 'mctest.localhost';
            break;
    }

    let current_dir = process.cwd();
    let parts = current_dir.split('/');
    let last = parts[parts.length-1];

    let ret = '';
    if ((last != "backend") && (last != "materialscommons.org")) {
        let message = 'Can not create proejct with process running in unexpected base dir: '
        message = message + current_dir;
        console.log("Build demo project fails - " + message);
        ret = {error: "Can not create demo project: admin see log"};
        return ret;
    }

    if (last == "backend") {
        prefix = current_dir + "/";
    } else {
        prefix = current_dir + "/backend/"
    }

    sourceDir = prefix + sourceDir;
    let host_string = `http://${apihost}/`;
    let command = `${sourceDir}build_project.py --host ${host_string} --apikey ${apikey} --datapath ${sourceDir}/demo_project_data`;
    let result = '';
    try {
        result = yield promiseExec(command);

        let buildOk =  (
            result == "Refreshed project with name = Demo Project\n" ||
            result == "Built project with name = Demo Project\n"
        );
        if (buildOk) {
            ret = {val: result};
        } else {
            console.log("in users - build demo project - results error: " + results);
            ret = {error: result};
        }
    } catch (err) {
        console.log("in users - build demo project - error: " + err);
        ret = {error: err};
    }
    return ret;
}

function promiseExec(command) {
    return new Promise(function (resolve, reject) {
        exec(command, (error, stdout, stderr) => {
            let results = stdout.toString();
            let errorReturn = stderr.toString();
            if (error) {
                reject(errorReturn);
            } else {
                resolve(results);
            }
        });
    });
}

function emailResetLinkToUser(userData, site) {
    var transporter = nodemailer.createTransport(mailTransport);
    var sendTo = userData.id;
    var validationLink = `${process.env.MC_BASE_API_LINK}/rvalidate/${userData.validate_uuid}`;
    if (site === 'mcpub') {
        validationLink = `${process.env.MCPUB_BASE_API_LINK}/rvalidate/${userData.validate_uuid}`
    }
    let emailMsg =
        `You have requested a password reset at Materials Commons reset in the account for this email.
            To complete this process please click on the given link or copy and paste in the url given below.`;
    var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
    var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following url: ${validationLink}`;

    var mailOptions = {
        from: process.env.MC_VERIFY_EMAIL,
        to: sendTo,
        subject: 'MaterialCommons - account verification',
        text: plainTextBody,
        html: htmlBody
    };

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function (error, info) {
        if (error) {
            return {error: error};
        }
    });

    return {val: userData}
}

function emailValidationLinkToUser(userData, site) {
    var transporter = nodemailer.createTransport(mailTransport);
    var sendTo = userData.id;
    var validationLink = `${process.env.MC_BASE_API_LINK}/validate/${userData.validate_uuid}`;
    if (site === 'mcpub') {
        validationLink = `${process.env.MCPUB_BASE_API_LINK}/validate/${userData.validate_uuid}`
    }
    let emailMsg =
        `Thank you for registering for an account with Materials Commons. To complete the registration
            process please click on the given link or copy and paste in the url given below.`;
    var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
    var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following url: ${validationLink}`;

    var mailOptions = {
        from: process.env.MC_VERIFY_EMAIL,
        to: sendTo,
        subject: 'MaterialCommons - account verification',
        text: plainTextBody,
        html: htmlBody
    };

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function (error, info) {
        if (error) {
            return {error: error};
        }
    });

    return {val: userData}
}

function mailTransportConfig() {
    if (process.env.MC_SMTP_HOST === 'localhost') {
        return smtpTransport();
    } else {
        return `smtps://${process.env.MC_VERIFY_EMAIL}:${process.env.MC_VERIFY_PASS}@${process.env.MC_SMTP_HOST}`;
    }
}

function createResource(router) {
    router.put('/users/:project_id', ra.validateProjectAccess, updateProjectFavorites);
    router.put('/users', updateUserSettings);
    router.get('/users/validate/:validation_id', getUserRegistrationFromUuid);
    router.get('/users/rvalidate/:validation_id', getUserForPasswordResetFromUuid);
    router.put('/users/:user_id/clear-reset-password', clearUserResetPasswordFlag);
    router.put('/users/:user_id/create_demo_project', createDemoProject);
    router.post('/accounts', createAccount);
    router.post('/accounts/reset', resetPasswordGenerateLink);
}

module.exports = {
    createResource,createDemoProjectRequest
};
<commit_msg>Tweek demo-project-builder setup to run both from test and from GUI. Chance log message.<commit_after>const users = require('../db/model/users');
const check = require('../db/model/check');
const schema = require('../schema');
const parse = require('co-body');
const status = require('http-status');
const _ = require('lodash');
const nodemailer = require('nodemailer');
const smtpTransport = require('nodemailer-smtp-transport');
const mailTransport = mailTransportConfig();
const ra = require('./resource-access');
const exec = require('child_process').exec;
const Promise = require('bluebird');
const os = require('os');

function* updateProjectFavorites(next) {
    let attrs = yield parse(this);
    this.body = yield users.updateProjectFavorites(this.reqctx.user.id, this.params.project_id, attrs);
    this.status = status.OK;
    yield next;
}

function* updateUserSettings(next) {
    let args = yield parse(this);
    let errors = yield validateUserSettingsArgs(args, this.reqctx.user.id);
    if (errors != null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let settingArgs = {};
        if (args.default_project) {
            settingArgs.default_project = args.default_project;
        }

        if (args.default_experiment) {
            settingArgs.default_experiment = args.default_experiment;
        }

        if (args.fullname) {
            settingArgs.fullname = args.fullname;
        }

        let rv = yield users.updateUserSettings(this.reqctx.user.id, settingArgs);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            this.body = rv.val;
        }
    }
    yield next;
}

function* validateUserSettingsArgs(args, userId) {
    if (args.default_project) {
        if (!_.isString(args.default_project)) {
            return {error: `Bad argument for default_project`};
        }

        let isValidProject = yield check.userHasProjectAccess(userId, args.default_project);
        if (!isValidProject) {
            return {error: `No such project: ${args.default_project}`};
        }
    }

    if (args.default_experiment) {
        if (!_.isString(args.default_experiment)) {
            return {error: `Bad argument for default_experiment`};
        }

        if (!args.default_project) {
            return {error: `You must specify a default project when setting a default experiment`};
        }

        let experimentInProject = yield check.experimentExistsInProject(args.default_project, args.default_experiment);
        if (!experimentInProject) {
            return {error: `No such experiment ${args.default_experiment} in project ${args.default_project}`};
        }
    }

    if (args.fullname) {
        if (!_.isString(args.fullname)) {
            return {error: `Bad argument for fullname`};
        }
    }

    return null;
}

function* createAccount(next) {
    let accountArgs = yield parse(this);
    schema.prepare(schema.userAccountSchema, accountArgs);
    let errors = yield validateCreateAccount(accountArgs);
    if (errors !== null) {
        this.status = status.BAD_REQUEST;
        this.body = errors;
    } else {
        let rv = yield users.createUnverifiedAccount(accountArgs);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            let evl = emailValidationLinkToUser(rv.val, accountArgs.site);
            if (evl.error) {
                this.status = status.BAD_REQUEST;
                this.body = evl;
            } else {
                this.body = evl.val;
            }
        }
    }
    yield next;
}

function* resetPasswordGenerateLink(next) {
    let resetArgs = yield parse(this);
    let user = yield users.get(resetArgs.email);
    if (!user) {
        this.status = status.BAD_REQUEST;
        let errorMessage = "No registered user for given email address: " + resetArgs.email + ". Please register anew.";
        this.body = {error: errorMessage};
    } else {
        let userId = user.id;
        let site = resetArgs.site;
        let rv = yield users.createPasswordResetRequest(user);
        if (rv.error) {
            this.status = status.BAD_REQUEST;
            this.body = rv;
        } else {
            schema.prepare(schema.userAccountSchema, user);
            let finalUser = yield users.get(userId);
            let evl = emailResetLinkToUser(finalUser, site);
            if (evl.error) {
                this.status = status.BAD_REQUEST;
                this.body = evl;
            } else {
                this.body = finalUser;
            }
        }
    }
    yield next;
}

function* getUserRegistrationFromUuid(next) {
    let result = yield users.getUserRegistrationFromUuid(this.params.validation_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* getUserForPasswordResetFromUuid(next) {
    let result = yield users.getUserForPasswordResetFromUuid(this.params.validation_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* setUserResetPasswordFlag(next) {
    let result = yield user.clearResetPassword(this.params.user_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* clearUserResetPasswordFlag(next) {
    let result = yield user.clearResetPassword(this.params.user_id);
    if (result.error) {
        this.status = status.BAD_REQUEST;
        this.body = result;
    } else {
        this.status = status.OK;
        this.body = result.val;
    }
    yield next;
}

function* validateCreateAccount(accountArgs) {
    return yield schema.validate(schema.userAccountSchema, accountArgs);
}

function* createDemoProject(next) {
    let userId = this.params.user_id;
    let checkId = this.reqctx.user.id;
    let user = yield users.getUser(userId);
    if ((!this.reqctx.user.isAdmin) && (userId != checkId)) {
        this.status = status.BAD_REQUEST;
        this.body = "Must be admin to create demo project for non-self user: " + userId;
    } else {
        if (! user) {
            this.status = status.BAD_REQUEST;
            this.body = "Unable to create demo project; no user = " + userId;
        } else {
            let apikey = user.apikey;
            let result = yield createDemoProjectRequest(apikey);
            if (result.error) {
                this.status = status.BAD_REQUEST;
                this.body = result;
            } else {
                this.status = status.OK;
                this.body = result.val;
            }
        }
    }
    console.log("createDemoProject for " + user.id + ' - ' + this.body);
    yield next;
}

function* createDemoProjectRequest(apikey){
    let port = process.env.MCDB_PORT,
        hostname = os.hostname(),
        apihost = '',
        prefix = './',
        sourceDir = 'scripts/demo-project/';

    switch (hostname) {
        case 'materialscommons':
            apihost = port === '30815' ? 'test.materialscommons.org' : 'materialscommons.org';
            break;
        case 'lift.miserver.it.umich.edu':
            apihost = 'lift.materialscommons.org';
            break;
        default:
            apihost = 'mctest.localhost';
            break;
    }

    let current_dir = process.cwd();
    let parts = current_dir.split('/');
    let last = parts[parts.length-1];

    let ret = '';
    if ((last != "backend") && (last != "materialscommons.org")) {
        let message = 'Can not create proejct with process running in unexpected base dir: '
        message = message + current_dir;
        console.log("Build demo project fails - " + message);
        ret = {error: "Can not create demo project: admin see log"};
        return ret;
    }

    if (last == "backend") {
        prefix = current_dir + "/";
    } else {
        prefix = current_dir + "/backend/"
    }

    sourceDir = prefix + sourceDir;
    let host_string = `http://${apihost}/`;
    let command = `${sourceDir}build_project.py --host ${host_string} --apikey ${apikey} --datapath ${sourceDir}/demo_project_data`;
    let result = '';
    try {
        result = yield promiseExec(command);

        let buildOk =  (
            result == "Refreshed project with name = Demo Project\n" ||
            result == "Built project with name = Demo Project\n"
        );
        if (buildOk) {
            ret = {val: result};
        } else {
            console.log("in users - build demo project - results error: " + results);
            ret = {error: result};
        }
    } catch (err) {
        console.log("in users - build demo project - error: " + err);
        ret = {error: err};
    }
    return ret;
}

function promiseExec(command) {
    return new Promise(function (resolve, reject) {
        exec(command, (error, stdout, stderr) => {
            let results = stdout.toString();
            let errorReturn = stderr.toString();
            if (error) {
                reject(errorReturn);
            } else {
                resolve(results);
            }
        });
    });
}

function emailResetLinkToUser(userData, site) {
    var transporter = nodemailer.createTransport(mailTransport);
    var sendTo = userData.id;
    var validationLink = `${process.env.MC_BASE_API_LINK}/rvalidate/${userData.validate_uuid}`;
    if (site === 'mcpub') {
        validationLink = `${process.env.MCPUB_BASE_API_LINK}/rvalidate/${userData.validate_uuid}`
    }
    let emailMsg =
        `You have requested a password reset at Materials Commons reset in the account for this email.
            To complete this process please click on the given link or copy and paste in the url given below.`;
    var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
    var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following url: ${validationLink}`;

    var mailOptions = {
        from: process.env.MC_VERIFY_EMAIL,
        to: sendTo,
        subject: 'MaterialCommons - account verification',
        text: plainTextBody,
        html: htmlBody
    };

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function (error, info) {
        if (error) {
            return {error: error};
        }
    });

    return {val: userData}
}

function emailValidationLinkToUser(userData, site) {
    var transporter = nodemailer.createTransport(mailTransport);
    var sendTo = userData.id;
    var validationLink = `${process.env.MC_BASE_API_LINK}/validate/${userData.validate_uuid}`;
    if (site === 'mcpub') {
        validationLink = `${process.env.MCPUB_BASE_API_LINK}/validate/${userData.validate_uuid}`
    }
    let emailMsg =
        `Thank you for registering for an account with Materials Commons. To complete the registration
            process please click on the given link or copy and paste in the url given below.`;
    var plainTextBody = `${emailMsg} Please validate using the following URL: ${validationLink}`;
    var htmlBody = `${emailMsg} Please validate by clicking <a href='${validationLink}'>here</a>  or using the following url: ${validationLink}`;

    var mailOptions = {
        from: process.env.MC_VERIFY_EMAIL,
        to: sendTo,
        subject: 'MaterialCommons - account verification',
        text: plainTextBody,
        html: htmlBody
    };

    // send mail with defined transport object
    transporter.sendMail(mailOptions, function (error, info) {
        if (error) {
            return {error: error};
        }
    });

    return {val: userData}
}

function mailTransportConfig() {
    if (process.env.MC_SMTP_HOST === 'localhost') {
        return smtpTransport();
    } else {
        return `smtps://${process.env.MC_VERIFY_EMAIL}:${process.env.MC_VERIFY_PASS}@${process.env.MC_SMTP_HOST}`;
    }
}

function createResource(router) {
    router.put('/users/:project_id', ra.validateProjectAccess, updateProjectFavorites);
    router.put('/users', updateUserSettings);
    router.get('/users/validate/:validation_id', getUserRegistrationFromUuid);
    router.get('/users/rvalidate/:validation_id', getUserForPasswordResetFromUuid);
    router.put('/users/:user_id/clear-reset-password', clearUserResetPasswordFlag);
    router.put('/users/:user_id/create_demo_project', createDemoProject);
    router.post('/accounts', createAccount);
    router.post('/accounts/reset', resetPasswordGenerateLink);
}

module.exports = {
    createResource,createDemoProjectRequest
};
<|endoftext|>


<commit_before>var path = require('path'),
crypto = require('crypto'),
_ = require('underscore');

var banks = {
	"CIC": "https://ssl.paiement.cic-banques.fr",
	"CM" : "https://paiement.creditmutuel.fr",
	"OBC": "https://ssl.paiement.banque-obc.fr"
};

var CMCIC = function() {};

/**
* TPE method
*/

CMCIC.prototype.tpe = function(config) {
	var tpe = {};
	this.init(config);
};

CMCIC.prototype.tpe.prototype.init = function(config) {
	this.tpe = {
		CMCIC_TPE			: '',
		CMCIC_CODESOCIETE	: '',
		CMCIC_CLE			: '',
		CMCIC_BANK			: 'CIC',
		CMCIC_VERSION		: '3.0',
		CMCIC_LNG			: 'FR',
		CMCIC_CURRENCY		: 'EUR',
		CMCIC_SERVEUR		: 'https://ssl.paiement.cic-banques.fr',
		CMCIC_URL_RETOUR	: "",
		CMCIC_URLOK			: "",
		CMCIC_URLKO			: "",
		test				: false
	};
	if (config) {
		_.extend(this.tpe, config);
	}
	this.setServer(this.tpe.CMCIC_BANK);
};

CMCIC.prototype.tpe.prototype.configure = function(cmcic) {
	if (cmcic) {
		_.extend(this.tpe, cmcic);
	}
	this.setServer(this.tpe.CMCIC_BANK);
};

CMCIC.prototype.tpe.prototype.setServer = function(bankName) {
	var server = banks[bankName];

	if (this.tpe.test) {
		server += "/test";
	}
	server += "/paiement.cgi";
	this.tpe.CMCIC_SERVEUR = server;
};

/**
* Transation method
*/

CMCIC.prototype.transaction = function (tpe, transactionData) {
	this.tpe = tpe;
	this.data = {};

	this.init(transactionData);
};

CMCIC.prototype.transaction.prototype.init = function (transactionData) {
	this.data = {
		date				: "",
		amount				: "",
		reference			: "",
		texteLibre			: {},
		email				: "",
		NbrEch				: "",
		DateEcheance1		: "",
		MontantEcheance1	: "",
		DateEcheance2		: "",
		MontantEcheance2	: "",
		DateEcheance3		: "",
		MontantEcheance3	: "",
		DateEcheance4		: "",
		MontantEcheance4	: "",
		options				: ""
	};
	_.extend(this.data, transactionData);
};

CMCIC.prototype.transaction.prototype.calculateMAC = function(data) {
	return crypto.createHmac('sha1', this.getHashKey()).update(data).digest('hex');
};

CMCIC.prototype.transaction.prototype.dataToSend = function(data) {
	if (!data) {
		data = this.data;
	}
	return this.tpe.tpe.CMCIC_TPE+
		'*' + data.date+
		'*' + data.amount + this.tpe.tpe.CMCIC_CURRENCY+
		'*' + data.reference+
		'*' + data.texteLibre+
		'*' + this.tpe.tpe.CMCIC_VERSION+
		'*' + this.tpe.tpe.CMCIC_LNG+
		'*' + this.tpe.tpe.CMCIC_CODESOCIETE+
		'*' + data.email+
		'*' + data.NbrEch+
		'*' + data.DateEcheance1+
		'*' + data.MontantEcheance1+
		'*' + data.DateEcheance2+
		'*' + data.MontantEcheance2+
		'*' + data.DateEcheance3+
		'*' + data.MontantEcheance3+
		'*' + data.DateEcheance4+
		'*' + data.MontantEcheance4+
		'*' + data.options;
};

CMCIC.prototype.transaction.prototype.getHashKey = function() {
	var data = this.tpe.tpe,
	hexStrkey = data.CMCIC_CLE.slice(0, 38),
	hexFinal = '' + data.CMCIC_CLE.slice(38) + '00',
	cca0 = hexFinal.charCodeAt(0);

	if (cca0  > 70 && cca0 < 97){
		hexStrkey += String.fromCharCode(cca0-23) + hexFinal.charAt(1);
	} else {
		if (hexFinal.charAt(1) == 'M'){
			hexStrkey += hexFinal.charAt(0) + '0';
		} else {
			hexStrkey += hexFinal.slice(0, 2);
		}
	}

	var r = "";
	for (var i = 0; i < hexStrkey.length / 2; i++) {
		var hex = hexStrkey[i*2] + hexStrkey[i*2 + 1];
		hexPack = parseInt(hex, 16);
		r +=  String.fromCharCode(hexPack);
	}
	return r;
};

CMCIC.prototype.transaction.prototype.getStringFromTexteLibre = function() {
	var result = '',
	i = 0;
	for (var key in this.data.texteLibre) {
		var s = key+'='+this.data.texteLibre[key];
		if (i !== 0) {
			s = '&'+s;
		}
		if (result.length + s.length > 3200) {
			break;
		}
		result += s;
		i++;
	}
	return result;
}

CMCIC.prototype.transaction.prototype.form = function(id, autosubmit) {
	var mac = this.calculateMAC(this.dataToSend()),
	texteLibre =	this.getStringFromTexteLibre(),
	result = '<form method="post" id="'+id+'" action='+this.tpe.tpe.CMCIC_SERVEUR+'>'+
		'<input type="hidden" name="version" value="'+this.tpe.tpe.CMCIC_VERSION+'">'+
		'<input type="hidden" name="TPE" value="'+this.tpe.tpe.CMCIC_TPE+'">'+
		'<input type="hidden" name="MAC" value="'+mac+'">'+
		'<input type="hidden" name="url_retour" value="'+this.tpe.tpe.CMCIC_URL_RETOUR+'">'+
		'<input type="hidden" name="url_retour_ok" value="'+this.tpe.tpe.CMCIC_URLOK+'">'+
		'<input type="hidden" name="url_retour_err" value="'+this.tpe.tpe.CMCIC_URLKO+'">'+
		'<input type="hidden" name="lgue" value="'+this.tpe.tpe.CMCIC_LNG+'">'+
		'<input type="hidden" name="societe" value="'+this.tpe.tpe.CMCIC_CODESOCIETE+'">'+
		'<input type="hidden" name="date" value="'+this.data.date+'">'+
		'<input type="hidden" name="montant" value="'+this.data.amount+this.tpe.tpe.CMCIC_CURRENCY+'">'+
		'<input type="hidden" name="reference" value="'+this.data.reference+'">'+
		'<input type="hidden" name="mail" value="'+this.data.email+'">'+
		'<input type="hidden" name="texte-libre" value="'+texteLibre+'">'+
		'
'+
		'<input type="submit" name="bouton" value="">'+
		'
</form>';
	if (autosubmit === true) {
		result += '';
	}
	return result;
};

module.exports = new CMCIC();
<commit_msg>Problem with mac calculation : solved<commit_after>var path = require('path'),
crypto = require('crypto'),
_ = require('underscore');

var banks = {
	"CIC": "https://ssl.paiement.cic-banques.fr",
	"CM" : "https://paiement.creditmutuel.fr",
	"OBC": "https://ssl.paiement.banque-obc.fr"
};

var CMCIC = function() {};

/**
* TPE method
*/

CMCIC.prototype.tpe = function(config) {
	var tpe = {};
	this.init(config);
};

CMCIC.prototype.tpe.prototype.init = function(config) {
	this.tpe = {
		CMCIC_TPE			: '',
		CMCIC_CODESOCIETE	: '',
		CMCIC_CLE			: '',
		CMCIC_BANK			: 'CIC',
		CMCIC_VERSION		: '3.0',
		CMCIC_LNG			: 'FR',
		CMCIC_CURRENCY		: 'EUR',
		CMCIC_SERVEUR		: 'https://ssl.paiement.cic-banques.fr',
		CMCIC_URL_RETOUR	: "",
		CMCIC_URLOK			: "",
		CMCIC_URLKO			: "",
		test				: false
	};
	if (config) {
		_.extend(this.tpe, config);
	}
	this.setServer(this.tpe.CMCIC_BANK);
};

CMCIC.prototype.tpe.prototype.configure = function(cmcic) {
	if (cmcic) {
		_.extend(this.tpe, cmcic);
	}
	this.setServer(this.tpe.CMCIC_BANK);
};

CMCIC.prototype.tpe.prototype.setServer = function(bankName) {
	var server = banks[bankName];

	if (this.tpe.test) {
		server += "/test";
	}
	server += "/paiement.cgi";
	this.tpe.CMCIC_SERVEUR = server;
};

/**
* Transation method
*/

CMCIC.prototype.transaction = function (tpe, transactionData) {
	this.tpe = tpe;
	this.data = {};

	this.init(transactionData);
};

CMCIC.prototype.transaction.prototype.init = function (transactionData) {
	this.data = {
		date				: "",
		amount				: "",
		reference			: "",
		texteLibre			: {},
		email				: "",
		NbrEch				: "",
		DateEcheance1		: "",
		MontantEcheance1	: "",
		DateEcheance2		: "",
		MontantEcheance2	: "",
		DateEcheance3		: "",
		MontantEcheance3	: "",
		DateEcheance4		: "",
		MontantEcheance4	: "",
		options				: ""
	};
	_.extend(this.data, transactionData);
};

CMCIC.prototype.transaction.prototype.calculateMAC = function(data) {
	return crypto.createHmac('sha1', this.getHashKey()).update(data).digest('hex');
};

CMCIC.prototype.transaction.prototype.dataToSend = function(data) {
	if (!data) {
		data = this.data;
	}
	return this.tpe.tpe.CMCIC_TPE+
		'*' + data.date+
		'*' + data.amount + this.tpe.tpe.CMCIC_CURRENCY+
		'*' + data.reference+
		'*' + data.texteLibre+
		'*' + this.tpe.tpe.CMCIC_VERSION+
		'*' + this.tpe.tpe.CMCIC_LNG+
		'*' + this.tpe.tpe.CMCIC_CODESOCIETE+
		'*' + data.email+
		'*' + data.NbrEch+
		'*' + data.DateEcheance1+
		'*' + data.MontantEcheance1+
		'*' + data.DateEcheance2+
		'*' + data.MontantEcheance2+
		'*' + data.DateEcheance3+
		'*' + data.MontantEcheance3+
		'*' + data.DateEcheance4+
		'*' + data.MontantEcheance4+
		'*' + data.options;
};

CMCIC.prototype.transaction.prototype.getHashKey = function() {
	var data = this.tpe.tpe,
	hexStrkey = data.CMCIC_CLE.slice(0, 38),
	hexFinal = '' + data.CMCIC_CLE.slice(38) + '00',
	cca0 = hexFinal.charCodeAt(0);

	if (cca0  > 70 && cca0 < 97){
		hexStrkey += String.fromCharCode(cca0-23) + hexFinal.charAt(1);
	} else {
		if (hexFinal.charAt(1) == 'M'){
			hexStrkey += hexFinal.charAt(0) + '0';
		} else {
			hexStrkey += hexFinal.slice(0, 2);
		}
	}

	var r = "";
	for (var i = 0; i < hexStrkey.length / 2; i++) {
		var hex = hexStrkey[i*2] + hexStrkey[i*2 + 1];
		hexPack = parseInt(hex, 16);
		r +=  String.fromCharCode(hexPack);
	}
	return r;
};

CMCIC.prototype.transaction.prototype.stringifyTexteLibre = function() {
	var result = '',
	i = 0;
	for (var key in this.data.texteLibre) {
		var s = key+'='+this.data.texteLibre[key];
		if (i !== 0) {
			s = '&'+s;
		}
		if (result.length + s.length > 3200) {
			break;
		}
		result += s;
		i++;
	}
	if (result != '') {
		this.data.texteLibre = result;
	}
}

CMCIC.prototype.transaction.prototype.form = function(id, autosubmit) {
	this.stringifyTexteLibre();
	var mac = this.calculateMAC(this.dataToSend());
	result = '<form method="post" id="'+id+'" action='+this.tpe.tpe.CMCIC_SERVEUR+'>'+
		'<input type="hidden" name="version" value="'+this.tpe.tpe.CMCIC_VERSION+'">'+
		'<input type="hidden" name="TPE" value="'+this.tpe.tpe.CMCIC_TPE+'">'+
		'<input type="hidden" name="MAC" value="'+mac+'">'+
		'<input type="hidden" name="url_retour" value="'+this.tpe.tpe.CMCIC_URL_RETOUR+'">'+
		'<input type="hidden" name="url_retour_ok" value="'+this.tpe.tpe.CMCIC_URLOK+'">'+
		'<input type="hidden" name="url_retour_err" value="'+this.tpe.tpe.CMCIC_URLKO+'">'+
		'<input type="hidden" name="lgue" value="'+this.tpe.tpe.CMCIC_LNG+'">'+
		'<input type="hidden" name="societe" value="'+this.tpe.tpe.CMCIC_CODESOCIETE+'">'+
		'<input type="hidden" name="date" value="'+this.data.date+'">'+
		'<input type="hidden" name="montant" value="'+this.data.amount+this.tpe.tpe.CMCIC_CURRENCY+'">'+
		'<input type="hidden" name="reference" value="'+this.data.reference+'">'+
		'<input type="hidden" name="mail" value="'+this.data.email+'">'+
		'<input type="hidden" name="texte-libre" value="'+this.data.texteLibre+'">'+
		'
'+
		'<input type="submit" name="bouton" value="">'+
		'
</form>';
	if (autosubmit === true) {
		result += '';
	}
	return result;
};

module.exports = new CMCIC();
<|endoftext|>


<commit_before>!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;o="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,o.basicLightbox=e()}}(function(){return function e(o,t,n){function r(f,c){if(!t[f]){if(!o[f]){var a="function"==typeof require&&require;if(!c&&a)return a(f,!0);if(i)return i(f,!0);var u=new Error("Cannot find module '"+f+"'");throw u.code="MODULE_NOT_FOUND",u}var l=t[f]={exports:{}};o[f][0].call(l.exports,function(e){var t=o[f][1][e];return r(t?t:e)},l,l.exports,e,o,t,n)}return t[f].exports}for(var i="function"==typeof require&&require,f=0;f<n.length;f++)r(n[f]);return r}({1:[function(e,o,t){"use strict";function n(e){if(Array.isArray(e)){for(var o=0,t=Array(e.length);o<e.length;o++)t[o]=e[o];return t}return Array.from(e)}Object.defineProperty(t,"__esModule",{value:!0});var r=function(e){"function"==typeof e.stopPropagation&&e.stopPropagation(),"function"==typeof e.preventDefault&&e.preventDefault()},i=function(e,o){e.onclick=function(e){var t=this.getAttribute("data-basicLightbox-show"),n=document.querySelector('[data-basicLightbox="'+t+'"]'),i=n.outerHTML;s(i,o),r(e)}},f=function(e,o){e.onclick=function(e){b(o),r(e)}},c=function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return e.closable!==!1&&(e.closable=!0),"function"==typeof e.className&&(e.className=e.className()),"string"!=typeof e.className&&(e.className=""),"function"!=typeof e.beforeShow&&(e.beforeShow=function(){}),"function"!=typeof e.afterShow&&(e.afterShow=function(){}),"function"!=typeof e.beforeClose&&(e.beforeClose=function(){}),"function"!=typeof e.afterClose&&(e.afterClose=function(){}),"function"==typeof e.beforeHTML&&(e.beforeHTML=e.beforeHTML()),"string"!=typeof e.beforeHTML&&(e.beforeHTML=""),"function"==typeof e.afterHTML&&(e.afterHTML=e.afterHTML()),"string"!=typeof e.afterHTML&&(e.afterHTML=""),"function"==typeof e.beforePlaceholder&&(e.beforePlaceholder=e.beforePlaceholder()),"string"!=typeof e.beforePlaceholder&&(e.beforePlaceholder=""),"function"==typeof e.afterPlaceholder&&(e.afterPlaceholder=e.afterPlaceholder()),"string"!=typeof e.afterPlaceholder&&(e.afterPlaceholder=""),e},a=function(){var e=arguments.length<=0||void 0===arguments[0]?"":arguments[0],o=arguments[1];return'\n
\n

\n			'+o.beforePlaceholder+'\n
\n				'+o.beforeHTML+"\n				"+e+"\n				"+o.afterHTML+"\n
\n			"+o.afterPlaceholder+"\n
\n	"},u=t.exists=function(){var e=document.querySelector(".basicLightbox");return null!=e},l=t.visible=function(){var e=document.querySelector(".basicLightbox--visible");return null!=e},s=t.show=function(e,o){if(u()===!0)return!1;if(o=c(o),o.beforeShow()===!1)return!1;document.body.insertAdjacentHTML("beforeend",a(e,o));var t=document.querySelector(".basicLightbox");return o.closable===!0&&f(t.querySelector(".basicLightbox__close"),o),setTimeout(function(){requestAnimationFrame(function(){t.classList.add("basicLightbox--visible"),o.afterShow()})},10),!0},b=t.close=function(e){if(e=c(e),e.beforeClose()===!1)return!1;var o=document.querySelector(".basicLightbox");return l()===!1?!1:(o.classList.remove("basicLightbox--visible"),setTimeout(function(){requestAnimationFrame(function(){return u()===!1?!1:(o.parentElement.removeChild(o),void e.afterClose())})},410),!0)};t.init=function(e,o){"string"==typeof e&&(e=[].concat(n(document.querySelectorAll(e)))),null==e&&(e=[].concat(n(document.querySelectorAll("[data-basicLightbox-show]")))),e.forEach(function(e){return i(e,o)})}},{}]},{},[1])(1)});<commit_msg>Rebuild<commit_after>!function(e){if("object"==typeof exports&&"undefined"!=typeof module)module.exports=e();else if("function"==typeof define&&define.amd)define([],e);else{var o;o="undefined"!=typeof window?window:"undefined"!=typeof global?global:"undefined"!=typeof self?self:this,o.basicLightbox=e()}}(function(){return function e(o,t,n){function r(f,c){if(!t[f]){if(!o[f]){var a="function"==typeof require&&require;if(!c&&a)return a(f,!0);if(i)return i(f,!0);var l=new Error("Cannot find module '"+f+"'");throw l.code="MODULE_NOT_FOUND",l}var u=t[f]={exports:{}};o[f][0].call(u.exports,function(e){var t=o[f][1][e];return r(t?t:e)},u,u.exports,e,o,t,n)}return t[f].exports}for(var i="function"==typeof require&&require,f=0;f<n.length;f++)r(n[f]);return r}({1:[function(e,o,t){"use strict";Object.defineProperty(t,"__esModule",{value:!0});var n=function(e){"function"==typeof e.stopPropagation&&e.stopPropagation(),"function"==typeof e.preventDefault&&e.preventDefault()},r=function(e,o){e.onclick=function(e){var t=this.getAttribute("data-basicLightbox-show"),r=document.querySelector('[data-basicLightbox="'+t+'"]'),i=r.outerHTML;u(i,o),n(e)}},i=function(e,o){e.onclick=function(e){s(o),n(e)}},f=function(){var e=arguments.length<=0||void 0===arguments[0]?{}:arguments[0];return e.closable!==!1&&(e.closable=!0),"function"==typeof e.className&&(e.className=e.className()),"string"!=typeof e.className&&(e.className=""),"function"!=typeof e.beforeShow&&(e.beforeShow=function(){}),"function"!=typeof e.afterShow&&(e.afterShow=function(){}),"function"!=typeof e.beforeClose&&(e.beforeClose=function(){}),"function"!=typeof e.afterClose&&(e.afterClose=function(){}),"function"==typeof e.beforeHTML&&(e.beforeHTML=e.beforeHTML()),"string"!=typeof e.beforeHTML&&(e.beforeHTML=""),"function"==typeof e.afterHTML&&(e.afterHTML=e.afterHTML()),"string"!=typeof e.afterHTML&&(e.afterHTML=""),"function"==typeof e.beforePlaceholder&&(e.beforePlaceholder=e.beforePlaceholder()),"string"!=typeof e.beforePlaceholder&&(e.beforePlaceholder=""),"function"==typeof e.afterPlaceholder&&(e.afterPlaceholder=e.afterPlaceholder()),"string"!=typeof e.afterPlaceholder&&(e.afterPlaceholder=""),e},c=function(){var e=arguments.length<=0||void 0===arguments[0]?"":arguments[0],o=arguments[1];return'\n
\n

\n			'+o.beforePlaceholder+'\n
\n				'+o.beforeHTML+"\n				"+e+"\n				"+o.afterHTML+"\n
\n			"+o.afterPlaceholder+"\n
\n	"},a=t.exists=function(){var e=document.querySelector(".basicLightbox");return null!=e},l=t.visible=function(){var e=document.querySelector(".basicLightbox--visible");return null!=e},u=t.show=function(e,o){if(a()===!0)return!1;if(o=f(o),o.beforeShow()===!1)return!1;document.body.insertAdjacentHTML("beforeend",c(e,o));var t=document.querySelector(".basicLightbox");return o.closable===!0&&i(t.querySelector(".basicLightbox__close"),o),setTimeout(function(){requestAnimationFrame(function(){t.classList.add("basicLightbox--visible"),o.afterShow()})},10),!0},s=t.close=function(e){if(e=f(e),e.beforeClose()===!1)return!1;var o=document.querySelector(".basicLightbox");return l()===!1?!1:(o.classList.remove("basicLightbox--visible"),setTimeout(function(){requestAnimationFrame(function(){return a()===!1?!1:(o.parentElement.removeChild(o),void e.afterClose())})},410),!0)};t.init=function(e,o){"string"==typeof e&&(e=document.querySelectorAll(e)),null==e&&(e=document.querySelectorAll("[data-basicLightbox-show]")),Array.prototype.forEach.call(e,function(e){return r(e,o)})}},{}]},{},[1])(1)});<|endoftext|>


<commit_before>/*
 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
 *
 * The main core of CNAPI is implemented here. Sets up the models, clients and
 * HTTP server.
 */

var amqp = require('./amqp-plus');
var async = require('async');
var Logger = require('bunyan');
var restify = require('restify');
var http = require('http');
var https = require('https');
var util = require('util');

var createModel = require('./models').createModel;
var ModelServer = require('./models/server');
var createServer = require('./server').createServer;
var Heartbeater = require('./heartbeater');
var Ur = require('./ur');

function CNAPI(config) {
    this.config = config;
    this.servers = {};
    this.log = new Logger({
        name: 'cnapi',
        level: config.logLevel,
        serializers: {
            err: Logger.stdSerializers.err,
            req: Logger.stdSerializers.req,
            res: restify.bunyan.serializers.response
        }
    });
}

CNAPI.prototype.start = function () {
    var self = this;

    http.globalAgent.maxSockets = self.config.maxHttpSockets || 100;
    https.globalAgent.maxSockets = self.config.maxHttpSockets || 100;

    var modelOptions = {
        amqp_use_system_config: false,
        amqp: self.config.amqp,
        ufds: self.config.ufds,
        redis: self.config.redis,
        wfapi: self.config.wfapi,
        napi: self.config.napi,
        cnapi: self.config.cnapi,
        assets: self.config.assets,
        datacenter: self.config.datacenter_name,
        log: self.log
    };

    var urOptions = {
        log: self.log
    };

    self.config.log = self.log;
    self.log.info(self.modelOptions);

    var heartbeaterOptions = {
        log: self.log
    };

    async.waterfall([
        function (callback) {
            self.initializeAmqp(callback);
        },
        function (callback) {
            self.initializeModel(modelOptions, callback);
        },
        function (callback) {
            self.initializeUr(urOptions, callback);
            self.model.setUr(self.ur);
        },
        function (callback) {
            self.initializeHeartbeater(heartbeaterOptions, callback);
        },
        function (callback) {
            self.ur.useConnection(self.amqpConnection);
            self.heartbeater.useConnection(self.amqpConnection);
            self.model.useConnection(self.amqpConnection);

            self.amqpConnection.reconnect();
            callback();
        },
        function (callback) {
            var serverOptions = {
                model: self.model,
                log: self.log
            };
            self.initializeServer(serverOptions, callback);
        }
    ],
    function (error) {
        self.server.listen(self.config.api.port, function () {
            self.log.info(
                '%s listening at %s',
                self.server.name,
                self.server.url);
        });
    });
};

CNAPI.prototype.initializeAmqp = function (callback) {
    var self = this;
    var connection = self.amqpConnection
        = amqp.createConnection(self.config.amqp, { log: self.log });

    connection.on('ready', function () {
        self.collectGlobalSysinfo();
    });

    callback();
    return;
};

CNAPI.prototype.initializeModel = function (options, callback) {
    var self = this;

    self.log.info('Initializing model and connecting to ufds');

    var model = self.model = createModel(options);
    model.connect(function (error) {
        if (error) {
            self.log.error(error, 'Error connecting to ufds');
            return callback(error);
        }
        self.log.debug('Model connected');
        return callback();
    });
};

CNAPI.prototype.initializeHeartbeater = function (options, callback) {
    var self = this;

    self.heartbeater = new Heartbeater(options);
    self.heartbeater.on('heartbeat', self.onHeartbeat.bind(self));
    callback();
    return;
};

CNAPI.prototype.initializeUr = function (options, callback) {
    var self = this;
    self.ur = new Ur(options);
    self.ur.on('serverStartup', self.onServerStartup.bind(self));
    callback();
    return;
};

CNAPI.prototype.collectGlobalSysinfo = function () {
    var self = this;
    self.ur.broadcastSysinfo(function (error, sysinfoCollection) {
        async.forEachSeries(
            sysinfoCollection,
            function (sysinfo, cb) {
                self.refreshServerFromSysinfo(sysinfo, cb);
            },
            function (err) {
                if (err) {
                self.log.error(
                    'Error updating server record from global broadcast: %s',
                    err.message);
                }
            });
    });
};

CNAPI.prototype.initializeServer = function (options, callback) {
    this.log.info('Initializing HTTP server');
    this.server = createServer(options);
    return callback();
};

/**
 * Given a sysinfo object, this function will check if the server exists in
 * UFDS. Because the sysinfo message is sent only on start-up, if the server
 * does exist in UFDS, we will update the record with the most recent
 * information.
 * If the server does not exist, it will be created in UFDS. In either case,
 * the Redis server cache will be updated to reflect that we currently know
 * about this server.
 */
CNAPI.prototype.refreshServerFromSysinfo =
function (sysinfo, callback) {
    var self = this;

    var uuid = sysinfo['UUID'];
    var lastBoot
      = new Date(Number(sysinfo['Boot Time']) * 1000).toISOString();

    var serverModel = new ModelServer(uuid);

    serverModel.get(function (getError, server) {
        if (getError) {
            self.log.error(
                getError, 'Error fetching server %s from ufds', uuid);
            callback(getError);
            return;
        }

        if (server) {
            var ufdsServer = serverModel.valuesFromSysinfo({
                sysinfo: sysinfo,
                last_boot: lastBoot
            });

            var changes = [
                {
                    type: 'replace',
                    modification: ufdsServer
                }
            ];

            serverModel.cacheSetServer(server, function (updateError) {
                if (updateError) {
                    self.log.error(updateError, 'Error updating server cache');
                    self.log.error(server, 'Object in question');
                    callback(updateError);
                    return;
                }
                serverModel.modify(
                    changes,
                    function (modifyError) {
                        if (modifyError) {
                            self.log.error(
                                modifyError,
                                'Error setting last_boot attribute on %s',
                                uuid);
                            self.log.error(
                                util.inspect(changes));
                            return;
                        }
                        self.log.info('Modified Server UFDS record');
                        callback();
                        return;
                    });
            });
        } else {
            serverModel.create(
                { sysinfo: sysinfo, last_boot: lastBoot },
                function (error, s) {
                    if (error) {
                        self.log.error(error, 'Error creating server in ufds');
                        return;
                    }
                    self.log.debug('Cached server in memory');
                    serverModel.cacheSetServer(
                        s,
                        function (updateError) {
                            if (updateError) {
                                self.log.error(
                                    updateError,
                                    'Error updating server cache');
                                self.log.error(util.inspect(s));
                                callback(updateError);
                                return;
                            }
                            callback();
                    });
                });
        }
    });
};

/**
 * Take a UUID and a heartbeat object. If the server exists in the cache,
 * update the memory usage cache and the server VMs cache. If the server
 * doesn't exist in the cache, check if it exists in UFDS. If it does exist in
 * UFDS, add the server to the servers cache and the VMs to the server VMs
 * cache. If the server does not exist in UFDS, then create the server in UFDS
 * and then add the appropriate values into UFDS.
 */
CNAPI.prototype.refreshServerFromHeartbeat =
function (uuid, heartbeat, cb) {
    var self = this;

    var serverModel = new ModelServer(uuid);

    serverModel.cacheCheckServerExists(function (error, exists) {
        if (error) {
            self.log.error(error, 'Error checking for server in redis cache');
        }
        if (exists) {
            self.log.trace('Server %s found in local "cache"', uuid);

            updateVmsCacheFromHeartbeat(function (cacheError) {
                if (cacheError) {
                    self.log.error('Could not update VMs cache on heartbeat');
                    return;
                }
                serverModel.cacheSetServerStatus(
                    'running',
                    function (statusError) {
                        if (statusError) {
                            self.log.error(
                                statusError,
                                'Error setting server (%s) status', self.uuid);
                        }
                        serverModel.updateMemoryFromHeartbeat(
                            heartbeat, callback);
                    });
            });
            return;
        }

        self.log.info('Server %s not found in cache, checking in ufds', uuid);

        serverModel.get(function (listError, server) {
            if (listError) {
                self.log.error(listError, 'Error listing servers in ufds');
                callback(listError);
                return;
            }

            updateVmsCacheFromHeartbeat(function (cacheError) {
                if (cacheError) {
                    self.log.error('Could not update VMs cache on heartbeat');
                }

                // Check if there were matching servers in UFDS
                if (server) {
                    serverModel.cacheSetServerStatus(
                        'running',
                        function (statusError) {
                            if (statusError) {
                                self.log.error(
                                    error,
                                    'Error setting server (%s) status',
                                    self.uuid);
                            }
                            serverModel.updateMemoryFromHeartbeat(
                                heartbeat, callback);
                        });
                } else {
                    serverModel.create(
                        { uuid: uuid, heartbeat: heartbeat },
                        function () {
                            serverModel.updateMemoryFromHeartbeat(
                                heartbeat, callback);
                        });
                    return;
                }
            });
        });
    });

    function updateVmsCacheFromHeartbeat(cacheCb) {
        var vms = {};
        heartbeat.zoneStatus.forEach(function (vm) {
            vms[vm[1]] = '1';
        });

        serverModel.cacheSetVms(vms, function (cacheError) {
            if (cacheError) {
                self.log.error('Could not update VMs cache on heartbeat');
                cacheCb(cacheError);
                return;
            }

            cacheCb();
        });
    }

    function callback(error) {
        if (cb) {
            cb(error);
            return;
        }
    }
};

/**
 * Execute this function whenver a sysinfo message is received via AMQP from
 * the Ur agent of a server which has started up.
 */
CNAPI.prototype.onServerStartup = function (message, routingKey) {
    var self = this;

    var uuid = routingKey.split('.')[2];
    self.log.info('Ur startup message received from %s', uuid);
    self.log.trace(message);

    ModelServer.beginSysinfoWorkflow(message);

    self.refreshServerFromSysinfo(
        message,
        function (error) {
            if (error) {
                self.log.error(
                    error,
                    'Error updating server from startup sysinfo');
                return;
            }
            self.log.info(message, 'Server %s startup sysinfo', uuid);
        });
};

/**
 * Execute this function whenever a heartbeat is received from a server.
 */
CNAPI.prototype.onHeartbeat = function (heartbeat, routingKey) {
    var self = this;
    var uuid = routingKey.split('.')[1];
    self.log.trace('Heartbeat (%s) received -- %d zones.',
        uuid, heartbeat.zoneStatus[0].length);

    self.refreshServerFromHeartbeat(
        uuid,
        heartbeat,
        function (refreshError, server) {
            if (refreshError) {
                self.log.error(
                    refreshError,
                    'Error refreshing server\'s record in ufds');
                return;
            }
        });
};

module.exports = CNAPI;
<commit_msg>Revert "CNAPI-119: Remove old workflow api library" due to lack of post info ability into wf-client. Also, because provisioning is totally broken.<commit_after>/*
 * Copyright (c) 2012, Joyent, Inc. All rights reserved.
 *
 * The main core of CNAPI is implemented here. Sets up the models, clients and
 * HTTP server.
 */

var amqp = require('./amqp-plus');
var async = require('async');
var Logger = require('bunyan');
var restify = require('restify');
var http = require('http');
var https = require('https');
var util = require('util');

var WFAPI = require('./wfapi');

var createModel = require('./models').createModel;
var ModelServer = require('./models/server');
var createServer = require('./server').createServer;
var Heartbeater = require('./heartbeater');
var Ur = require('./ur');

function CNAPI(config) {
    this.config = config;
    this.servers = {};
    this.log = new Logger({
        name: 'cnapi',
        level: config.logLevel,
        serializers: {
            err: Logger.stdSerializers.err,
            req: Logger.stdSerializers.req,
            res: restify.bunyan.serializers.response
        }
    });
}

CNAPI.prototype.start = function () {
    var self = this;

    http.globalAgent.maxSockets = self.config.maxHttpSockets || 100;
    https.globalAgent.maxSockets = self.config.maxHttpSockets || 100;

    var modelOptions = {
        amqp_use_system_config: false,
        amqp: self.config.amqp,
        ufds: self.config.ufds,
        redis: self.config.redis,
        wfapi: self.config.wfapi,
        napi: self.config.napi,
        cnapi: self.config.cnapi,
        assets: self.config.assets,
        datacenter: self.config.datacenter_name,
        log: self.log
    };

    var urOptions = {
        log: self.log
    };

    self.config.log = self.log;
    self.log.info(self.modelOptions);

    var heartbeaterOptions = {
        log: self.log
    };

    async.waterfall([
        function (callback) {
            self.initializeAmqp(callback);
        },
        function (callback) {
            self.initializeModel(modelOptions, callback);
        },
        function (callback) {
            var wfapi = new WFAPI(self.config);
            wfapi.initWorkflows();
            self.model.setWfapi(wfapi);
            return (callback());
        },
        function (callback) {
            self.initializeUr(urOptions, callback);
            self.model.setUr(self.ur);
        },
        function (callback) {
            self.initializeHeartbeater(heartbeaterOptions, callback);
        },
        function (callback) {
            self.ur.useConnection(self.amqpConnection);
            self.heartbeater.useConnection(self.amqpConnection);
            self.model.useConnection(self.amqpConnection);

            self.amqpConnection.reconnect();
            callback();
        },
        function (callback) {
            var serverOptions = {
                model: self.model,
                log: self.log
            };
            self.initializeServer(serverOptions, callback);
        }
    ],
    function (error) {
        self.server.listen(self.config.api.port, function () {
            self.log.info(
                '%s listening at %s',
                self.server.name,
                self.server.url);
        });
    });
};

CNAPI.prototype.initializeAmqp = function (callback) {
    var self = this;
    var connection = self.amqpConnection
        = amqp.createConnection(self.config.amqp, { log: self.log });

    connection.on('ready', function () {
        self.collectGlobalSysinfo();
    });

    callback();
    return;
};

CNAPI.prototype.initializeModel = function (options, callback) {
    var self = this;

    self.log.info('Initializing model and connecting to ufds');

    var model = self.model = createModel(options);
    model.connect(function (error) {
        if (error) {
            self.log.error(error, 'Error connecting to ufds');
            return callback(error);
        }
        self.log.debug('Model connected');
        return callback();
    });
};

CNAPI.prototype.initializeHeartbeater = function (options, callback) {
    var self = this;

    self.heartbeater = new Heartbeater(options);
    self.heartbeater.on('heartbeat', self.onHeartbeat.bind(self));
    callback();
    return;
};

CNAPI.prototype.initializeUr = function (options, callback) {
    var self = this;
    self.ur = new Ur(options);
    self.ur.on('serverStartup', self.onServerStartup.bind(self));
    callback();
    return;
};

CNAPI.prototype.collectGlobalSysinfo = function () {
    var self = this;
    self.ur.broadcastSysinfo(function (error, sysinfoCollection) {
        async.forEachSeries(
            sysinfoCollection,
            function (sysinfo, cb) {
                self.refreshServerFromSysinfo(sysinfo, cb);
            },
            function (err) {
                if (err) {
                self.log.error(
                    'Error updating server record from global broadcast: %s',
                    err.message);
                }
            });
    });
};

CNAPI.prototype.initializeServer = function (options, callback) {
    this.log.info('Initializing HTTP server');
    this.server = createServer(options);
    return callback();
};

/**
 * Given a sysinfo object, this function will check if the server exists in
 * UFDS. Because the sysinfo message is sent only on start-up, if the server
 * does exist in UFDS, we will update the record with the most recent
 * information.
 * If the server does not exist, it will be created in UFDS. In either case,
 * the Redis server cache will be updated to reflect that we currently know
 * about this server.
 */
CNAPI.prototype.refreshServerFromSysinfo =
function (sysinfo, callback) {
    var self = this;

    var uuid = sysinfo['UUID'];
    var lastBoot
      = new Date(Number(sysinfo['Boot Time']) * 1000).toISOString();

    var serverModel = new ModelServer(uuid);

    serverModel.get(function (getError, server) {
        if (getError) {
            self.log.error(
                getError, 'Error fetching server %s from ufds', uuid);
            callback(getError);
            return;
        }

        if (server) {
            var ufdsServer = serverModel.valuesFromSysinfo({
                sysinfo: sysinfo,
                last_boot: lastBoot
            });

            var changes = [
                {
                    type: 'replace',
                    modification: ufdsServer
                }
            ];

            serverModel.cacheSetServer(server, function (updateError) {
                if (updateError) {
                    self.log.error(updateError, 'Error updating server cache');
                    self.log.error(server, 'Object in question');
                    callback(updateError);
                    return;
                }
                serverModel.modify(
                    changes,
                    function (modifyError) {
                        if (modifyError) {
                            self.log.error(
                                modifyError,
                                'Error setting last_boot attribute on %s',
                                uuid);
                            self.log.error(
                                util.inspect(changes));
                            return;
                        }
                        self.log.info('Modified Server UFDS record');
                        callback();
                        return;
                    });
            });
        } else {
            serverModel.create(
                { sysinfo: sysinfo, last_boot: lastBoot },
                function (error, s) {
                    if (error) {
                        self.log.error(error, 'Error creating server in ufds');
                        return;
                    }
                    self.log.debug('Cached server in memory');
                    serverModel.cacheSetServer(
                        s,
                        function (updateError) {
                            if (updateError) {
                                self.log.error(
                                    updateError,
                                    'Error updating server cache');
                                self.log.error(util.inspect(s));
                                callback(updateError);
                                return;
                            }
                            callback();
                    });
                });
        }
    });
};

/**
 * Take a UUID and a heartbeat object. If the server exists in the cache,
 * update the memory usage cache and the server VMs cache. If the server
 * doesn't exist in the cache, check if it exists in UFDS. If it does exist in
 * UFDS, add the server to the servers cache and the VMs to the server VMs
 * cache. If the server does not exist in UFDS, then create the server in UFDS
 * and then add the appropriate values into UFDS.
 */
CNAPI.prototype.refreshServerFromHeartbeat =
function (uuid, heartbeat, cb) {
    var self = this;

    var serverModel = new ModelServer(uuid);

    serverModel.cacheCheckServerExists(function (error, exists) {
        if (error) {
            self.log.error(error, 'Error checking for server in redis cache');
        }
        if (exists) {
            self.log.trace('Server %s found in local "cache"', uuid);

            updateVmsCacheFromHeartbeat(function (cacheError) {
                if (cacheError) {
                    self.log.error('Could not update VMs cache on heartbeat');
                    return;
                }
                serverModel.cacheSetServerStatus(
                    'running',
                    function (statusError) {
                        if (statusError) {
                            self.log.error(
                                statusError,
                                'Error setting server (%s) status', self.uuid);
                        }
                        serverModel.updateMemoryFromHeartbeat(
                            heartbeat, callback);
                    });
            });
            return;
        }

        self.log.info('Server %s not found in cache, checking in ufds', uuid);

        serverModel.get(function (listError, server) {
            if (listError) {
                self.log.error(listError, 'Error listing servers in ufds');
                callback(listError);
                return;
            }

            updateVmsCacheFromHeartbeat(function (cacheError) {
                if (cacheError) {
                    self.log.error('Could not update VMs cache on heartbeat');
                }

                // Check if there were matching servers in UFDS
                if (server) {
                    serverModel.cacheSetServerStatus(
                        'running',
                        function (statusError) {
                            if (statusError) {
                                self.log.error(
                                    error,
                                    'Error setting server (%s) status',
                                    self.uuid);
                            }
                            serverModel.updateMemoryFromHeartbeat(
                                heartbeat, callback);
                        });
                } else {
                    serverModel.create(
                        { uuid: uuid, heartbeat: heartbeat },
                        function () {
                            serverModel.updateMemoryFromHeartbeat(
                                heartbeat, callback);
                        });
                    return;
                }
            });
        });
    });

    function updateVmsCacheFromHeartbeat(cacheCb) {
        var vms = {};
        heartbeat.zoneStatus.forEach(function (vm) {
            vms[vm[1]] = '1';
        });

        serverModel.cacheSetVms(vms, function (cacheError) {
            if (cacheError) {
                self.log.error('Could not update VMs cache on heartbeat');
                cacheCb(cacheError);
                return;
            }

            cacheCb();
        });
    }

    function callback(error) {
        if (cb) {
            cb(error);
            return;
        }
    }
};

/**
 * Execute this function whenver a sysinfo message is received via AMQP from
 * the Ur agent of a server which has started up.
 */
CNAPI.prototype.onServerStartup = function (message, routingKey) {
    var self = this;

    var uuid = routingKey.split('.')[2];
    self.log.info('Ur startup message received from %s', uuid);
    self.log.trace(message);

    ModelServer.beginSysinfoWorkflow(message);

    self.refreshServerFromSysinfo(
        message,
        function (error) {
            if (error) {
                self.log.error(
                    error,
                    'Error updating server from startup sysinfo');
                return;
            }
            self.log.info(message, 'Server %s startup sysinfo', uuid);
        });
};

/**
 * Execute this function whenever a heartbeat is received from a server.
 */
CNAPI.prototype.onHeartbeat = function (heartbeat, routingKey) {
    var self = this;
    var uuid = routingKey.split('.')[1];
    self.log.trace('Heartbeat (%s) received -- %d zones.',
        uuid, heartbeat.zoneStatus[0].length);

    self.refreshServerFromHeartbeat(
        uuid,
        heartbeat,
        function (refreshError, server) {
            if (refreshError) {
                self.log.error(
                    refreshError,
                    'Error refreshing server\'s record in ufds');
                return;
            }
        });
};

module.exports = CNAPI;
<|endoftext|>


<commit_before>var async = require('async');
var Logger = require('bunyan');
var restify = require('restify');
var http = require('http');
var https = require('https');
var wfapi = require('./wfapi');

var createModel = require('./models').createModel;
var createServer = require('./server').createServer;
var sysinfoToLdapServer = require('./models').sysinfoToLdapServer;
var Heartbeater = require('./heartbeater');
var Ur = require('./ur');

function CNAPI(config) {
    this.config = config;
    this.servers = {};
    this.log = new Logger({
        name: 'cnapi',
        level: config.logLevel,
        serializers: {
            err: Logger.stdSerializers.err,
            req: Logger.stdSerializers.req,
            res: restify.bunyan.serializers.response
        }
    });
}

CNAPI.prototype.start = function () {
    var self = this;

    http.globalAgent.maxSockets = self.config.maxHttpSockets || 100;
    https.globalAgent.maxSockets = self.config.maxHttpSockets || 100;

    var modelOptions = {
        amqp_use_system_config: false,
        ufds: self.config.ufds,
        amqp: self.config.amqp,
        wfapi: self.config.wfapi,
        datacenter: self.config.datacenter_name,
        log: self.log
    };

    self.config.log = self.log;
    wfapi.initializeClient(self.config);

    self.log.info(self.modelOptions);

    var heartbeaterOptions = {
        host: self.config.amqp.host,
        log: self.log
    };

    var urOptions = {
        host: self.config.amqp.host,
        log: self.log
    };

    async.waterfall([
        function (callback) {
            self.initializeModel(modelOptions, callback);
        },
        function (callback) {
            var serverOptions = {
                model: self.model
            };
            self.initializeServer(serverOptions, callback);
        },
        function (callback) {
            self.initializeHeartbeater(heartbeaterOptions, callback);
        },
        function (callback) {
            self.initializeUr(urOptions, callback);
        }
    ],
    function (error) {
        self.server.listen(self.config.api.port, function () {
            self.log.info(
                '%s listening at %s',
                self.server.name,
                self.server.url);
        });
    });
};

CNAPI.prototype.initializeModel = function (options, callback) {
    var self = this;

    self.log.info('Initializing model and connecting to ufds');

    var model = self.model = createModel(options);
    model.connect(function (error) {
        if (error) {
            self.log.error(error, 'Error connecting to ufds');
            return callback(error);
        }
        self.log.debug('Model connected');
        return callback();
    });
};

CNAPI.prototype.initializeServer = function (options, callback) {
    this.server = createServer(options);
    return callback();
};

CNAPI.prototype.initializeHeartbeater = function (options, callback) {
    var self = this;

    var heartbeaterListener = new Heartbeater(options);
    heartbeaterListener.on('heartbeat', self.onHeartbeat.bind(self));
    heartbeaterListener.on('connectionError', function (err) {
        self.log.info('Connection error ' + err.message);
    });

    return callback();
};

CNAPI.prototype.initializeUr = function (options, callback) {
    var self = this;
    var ur = self.ur = new Ur(options);

    ur.connect(function () {
        ur.on('serverStartup', self.onServerStartup.bind(self));

        ur.on('connectionError', function (err) {
            self.log.info('Ur AMQP connection error');
        });
    });

    return callback();
};

CNAPI.prototype.refreshServerFromSysinfo = function (uuid, sysinfo, callback) {
    var self = this;

    if (self.servers[uuid]) {
        self.log.debug('Server %s found in local "cache"', uuid);
        return;
    }

    self.log.info('Server %s not found in cache, checking in ufds', uuid);

    self.model.listServers({ uuid: uuid }, function (list$error, servers) {
        if (list$error) {
            self.log.error(list$error, 'Error listing servers in ufds');
            callback(list$error);
            return;
        }

        if (servers.length > 0) {
            self.log.info(servers[0], '%s already in ufds', uuid);
            self.servers[uuid] = servers[0];
            callback(null, servers[0]);
            return;
        } else {
            self.createUfdsServerObject(
                { uuid: uuid, sysinfo: sysinfo },
                function (error, server) {
                    if (error) {
                        self.log.info(error, 'Error creating server in ufds');
                        return;
                    }
                    self.log.debug('Cached server in memory');
                    self.servers[uuid] = server;
                });
            return;
        }
    });
};

CNAPI.prototype.updateServerMemory =
function (uuid, server, heartbeat, callback) {
    var self = this;
    var changes = [];

    self.log.info('%s already in UFDS', uuid);

    // Check if memory values have changed. Compile a list of
    // modifications if any have.
    var memoryKeys = [
        ['availrmem_bytes', 'memoryavailablebytes'],
        ['arcsize_bytes', 'memoryarcbytes'],
        ['total_bytes', 'memorytotalbytes'] ];

    memoryKeys.forEach(function (keys) {
        var hbVal = heartbeat.meminfo[keys[0]].toString();
        var serverVal = server[keys[1]] ? server[keys[1]].toString() : '';

        if (hbVal !== serverVal) {
            var change = {
                type: 'replace',
                modification: {}
            };

            change.modification[keys[1]] = hbVal.toString();
            changes.push(change);
        }
    });

    // If there are changes to be pushed to UFDS, do so then update the cache.
    if (changes.length) {
        // Attempt to modify memory values for server in UFDS, and if
        // that succeeds, cache those values too.
        changes.push({
            type: 'replace',
            modification: {
                lastupdated: (new Date()).toISOString()
            }
        });
        self.log.debug(changes, 'UFDS changes');
        self.model.modifyServer(uuid, changes, function (modify$error) {
            if (modify$error) {
                self.log.error(
                    modify$error,
                    'Error modifying server record in UFDS');
                callback();
                return;
            }
            memoryKeys.forEach(function (keys) {
                var hbVal = heartbeat.meminfo[keys[0]].toString();
                server[keys[1]] = hbVal;
            });
            self.servers[uuid] = server;
        });
    } else {
        memoryKeys.forEach(function (keys) {
            var hbVal = heartbeat.meminfo[keys[0]].toString();
            server[keys[1]] = hbVal;
        });
        self.servers[uuid] = server;
        callback();
    }
};

CNAPI.prototype.refreshServerFromHeartbeat =
function (uuid, heartbeat, cb) {
    var self = this;

    if (self.servers[uuid]) {
        self.log.debug('Server %s found in local "cache"', uuid);

        self.updateServerMemory(
            uuid, self.servers[uuid], heartbeat, function () {
            callback();
            return;
        });
        return;
    }

    self.log.info('Server %s not found in cache, checking in ufds', uuid);

    self.model.listServers({ uuid: uuid }, function (list$error, servers) {
        if (list$error) {
            self.log.error(list$error, 'Error listing servers in ufds');
            callback(list$error);
            return;
        }

        // Check if there were matching servers in UFDS
        if (servers.length > 0) {
            self.updateServerMemory(
                uuid, servers[0], heartbeat, function (update$error) {
                callback();
                return;
            });
            return;
        } else {
            self.createUfdsServerObject(
                { uuid: uuid, heartbeat: heartbeat },
                callback);
            return;
        }
    });

    function callback(error) {
        if (cb) {
            cb(error);
            return;
        }
    }
};

CNAPI.prototype.createUfdsServerObject = function (opts, callback) {
    var self = this;
    var uuid = opts.uuid;
    var serverSysinfo;

    if (opts.sysinfo) {
        serverSysinfo = opts.sysinfo;
        create();
        return;
    } else {
        self.log.info('Querying Ur agent for server sysinfo');
        self.ur.serverSysinfo(uuid, function (error, sysinfo) {
            serverSysinfo = sysinfo;
            create();
            return;
        });
    }

    function create() {
        var heartbeat = opts.heartbeat;
        var server = self.serverObjectFromSysinfo({
            heartbeat: heartbeat,
            sysinfo: serverSysinfo
        });

        self.log.debug(server, 'Creating server in UFDS');
        console.dir(server);
        self.model.createServer(
            server,
            function (create$error, createdServer) {
                if (create$error) {
                    self.log.info('Error creating server in UFDS');
                    callback(create$error);
                    return;
                }
                self.log.info('Created server entry in UFDS');
                self.servers[uuid] = server;
                callback();
                return;
            });
    }
};

CNAPI.prototype.serverObjectFromSysinfo = function (opts) {
    var self = this;

    var server = {};
    var sysinfo = opts.sysinfo;
    var heartbeat = opts.heartbeat;

    server.sysinfo = JSON.stringify(sysinfo);
    server.datacenter = self.config.datacenter_name;

    server.uuid = sysinfo.UUID;
    server.hostname = sysinfo.Hostname;
    server.reserved = 'false';
    server.headnode
        = sysinfo['Boot Parameters']['headnode'] ? 'true' : 'false';

    server.status = 'running';
    server.defaultconsole = 'vga';
    server.serial = 'ttyb';
    server.serialspeed = '115200';
    server.objectclass = 'server';

    if (heartbeat) {
        var meminfo = heartbeat.meminfo;
        server.memoryavailablebytes = meminfo.availrmem_bytes.toString();
        server.memoryarcbytes = meminfo.arcsize_bytes.toString();
        server.memorytotalbytes = meminfo.total_bytes.toString();
    }

    server.lastupdated = (new Date()).toISOString();

    return server;
}

CNAPI.prototype.onServerStartup = function (message, routingKey) {
    var self = this;

    var uuid = routingKey.split('.')[2];
    self.log.info('Ur startup message received from %s', uuid);
    self.log.trace(message);

    self.refreshServerFromSysinfo(uuid, message);
};

CNAPI.prototype.onHeartbeat = function (heartbeat, routingKey) {
    var self = this;
    var uuid = routingKey.split('.')[1];
    self.log.debug('Heartbeat (%s) received -- %d zones.',
        uuid, heartbeat.zoneStatus[0].length);

    self.refreshServerFromHeartbeat(uuid, heartbeat, function (error, server) {
        if (error) {
            self.log.error(error, 'Error refreshing server\'s record in ufds');
            return;
        }
    });
};

module.exports = CNAPI;
<commit_msg>CNAPI-35: Add underscores to Server LDAP object keys<commit_after>var async = require('async');
var Logger = require('bunyan');
var restify = require('restify');
var http = require('http');
var https = require('https');
var wfapi = require('./wfapi');

var createModel = require('./models').createModel;
var createServer = require('./server').createServer;
var sysinfoToLdapServer = require('./models').sysinfoToLdapServer;
var Heartbeater = require('./heartbeater');
var Ur = require('./ur');

function CNAPI(config) {
    this.config = config;
    this.servers = {};
    this.log = new Logger({
        name: 'cnapi',
        level: config.logLevel,
        serializers: {
            err: Logger.stdSerializers.err,
            req: Logger.stdSerializers.req,
            res: restify.bunyan.serializers.response
        }
    });
}

CNAPI.prototype.start = function () {
    var self = this;

    http.globalAgent.maxSockets = self.config.maxHttpSockets || 100;
    https.globalAgent.maxSockets = self.config.maxHttpSockets || 100;

    var modelOptions = {
        amqp_use_system_config: false,
        ufds: self.config.ufds,
        amqp: self.config.amqp,
        wfapi: self.config.wfapi,
        datacenter: self.config.datacenter_name,
        log: self.log
    };

    self.config.log = self.log;
    wfapi.initializeClient(self.config);

    self.log.info(self.modelOptions);

    var heartbeaterOptions = {
        host: self.config.amqp.host,
        log: self.log
    };

    var urOptions = {
        host: self.config.amqp.host,
        log: self.log
    };

    async.waterfall([
        function (callback) {
            self.initializeModel(modelOptions, callback);
        },
        function (callback) {
            var serverOptions = {
                model: self.model
            };
            self.initializeServer(serverOptions, callback);
        },
        function (callback) {
            self.initializeHeartbeater(heartbeaterOptions, callback);
        },
        function (callback) {
            self.initializeUr(urOptions, callback);
        }
    ],
    function (error) {
        self.server.listen(self.config.api.port, function () {
            self.log.info(
                '%s listening at %s',
                self.server.name,
                self.server.url);
        });
    });
};

CNAPI.prototype.initializeModel = function (options, callback) {
    var self = this;

    self.log.info('Initializing model and connecting to ufds');

    var model = self.model = createModel(options);
    model.connect(function (error) {
        if (error) {
            self.log.error(error, 'Error connecting to ufds');
            return callback(error);
        }
        self.log.debug('Model connected');
        return callback();
    });
};

CNAPI.prototype.initializeServer = function (options, callback) {
    this.server = createServer(options);
    return callback();
};

CNAPI.prototype.initializeHeartbeater = function (options, callback) {
    var self = this;

    var heartbeaterListener = new Heartbeater(options);
    heartbeaterListener.on('heartbeat', self.onHeartbeat.bind(self));
    heartbeaterListener.on('connectionError', function (err) {
        self.log.info('Connection error ' + err.message);
    });

    return callback();
};

CNAPI.prototype.initializeUr = function (options, callback) {
    var self = this;
    var ur = self.ur = new Ur(options);

    ur.connect(function () {
        ur.on('serverStartup', self.onServerStartup.bind(self));

        ur.on('connectionError', function (err) {
            self.log.info('Ur AMQP connection error');
        });
    });

    return callback();
};

CNAPI.prototype.refreshServerFromSysinfo = function (uuid, sysinfo, callback) {
    var self = this;

    if (self.servers[uuid]) {
        self.log.debug('Server %s found in local "cache"', uuid);
        return;
    }

    self.log.info('Server %s not found in cache, checking in ufds', uuid);

    self.model.listServers({ uuid: uuid }, function (list$error, servers) {
        if (list$error) {
            self.log.error(list$error, 'Error listing servers in ufds');
            callback(list$error);
            return;
        }

        if (servers.length > 0) {
            self.log.info(servers[0], '%s already in ufds', uuid);
            self.servers[uuid] = servers[0];
            callback(null, servers[0]);
            return;
        } else {
            self.createUfdsServerObject(
                { uuid: uuid, sysinfo: sysinfo },
                function (error, server) {
                    if (error) {
                        self.log.info(error, 'Error creating server in ufds');
                        return;
                    }
                    self.log.debug('Cached server in memory');
                    self.servers[uuid] = server;
                });
            return;
        }
    });
};

CNAPI.prototype.updateServerMemory =
function (uuid, server, heartbeat, callback) {
    var self = this;
    var changes = [];

    self.log.info('%s already in UFDS', uuid);

    // Check if memory values have changed. Compile a list of
    // modifications if any have.
    var memoryKeys = [
        ['availrmem_bytes', 'memory_available_bytes'],
        ['arcsize_bytes', 'memory_arc_bytes'],
        ['total_bytes', 'memory_total_bytes'] ];

    memoryKeys.forEach(function (keys) {
        var hbVal = heartbeat.meminfo[keys[0]].toString();
        var serverVal = server[keys[1]] ? server[keys[1]].toString() : '';

        if (hbVal !== serverVal) {
            var change = {
                type: 'replace',
                modification: {}
            };

            change.modification[keys[1]] = hbVal.toString();
            changes.push(change);
        }
    });

    // If there are changes to be pushed to UFDS, do so then update the cache.
    if (changes.length) {
        // Attempt to modify memory values for server in UFDS, and if
        // that succeeds, cache those values too.
        changes.push({
            type: 'replace',
            modification: {
                lastupdated: (new Date()).toISOString()
            }
        });
        self.log.debug(changes, 'UFDS changes');
        self.model.modifyServer(uuid, changes, function (modify$error) {
            if (modify$error) {
                self.log.error(
                    modify$error,
                    'Error modifying server record in UFDS');
                callback();
                return;
            }
            memoryKeys.forEach(function (keys) {
                var hbVal = heartbeat.meminfo[keys[0]].toString();
                server[keys[1]] = hbVal;
            });
            self.servers[uuid] = server;
        });
    } else {
        memoryKeys.forEach(function (keys) {
            var hbVal = heartbeat.meminfo[keys[0]].toString();
            server[keys[1]] = hbVal;
        });
        self.servers[uuid] = server;
        callback();
    }
};

CNAPI.prototype.refreshServerFromHeartbeat =
function (uuid, heartbeat, cb) {
    var self = this;

    if (self.servers[uuid]) {
        self.log.debug('Server %s found in local "cache"', uuid);

        self.updateServerMemory(
            uuid, self.servers[uuid], heartbeat, function () {
            callback();
            return;
        });
        return;
    }

    self.log.info('Server %s not found in cache, checking in ufds', uuid);

    self.model.listServers({ uuid: uuid }, function (list$error, servers) {
        if (list$error) {
            self.log.error(list$error, 'Error listing servers in ufds');
            callback(list$error);
            return;
        }

        // Check if there were matching servers in UFDS
        if (servers.length > 0) {
            self.updateServerMemory(
                uuid, servers[0], heartbeat, function (update$error) {
                callback();
                return;
            });
            return;
        } else {
            self.createUfdsServerObject(
                { uuid: uuid, heartbeat: heartbeat },
                callback);
            return;
        }
    });

    function callback(error) {
        if (cb) {
            cb(error);
            return;
        }
    }
};

CNAPI.prototype.createUfdsServerObject = function (opts, callback) {
    var self = this;
    var uuid = opts.uuid;
    var serverSysinfo;

    if (opts.sysinfo) {
        serverSysinfo = opts.sysinfo;
        create();
        return;
    } else {
        self.log.info('Querying Ur agent for server sysinfo');
        self.ur.serverSysinfo(uuid, function (error, sysinfo) {
            serverSysinfo = sysinfo;
            create();
            return;
        });
    }

    function create() {
        var heartbeat = opts.heartbeat;
        var server = self.serverObjectFromSysinfo({
            heartbeat: heartbeat,
            sysinfo: serverSysinfo
        });

        self.log.debug(server, 'Creating server in UFDS');
        console.dir(server);
        self.model.createServer(
            server,
            function (create$error, createdServer) {
                if (create$error) {
                    self.log.info('Error creating server in UFDS');
                    callback(create$error);
                    return;
                }
                self.log.info('Created server entry in UFDS');
                self.servers[uuid] = server;
                callback();
                return;
            });
    }
};

CNAPI.prototype.serverObjectFromSysinfo = function (opts) {
    var self = this;

    var server = {};
    var sysinfo = opts.sysinfo;
    var heartbeat = opts.heartbeat;

    server.sysinfo = JSON.stringify(sysinfo);
    server.datacenter = self.config.datacenter_name;

    server.uuid = sysinfo.UUID;
    server.hostname = sysinfo.Hostname;
    server.reserved = 'false';
    server.headnode
        = sysinfo['Boot Parameters']['headnode'] === 'true' ? 'true' : 'false';

    server.status = 'running';
    server.default_console = 'vga';
    server.serial = 'ttyb';
    server.serial_speed = '115200';
    server.objectclass = 'server';

    if (heartbeat) {
        var meminfo = heartbeat.meminfo;
        server.memory_available_bytes = meminfo.availrmem_bytes.toString();
        server.memory_arc_bytes = meminfo.arcsize_bytes.toString();
        server.memory_total_bytes = meminfo.total_bytes.toString();
    }

    server.lastupdated = (new Date()).toISOString();

    return server;
}

CNAPI.prototype.onServerStartup = function (message, routingKey) {
    var self = this;

    var uuid = routingKey.split('.')[2];
    self.log.info('Ur startup message received from %s', uuid);
    self.log.trace(message);

    self.refreshServerFromSysinfo(uuid, message);
};

CNAPI.prototype.onHeartbeat = function (heartbeat, routingKey) {
    var self = this;
    var uuid = routingKey.split('.')[1];
    self.log.debug('Heartbeat (%s) received -- %d zones.',
        uuid, heartbeat.zoneStatus[0].length);

    self.refreshServerFromHeartbeat(uuid, heartbeat, function (error, server) {
        if (error) {
            self.log.error(error, 'Error refreshing server\'s record in ufds');
            return;
        }
    });
};

module.exports = CNAPI;
<|endoftext|>