UNPKG

ansible-dynamic-inventory

Version:

Ansible Dynamic Inventory Script for LXD Containers

228 lines (219 loc) 8.77 kB
#!/usr/bin/env nodejs module.exports = (function() { 'use strict'; function readConfigFile(file, callback) { var ANSIBLE_HOME = process.env.ANSIBLE_HOME; if (! process.env.ANSIBLE_HOME) ANSIBLE_HOME = process.cwd(); try { return callback(ini.parse(fs.readFileSync(path.join(ANSIBLE_HOME, file), 'utf-8'))); } catch (error) { console.log('Your CWD does not seem to be ANSIBLE_HOME. Please set the variable ANSIBLE_HOME or change to that directory before running this script.'); if (process.argv.find(function (element) { return (element === "--debug") })) console.log(error.message); process.exit(1); } } const Promise = require('bluebird'), Unirest = Promise.promisifyAll(require('unirest')), fs = require('fs'), os = require('os'), writeFile = Promise.promisify(fs.writeFile), mkdirp = Promise.promisify(require('mkdirp')), openssl = require('openssl-wrapper'), ini = require('ini'), JSONPath = require('jsonpath-plus'), path = require('path'), Readline = require('readline'), configAnsible = readConfigFile('ansible.cfg', function(configAnsible) { return configAnsible; }), opensslAsync = Promise.promisify(openssl.exec), config = readConfigFile('inventory/lxd.ini', function(configAnsible) { return configAnsible; }), ANSIBLE_REMOTE_TMP = function() { if (configAnsible.defaults.remote_tmp) return configAnsible.defaults.remote_tmp.replace("~", os.homedir()).replace("$HOME", os.homedir()) }() || os.homedir() +"/.ansible/tmp", KEYDIR = path.join(ANSIBLE_REMOTE_TMP, "ssl"), CA_KEY = path.join(KEYDIR, "ca.key"), CA_CRT = path.join(KEYDIR, "ca.crt"), CLIENT_KEY = path.join(KEYDIR, "client.key"), CLIENT_CRT = path.join(KEYDIR, "client.crt"), CLIENT_CSR = path.join(KEYDIR, "client.csr"); var debug; var getLxdHosts = function() { return new Promise(function(resolve, reject) { var parseConfigFile = function(inventoryFile) { return new Promise(function(resolve, reject) { var lxdHosts = []; const lineReader = Readline.createInterface({ input: fs.createReadStream(inventoryFile)}); lineReader.on('line', function(line) { if (line.includes('ansible_connection=lxd')) { lxdHosts.push(line.split(" ")[0]); } }) .on('close', function() { resolve(lxdHosts); }); }); } var ansibleInventory = configAnsible.defaults.inventory || "./inventory"; ansibleInventory = ansibleInventory.replace("~", os.homedir()).replace("$HOME", os.homedir()); if (fs.statSync(ansibleInventory).isDirectory()) { var inventoryReadPromises = []; fs.readdirSync(ansibleInventory).forEach(function(file) { if (! file.startsWith(".") && file.match(/^(.*\.(?!(orig|bak|ini|retry|pyc|pyo|js|nex)$))?[^.]*$/i)) { inventoryReadPromises.push(parseConfigFile(path.join(ansibleInventory, file))); } }) Promise.all(inventoryReadPromises).then(function(lxdHostsArrayFromDifferentInventories) { var allLxdHostsFromAllInvetorySources = []; lxdHostsArrayFromDifferentInventories.forEach(function(array) { allLxdHostsFromAllInvetorySources = allLxdHostsFromAllInvetorySources.concat(array); }); resolve(allLxdHostsFromAllInvetorySources); }); } else { parseConfigFile(ansibleInventory).then(function(singleInventoryOfLxdHosts) { resolve(singleInventoryOfLxdHosts); }); } }); } var generateClientCertificate = function(host) { if (fs.existsSync(CLIENT_CRT) && fs.existsSync(CLIENT_KEY)) { return Promise.resolve("Client cert found. Skipping certificate generation."); } var subj = "/C="+config.lxd.ssl_country+"/ST="+config.lxd.ssl_stateOrProvince+"/L="+config.lxd.ssl_locality+"/O="+config.lxd.ssl_organization+"/CN="+config.lxd.ssl_commonName; return new Promise(function(resolve, reject) { mkdirp(KEYDIR) .then(function() { return opensslAsync('genrsa', {des3: false, '4096': false}) }) .then(function(buffer) { return writeFile(CA_KEY, buffer); }, function(error) { console.log("ERROR: " + error); }) .then(function() { return opensslAsync('req', {new: true, x509: true, days: 3650, subj: subj, key: CA_KEY}) }) .then(function(buffer) { return writeFile(CA_CRT, buffer); }) .then(function() { return opensslAsync('genrsa', {des3: false, '4096': false}) }) .then(function(buffer) { return writeFile(CLIENT_KEY, buffer); }) .then(function() { return opensslAsync('req', {new: true, subj: subj, key: CLIENT_KEY}) }) .then(function(buffer) { return writeFile(CLIENT_CSR, buffer); }) .then(function() { return opensslAsync('x509', {req: true, days: 3650, in: CLIENT_CSR, CA: CA_CRT, CAkey: CA_KEY, set_serial: '01'}) }) .then(function(buffer) { return writeFile(CLIENT_CRT, buffer); }) .then(function(result) { console.log("Done generating self signed client cert."); resolve(); }) .catch(function(e) { reject("Error setting up client certificate: " + e); }); }); } var registerCertWithLXDHost = function(host) { return new Promise(function(resolve, reject) { var Request = Unirest.post('https://'+host+':8443/1.0/certificates') .headers({'Accept': 'application/json', 'Content-Type': 'application/json'}); Request.options.cert = fs.readFileSync(CLIENT_CRT); Request.options.key = fs.readFileSync(CLIENT_KEY); Request.send({ "type": "client", "password": "8hJKBwMzxAycXf0CfVWy" }) .end(function (response) { resolve(host); }); }); } process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; var getContainers = function(host) { return new Promise(function(resolve, reject) { var Request = Unirest.get('https://'+host+':8443/1.0/containers'); Request.options.cert = fs.readFileSync(CLIENT_CRT); Request.options.key = fs.readFileSync(CLIENT_KEY); Request.end(function (response) { resolve(response.body); }); }); } var doRestCall = function(endpoint) { return new Promise(function(resolve, reject) { var Request = Unirest.get(endpoint); Request.options.cert = fs.readFileSync(CLIENT_CRT); Request.options.key = fs.readFileSync(CLIENT_KEY); Request.end(function (response) { resolve(response.body); }); }); } var listOptionHandler = function(host) { return new Promise(function(resolve, reject) { registerCertWithLXDHost(host) .then(function() { return getContainers(host)}) .then(function(response) { var containers = []; response.metadata.forEach(function(container) { var RESTcalls = [ doRestCall("https://"+host+':8443' + container), doRestCall("https://"+host+':8443' + container + "/state") ]; containers.push(Promise.all(RESTcalls)); }); return Promise.all(containers).then(function() { var result = []; containers.forEach(function(container) { result.push(container.value()); }); return(result); }); }) .then(function(restResponses) { return new Promise(function(resolve, reject) { var groups = JSONPath({json: restResponses, path: "*[*.metadata.config[user.ansible.group]]"}); var uniqueGroups = groups.filter(function(element, index) { return groups.indexOf(element) == index; }); if (uniqueGroups) resolve({restResponses: restResponses, groups: uniqueGroups}); }); }) .then(function(restResponseDataObject) { var groups = restResponseDataObject.groups; var restResponses = restResponseDataObject.restResponses; console.log("{") console.log("\"_meta\": { \"hostvars\": {}},") groups.forEach(function(group, i2, groups) { process.stdout.write("\""+group+"\": {\n\"hosts\": ["); restResponses.forEach(function(restResponse, index, restResponses) { var name = restResponse[0].metadata.name; var ipv4Address = JSONPath({json: restResponse[1], path: "*.network.eth0.addresses.[0].address"}); if (JSONPath({json: restResponse[0], path: "*.config[user.ansible.group]"}) == group) { if ((restResponses.length-1) == index) { console.log("\""+host+":"+name+"\"],"); console.log("\"vars\": { \"ansible_connection\":\"lxd\"}"); } else { process.stdout.write("\""+host+":"+name+"\", "); } } }); if ((groups.length-2) <= i2) console.log("}"); else console.log("}, "); }); console.log("}") }) .finally(resolve()); }); } generateClientCertificate() .then(function(restResponse) { var command = process.argv[2], debug = process.argv.find(function (element) { return (element === "--debug") }) switch(command) { case "--list": getLxdHosts() .then(function(hosts) { var hostsToInventory = []; hosts.forEach(function(host) { hostsToInventory.push(new Promise(function(resolve, reject) { listOptionHandler(host) .finally(function() { resolve(); }); })); }); Promise.all(hostsToInventory) .then(function() {}); }); break; case "--host": console.log("{}"); break; } }); })();