/*************************************************************************** * Copyright (c) 2012-2013 VMware, Inc. All Rights Reserved. * Licensed 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 com.vmware.aurora.vc; import java.text.SimpleDateFormat; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; import org.apache.log4j.Logger; import com.vmware.aurora.exception.VcException; import com.vmware.aurora.util.AuAssert; import com.vmware.aurora.vc.vcservice.VcContext; import com.vmware.aurora.vc.vcservice.VcLongCallHandler.VcLongCall; import com.vmware.aurora.vc.vcservice.VcSession; import com.vmware.vim.binding.impl.vim.PerformanceManager_Impl.MetricIdImpl; import com.vmware.vim.binding.impl.vim.PerformanceManager_Impl.QuerySpecImpl; import com.vmware.vim.binding.vim.ElementDescription; import com.vmware.vim.binding.vim.HistoricalInterval; import com.vmware.vim.binding.vim.PerformanceManager; import com.vmware.vim.binding.vim.PerformanceManager.CounterInfo; import com.vmware.vim.binding.vim.PerformanceManager.EntityMetric; import com.vmware.vim.binding.vim.PerformanceManager.EntityMetricBase; import com.vmware.vim.binding.vim.PerformanceManager.IntSeries; import com.vmware.vim.binding.vim.PerformanceManager.MetricId; import com.vmware.vim.binding.vim.PerformanceManager.MetricSeries; import com.vmware.vim.binding.vim.PerformanceManager.ProviderSummary; import com.vmware.vim.binding.vim.PerformanceManager.QuerySpec; import com.vmware.vim.binding.vim.ResourcePool; import com.vmware.vim.binding.vim.VirtualApp; import com.vmware.vim.binding.vmodl.ManagedObjectReference; /* * Class to query VC for performance stats from a virtual machine (VM) or resource pool (RP), * measuring use of memory/cpu/network. * * TO DO: * - units: obtain scale, find way of getting relative units for all queries we want to run, * or else upper layers need to do this (which thye can probably do more efficiently) * - any historical/average performance gathering needed by dashboard and not supported innately by VC. * * BUGS (inherited from VC): * - the limitRealtimeSamples parameter to queryPerformance() is ignored (bug 658620). * - relative stats claim to be in % (1/100) but are actually in %/100 (1/10000) (bug 699834). * - RPs won't answer queries entirely inside the last half hour (bug 699830). * * Some notes on how VC exposes statistics for RP objects: * - RP stats are not available realtime, only for historical intervals (5 minutes and above). * - newest RP stat is always > 5 minutes old. * - RP stats are not accurate when VMs move in/out of RP (VC implements by aggregating VM * stats from all VMs in RP at time of query) * - RP doesn't support % based counters like mem::usage and cpu::usage, only the underlying * physical-unit-based counters, because in its view, there's no obvious maximum to use for * the denominator (our use, where limit==reservation, is easier). * - RP doesn't support mem::active (until VC 5.0, where it's named mem::capacity.usage but * means the same thing). * All of this taken together means that we can aggregate VM stats into RP stats ourselves, * and potentially do a better job than VC does for our purposes. * * We should consider adding 2 new areas of functionality to this module: * - scale: per object, know what the limits (vm host core speed, vm mem assigned, rp cpu assigned, * rp mem assigned) are, cache these per VC connection, be able to report it * - rp aggregation: separate method to query rp stats, which queries individual vm stats and aggregates * Unless the client wants to just do those things itself. But the client does want those things. */ /* * Class VcPerformance: use this class to issue queries for performance statistics * from VirtualCenter's PerformanceManager. */ /** * @author mginzton * */ public class VcPerformance { private static final Logger logger = Logger.getLogger(VcVmBase.class); /* * VC session cache: try to avoid repeated queries to VC for data that doesn't change; * query once every 5 min. */ private long nextRefreshTime; private static final long PERFCOUNTER_REFRESH_PERIOD = TimeUnit.MINUTES.toNanos(5); private CounterInfo[] sessionPerfCounters; // access only via getCachedPerfCounterList() /* * Class PerformanceType: used to specify which object you want performance * statistics for. */ public enum PerformanceType { cpuAbs, // absolute: in MHz; works for VMs and RPs cpuRel, // relative: in %x100 of underlying core(s); works for VMs but not RPs memOccupiedAbs, // absolute: in KB; based on consumption: all memory occupying physical pages; works for VMs and RPs memActiveAbs, // absolute: in KB; based on active: memory recently touched; works for VMs, and also RPs starting with vSphere 5.0 memActiveRel, // relative: in %x100 of assigned size; based on active: memory recently touched; works for VMs but not RPs memOverheadAbs, // absolute: in KB; based on overhead: memory overhead consumed by vm kernel; works for VMs and RPs vmSize, // absolute: in KB; vm size netInAbs, // absolute: in kbps; netOutAbs, // absolute: in kbps; netAbs // absolute: in kbps; works for VMs but not RPs } /* * Class PerformanceSample: used to encapsulate one performance sample from VC. */ public static class PerformanceSample { private long sample; // could be ratio, or absolute private Calendar timestamp; private String unit; /** * @return the sample */ public long getSample() { return sample; } /** * @return the timestamp */ public Calendar getTimestamp() { return timestamp; } /** * @return the unit */ public String getUnit() { return unit; } /** * Format as string (intended for human readability) */ public String toString() { return String.format("[%s %s@%s]", sample, unit, new SimpleDateFormat("yyyy/mm/dd HH:mm:ss").format(timestamp.getTime())); } } /** * getRealtimeInterval(): ask VC for the interval between "real-time" updates * from a performance provider. * * @param targetId: RefId of VC entity that is a performance provider (normally a VM). * @return Realtime sampling interval (aka refresh rate) for target entity. */ public Integer getRealtimeInterval(final String targetId) { return VcContext.inVcSessionDo(new VcSession<Integer>() { public Integer body() { ManagedObjectReference target = MoUtil.stringToMoref(targetId); ProviderSummary summary = getCachedPerfMgr().queryProviderSummary(target); Integer refresh = summary.getRefreshRate(); if (summary.isCurrentSupported()) { return refresh; } return null; } }); } /** * getHistoricalIntervals(): ask VC for the intervals between historical * statistics collection. * * @return List of sampling intervals. */ public Integer[] getHistoricalIntervals() { return VcContext.inVcSessionDo(new VcSession<Integer[]>() { public Integer[] body() { ArrayList<Integer> intervals = new ArrayList<Integer>(); for (HistoricalInterval interval: getCachedPerfMgr().getHistoricalInterval()) { intervals.add(interval.getSamplingPeriod()); } return intervals.toArray(new Integer[intervals.size()]); } }); } /** * getAllSamplingIntervals(): convenience method to determine all supported * sampling intervals for a given performance provider; combines the results * of getHistoricalIntervals() and getRealtimeInterval(). * * @param targetId: RefId of VC entity that is a performance provider (normally a VM). * @return List of sampling intervals. */ public Integer[] getAllSamplingIntervals(final String targetId) { ArrayList<Integer> intervals = new ArrayList<Integer>(); Integer interval = getRealtimeInterval(targetId); if (interval != null) { intervals.add(interval); } intervals.addAll(Arrays.asList(getHistoricalIntervals())); return intervals.toArray(new Integer[intervals.size()]); } /** * queryVcCurrentTime(): get the current time according to the VC server. * * @return Time, as java.util.Calendar. */ public Calendar queryVcCurrentTime() { return VcContext.inVcSessionDo(new VcSession<Calendar>() { public Calendar body() { return VcContext.getService().getServiceInstance().currentTime(); } }); } /** * queryLatestPerformance(): query VC for most recent performance statistics * available for the given entity, at the highest resolution (shortest sampling * interval) supported. Convenience wrapper around queryPerformance(). * * @param targetIds: RefIds of VC entities that are performance providers (VM or RP), all entities must be the same type. * @param type: type of performance statistics to retrieve. * @return Map of String and PerformanceSample, with reference id of String as key. */ public Map<String, PerformanceSample> queryLatestPerformance( List<String> targetIds, PerformanceType type) { AuAssert.check(targetIds != null && targetIds.size() > 0); Integer[] intervals = getAllSamplingIntervals(targetIds.get(0)); // Ugly: for VM, we need to use value inside last half hour to get newest results; // for RP, we need to use value outside last half hour to get any results at all // (VC bug 699830; see comment preceding the main queryPerformance method). // So, we behave differently depending on whether this target is a VM or RP. boolean isRP = MoUtil.isOfType(targetIds.get(0), ResourcePool.class) || MoUtil.isOfType(targetIds.get(0), VirtualApp.class); int startMinutesAgo = isRP ? 30 : 5; Calendar startTime = queryVcCurrentTime(); refreshPerfCounters(); startTime.add(Calendar.MINUTE, -startMinutesAgo); Map<String, PerformanceSample[]> samples = queryPerformance(targetIds, type, intervals[0], 0, startTime, null); Map<String, PerformanceSample> result = new HashMap<String, PerformanceSample>(samples.size()); for (String refId : samples.keySet()) { PerformanceSample[] sample = samples.get(refId); if (sample != null && sample.length > 0) { try { if (isRP) { // for RP, we can't trust the newest sample (PR 703835, PR 723127). So ignore the // newest sample and return 2nd newest. Because of the PR 699830 workaround above, // we should always have multiple (usually 5) samples anyway. result.put(refId, sample[sample.length - 2]); } else { // for VM, return last sample (in practice the only sample, the list should have // only one item) result.put(refId, sample[sample.length - 1]); } } catch (ArrayIndexOutOfBoundsException ex) { } } } return result; } /* * Note on the various ways of limiting samples returned: ESX and VC sample various counters only as defined * for each performance provider (such as VM or RP), and only at certain intervals which vary by counter. * * VC makes a distinction between "realtime" and "historical" stats, which is determined by the sampling * interval: a performance provider may or may not implement a realtime sampling interval (also known as * "refresh rate" in VC docs); VMs do and RPs do not. VC aggregates the data from more frequent intervals * into the less frequent intervals, and stores the coarser-grained data going back farther into the past * in its database. * * Queries for performance data may be answered by VC from its database, or may be forwarded directly to * ESX servers. VC has a documented optimization where it treats requests inside the last half hour * specially. (This means the begin time must be less than half an hour ago, and the end time after that, * or null.) I note that when querying VM statistics, if I ask for samples outside this range, the newest * sample is always 1 interval stale (so for the 5-minute interval, best case is the newest sample is 6 * minutes old, and it may well be 9 minutes old). However, when querying RP statistics, the optimization * appears to break things; it returns no data at all for the optimized window (but a window starting >= * 30 minutes ago does return data, including the range at the in-optimization-window query returns no * results for). I even confirmed this in vSphere Client, interactively: a chart for the last 25 minutes is * empty, a chart for the last 30 minutes has data. * (See http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/vsp41_vsdk_prog_guide.pdf, page 156, * "Optimizing query response time".) Filed as VC bug 699830. * * Thus, not only the interval between samples, but also the time range over which data is available, and * whether data was even collected in the first place, will depend on the sampling interval. The interval * is the primary factor; it must be specified for all queries, and must be one of the values returned * from getSamplingIntervals(). * * For a given interval, you can narrow the amount of data returned by specifying the optional start & end * time parameters. Additionally, the API offers a way to further narrow the data returned to the N newest * samples; this is documented to work only for the realtime interval, and is apparently broken entirely * in ESX 5.0. */ /** * queryPerformance(): * @param targetId: RefId of VC entity that is a performance provider (VM or RP). * @param type: type of performance statistics to retrieve. * @param sampleInterval: interval between samples, in seconds * @param limitRealtimeSamples: maximum number of realtime samples to retrieve (BUG: apparently ignored by VC). * @param historicalStart: retrieve historical stats only after this time, if not null * @param historicalEnd: retrieve historical stats only before this time, if not null * @return List of results, as PerformanceSamples. */ public PerformanceSample[] queryPerformance(final String targetId, // RefId to VM or RP final PerformanceType type, // mem/cpu/net final int sampleInterval, // time between samples in seconds final int limitRealtimeSamples, // realtime samples: limit number of samples to return, 0 for unlimited final Calendar historicalStart, // historical samples: null for oldest sample available final Calendar historicalEnd) // historical samples: null (or now) for up-to-current { if (targetId == null || targetId.equals("")) { return null; } else { List<String> id = new ArrayList<String>(1); id.add(targetId); return queryPerformance(id, type, sampleInterval, limitRealtimeSamples, historicalStart, historicalEnd).get(targetId); } } /** * queryPerformance(): * @param targetIds: RefIds of VC entities that are performance providers (VM or RP). All entities must be the same type. * @param type: type of performance statistics to retrieve. * @param sampleInterval: interval between samples, in seconds * @param limitRealtimeSamples: maximum number of realtime samples to retrieve (BUG: apparently ignored by VC). * @param historicalStart: retrieve historical stats only after this time, if not null * @param historicalEnd: retrieve historical stats only before this time, if not null * @return Map of (String and PerformanceSample[]), with Reference Id of String as key. */ public Map<String, PerformanceSample[]> queryPerformance( final List<String> targetIds, // RefId to VM or RP final PerformanceType type, // mem/cpu/net final int sampleInterval, // time between samples in seconds final int limitRealtimeSamples, // realtime samples: limit number of samples to return, 0 for unlimited final Calendar historicalStart, // historical samples: null for oldest sample available final Calendar historicalEnd) // historical samples: null (or now) for up-to-current { // Check for possible bad things as assertions. AuAssert.check(historicalStart == null || historicalEnd == null || historicalStart.before(historicalEnd)); AuAssert.check(targetIds != null && targetIds.size() > 0); return VcContext.getVcLongCallHandler().execute( new VcLongCall<Map<String, PerformanceSample[]>>() { public Map<String, PerformanceSample[]> callVc() { List<ManagedObjectReference> targetMOB = new ArrayList<ManagedObjectReference>(targetIds.size()); String firstType = null; for (String refId : targetIds) { ManagedObjectReference moRef = MoUtil.stringToMoref(refId); if (firstType == null) { firstType = moRef.getType(); } else if (!moRef.getType().equals(firstType)) { throw VcException.INVALID_ARGUMENT(); } targetMOB.add(moRef); } // counters: get info on all performance counters; build map of those relevant to this query type CountersByType counters = getCountersOfType(type, targetIds.get(0)); MetricId[] metrics = getMatchingMetrics(targetMOB.get(0), counters, sampleInterval); // construct query spec QuerySpec[] specs = new QuerySpecImpl[targetMOB.size()]; for (int i = 0; i < targetMOB.size(); i++) { specs[i] = new QuerySpecImpl(); specs[i].setEntity(targetMOB.get(i)); specs[i].setIntervalId(sampleInterval); specs[i].setFormat("normal"); specs[i].setMetricId(metrics); // selection of samples (historical) if (historicalStart != null) { specs[i].setStartTime(historicalStart); } if (historicalEnd != null) { specs[i].setEndTime(historicalEnd); } if (limitRealtimeSamples > 0) { specs[i].setMaxSample(limitRealtimeSamples); } } /* * Submit query request * * NB 1: impedance matching: we have one querySpec and want one result, but API takes a list and returns * a list, I assume 1:1, so we stick our single querySpec in a list, call function, and extract * the first member of the returned list. * NB 2: we can ask for information in default or csv format; objects returned will be EntityMetric or EntityMetricCSV * NB 3: for default format: EntityMetric stats = ((EntityMetric) metricBase[0]); * - stats.getValue()[0].getValue() is a list of ints: N samples * - stats.getSampleInfo() is a list of strings: N descriptions * NB 4: for csv format: EntityMetricCSV csv = ((EntityMetricCSV) metricBase[0]); * - csv.getValue()[0].getValue() is a string, splits by "," into N strings which are int samples * - csv.getSampleInfo.CSV() is a string, splits by "," into 2N strings which are (interval, date) tuples */ //submit query request //VC will filter all query request, and only return existing performance data //so we should construct performance result base on return value //and use reference id as key. EntityMetricBase[] metricBase = getCachedPerfMgr().queryStats(specs); Map<String, PerformanceSample[]> result = new HashMap<String, PerformanceSample[]>(); if (metricBase != null && metricBase.length > 0) { for (EntityMetricBase perf : metricBase) { EntityMetric stats = ((EntityMetric) perf); if (stats.getValue() != null && stats.getSampleInfo() != null) { IntSeries samples = (IntSeries) stats.getValue()[0]; if (samples != null) { int numSamples = samples.getValue().length; List<PerformanceSample> vals = new ArrayList<PerformanceSample>(); for (int i = 0; i < numSamples; i++) { if (samples.getValue()[i] >= 0) { PerformanceSample output = new PerformanceSample(); output.sample = samples.getValue()[i]; output.timestamp = stats.getSampleInfo()[i].getTimestamp(); ElementDescription desc = counters.map.get(samples.getId().getCounterId()).getUnitInfo(); output.unit = desc.getLabel(); vals.add(output); } } if (vals.size() > 0) { result.put(MoUtil.morefToString(perf.getEntity()), vals.toArray(new PerformanceSample[vals.size()])); } } } } } return result; } }); } /** * query performance data of children locates on specified entity; * currently only used for vm size on each datastore. * @param childrenMap Map of [entity reference Id] : [List of children identifier]. * child identifier could be 12345 if child vm reference id is vm-12345; * @param type performance type, currently only support PerformanceType.dataStorage * @return Map of (String and PerformanceSample[]), with children identifier of String as key. */ public Map<String, PerformanceSample> queryPerformance( final Map<String, List<String>> childrenMap, //map of Datastore refId and List of VirtualMachine identifier final PerformanceType type) // disk used { //use first level historical sample interval, should be 300s as default. final Integer sampleInterval = getHistoricalIntervals()[0]; return VcContext.getVcLongCallHandler().execute( new VcLongCall<Map<String, PerformanceSample>>() { @Override public Map<String, PerformanceSample> callVc() { Map<String, PerformanceSample> result = new HashMap<String, PerformanceSample>(); if (childrenMap.size() == 0) { return result; } CountersByType counters = getCountersOfType(type, null); //use first matched counter Integer counterKey = (Integer) counters.map.keySet().toArray()[0]; /* * use half hour as default query time range, * it is the least time range for the first level sample interval. */ Calendar historicalEnd = VcContext.getService().getServiceInstance().currentTime(); Calendar historicalStart = (Calendar) historicalEnd.clone(); historicalStart.add(Calendar.MINUTE, (0 - 30)); /* * construct query spec according to Map of entity:children list */ QuerySpec[] specs = new QuerySpecImpl[childrenMap.size()]; int index = 0; for (String refId : childrenMap.keySet()) { List<String> childrenList = childrenMap.get(refId); if (childrenList != null && childrenList.size() > 0) { ManagedObjectReference moRef = MoUtil.stringToMoref(refId); specs[index] = new QuerySpecImpl(); specs[index].setEntity(moRef); specs[index].setIntervalId(sampleInterval); specs[index].setFormat("normal"); MetricId[] metrics = new MetricIdImpl[childrenList.size()]; for (int j = 0; j < childrenList.size(); j++) { metrics[j] = new MetricIdImpl(); metrics[j].setCounterId(counterKey); metrics[j].setInstance(childrenList.get(j)); } specs[index].setMetricId(metrics); specs[index].setStartTime(historicalStart); specs[index].setEndTime(historicalEnd); index++; } } /* * submit query request * VC will filter the request, * and return performance data for each child entity on each parent entity; * if child entity has no relationship with parent entity, * the return value should be null. * we parse all return result, and take the last performance data; * and construct a map of [child identifier]:[performance data]. * if a child entity has relationship with multi-parent, * we should aggregate all performance data for this child entity. */ EntityMetricBase[] metricBase = getCachedPerfMgr().queryStats(specs); if (metricBase != null && metricBase.length > 0) { for (EntityMetricBase perf : metricBase) { EntityMetric stats = ((EntityMetric) perf); if (stats.getValue() != null && stats.getSampleInfo() != null) { MetricSeries[] samples = (MetricSeries[]) stats.getValue(); if (samples != null) { for (int i = 0; i < samples.length; i++) { IntSeries sample = (IntSeries) samples[i]; if (sample.getValue() != null) { String id = sample.getId().getInstance(); PerformanceSample val = result.get(id); if (val == null) { val = new PerformanceSample(); val.sample = sample.getValue()[sample.getValue().length - 1]; val.timestamp = stats.getSampleInfo()[stats.getSampleInfo().length - 1].getTimestamp(); ElementDescription desc = counters.map.get(samples[i].getId().getCounterId()).getUnitInfo(); val.unit = desc.getLabel(); result.put(id, val); } else { val.sample = val.sample + sample.getValue()[sample.getValue().length - 1]; } } } } } } } return result; } }); } /** * getCountersOfType(): return set of performance counters supported by VC, limited to those * relevant to a certain PerformanceType and expressed as a map indexed by counter ID. * * @param type: PerformanceType to filter on * @param targetType: VIM type of the entity to be queried * @return Map from counter ID to counter details, holding relevant counters. */ private CountersByType getCountersOfType(PerformanceType type, String targetId) { CountersByType counters = new CountersByType(); switch (type) { case cpuRel: AuAssert.check(!MoUtil.isOfType(targetId, ResourcePool.class)); counters.group = "cpu"; counters.name = "usage"; break; case cpuAbs: counters.group = "cpu"; counters.name = "usagemhz"; break; case memOccupiedAbs: counters.group = "mem"; counters.name = "consumed"; break; case memActiveAbs: counters.group = "mem"; counters.name = (MoUtil.isOfType(targetId, ResourcePool.class)) ? "capacity.usage" : "active"; break; case memActiveRel: AuAssert.check(!MoUtil.isOfType(targetId, ResourcePool.class)); counters.group = "mem"; counters.name = "usage"; break; case memOverheadAbs: counters.group = "mem"; counters.name = "overhead"; break; case vmSize: counters.group = "disk"; counters.name = "used"; break; case netInAbs: AuAssert.check(!MoUtil.isOfType(targetId, ResourcePool.class)); counters.group = "net"; counters.name = "received"; break; case netOutAbs: AuAssert.check(!MoUtil.isOfType(targetId, ResourcePool.class)); counters.group = "net"; counters.name = "transmitted"; break; case netAbs: AuAssert.check(!MoUtil.isOfType(targetId, ResourcePool.class)); counters.group = "net"; counters.name = "usage"; break; default: logger.debug("Performance type " + type + " is not supported"); throw VcException.INVALID_ARGUMENT(); } CounterInfo[] allCounters = getCachedPerfCounterList(); if (allCounters == null) { logger.debug("No available performance counters in specified vCenter"); throw VcException.PERFORMANCE_ERROR(); } for (CounterInfo counter: allCounters) { if (counter.getGroupInfo().getKey().equals(counters.group) && counter.getNameInfo().getKey().equals(counters.name)) { counters.map.put(counter.getKey(), counter); } } return counters; } /** * getMatchingMetrics(): query VC for metrics supported by a given target and relevant to a given query * (expressed by the provided counterMap). * * @param target: RefId of VC entity that is a performance provider. * @param counterMap: map populated with counters caller is interested in. * @param interval: sampling interval, used to restrict metrics. * @return List of metric IDs. */ private MetricId[] getMatchingMetrics(ManagedObjectReference target, CountersByType counters, Integer interval) { AuAssert.check(VcContext.isInSession()); /* * Metrics: get metrics available for the target, then filter those for ones specifying * a counter of the right type. * * Note on interval to query: This must be a legal interval; i.e. one of the historical * intervals or, for entities supporting realtime stats, the refresh rate; otherwise * queryAvailableMetric throws an exception. The realtime or historical intervals do work, * with one caveat: for powered off VMs, asking for interval 20 (realtime refresh rate) * yields null, whereas asking for interval 300 (shortest historical interval) yields an * empty list; so we have to check for both cases. * * XXX: is it useful to populate start/end times here? */ MetricId[] allMetrics = getCachedPerfMgr().queryAvailableMetric(target, null, null, interval); ArrayList<MetricId> metrics = new ArrayList<MetricId>(); // filter metrics and get the specified one if (allMetrics != null) { for (MetricId metric: allMetrics) { if (counters.map.containsKey(metric.getCounterId())) { if (metric.getInstance().equals("")) { // only add one instance per counter // XXX: does this get all cores for cpus? Should we not just construct a metric with (counter id, *)? // See http://www.vmware.com/support/developer/vc-sdk/visdk41pubs/ApiReference/vim.PerformanceManager.MetricId.html metrics.add(metric); } } } } if (metrics.isEmpty()) { logger.debug("No available metrics for counter " + counters.group + "::" + counters.name); throw VcException.PERFORMANCE_ERROR(); } return metrics.toArray(new MetricId[metrics.size()]); } private PerformanceManager getCachedPerfMgr() { AuAssert.check(VcContext.isInSession()); return VcContext.getService().getPerfManager(); } private void refreshPerfCounters() { long curTime = System.nanoTime(); if (nextRefreshTime <= curTime) { sessionPerfCounters = null; nextRefreshTime = curTime + PERFCOUNTER_REFRESH_PERIOD; } } private CounterInfo[] getCachedPerfCounterList() { AuAssert.check(VcContext.isInSession()); if (sessionPerfCounters == null) { sessionPerfCounters = getCachedPerfMgr().getPerfCounter(); } return sessionPerfCounters; } /* * Helpers used internally. */ private static class CountersByType { Map<Integer, CounterInfo> map; String name; String group; CountersByType() { map = new HashMap<Integer, CounterInfo>(); } } private boolean isValidSamplingInterval(String targetId, int requested) { Integer[] intervals = getAllSamplingIntervals(targetId); for (Integer interval: intervals) { if (interval == requested) { return true; } } return false; } }