/*
* Copyright 2014 Ricardo Lorenzo<unshakablespirit@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package utils.puppet;
import conf.PlayConfiguration;
import utils.puppet.disk.PuppetDiskConfiguration;
import utils.puppet.manifest.PuppetClass;
import utils.puppet.manifest.PuppetModule;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by ricardolorenzo on 22/07/2014.
*/
public class PuppetConfiguration {
public static final PuppetDiskConfiguration DISK_PER_PROCESS;
public static final PuppetDiskConfiguration DISK_RAID0;
public static final List<String> SUPPORTED_FILESYSTEMS;
public static final int PUPPET_MANIFEST = 1;
public static final int PUPPET_FILE = 2;
private static final String DEBIAN_INSTALL_OPTS = "-o DPkg::options::=\"--force-confdef\" \\\n" +
" -o DPkg::options::=\"--force-confold\" -o Dpkg::Options::=\"--force-overwrite\" -y";
private static final String PUPPET_HOME_DIR = "/etc/puppet";
private static final String PUPPET_MANIFESTS_DIR = "manifests";
private static final String PUPPET_FILES_DIR = "files";
private static final String PUPPET_TEMPLATES_DIR = "templates";
private static final String MONGODB_MOUNT_DIR = "/mnt/mongodb";
private static final String projectId;
static {
SUPPORTED_FILESYSTEMS = new ArrayList<>();
SUPPORTED_FILESYSTEMS.add("ext4");
SUPPORTED_FILESYSTEMS.add("xfs");
SUPPORTED_FILESYSTEMS.add("btrfs");
DISK_PER_PROCESS = new PuppetDiskConfiguration("disk per process", "One process per disk, processes not share the disk");
DISK_RAID0 = new PuppetDiskConfiguration("raid0", "RAID0, all processes share the same disk pool");
projectId = PlayConfiguration.getProperty("google.projectId");
}
public static List<PuppetDiskConfiguration> getSupportedDiskConfigurations() {
List<PuppetDiskConfiguration> supported_disk_configs = new ArrayList<>();
supported_disk_configs.add(DISK_PER_PROCESS);
supported_disk_configs.add(DISK_RAID0);
return supported_disk_configs;
}
public static List<String> getSupportedDiskFileSystems() {
return SUPPORTED_FILESYSTEMS;
}
public static String getPuppetHomeDirectory() {
return PUPPET_HOME_DIR;
}
public static String getPuppetManifestsDirectory() {
return PUPPET_HOME_DIR + "/" + PUPPET_MANIFESTS_DIR;
}
public static String getPuppetFilesDirectory() {
return PUPPET_HOME_DIR + "/" + PUPPET_FILES_DIR;
}
public static String getPuppetTemplatesDirectory() {
return PUPPET_HOME_DIR + "/" + PUPPET_TEMPLATES_DIR;
}
public static String generateNodeManifest(String clusterName) {
StringBuilder sb = new StringBuilder();
sb.append("node mongodb {\n include mongodb-base\n}\n\n");
sb.append("node /^");
sb.append(clusterName);
sb.append("-conf-node-\\d+$/ inherits mongodb {\n include ulimit\ninclude mongodb-conf\n}\n\n");
sb.append("node /^");
sb.append(clusterName);
sb.append("-shard-node-\\d+$/ inherits mongodb {\n include ulimit\ninclude mongodb-shard\n}\n\n");
sb.append("import 'mongodb-base.pp'\nimport 'mongodb-conf.pp'\nimport 'mongodb-shard.pp'\n");
return sb.toString();
}
public static String generateMongoConfClassManifest() throws PuppetConfigurationException {
PuppetClass confClass = new PuppetClass("mongodb-conf");
confClass.setModule(new PuppetModule(PuppetModule.TYPE_SERVICE, "mongod")
.setProperty("ensure", "running")
.setProperty("enable", "true")
.setSubscribe(PuppetModule.TYPE_FILE, "/etc/mongod.conf"));
confClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/etc/mongod.conf")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "644")
.setStringProperty("source", "puppet:///files/mongodb-config-server.conf")
.setNotify(PuppetModule.TYPE_SERVICE, "mongod")
.setRequire(PuppetModule.TYPE_EXEC, "mongodb-10gen"));
return confClass.toString();
}
public static String generateMongoShardClassManifest()
throws PuppetConfigurationException {
PuppetClass shardClass = new PuppetClass("mongodb-shard");
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/etc/mongodb-shards.conf")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "644")
.setStringProperty("source", "puppet:///files/mongodb-shards.conf"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/mnt/mongodb")
.setStringProperty("owner", "mongodb")
.setStringProperty("group", "mongodb")
.setProperty("mode", "755")
.setStringProperty("ensure", "directory")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/usr/local/bin/puppet-disk-format")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "755")
.setStringProperty("source", "puppet:///files/puppet-disk-format.sh")
.setRequire(PuppetModule.TYPE_FILE, "/etc/mongodb-shards.conf"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/etc/init.d/mongodb-microshards")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "755")
.setStringProperty("source", "puppet:///files/puppet-mongodb-microshards.sh")
.setRequire(PuppetModule.TYPE_EXEC, "disk-format")
.setRequire(PuppetModule.TYPE_EXEC, "test-mongodb-microshards"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "mongod")
.setStringProperty("command", "/usr/sbin/update-rc.d -f mongod remove && /usr/sbin/service mongod stop")
.setNotify(PuppetModule.TYPE_EXEC, "test-mongodb-microshards")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit")
.setRequire(PuppetModule.TYPE_EXEC, "mongodb-10gen"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "disk-format")
.setProperty("onlyif", "\\\"/usr/bin/test -e " + MONGODB_MOUNT_DIR + " -a 0 -eq \\$(ls " +
MONGODB_MOUNT_DIR + " | wc -l)\\\"")
.setStringProperty("command", "/usr/local/bin/puppet-disk-format")
.setNotify(PuppetModule.TYPE_SERVICE, "mongodb-microshards")
.setSubscribe(PuppetModule.TYPE_EXEC, "test-mongodb-microshards")
.setRequire(PuppetModule.TYPE_FILE, "/mnt/mongodb")
.setRequire(PuppetModule.TYPE_FILE, "/usr/local/bin/puppet-disk-format"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "test-mongodb-microshards")
.setProperty("onlyif", "\\\"/usr/bin/test 1 -eq \\$(cat /proc/cgroups | grep memory | awk '{ print $4 }')\\\"")
.setStringProperty("command", "/usr/bin/test 0")
.setNotify(PuppetModule.TYPE_EXEC, "disk-format")
.setSubscribe(PuppetModule.TYPE_EXEC, "mongod")
.setRequire(PuppetModule.TYPE_PACKAGE, "cgroup-bin"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_SERVICE, "mongodb-microshards")
.setProperty("ensure", "running")
.setProperty("enable", "true")
.setSubscribe(PuppetModule.TYPE_EXEC, "disk-format")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit")
.setRequire(PuppetModule.TYPE_EXEC, "mongod")
.setRequire(PuppetModule.TYPE_FILE, "/etc/init.d/mongodb-microshards")
.setRequire(PuppetModule.TYPE_EXEC, "test-mongodb-microshards"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/etc/default/grub")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "644")
.setStringProperty("source", "puppet:///files/grub")
.setNotify(PuppetModule.TYPE_EXEC, "update-grub")
.setRequire(PuppetModule.TYPE_EXEC, "mongodb-10gen"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_PACKAGE, "cgroup-bin")
.setProperty("ensure", "present"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "update-grub")
.setStringProperty("command", "/usr/sbin/update-grub")
.setStringProperty("refreshonly", "true")
.setNotify(PuppetModule.TYPE_EXEC, "reboot")
.setSubscribe(PuppetModule.TYPE_FILE, "/etc/default/grub")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit"));
shardClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "reboot")
.setStringProperty("command", "/sbin/reboot")
.setStringProperty("refreshonly", "true")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit")
.setSubscribe(PuppetModule.TYPE_EXEC, "update-grub"));
return shardClass.toString();
}
public static String generateMongoBaseClassManifest() throws PuppetConfigurationException {
PuppetClass baseClass = new PuppetClass("mongodb-base");
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_CLASS, "timezone")
.setStringProperty("timezone", "UTC"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_PACKAGE, "ntp")
.setProperty("ensure", "present"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_SERVICE, "ntp")
.setProperty("ensure", "running")
.setProperty("enable", "true"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setStringProperty("ulimit_domain", "*")
.setStringProperty("ulimit_type", "hard")
.setStringProperty("ulimit_item", "nofile")
.setStringProperty("ulimit_value", "64000"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_ULIMITS, "processesLimit")
.setStringProperty("ulimit_domain", "*")
.setStringProperty("ulimit_type", "hard")
.setStringProperty("ulimit_item", "nproc")
.setStringProperty("ulimit_value", "64000"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_FILE, "/etc/hosts")
.setStringProperty("owner", "root")
.setStringProperty("group", "root")
.setProperty("mode", "644")
.setProperty("content", "template('hosts/hosts.erb')"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_GROUP, "mongodb")
.setProperty("ensure", "present"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_USER, "mongodb")
.setProperty("ensure", "present")
.setStringProperty("gid", "mongodb")
.setStringProperty("shell", "/bin/bash")
.setRequire(PuppetModule.TYPE_GROUP, "mongodb"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "apt-get update")
.setStringProperty("command", "/usr/bin/apt-get update")
.setNotify(PuppetModule.TYPE_EXEC, "mongodb-10gen")
.setSubscribe(PuppetModule.TYPE_APT_SOURCE, "mongodb"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_APT_KEY, "mongodb")
.setStringProperty("key", "7F0CEB10")
.setStringProperty("key_server", "keyserver.ubuntu.com"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_APT_SOURCE, "mongodb")
.setStringProperty("location", "http://downloads-distro.mongodb.org/repo/debian-sysvinit")
.setStringProperty("release", "dist")
.setStringProperty("repos", "10gen")
.setStringProperty("key", "7F0CEB10")
.setProperty("include_src", "false")
.setStringProperty("key_server", "keyserver.ubuntu.com")
.setNotify(PuppetModule.TYPE_EXEC, "apt-get update")
.setRequire(PuppetModule.TYPE_APT_KEY, "mongodb"));
baseClass.setModule(new PuppetModule(PuppetModule.TYPE_EXEC, "mongodb-10gen")
.setStringProperty("command", "/usr/bin/apt-get install -y --force-yes mongodb-org-server mongodb-org-tools mongodb-org-shell")
.setSubscribe(PuppetModule.TYPE_EXEC, "apt-get update")
.setRequire(PuppetModule.TYPE_ULIMITS, "openFilesLimit")
.setRequire(PuppetModule.TYPE_ULIMITS, "processesLimit")
.setRequire(PuppetModule.TYPE_EXEC, "apt-get update")
.setRequire(PuppetModule.TYPE_APT_SOURCE, "mongodb"));
return baseClass.toString();
}
public static String getNodeStartupScriptContent(String serverName) throws IOException {
StringBuilder sb = new StringBuilder();
sb.append(PlayConfiguration.getFileContent("scripts/startup-common.sh"));
sb.append("\nsetProxy ");
sb.append(serverName);
sb.append("\ncheckConnection http://http.debian.net/debian/dists/wheezy/Release.gpg\n");
sb.append("installPackage mdadm\n");
sb.append("installPackage xfsprogs\n");
sb.append("installPackage btrfs-tools\n");
sb.append("installPackage puppet\n");
sb.append("echo \"[main]\n");
sb.append("logdir=/var/log/puppet\nvardir=/var/lib/puppet\nssldir=/var/lib/puppet/ssl\n");
sb.append("rundir=/var/run/puppet\nfactpath=$vardir/lib/facter\ntemplatedir=$confdir/templates\n");
sb.append("prerun_command=");
sb.append(PUPPET_HOME_DIR);
sb.append("/etckeeper-commit-pre\npostrun_command=");
sb.append(PUPPET_HOME_DIR);
sb.append("/etckeeper-commit-post\nserver=");
sb.append(serverName);
sb.append("\nlisten=true\n\n[master]\n");
sb.append("ssl_client_header = SSL_CLIENT_S_DN\nssl_client_verify_header = SSL_CLIENT_VERIFY\" > ");
sb.append(PUPPET_HOME_DIR);
sb.append("/puppet.conf\n\necho \"# Defaults for puppet - sourced by /etc/init.d/puppet\n\n");
sb.append("# Start puppet on boot?\nSTART=yes\n\n");
sb.append("# Startup options\nDAEMON_OPTS=\" > /etc/default/puppet\n\necho \"path /run\nallow ");
sb.append(serverName);
sb.append("\" > ");
sb.append(PUPPET_HOME_DIR);
sb.append("/auth.conf\n");
sb.append("service puppet stop\nrunCommand puppet agent --test --waitforcert 60\nsleep 1\nservice puppet start\n");
return sb.toString();
}
public static String getPuppetStartupScriptContent(String clusterName, String networkRange, Integer processes,
String diskRaid, String dataFileSystem) throws IOException {
if(networkRange == null || networkRange.isEmpty()) {
throw new IllegalArgumentException("incorrect network range");
}
StringBuilder sb = new StringBuilder();
sb.append(PlayConfiguration.getFileContent("scripts/startup-common.sh"));
/**
* Apache proxy configuration
*/
sb.append("function configApache() {\n");
sb.append(" if ! [ -e /etc/apache2/mods-enabled/proxy.load ]; then\n");
sb.append(" ln -s /etc/apache2/mods-available/proxy.load /etc/apache2/mods-enabled/proxy.load\n");
sb.append(" ln -s /etc/apache2/mods-available/proxy_http.load /etc/apache2/mods-enabled/proxy_http.load\n");
sb.append(" echo \"ProxyRequests On\nProxyPreserveHost On\n\n<Proxy *>\n Order deny,allow\n");
sb.append(" Deny from all\n Allow from ");
sb.append(networkRange);
sb.append("\n</Proxy>\" > /etc/apache2/conf.d/proxy\n");
sb.append(" fi\n");
sb.append(" service apache2 restart\n");
sb.append("}\n");
sb.append("installPackage apache2 configApache\n");
/**
* Puppet basic configuration
*/
sb.append("function configPuppet() {\n");
sb.append(" installPackage unzip\n");
sb.append(" wget -O /tmp/puppet-timezone.zip https://github.com/saz/puppet-timezone/archive/master.zip\n");
sb.append(" cd /tmp && unzip puppet-timezone.zip\n cd puppet-timezone-master && puppet module build .\n");
sb.append(" puppet module install pkg/saz-timezone-*.tar.gz\n puppet module install puppetlabs-apt\n");
sb.append(" puppet module install arioch-ulimit\n");
sb.append(" makeDirectory ");
sb.append(getPuppetTemplatesDirectory());
sb.append("/hosts\n cat /etc/hosts > ");
sb.append(getPuppetTemplatesDirectory());
sb.append("/hosts/hosts.erb\n");
sb.append(" echo \"<%= ipaddress %> <%= fqdn %> <%= hostname %>\" >> ");
sb.append(getPuppetTemplatesDirectory());
sb.append("/hosts/hosts.erb\n");
sb.append(" echo \"[files]\n path ");
sb.append(getPuppetFilesDirectory());
sb.append("\n allow *\n\n[plugins]\n\n\" > ");
sb.append(PUPPET_HOME_DIR);
sb.append("/fileserver.conf\n");
sb.append(" echo \"autosign = true\" >> ");
sb.append(PUPPET_HOME_DIR);
sb.append("/puppet.conf\n");
/**
* Generate puppet manifest files
*/
sb.append(" echo \"");
sb.append(generateNodeManifest(clusterName));
sb.append("\" > ");
sb.append(getPuppetManifestsDirectory());
sb.append("/site.pp\n");
sb.append(" echo \"");
try {
sb.append(generateMongoBaseClassManifest());
} catch(PuppetConfigurationException e) {}
sb.append("\" > ");
sb.append(getPuppetManifestsDirectory());
sb.append("/mongodb-base.pp\n");
sb.append(" echo \"");
try {
sb.append(generateMongoShardClassManifest());
} catch(PuppetConfigurationException e) {}
sb.append("\" > ");
sb.append(getPuppetManifestsDirectory());
sb.append("/mongodb-shard.pp\n");
sb.append(" echo \"");
try {
sb.append(generateMongoConfClassManifest());
} catch(PuppetConfigurationException e) {}
sb.append("\" > ");
sb.append(getPuppetManifestsDirectory());
sb.append("/mongodb-conf.pp\n");
/**
* Generate puppet files
*/
sb.append(" makeDirectory ");
sb.append(getPuppetFilesDirectory());
sb.append("\n echo \"GRUB_DEFAULT=0\nGRUB_TIMEOUT=0\nGRUB_DISTRIBUTOR=\\\"Debian\\\"\n");
sb.append("GRUB_CMDLINE_LINUX_DEFAULT=\\\"quiet\\\"\n");
sb.append("GRUB_CMDLINE_LINUX=\\\"console=ttyS0,38400n8 cgroup_enable=memory swapaccount=1\\\"\n");
sb.append("\" > ");
sb.append(getPuppetFilesDirectory());
sb.append("/grub\n");
sb.append(" echo \"port = 27019\nconfigsvr=true\ndbpath=/var/lib/mongodb\n");
sb.append("logpath=/var/log/mongodb/mongodb.log\nlogappend=true\n\" > ");
sb.append(getPuppetFilesDirectory());
sb.append("/mongodb-config-server.conf\n");
sb.append(" echo \"");
sb.append(formatScriptForEcho(PlayConfiguration.getFileContent("scripts/puppet-disk-format.sh")));
sb.append("\" > ");
sb.append(getPuppetFilesDirectory());
sb.append("/puppet-disk-format.sh\n");
sb.append(" echo \"");
sb.append(formatScriptForEcho(PlayConfiguration.getFileContent("scripts/puppet-mongodb-microshards.sh")));
sb.append("\" > ");
sb.append(getPuppetFilesDirectory());
sb.append("/puppet-mongodb-microshards.sh\n");
sb.append(" echo \"MOUNT_DIRECTORY=\\\"");
sb.append(MONGODB_MOUNT_DIR);
sb.append("\\\"\n");
sb.append("PROCESSES=");
sb.append(processes);
sb.append("\n");
if(diskRaid != null && !diskRaid.isEmpty()) {
sb.append("RAID_TYPE=\\\"");
sb.append(diskRaid.toLowerCase());
sb.append("\\\"\n");
}
if(dataFileSystem != null && !dataFileSystem.isEmpty()) {
sb.append("FS_TYPE=\\\"");
sb.append(dataFileSystem.toLowerCase());
sb.append("\\\"\n");
}
sb.append("\" > ");
sb.append(getPuppetFilesDirectory());
sb.append("/mongodb-shards.conf\n");
sb.append("\n service puppetmaster restart\n");
sb.append("}\n");
sb.append("installPackage puppetmaster configPuppet\n");
return sb.toString();
}
private static String generateUlimitModule() {
return null;
}
private static String formatScriptForEcho(String scriptContent) {
scriptContent = scriptContent.replace("\"", "\\\"");
return scriptContent.replace("$", "\\$");
}
}