/*
* 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));
}
}
}