| /* |
| * Licensed to the Apache Software Foundation (ASF) under one |
| * or more contributor license agreements. See the NOTICE file |
| * distributed with this work for additional information |
| * regarding copyright ownership. The ASF licenses this file |
| * to you 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 org.apache.brooklyn.entity.database.mariadb; |
| |
| import static java.lang.String.format; |
| import static org.apache.brooklyn.util.JavaGroovyEquivalents.groovyTruth; |
| import static org.apache.brooklyn.util.ssh.BashCommands.commandsToDownloadUrlsAs; |
| import static org.apache.brooklyn.util.ssh.BashCommands.installPackage; |
| import static org.apache.brooklyn.util.ssh.BashCommands.ok; |
| |
| import java.io.InputStream; |
| import java.io.Reader; |
| import java.io.StringReader; |
| import java.util.LinkedList; |
| import java.util.List; |
| import java.util.Map; |
| |
| import org.slf4j.Logger; |
| import org.slf4j.LoggerFactory; |
| import org.apache.brooklyn.entity.database.DatastoreMixins; |
| import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; |
| import org.apache.brooklyn.api.location.OsDetails; |
| import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; |
| import org.apache.brooklyn.core.entity.Attributes; |
| import org.apache.brooklyn.core.entity.Entities; |
| import org.apache.brooklyn.location.ssh.SshMachineLocation; |
| import org.apache.brooklyn.util.collections.MutableMap; |
| import org.apache.brooklyn.util.core.task.DynamicTasks; |
| import org.apache.brooklyn.util.core.task.system.ProcessTaskWrapper; |
| import org.apache.brooklyn.util.net.Urls; |
| import org.apache.brooklyn.util.os.Os; |
| import org.apache.brooklyn.util.ssh.BashCommands; |
| import org.apache.brooklyn.util.text.Identifiers; |
| import org.apache.brooklyn.util.text.Strings; |
| import org.apache.brooklyn.util.time.CountdownTimer; |
| import org.apache.brooklyn.util.time.Duration; |
| |
| import com.google.common.collect.ImmutableMap; |
| |
| /** |
| * The SSH implementation of the {@link MariaDbDriver}. |
| */ |
| public class MariaDbSshDriver extends AbstractSoftwareProcessSshDriver implements MariaDbDriver { |
| |
| public static final Logger log = LoggerFactory.getLogger(MariaDbSshDriver.class); |
| |
| public MariaDbSshDriver(MariaDbNodeImpl entity, SshMachineLocation machine) { |
| super(entity, machine); |
| |
| entity.sensors().set(Attributes.LOG_FILE_LOCATION, getLogFile()); |
| } |
| |
| public String getOsTag() { |
| OsDetails os = getLocation().getOsDetails(); |
| // NOTE: cannot rely on OsDetails.isLinux() to return true for all linux flavours, so |
| // explicitly test for unsupported OSes, otherwise assume generic linux. |
| if (os == null) return "linux-i686"; |
| if (os.isWindows() || os.isMac()) |
| throw new UnsupportedOperationException("only support linux versions just now; OS details: " + os); |
| return (os.is64bit() ? "linux-x86_64" : "linux-i686"); |
| } |
| |
| public String getDownloadParentDir() { |
| // NOTE: cannot rely on OsDetails.isLinux() to return true for all linux flavours, so |
| // explicitly test for unsupported OSes, otherwise assume generic linux. |
| OsDetails os = getLocation().getOsDetails(); |
| if (os == null) return "bintar-linux-x86"; |
| if (os.isWindows() || os.isMac()) |
| throw new UnsupportedOperationException("only support linux versions just now; OS details: " + os); |
| return (os.is64bit() ? "bintar-linux-x86_64" : "bintar-linux-x86"); |
| } |
| |
| public String getMirrorUrl() { |
| return entity.getConfig(MariaDbNode.MIRROR_URL); |
| } |
| |
| public String getBaseDir() { return getExpandedInstallDir(); } |
| |
| public String getDataDir() { |
| String result = entity.getConfig(MariaDbNode.DATA_DIR); |
| return (result == null) ? "." : result; |
| } |
| |
| public String getLogFile() { |
| return Urls.mergePaths(getRunDir(), "console.log"); |
| } |
| |
| public String getConfigFile() { |
| return "my.cnf"; |
| } |
| |
| public String getInstallFilename() { |
| return String.format("mariadb-%s-%s.tar.gz", getVersion(), getOsTag()); |
| } |
| |
| @Override |
| public void prepare() { |
| resolver = Entities.newDownloader(this, ImmutableMap.of("filename", getInstallFilename())); |
| setExpandedInstallDir(Os.mergePaths(getInstallDir(), resolver.getUnpackedDirectoryName(format("mariadb-%s-%s", getVersion(), getOsTag())))); |
| } |
| |
| @Override |
| public void install() { |
| List<String> urls = resolver.getTargets(); |
| String saveAs = resolver.getFilename(); |
| |
| List<String> commands = new LinkedList<String>(); |
| commands.add(BashCommands.INSTALL_TAR); |
| commands.add(BashCommands.INSTALL_CURL); |
| |
| commands.add("echo installing extra packages"); |
| commands.add(installPackage(ImmutableMap.of("yum", "libgcc_s.so.1"), null)); |
| commands.add(installPackage(ImmutableMap.of("yum", "libaio.so.1 libncurses.so.5", "apt", "libaio1 libaio-dev"), null)); |
| |
| // these deps are needed on some OS versions but others don't need them so ignore failures (ok(...)) |
| commands.add(ok(installPackage(ImmutableMap.of("yum", "libaio", "apt", "ia32-libs"), null))); |
| commands.add("echo finished installing extra packages"); |
| |
| commands.addAll(commandsToDownloadUrlsAs(urls, saveAs)); |
| commands.add(format("tar xfvz %s", saveAs)); |
| |
| newScript(INSTALLING).body.append(commands).execute(); |
| } |
| |
| @Override |
| public MariaDbNodeImpl getEntity() { return (MariaDbNodeImpl) super.getEntity(); } |
| public int getPort() { return getEntity().getPort(); } |
| public String getSocketUid() { return getEntity().getSocketUid(); } |
| public String getPassword() { return getEntity().getPassword(); } |
| |
| @Override |
| public void customize() { |
| copyDatabaseConfigScript(); |
| |
| newScript(CUSTOMIZING) |
| .updateTaskAndFailOnNonZeroResultCode() |
| .body.append( |
| "chmod 600 "+getConfigFile(), |
| getBaseDir()+"/scripts/mysql_install_db "+ |
| "--basedir="+getBaseDir()+" --datadir="+getDataDir()+" "+ |
| "--defaults-file="+getConfigFile()) |
| .execute(); |
| |
| // launch, then we will configure it |
| launch(); |
| |
| CountdownTimer timer = Duration.seconds(20).countdownTimer(); |
| boolean hasCreationScript = copyDatabaseCreationScript(); |
| timer.waitForExpiryUnchecked(); |
| |
| DynamicTasks.queue( |
| SshEffectorTasks.ssh( |
| "cd "+getRunDir(), |
| getBaseDir()+"/bin/mysqladmin --defaults-file="+getConfigFile()+" --password= password "+getPassword() |
| ).summary("setting password")); |
| |
| if (hasCreationScript) |
| executeScriptFromInstalledFileAsync("creation-script.sql"); |
| |
| // not sure necessary to stop then subsequently launch, but seems safest |
| // (if skipping, use a flag in launch to indicate we've just launched it) |
| stop(); |
| } |
| |
| private void copyDatabaseConfigScript() { |
| newScript(CUSTOMIZING).execute(); //create the directory |
| |
| String configScriptContents = processTemplate(entity.getAttribute(MariaDbNode.TEMPLATE_CONFIGURATION_URL)); |
| Reader configContents = new StringReader(configScriptContents); |
| |
| getMachine().copyTo(configContents, Urls.mergePaths(getRunDir(), getConfigFile())); |
| } |
| |
| private boolean copyDatabaseCreationScript() { |
| InputStream creationScript = DatastoreMixins.getDatabaseCreationScript(entity); |
| if (creationScript==null) return false; |
| getMachine().copyTo(creationScript, getRunDir() + "/creation-script.sql"); |
| return true; |
| } |
| |
| public String getMariaDbServerOptionsString() { |
| Map<String, Object> options = entity.getConfig(MariaDbNode.MARIADB_SERVER_CONF); |
| StringBuilder result = new StringBuilder(); |
| if (groovyTruth(options)) { |
| for (Map.Entry<String, Object> entry : options.entrySet()) { |
| result.append(entry.getKey()); |
| String value = entry.getValue().toString(); |
| if (!Strings.isEmpty(value)) { |
| result.append(" = ").append(value); |
| } |
| result.append('\n'); |
| } |
| } |
| return result.toString(); |
| } |
| |
| @Override |
| public void launch() { |
| newScript(MutableMap.of("usePidFile", true), LAUNCHING) |
| .updateTaskAndFailOnNonZeroResultCode() |
| .body.append(format("nohup %s/bin/mysqld --defaults-file=%s --user=`whoami` > %s 2>&1 < /dev/null &", getBaseDir(), getConfigFile(), getLogFile())) |
| .execute(); |
| } |
| |
| @Override |
| public boolean isRunning() { |
| return newScript(MutableMap.of("usePidFile", false), CHECK_RUNNING) |
| .body.append(getStatusCmd()) |
| .execute() == 0; |
| } |
| |
| @Override |
| public void stop() { |
| newScript(MutableMap.of("usePidFile", true), STOPPING).execute(); |
| } |
| |
| @Override |
| public void kill() { |
| newScript(MutableMap.of("usePidFile", true), KILLING).execute(); |
| } |
| |
| @Override |
| public String getStatusCmd() { |
| return format("%s/bin/mysqladmin --defaults-file=%s status", getExpandedInstallDir(), Urls.mergePaths(getRunDir(), getConfigFile())); |
| } |
| |
| @Override |
| public ProcessTaskWrapper<Integer> executeScriptAsync(String commands) { |
| String filename = "mariadb-commands-"+Identifiers.makeRandomId(8); |
| DynamicTasks.queue(SshEffectorTasks.put(Urls.mergePaths(getRunDir(), filename)).contents(commands).summary("copying datastore script to execute "+filename)); |
| return executeScriptFromInstalledFileAsync(filename); |
| } |
| |
| public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) { |
| return DynamicTasks.queue( |
| SshEffectorTasks.ssh( |
| "cd "+getRunDir(), |
| getBaseDir()+"/bin/mysql --defaults-file="+getConfigFile()+" < "+filenameAlreadyInstalledAtServer) |
| .requiringExitCodeZero() |
| .summary("executing datastore script "+filenameAlreadyInstalledAtServer)); |
| } |
| |
| } |