/* * 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.qa.load; import static java.lang.String.format; import java.util.concurrent.Callable; import org.apache.brooklyn.config.ConfigKey; import org.apache.brooklyn.core.effector.ssh.SshEffectorTasks; import org.apache.brooklyn.entity.database.mysql.MySqlNode; import org.apache.brooklyn.entity.database.mysql.MySqlNodeImpl; import org.apache.brooklyn.entity.database.mysql.MySqlSshDriver; import org.apache.brooklyn.entity.software.base.AbstractSoftwareProcessSshDriver; import org.apache.brooklyn.feed.function.FunctionFeed; import org.apache.brooklyn.feed.function.FunctionPollConfig; 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.time.CountdownTimer; import org.apache.brooklyn.util.time.Duration; /** * @see SimulatedJBoss7ServerImpl for description of purpose and configuration options. */ public class SimulatedMySqlNodeImpl extends MySqlNodeImpl { public static final ConfigKey<Boolean> SIMULATE_ENTITY = SimulatedTheeTierApp.SIMULATE_ENTITY; public static final ConfigKey<Boolean> SIMULATE_EXTERNAL_MONITORING = SimulatedTheeTierApp.SIMULATE_EXTERNAL_MONITORING; public static final ConfigKey<Boolean> SKIP_SSH_ON_START = SimulatedTheeTierApp.SKIP_SSH_ON_START; private FunctionFeed feed; @Override public Class<?> getDriverInterface() { return SimulatedMySqlSshDriver.class; } @Override protected void connectSensors() { boolean simulateExternalMonitoring = getConfig(SIMULATE_EXTERNAL_MONITORING); if (simulateExternalMonitoring) { sensors().set(DATASTORE_URL, String.format("mysql://%s:%s/", getAttribute(HOSTNAME), getAttribute(MYSQL_PORT))); feed = FunctionFeed.builder() .entity(this) .period(Duration.FIVE_SECONDS) .poll(new FunctionPollConfig<Boolean, Boolean>(SERVICE_UP) .callable(new Callable<Boolean>() { private int counter = 0; public Boolean call() { sensors().set(QUERIES_PER_SECOND_FROM_MYSQL, (double)(counter++ % 100)); return true; }}) .setOnFailureOrException(false)) .build(); } else { super.connectSensors(); } } public static class SimulatedMySqlSshDriver extends MySqlSshDriver { private int counter = 0; public SimulatedMySqlSshDriver(SimulatedMySqlNodeImpl entity, SshMachineLocation machine) { super(entity, machine); } // simulate metrics, for if using ssh polling @Override public String getStatusCmd() { if (entity.getConfig(SIMULATE_ENTITY)) { return "echo Uptime: 2427 Threads: 1 Questions: 581 Slow queries: 0 Opens: 53 Flush tables: 1 Open tables: 35 Queries per second avg: "+(counter++ % 100); } else { return super.getStatusCmd(); } } @Override public void install() { if (entity.getConfig(SKIP_SSH_ON_START)) { // no-op } else { super.install(); } } // Not applying creation-script etc, as that requires launching msyqld (so would not scale for single-machine testing) // This is a copy of super.customize, but with the mysqladmin-exec disabled @Override public void customize() { if (!entity.getConfig(SIMULATE_ENTITY)) { super.customize(); return; } else if (entity.getConfig(SKIP_SSH_ON_START)) { // no-op } else { 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(); // DELIBERATELY SKIPPED FOR SCALABILITY TESTING ON SINGLE MACHINE DynamicTasks.queue( SshEffectorTasks.ssh( "cd "+getRunDir(), "echo skipping exec of "+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(); } } @Override public void launch() { if (!entity.getConfig(SIMULATE_ENTITY)) { super.launch(); return; } entity.sensors().set(MySqlNode.PID_FILE, getRunDir() + "/" + AbstractSoftwareProcessSshDriver.PID_FILENAME); if (entity.getConfig(SKIP_SSH_ON_START)) { // minimal ssh, so that isRunning will subsequently work newScript(MutableMap.of("usePidFile", true), LAUNCHING) .body.append( format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile())) .execute(); } else { newScript(MutableMap.of("usePidFile", true), LAUNCHING) .updateTaskAndFailOnNonZeroResultCode() .body.append(format("echo skipping normal exec of nohup %s/bin/mysqld --defaults-file=%s --user=`whoami` > %s 2>&1 < /dev/null &", getBaseDir(), getConfigFile(), getLogFile())) .body.append(format("nohup sleep 100000 > %s 2>&1 < /dev/null &", getLogFile())) .execute(); } } @Override public ProcessTaskWrapper<Integer> executeScriptFromInstalledFileAsync(String filenameAlreadyInstalledAtServer) { return DynamicTasks.queue( SshEffectorTasks.ssh( "cd "+getRunDir(), "echo skipping exec of "+getBaseDir()+"/bin/mysql --defaults-file="+getConfigFile()+" < "+filenameAlreadyInstalledAtServer) .summary("executing datastore script "+filenameAlreadyInstalledAtServer)); } } }