blob: 6b1d0964655a5122b935690765d2125ae310ee36 [file] [log] [blame]
/*
* 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 java.net.URI;
import java.util.concurrent.Callable;
import org.apache.brooklyn.api.sensor.AttributeSensor;
import org.apache.brooklyn.config.ConfigKey;
import org.apache.brooklyn.core.config.ConfigKeys;
import org.apache.brooklyn.core.entity.BrooklynConfigKeys;
import org.apache.brooklyn.core.entity.lifecycle.ServiceStateLogic.ServiceNotUpLogic;
import org.apache.brooklyn.core.sensor.Sensors;
import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcess;
import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcessImpl;
import org.apache.brooklyn.entity.software.base.VanillaSoftwareProcessSshDriver;
import org.apache.brooklyn.feed.function.FunctionFeed;
import org.apache.brooklyn.feed.function.FunctionPollConfig;
import org.apache.brooklyn.feed.http.HttpFeed;
import org.apache.brooklyn.feed.http.HttpPollConfig;
import org.apache.brooklyn.feed.http.HttpValueFunctions;
import org.apache.brooklyn.location.ssh.SshMachineLocation;
import org.apache.brooklyn.util.time.Duration;
/**
* For simulating various aspects of the {@link VanillaSoftwareProcess} entity.
*
* It is assumed that the ssh commands for install, launch, etc will be written with testability in mind.
* For example, they might just be {@code echo} statements, because there is insufficient resources to
* run 100s of processes.
*
* It is thus possible to simulate aspects of the behaviour, for performance and load testing purposes.
*
* There is configuration for:
* <ul>
* <li>{@code skipSshOnStart}
* <ul>
* <li>If true, then no ssh commands will be executed at deploy-time.
* This is useful for speeding up load testing, to get to the desired number of entities.
* <li>If false, the ssh commands will be executed.
* </ul>
* <li>{@code simulateEntity}
* <ul>
* <li>if true, no underlying entity will be started. Instead a sleep 100000 job will be run and monitored.
* <li>if false, the underlying entity (i.e. a JBoss app-server) will be started as normal.
* </ul>
* <li>{@code simulateExternalMonitoring}
* <ul>
* <li>if true, disables the default monitoring mechanism. Instead, a function will periodically execute
* to set the entity's sensors (as though the values had been obtained from the external monitoring tool).
* <li>if false, then:
* <ul>
* <li>If {@code simulateEntity==true} it will execute comparable commands (e.g. execute a command of the same
* size over ssh or do a comparable number of http GET requests).
* <li>If {@code simulateEntity==false} then normal monitoring will be done.
* </ul>
* </ul>
* </ul>
*/
public class SimulatedVanillaSoftwareProcessImpl extends VanillaSoftwareProcessImpl {
public static final ConfigKey<Boolean> EXEC_SSH_ON_START = ConfigKeys.newBooleanConfigKey(
"execSshOnStart",
"If true, will execute the ssh commands on install/launch; if false, will skip them",
true);
public static final ConfigKey<URI> HTTP_FEED_URI = ConfigKeys.newConfigKey(
URI.class,
"httpFeed.uri",
"If non-null, the URI to poll periodically using a HttpFeed", null);
public static final ConfigKey<Duration> HTTP_FEED_POLL_PERIOD = ConfigKeys.newConfigKey(
Duration.class,
"httpFeed.pollPeriod",
"The poll priod for the HttpFeed (if 'httpFeed.uri' was non-null)",
Duration.ONE_SECOND);
public static final ConfigKey<Duration> FUNCTION_FEED_POLL_PERIOD = ConfigKeys.newConfigKey(
Duration.class,
"functionFeed.pollPeriod",
"The poll priod for a function that increments 'counter' periodically (if null, then no-op)",
Duration.ONE_SECOND);
// see SERVICE_PROCESS_IS_RUNNING_POLL_PERIOD
// Inspired by EmptySoftwareProcess.USE_SSH_MONITORING
public static final ConfigKey<Boolean> USE_SSH_MONITORING = ConfigKeys.newConfigKey(
"sshMonitoring.enabled",
"Whether to poll periodically over ssh, using the driver.isRunning check",
Boolean.TRUE);
private static final AttributeSensor<String> HTTP_STRING_ATTRIBUTE = Sensors.newStringSensor("httpStringAttribute");
private static final AttributeSensor<Integer> HTTP_INT_ATTRIBUTE = Sensors.newIntegerSensor("httpIntAttribute");
private static final AttributeSensor<Long> FUNCTION_COUNTER = Sensors.newLongSensor("functionCounter");
private FunctionFeed functionFeed;
private HttpFeed httpFeed;
@Override
public void init() {
super.init();
if (Boolean.FALSE.equals(config().get(EXEC_SSH_ON_START))) {
config().set(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, true);
}
}
@Override
public Class<?> getDriverInterface() {
return SimulatedVanillaSoftwareProcessSshDriver.class;
}
@Override
protected void connectServiceUpIsRunning() {
boolean useSshMonitoring = Boolean.TRUE.equals(config().get(USE_SSH_MONITORING));
if (useSshMonitoring) {
super.connectServiceUpIsRunning();
}
}
@Override
protected void initEnrichers() {
super.initEnrichers();
}
@Override
protected void connectSensors() {
super.connectSensors();
boolean useSshMonitoring = Boolean.TRUE.equals(config().get(USE_SSH_MONITORING));
Duration functionFeedPeriod = config().get(FUNCTION_FEED_POLL_PERIOD);
URI httpFeedUri = config().get(HTTP_FEED_URI);
if (!useSshMonitoring) {
ServiceNotUpLogic.clearNotUpIndicator(this, SERVICE_PROCESS_IS_RUNNING);
}
if (functionFeedPeriod != null) {
functionFeed = feeds().add(FunctionFeed.builder()
.entity(this)
.period(functionFeedPeriod)
.poll(FunctionPollConfig.forSensor(FUNCTION_COUNTER)
.callable(new Callable<Long>() {
@Override public Long call() throws Exception {
Long oldVal = sensors().get(FUNCTION_COUNTER);
return (oldVal == null) ? 1 : oldVal + 1;
}
}))
.build());
}
if (httpFeedUri != null) {
httpFeed = feeds().add(HttpFeed.builder()
.entity(this)
.period(config().get(HTTP_FEED_POLL_PERIOD))
.baseUri(httpFeedUri)
.poll(new HttpPollConfig<Integer>(HTTP_INT_ATTRIBUTE)
.onSuccess(HttpValueFunctions.responseCode()))
.poll(new HttpPollConfig<String>(HTTP_STRING_ATTRIBUTE)
.onSuccess(HttpValueFunctions.stringContentsFunction()))
.build());
}
}
@Override
protected void disconnectSensors() {
super.disconnectSensors();
if (functionFeed != null) functionFeed.stop();
if (httpFeed != null) httpFeed.stop();
}
public static class SimulatedVanillaSoftwareProcessSshDriver extends VanillaSoftwareProcessSshDriver {
public SimulatedVanillaSoftwareProcessSshDriver(SimulatedVanillaSoftwareProcessImpl entity, SshMachineLocation machine) {
super(entity, machine);
}
@Override
public void install() {
if (Boolean.TRUE.equals(entity.getConfig(EXEC_SSH_ON_START))) {
super.install();
} else {
// no-op
}
}
@Override
public void customize() {
if (Boolean.TRUE.equals(entity.getConfig(EXEC_SSH_ON_START))) {
super.customize();
} else {
// no-op
}
}
@Override
public void launch() {
if (Boolean.TRUE.equals(entity.getConfig(EXEC_SSH_ON_START))) {
super.launch();
} else {
// no-op
}
}
@Override
public boolean isRunning() {
if (Boolean.TRUE.equals(entity.getConfig(USE_SSH_MONITORING))) {
return super.isRunning();
} else {
return true;
}
}
}
}