Adds performance counters
Project: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/repo Commit: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/commit/fbb4ce9c Tree: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/tree/fbb4ce9c Diff: http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/diff/fbb4ce9c Branch: refs/heads/master Commit: fbb4ce9ca9f0077c19a88b8dc1410f69d0d56dd5 Parents: 902f97e Author: Martin Harris <[email protected]> Authored: Wed May 13 08:58:30 2015 +0100 Committer: Richard Downer <[email protected]> Committed: Thu May 28 17:27:35 2015 +0100 ---------------------------------------------------------------------- .../windows/WindowsPerformanceCounterFeed.java | 142 ++++++++++++++----- .../WindowsPerformanceCounterPollConfig.java | 1 + .../basic/VanillaWindowsProcessWinRmDriver.java | 3 +- .../winrm/WindowsPerformanceCounterSensors.java | 74 ++++++++++ 4 files changed, 181 insertions(+), 39 deletions(-) ---------------------------------------------------------------------- http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fbb4ce9c/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java index 468af57..35ffa93 100644 --- a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java +++ b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterFeed.java @@ -23,6 +23,7 @@ import static com.google.common.base.Preconditions.checkNotNull; import java.math.BigInteger; import java.util.Iterator; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.SortedMap; @@ -38,30 +39,35 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import brooklyn.config.ConfigKey; +import brooklyn.entity.Entity; import brooklyn.entity.basic.ConfigKeys; import brooklyn.entity.basic.EntityInternal; import brooklyn.entity.basic.EntityLocal; import brooklyn.entity.effector.EffectorTasks; import brooklyn.event.AttributeSensor; +import brooklyn.event.basic.Sensors; import brooklyn.event.feed.AbstractFeed; import brooklyn.event.feed.PollHandler; import brooklyn.event.feed.Poller; import brooklyn.event.feed.ssh.SshPollValue; import brooklyn.location.basic.SshMachineLocation; +import brooklyn.location.basic.WinRmMachineLocation; import brooklyn.management.ExecutionContext; import brooklyn.util.flags.TypeCoercions; +import brooklyn.util.guava.Maybe; import brooklyn.util.task.ssh.SshTasks; import brooklyn.util.task.system.ProcessTaskFactory; import brooklyn.util.task.system.ProcessTaskWrapper; import brooklyn.util.time.Duration; +import io.cloudsoft.winrm4j.winrm.WinRmToolResponse; import com.google.common.base.Function; import com.google.common.base.Joiner; -import com.google.common.base.Optional; import com.google.common.base.Strings; import com.google.common.collect.ImmutableList; import com.google.common.collect.ImmutableSet; import com.google.common.collect.Iterables; +import com.google.common.collect.Lists; import com.google.common.collect.Maps; import com.google.common.collect.Sets; import com.google.common.reflect.TypeToken; @@ -98,10 +104,11 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { // This pattern matches CSV line(s) with the date in the first field, and at least one further field. protected static final Pattern lineWithPerfData = Pattern.compile("^\"[\\d:/\\-. ]+\",\".*\"$", Pattern.MULTILINE); private static final Joiner JOINER_ON_SPACE = Joiner.on(' '); + private static final Joiner JOINER_ON_COMMA = Joiner.on(','); @SuppressWarnings("serial") - public static final ConfigKey<Set<WindowsPerformanceCounterPollConfig<?>>> POLLS = ConfigKeys.newConfigKey( - new TypeToken<Set<WindowsPerformanceCounterPollConfig<?>>>() {}, + public static final ConfigKey<List<WindowsPerformanceCounterPollConfig<?>>> POLLS = ConfigKeys.newConfigKey( + new TypeToken<List<WindowsPerformanceCounterPollConfig<?>>>() {}, "polls"); public static Builder builder() { @@ -133,7 +140,7 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { return this; } public Builder period(Duration period) { - period = checkNotNull(period, "period"); + this.period = checkNotNull(period, "period"); return this; } public Builder period(long millis) { @@ -166,7 +173,7 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { } protected WindowsPerformanceCounterFeed(Builder builder) { - Set<WindowsPerformanceCounterPollConfig<?>> polls = Sets.newLinkedHashSet(); + List<WindowsPerformanceCounterPollConfig<?>> polls = Lists.newArrayList(); for (WindowsPerformanceCounterPollConfig<?> config : builder.polls) { @SuppressWarnings({ "unchecked", "rawtypes" }) WindowsPerformanceCounterPollConfig<?> configCopy = new WindowsPerformanceCounterPollConfig(config); @@ -179,50 +186,50 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { @Override protected void preStart() { - Set<WindowsPerformanceCounterPollConfig<?>> polls = getConfig(POLLS); + List<WindowsPerformanceCounterPollConfig<?>> polls = getConfig(POLLS); long minPeriod = Integer.MAX_VALUE; - SortedSet<String> performanceCounterNames = Sets.newTreeSet(); + List<String> performanceCounterNames = Lists.newArrayList(); for (WindowsPerformanceCounterPollConfig<?> config : polls) { minPeriod = Math.min(minPeriod, config.getPeriod()); performanceCounterNames.add(config.getPerformanceCounterName()); } - SshMachineLocation machine = EffectorTasks.getSshMachine(getEntity()); Iterable<String> allParams = ImmutableList.<String>builder() - .add("typeperf") - .addAll(Iterables.transform(performanceCounterNames, QuoteStringFunction.INSTANCE)) - .add("-sc") - .add("1") + .add("Get-Counter") + .add("-Counter") + .add(JOINER_ON_COMMA.join(Iterables.transform(performanceCounterNames, QuoteStringFunction.INSTANCE))) + .add("-SampleInterval") + .add("2") // TODO: This should be a config key .build(); - String command = JOINER_ON_SPACE.join(allParams); + String command = String.format("(%s).CounterSamples.CookedValue", JOINER_ON_SPACE.join(allParams)); log.debug("Windows performance counter poll command will be: {}", command); - - final ProcessTaskFactory<Integer> taskFactory = SshTasks.newSshExecTaskFactory(machine, command) - .allowingNonZeroExitCode() - .runAsCommand(); - - final Callable<SshPollValue> queryForCounterValues = new Callable<SshPollValue>() { - public SshPollValue call() throws Exception { - ProcessTaskWrapper<Integer> taskWrapper = taskFactory.newTask(); - final ExecutionContext executionContext = - ((EntityInternal) entity).getManagementSupport().getExecutionContext(); - executionContext.submit(taskWrapper); - taskWrapper.block(); - Optional<Integer> exitCode = Optional.fromNullable(taskWrapper.getExitCode()); - return new SshPollValue(null, exitCode.or(-1), taskWrapper.getStdout(), taskWrapper.getStderr()); - } - }; - getPoller().scheduleAtFixedRate( - new CallInEntityExecutionContext<SshPollValue>(entity, queryForCounterValues), - new SendPerfCountersToSensors(entity, polls), - minPeriod); + getPoller().scheduleAtFixedRate(new GetPerformanceCountersJob<WinRmToolResponse>(getEntity(), command), + new SendPerfCountersToSensors(getEntity(), getConfig(POLLS)), 100L); + } + + private static class GetPerformanceCountersJob<T> implements Callable<T> { + + private final Entity entity; + private final String command; + + GetPerformanceCountersJob(Entity entity, String command) { + this.entity = entity; + this.command = command; + } + + @Override + public T call() throws Exception { + WinRmMachineLocation machine = EffectorTasks.getWinRmMachine(entity); + WinRmToolResponse response = machine.executePsScript(command); + return (T)response; + } } @SuppressWarnings("unchecked") - protected Poller<SshPollValue> getPoller() { - return (Poller<SshPollValue>) super.getPoller(); + protected Poller<WinRmToolResponse> getPoller() { + return (Poller<WinRmToolResponse>) super.getPoller(); } /** @@ -251,10 +258,71 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { } } + private static class SendPerfCountersToSensors implements PollHandler<WinRmToolResponse> { + + private final EntityLocal entity; + private final List<WindowsPerformanceCounterPollConfig<?>> polls; + + public SendPerfCountersToSensors(EntityLocal entity, List<WindowsPerformanceCounterPollConfig<?>> polls) { + this.entity = entity; + this.polls = polls; + } + + @Override + public boolean checkSuccess(WinRmToolResponse val) { + if (val.getStatusCode() != 0) return false; + String stderr = val.getStdErr(); + if (stderr == null || stderr.length() != 0) return false; + String out = val.getStdOut(); + if (out == null || out.length() == 0) return false; + return true; + } + + @Override + public void onSuccess(WinRmToolResponse val) { + String[] values = val.getStdOut().split("\r\n"); + for (int i = 0; i < polls.size(); i++) { + Class<?> clazz = polls.get(i).getSensor().getType(); + Maybe<? extends Object> maybeValue = TypeCoercions.tryCoerce(values[i], TypeToken.of(clazz)); + Object value = maybeValue.isAbsent() ? null : maybeValue.get(); + AttributeSensor<Object> attribute = (AttributeSensor<Object>) Sensors.newSensor(clazz, polls.get(i).getSensor().getName(), polls.get(i).getDescription()); + entity.setAttribute(attribute, value); + } + } + + @Override + public void onFailure(WinRmToolResponse val) { + log.error("Windows Performance Counter query did not respond as expected. exitcode={} stdout={} stderr={}", + new Object[]{val.getStatusCode(), val.getStdOut(), val.getStdErr()}); + for (WindowsPerformanceCounterPollConfig<?> config : polls) { + entity.setAttribute(Sensors.newSensor(config.getSensor().getClass(), config.getPerformanceCounterName(), config.getDescription()), null); + } + } + + @Override + public void onException(Exception exception) { + log.error("Detected exception while retrieving Windows Performance Counters from entity " + + entity.getDisplayName(), exception); + for (WindowsPerformanceCounterPollConfig<?> config : polls) { + entity.setAttribute(Sensors.newSensor(config.getSensor().getClass(), config.getPerformanceCounterName(), config.getDescription()), null); + } + } + + @Override + public String getDescription() { + return "" + polls; + } + + @Override + public String toString() { + return super.toString()+"["+getDescription()+"]"; + } + } + /** * A poll handler that takes the result of the <tt>typeperf</tt> invocation and sets the appropriate sensors. */ - private static class SendPerfCountersToSensors implements PollHandler<SshPollValue> { + private static class SendPerfCountersToSensors2 implements PollHandler<SshPollValue> { private static final Set<? extends Class<? extends Number>> INTEGER_TYPES = ImmutableSet.of(Integer.class, Long.class, Byte.class, Short.class, BigInteger.class); @@ -262,7 +330,7 @@ public class WindowsPerformanceCounterFeed extends AbstractFeed { private final EntityLocal entity; private final SortedMap<String, AttributeSensor> sensorMap; - public SendPerfCountersToSensors(EntityLocal entity, Set<WindowsPerformanceCounterPollConfig<?>> polls) { + public SendPerfCountersToSensors2(EntityLocal entity, List<WindowsPerformanceCounterPollConfig<?>> polls) { this.entity = entity; sensorMap = Maps.newTreeMap(); http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fbb4ce9c/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterPollConfig.java ---------------------------------------------------------------------- diff --git a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterPollConfig.java b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterPollConfig.java index a5d2abd..77681ea 100644 --- a/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterPollConfig.java +++ b/core/src/main/java/brooklyn/event/feed/windows/WindowsPerformanceCounterPollConfig.java @@ -31,6 +31,7 @@ public class WindowsPerformanceCounterPollConfig<T> extends PollConfig<Object, T @SuppressWarnings({ "unchecked", "rawtypes" }) public WindowsPerformanceCounterPollConfig(AttributeSensor<T> sensor) { super(sensor); + description(sensor.getDescription()); onSuccess((Function)Functions.identity()); } http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fbb4ce9c/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java index a5c91db..4e66180 100644 --- a/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java +++ b/software/base/src/main/java/brooklyn/entity/basic/VanillaWindowsProcessWinRmDriver.java @@ -19,7 +19,6 @@ package brooklyn.entity.basic; import brooklyn.location.basic.WinRmMachineLocation; -import brooklyn.util.time.Duration; public class VanillaWindowsProcessWinRmDriver extends AbstractSoftwareProcessWinRmDriver implements VanillaWindowsProcessDriver { @@ -47,7 +46,7 @@ public class VanillaWindowsProcessWinRmDriver extends AbstractSoftwareProcessWin @Override public boolean isRunning() { return executeCommand(VanillaWindowsProcess.CHECK_RUNNING_COMMAND, - VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, false); + VanillaWindowsProcess.CHECK_RUNNING_POWERSHELL_COMMAND, false).getStatusCode() == 0; } @Override http://git-wip-us.apache.org/repos/asf/incubator-brooklyn/blob/fbb4ce9c/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java ---------------------------------------------------------------------- diff --git a/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java b/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java new file mode 100644 index 0000000..e36d285 --- /dev/null +++ b/software/base/src/main/java/brooklyn/entity/software/winrm/WindowsPerformanceCounterSensors.java @@ -0,0 +1,74 @@ +/* + * 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 brooklyn.entity.software.winrm; + +import java.util.Map; +import java.util.Set; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; + +import brooklyn.config.ConfigKey; +import brooklyn.entity.basic.ConfigKeys; +import brooklyn.entity.basic.EntityInternal; +import brooklyn.entity.basic.EntityLocal; +import brooklyn.entity.proxying.EntityInitializer; +import brooklyn.event.basic.Sensors; +import brooklyn.event.feed.windows.WindowsPerformanceCounterFeed; +import brooklyn.util.config.ConfigBag; +import brooklyn.util.text.Strings; + +import com.google.common.reflect.TypeToken; + +public class WindowsPerformanceCounterSensors implements EntityInitializer { + + private static final Logger LOG = LoggerFactory.getLogger(WindowsPerformanceCounterSensors.class); + + public final static ConfigKey<Set<Map<String, String>>> PERFORMANCE_COUNTERS = ConfigKeys.newConfigKey(new TypeToken<Set<Map<String, String>>>(){}, "performance.counters"); + + protected final Set<Map<String, String>> sensors; + + public WindowsPerformanceCounterSensors(ConfigBag params) { + sensors = params.get(PERFORMANCE_COUNTERS); + } + + public WindowsPerformanceCounterSensors(Map<String, String> params) { + this(ConfigBag.newInstance(params)); + } + + @Override + public void apply(EntityLocal entity) { + WindowsPerformanceCounterFeed.Builder builder = WindowsPerformanceCounterFeed.builder() + .entity(entity); + for (Map<String, String> sensorConfig : sensors) { + String sensorType = sensorConfig.get("sensorType"); + Class<?> clazz; + try { + clazz = Strings.isNonEmpty(sensorType) ? + ((EntityInternal)entity).getManagementContext().getCatalog().getRootClassLoader().loadClass(sensorType) : String.class; + } catch (ClassNotFoundException e) { + LOG.warn("Could not load type {} for sensor {}", sensorType, sensorConfig.get("name")); + clazz = String.class; + } + builder.addSensor(sensorConfig.get("counter"), Sensors.newSensor(clazz, sensorConfig.get("name"), sensorConfig.get("description"))); + } + builder.build(); + } + +}
