Repository: brooklyn-server Updated Branches: refs/heads/master c7731a4fa -> a58ba05c7
Added a percentage enricher Project: http://git-wip-us.apache.org/repos/asf/brooklyn-server/repo Commit: http://git-wip-us.apache.org/repos/asf/brooklyn-server/commit/1b7c6be6 Tree: http://git-wip-us.apache.org/repos/asf/brooklyn-server/tree/1b7c6be6 Diff: http://git-wip-us.apache.org/repos/asf/brooklyn-server/diff/1b7c6be6 Branch: refs/heads/master Commit: 1b7c6be62592421ea2144a6bd15a0c635bf544a2 Parents: a941963 Author: graeme.miller <[email protected]> Authored: Thu Jun 23 14:34:21 2016 +0100 Committer: graeme.miller <[email protected]> Committed: Fri Jun 24 14:16:45 2016 +0100 ---------------------------------------------------------------------- .../enricher/stock/PercentageEnricher.java | 127 +++++++++ .../enricher/stock/PercentageEnricherTest.java | 275 +++++++++++++++++++ .../entity/machine/AddMachineMetrics.java | 8 + .../entity/machine/MachineAttributes.java | 6 + 4 files changed, 416 insertions(+) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b7c6be6/core/src/main/java/org/apache/brooklyn/enricher/stock/PercentageEnricher.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/org/apache/brooklyn/enricher/stock/PercentageEnricher.java b/core/src/main/java/org/apache/brooklyn/enricher/stock/PercentageEnricher.java new file mode 100644 index 0000000..9bc7e7e --- /dev/null +++ b/core/src/main/java/org/apache/brooklyn/enricher/stock/PercentageEnricher.java @@ -0,0 +1,127 @@ +/* + * 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.enricher.stock; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import com.google.common.reflect.TypeToken; + +import org.apache.brooklyn.api.entity.Entity; +import org.apache.brooklyn.api.entity.EntityLocal; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.api.sensor.SensorEvent; +import org.apache.brooklyn.api.sensor.SensorEventListener; +import org.apache.brooklyn.config.ConfigKey; +import org.apache.brooklyn.core.config.ConfigKeys; +import org.apache.brooklyn.core.enricher.AbstractEnricher; +import org.apache.brooklyn.util.collections.MutableMap; +import org.apache.brooklyn.util.javalang.JavaClassNames; + +public class PercentageEnricher extends AbstractEnricher implements SensorEventListener<Number> { + + private static final Logger LOG = LoggerFactory.getLogger(PercentageEnricher.class); + + public static final ConfigKey<AttributeSensor<? extends Number>> SOURCE_CURRENT_SENSOR = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<? extends Number>>() { + }, "enricher.sourceCurrentSensor"); + + public static final ConfigKey<AttributeSensor<? extends Number>> SOURCE_TOTAL_SENSOR = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<? extends Number>>() { + }, "enricher.sourceTotalSensor"); + + public static final ConfigKey<AttributeSensor<Double>> TARGET_SENSOR = ConfigKeys.newConfigKey(new TypeToken<AttributeSensor<Double>>() { + }, "enricher.targetSensor"); + + public static final ConfigKey<Entity> PRODUCER = ConfigKeys.newConfigKey(Entity.class, "enricher.producer"); + + protected AttributeSensor<? extends Number> sourceCurrentSensor; + protected AttributeSensor<? extends Number> sourceTotalSensor; + protected AttributeSensor<Double> targetSensor; + protected Entity producer; + + @SuppressWarnings({"unchecked", "rawtypes"}) + @Override + public void setEntity(EntityLocal entity) { + super.setEntity(entity); + + this.sourceCurrentSensor = getConfig(SOURCE_CURRENT_SENSOR); + this.sourceTotalSensor = getConfig(SOURCE_TOTAL_SENSOR); + this.targetSensor = getConfig(TARGET_SENSOR); + this.producer = getConfig(PRODUCER) == null ? entity : getConfig(PRODUCER); + + if (sourceCurrentSensor == null) { + throw new IllegalArgumentException("Enricher " + JavaClassNames.simpleClassName(this) + " has no " + SOURCE_CURRENT_SENSOR.getName()); + } + if (sourceTotalSensor == null) { + throw new IllegalArgumentException("Enricher " + JavaClassNames.simpleClassName(this) + " has no " + SOURCE_TOTAL_SENSOR.getName()); + } + if (targetSensor == null) { + throw new IllegalArgumentException("Enricher " + JavaClassNames.simpleClassName(this) + " has no " + TARGET_SENSOR.getName()); + } + + if (targetSensor.equals(sourceCurrentSensor)) { + throw new IllegalArgumentException("Enricher " + JavaClassNames.simpleClassName(this) + " detect cycle with " + SOURCE_CURRENT_SENSOR.getName()); + } + if (targetSensor.equals(sourceTotalSensor)) { + throw new IllegalArgumentException("Enricher " + JavaClassNames.simpleClassName(this) + " detect cycle with " + SOURCE_TOTAL_SENSOR.getName()); + } + + subscriptions().subscribe(MutableMap.of("notifyOfInitialValue", true), producer, sourceCurrentSensor, this); + subscriptions().subscribe(MutableMap.of("notifyOfInitialValue", true), producer, sourceTotalSensor, this); + + } + + @Override + public void onEvent(SensorEvent<Number> event) { + Number current = producer.sensors().get(sourceCurrentSensor); + Number total = producer.sensors().get(sourceTotalSensor); + Double result = null; + + if (current == null) { + LOG.debug("Current is null, returning"); + return; + } + + if (total == null || total.equals(0)) { + LOG.debug("total {" + total + "} is null or zero, returning"); + return; + } + + Double currentDouble = current.doubleValue(); + Double totalDouble = total.doubleValue(); + + if (currentDouble > totalDouble) { + LOG.debug("Current is greater than total, returning"); + return; + } + + if (currentDouble < 0 || totalDouble < 0) { + LOG.debug("Current {"+currentDouble+"} or total {"+totalDouble+"} is negative, returning"); + return; + } + + if (current.equals(0)) { + LOG.debug("current is zero, setting percent to zero"); + result = 0d; + } else { + result = currentDouble / totalDouble; + } + + emit(targetSensor, result); + } +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b7c6be6/core/src/test/java/org/apache/brooklyn/enricher/stock/PercentageEnricherTest.java ---------------------------------------------------------------------- diff --git a/core/src/test/java/org/apache/brooklyn/enricher/stock/PercentageEnricherTest.java b/core/src/test/java/org/apache/brooklyn/enricher/stock/PercentageEnricherTest.java new file mode 100644 index 0000000..575970f --- /dev/null +++ b/core/src/test/java/org/apache/brooklyn/enricher/stock/PercentageEnricherTest.java @@ -0,0 +1,275 @@ +/* + * 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.enricher.stock; + +import static org.testng.Assert.assertEquals; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.testng.annotations.BeforeMethod; +import org.testng.annotations.Test; + +import com.google.common.collect.ImmutableList; + +import org.apache.brooklyn.api.entity.EntitySpec; +import org.apache.brooklyn.api.sensor.AttributeSensor; +import org.apache.brooklyn.api.sensor.EnricherSpec; +import org.apache.brooklyn.core.entity.BrooklynConfigKeys; +import org.apache.brooklyn.core.entity.EntityAsserts; +import org.apache.brooklyn.core.location.SimulatedLocation; +import org.apache.brooklyn.core.sensor.BasicAttributeSensor; +import org.apache.brooklyn.core.test.BrooklynAppUnitTestSupport; +import org.apache.brooklyn.core.test.entity.TestApplication; + +public class PercentageEnricherTest extends BrooklynAppUnitTestSupport { + + public static final Logger log = LoggerFactory.getLogger(PercentageEnricherTest.class); + + AttributeSensor<Double> currentSensor; + AttributeSensor<Double> totalSensor; + AttributeSensor<Double> targetSensor; + + @BeforeMethod(alwaysRun = true) + @Override + public void setUp() throws Exception { + super.setUp(); + currentSensor = new BasicAttributeSensor<Double>(Double.class, "current"); + totalSensor = new BasicAttributeSensor<Double>(Double.class, "total"); + targetSensor = new BasicAttributeSensor<Double>(Double.class, "target"); + + app.start(ImmutableList.of(new SimulatedLocation())); + } + + private void addEnricher() { + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor)); + } + + @Test + public void vanillaTest() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, 100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + } + + @Test + public void currentNullTest() { + addEnricher(); + + app.sensors().set(currentSensor, null); + app.sensors().set(totalSensor, 100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void totalNullTest() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, null); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void bothInputNullTest() { + addEnricher(); + + app.sensors().set(currentSensor, null); + app.sensors().set(totalSensor, null); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void currentZeroTest() { + addEnricher(); + + app.sensors().set(currentSensor, 0d); + app.sensors().set(totalSensor, 100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 0d); + } + + @Test + public void totalZeroTest() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, 0d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void totalLessThanCurrentTest() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, 25d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void oneHundredPercentTest() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, 50d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 100d); + } + + @Test + public void negativeCurrent() { + addEnricher(); + + app.sensors().set(currentSensor, -50d); + app.sensors().set(totalSensor, 100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void negativeTotal() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, -100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void bothSourceNegative() { + addEnricher(); + + app.sensors().set(currentSensor, -50d); + app.sensors().set(totalSensor, -100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, null); + } + + @Test + public void totalDoubleMaxValue() { + addEnricher(); + + app.sensors().set(currentSensor, Double.MAX_VALUE); + app.sensors().set(totalSensor, Double.MAX_VALUE); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 100d); + } + + //SETUP TESTS + @Test(expectedExceptions = IllegalArgumentException.class) + public void totalNoCurrentSensor() { + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void totalNoTotalSensor() { + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor)); + } + + @Test(expectedExceptions = IllegalArgumentException.class) + public void totalNoTargetSensor() { + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor)); + } + + @Test + public void testDifferentProducer() { + EntitySpec<TestApplication> appSpec = EntitySpec.create(TestApplication.class) + .configure(BrooklynConfigKeys.SKIP_ON_BOX_BASE_DIR_RESOLUTION, shouldSkipOnBoxBaseDirResolution()); + + TestApplication producer = mgmt.getEntityManager().createEntity(appSpec); + + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor) + .configure(PercentageEnricher.PRODUCER, producer) + ); + + producer.sensors().set(currentSensor, 25d); + producer.sensors().set(totalSensor, 50d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + EntityAsserts.assertAttributeEqualsEventually(producer, targetSensor, null); + } + + @Test + public void testLong() { + AttributeSensor<Long> currentSensor = new BasicAttributeSensor<Long>(Long.class, "current"); + AttributeSensor<Long> totalSensor = new BasicAttributeSensor<Long>(Long.class, "total"); + + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor) + ); + + app.sensors().set(currentSensor, 25l); + app.sensors().set(totalSensor, 50l); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + } + + @Test + public void testInteger() { + AttributeSensor<Integer> currentSensor = new BasicAttributeSensor<Integer>(Integer.class, "current"); + AttributeSensor<Integer> totalSensor = new BasicAttributeSensor<Integer>(Integer.class, "total"); + + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor) + ); + + app.sensors().set(currentSensor, 25); + app.sensors().set(totalSensor, 50); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + } + + @Test + public void testFloat() { + AttributeSensor<Float> currentSensor = new BasicAttributeSensor<Float>(Float.class, "current"); + AttributeSensor<Float> totalSensor = new BasicAttributeSensor<Float>(Float.class, "total"); + + app.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, currentSensor) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, totalSensor) + .configure(PercentageEnricher.TARGET_SENSOR, targetSensor) + ); + + app.sensors().set(currentSensor, 25f); + app.sensors().set(totalSensor, 50f); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + } + + @Test + public void validThenInvalid() { + addEnricher(); + + app.sensors().set(currentSensor, 50d); + app.sensors().set(totalSensor, 100d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + app.sensors().set(totalSensor, 0d); + EntityAsserts.assertAttributeEqualsEventually(app, targetSensor, 50d); + } + +} http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b7c6be6/software/base/src/main/java/org/apache/brooklyn/entity/machine/AddMachineMetrics.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/AddMachineMetrics.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/AddMachineMetrics.java index e118b93..a61d4ee 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/AddMachineMetrics.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/AddMachineMetrics.java @@ -35,6 +35,8 @@ import org.apache.brooklyn.api.entity.EntityInitializer; import org.apache.brooklyn.api.entity.EntityLocal; import org.apache.brooklyn.api.sensor.EnricherSpec; import org.apache.brooklyn.core.entity.EntityInternal; +import org.apache.brooklyn.enricher.stock.PercentageEnricher; +import org.apache.brooklyn.enricher.stock.Transformer; import org.apache.brooklyn.enricher.stock.YamlRollingTimeWindowMeanEnricher; import org.apache.brooklyn.enricher.stock.YamlTimeWeightedDeltaEnricher; import org.apache.brooklyn.entity.software.base.SoftwareProcess; @@ -75,6 +77,12 @@ public class AddMachineMetrics implements EntityInitializer { entity.enrichers().add(EnricherSpec.create(YamlRollingTimeWindowMeanEnricher.class) .configure(YamlRollingTimeWindowMeanEnricher.SOURCE_SENSOR, MachineAttributes.USED_MEMORY_DELTA_PER_SECOND_LAST) .configure(YamlRollingTimeWindowMeanEnricher.TARGET_SENSOR, MachineAttributes.USED_MEMORY_DELTA_PER_SECOND_IN_WINDOW)); + + entity.enrichers().add(EnricherSpec.create(PercentageEnricher.class) + .configure(PercentageEnricher.SOURCE_CURRENT_SENSOR, MachineAttributes.USED_MEMORY) + .configure(PercentageEnricher.SOURCE_TOTAL_SENSOR, MachineAttributes.TOTAL_MEMORY) + .configure(PercentageEnricher.TARGET_SENSOR, MachineAttributes.USED_MEMORY_PERCENT)); + } public static SshFeed createMachineMetricsFeed(EntityLocal entity) { http://git-wip-us.apache.org/repos/asf/brooklyn-server/blob/1b7c6be6/software/base/src/main/java/org/apache/brooklyn/entity/machine/MachineAttributes.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/org/apache/brooklyn/entity/machine/MachineAttributes.java b/software/base/src/main/java/org/apache/brooklyn/entity/machine/MachineAttributes.java index ff3ef86..fc852b6 100644 --- a/software/base/src/main/java/org/apache/brooklyn/entity/machine/MachineAttributes.java +++ b/software/base/src/main/java/org/apache/brooklyn/entity/machine/MachineAttributes.java @@ -56,6 +56,9 @@ public class MachineAttributes { public static final AttributeSensor<Double> USED_MEMORY_DELTA_PER_SECOND_LAST = Sensors.newDoubleSensor("memory.used.delta", "Change in memory usage per second"); public static final AttributeSensor<Double> USED_MEMORY_DELTA_PER_SECOND_IN_WINDOW = Sensors.newDoubleSensor("memory.used.windowed", "Average change in memory usage over 30s"); + public static final AttributeSensor<Double> USED_MEMORY_PERCENT = Sensors.newDoubleSensor("memory.used.percent", "The percentage of memory used"); + public static final AttributeSensor<Double> AVERAGE_USED_MEMORY_PERCENT = Sensors.newDoubleSensor("memory.used.percent.average", "Average percentage of memory used across the cluster"); + private static AtomicBoolean initialized = new AtomicBoolean(false); /** @@ -75,6 +78,9 @@ public class MachineAttributes { RendererHints.register(CPU_USAGE, RendererHints.displayValue(MathFunctions.percent(2))); RendererHints.register(AVERAGE_CPU_USAGE, RendererHints.displayValue(MathFunctions.percent(2))); + RendererHints.register(USED_MEMORY_PERCENT, RendererHints.displayValue(MathFunctions.percent(2))); + RendererHints.register(AVERAGE_USED_MEMORY_PERCENT, RendererHints.displayValue(MathFunctions.percent(2))); + RendererHints.register(FREE_MEMORY, RendererHints.displayValue(Functionals.chain(MathFunctions.times(1000L), ByteSizeStrings.metric()))); RendererHints.register(TOTAL_MEMORY, RendererHints.displayValue(Functionals.chain(MathFunctions.times(1000L), ByteSizeStrings.metric()))); RendererHints.register(USED_MEMORY, RendererHints.displayValue(Functionals.chain(MathFunctions.times(1000L), ByteSizeStrings.metric())));
