Package com.vmware.aurora.vc

Source Code of com.vmware.aurora.vc.VcPerformance$CountersByType

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

Related Classes of com.vmware.aurora.vc.VcPerformance$CountersByType

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.